mirror of
https://github.com/HeyPuter/puter.git
synced 2026-11-13 20:12:57 -10:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76ad6cbc2e | ||
|
|
9251bfa30b | ||
|
|
bf99c3c680 | ||
|
|
72de6e16b2 | ||
|
|
234994d3af |
No files matched your search
+1
-1
@@ -40,7 +40,7 @@ The following have already been reviewed and determined **not to be vulnerabilit
|
||||
- **SSRF via `secureFetch`.** Production routes outbound requests through an isolated proxy that has no access to internal/SSRF-sensitive resources.
|
||||
- **Attacks that depend on guessing an `appInstanceID` or app UID.** These are random 128-bit secret values and are not considered guessable.
|
||||
- **Apps invoking drivers, creating workers, or using KV.** Applications are intended to do this; worker permissions are scoped to the owning app. This is by design.
|
||||
- **App metadata or app user-count "leaks".** This information is currently public by design. This covers **private apps** too: private apps are an unreleased, admin-only beta for paid listings in the Puter App Center. Their `index_url` and other metadata are not secret, the flag is not user-settable, and no production app relies on it. Reports that a private app's metadata is reachable through a sibling route (taskbar items, `/get-launch-apps`, `index_url` drift on the hosting gate, etc.) are normalization work, not vulnerabilities.
|
||||
- **App metadata or app user-count "leaks".** This information is currently public by design. This covers **private apps** too: private apps are an unreleased, admin-only beta for paid listings in the Puter App Center. Their `index_url` and other metadata are not secret, the flag is not user-settable, and no production app relies on it. Reports that a private app's metadata is reachable through a sibling route (`/get-launch-apps`, `index_url` drift on the hosting gate, etc.) are normalization work, not vulnerabilities.
|
||||
- **Worker hot-reload driven by a share recipient.** Sharing a worker's source file with write access is how you let a collaborator change that worker's code and have it deploy live. The redeploy running as the file owner, with the owner's worker credentials, is the intended behavior of that grant — the owner chose to hand out write access to executable code. If you do not want that, do not share the source file with write access.
|
||||
- **Re-claiming a site or worker name its owner deleted.** A hosted site or worker name that its owner deletes is treated as abandoned by that owner. If one of their apps still points at it, whoever claims the name next serves that app's `index_url` and receives what the app is launched with, and a new app on the same origin may share the old one's identity. Guarding app identities against being recycled this way is ongoing hardening, not a security boundary, so new ways to re-claim an abandoned name, or to inherit an app's tokens or data through one, are not eligible.
|
||||
- **Share-mode downgrades that do not cascade.** Demoting a delegate does not retroactively revoke the grants that delegate already issued downstream. Shares are independent grants once issued, not a live-evaluated chain. Revoking the downstream grant is a separate, supported action.
|
||||
|
||||
+40
-10
@@ -257,8 +257,41 @@ describe('whoami extension — handleWhoami', () => {
|
||||
expect(body.created_ts).toBeGreaterThan(0);
|
||||
// `directories` is only sent to user actors — confirm it’s present.
|
||||
expect(body.directories).toBeDefined();
|
||||
// taskbar_items is only sent to user actors.
|
||||
expect(body).toHaveProperty('taskbar_items');
|
||||
});
|
||||
|
||||
it('leaves out taskbar items saved on the account', async () => {
|
||||
const user = await seedUser();
|
||||
await server.stores.user.update(user.id as number, {
|
||||
taskbar_items: JSON.stringify([{ type: 'app', name: 'editor' }]),
|
||||
});
|
||||
const { res, captured } = makeRes();
|
||||
|
||||
await runWithContext(
|
||||
{ actor: { user: { uuid: user.uuid, id: user.id as number } } },
|
||||
() => handleWhoami(makeReq(), res),
|
||||
);
|
||||
|
||||
expect(captured.body).not.toHaveProperty('taskbar_items');
|
||||
});
|
||||
|
||||
it('leaves out a desktop background saved on the account', async () => {
|
||||
const user = await seedUser();
|
||||
await server.stores.user.update(user.id as number, {
|
||||
desktop_bg_url: 'https://cdn.test/wallpaper.png',
|
||||
desktop_bg_color: '#000000',
|
||||
desktop_bg_fit: 'cover',
|
||||
});
|
||||
const { res, captured } = makeRes();
|
||||
|
||||
await runWithContext(
|
||||
{ actor: { user: { uuid: user.uuid, id: user.id as number } } },
|
||||
() => handleWhoami(makeReq(), res),
|
||||
);
|
||||
|
||||
const body = captured.body as Record<string, unknown>;
|
||||
expect(body).not.toHaveProperty('desktop_bg_url');
|
||||
expect(body).not.toHaveProperty('desktop_bg_color');
|
||||
expect(body).not.toHaveProperty('desktop_bg_fit');
|
||||
});
|
||||
|
||||
it('reports the SMS-to-card fallback only once a send has opened it', async () => {
|
||||
@@ -322,7 +355,7 @@ describe('whoami extension — handleWhoami', () => {
|
||||
expect(flags.payment_bypass).toBeUndefined();
|
||||
});
|
||||
|
||||
it('strips desktop_bg_*, created_ts and human_readable_age fields for app actors', async () => {
|
||||
it('strips created_ts and human_readable_age fields for app actors', async () => {
|
||||
const user = await seedUser();
|
||||
const { res, captured } = makeRes();
|
||||
|
||||
@@ -340,9 +373,6 @@ describe('whoami extension — handleWhoami', () => {
|
||||
expect(body.app_name).toBe('app-test-actor');
|
||||
// is_user_token is stripped for app actors.
|
||||
expect(body.is_user_token).toBeUndefined();
|
||||
expect(body.desktop_bg_url).toBeUndefined();
|
||||
expect(body.desktop_bg_color).toBeUndefined();
|
||||
expect(body.desktop_bg_fit).toBeUndefined();
|
||||
expect(body.human_readable_age).toBeUndefined();
|
||||
// Account age, in either form, is not exposed to apps.
|
||||
expect(body.created_ts).toBeUndefined();
|
||||
@@ -432,7 +462,7 @@ describe('whoami extension — handleWhoami', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('cannot add taskbar_items for an app actor, which core omits', async () => {
|
||||
it('cannot add directories for an app actor, which core omits', async () => {
|
||||
const user = await seedUser();
|
||||
const body = await whoamiWithListener(
|
||||
makeActor({
|
||||
@@ -440,11 +470,11 @@ describe('whoami extension — handleWhoami', () => {
|
||||
app: { uid: 'app-test-actor' },
|
||||
}),
|
||||
(_key, event) => {
|
||||
event.details.taskbar_items = [{ name: 'injected' }];
|
||||
event.details.directories = { '/injected': 'uuid' };
|
||||
},
|
||||
);
|
||||
|
||||
expect(body).not.toHaveProperty('taskbar_items');
|
||||
expect(body).not.toHaveProperty('directories');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -525,7 +555,7 @@ describe('whoami extension — handleWhoami', () => {
|
||||
expect(body.otp_secret).toBeUndefined();
|
||||
expect(body.signup_ip).toBeUndefined();
|
||||
// Not just absent as a key — the values must not appear anywhere
|
||||
// in the payload (nested under metadata, taskbar items, …).
|
||||
// in the payload (nested under metadata, …).
|
||||
const serialized = JSON.stringify(body);
|
||||
expect(serialized).not.toContain('5551234567');
|
||||
expect(serialized).not.toContain('fp_ABC123');
|
||||
|
||||
+2
-16
@@ -1,6 +1,5 @@
|
||||
import { Context } from '@heyputer/backend/src/core';
|
||||
import { extension } from '@heyputer/backend/src/extensions';
|
||||
import { APP_ICON_SIZES } from '@heyputer/backend/src/util/appIcon.js';
|
||||
import {
|
||||
buildUserDetails,
|
||||
scrubSensitive,
|
||||
@@ -54,19 +53,10 @@ export const handleWhoami = async (
|
||||
return;
|
||||
}
|
||||
|
||||
const rawIconSize =
|
||||
typeof req.query?.icon_size === 'string'
|
||||
? Number(req.query.icon_size)
|
||||
: undefined;
|
||||
const iconSize =
|
||||
rawIconSize !== undefined && APP_ICON_SIZES.includes(rawIconSize)
|
||||
? rawIconSize
|
||||
: undefined;
|
||||
|
||||
const details = await buildUserDetails(
|
||||
user,
|
||||
{ config: extension.config, clients, stores, services },
|
||||
{ isUser, iconSize, noIcons: !iconSize },
|
||||
{ isUser },
|
||||
);
|
||||
details.human_readable_age = user.timestamp
|
||||
? timeago.format(new Date(user.timestamp as string))
|
||||
@@ -81,9 +71,6 @@ export const handleWhoami = async (
|
||||
delete details.email;
|
||||
delete details.unconfirmed_email;
|
||||
}
|
||||
delete details.desktop_bg_url;
|
||||
delete details.desktop_bg_color;
|
||||
delete details.desktop_bg_fit;
|
||||
delete details.human_readable_age;
|
||||
delete details.created_ts;
|
||||
delete details.is_user_token;
|
||||
@@ -121,8 +108,7 @@ export const handleWhoami = async (
|
||||
}
|
||||
|
||||
const subscription = details.subscription as
|
||||
| { offering?: Record<string, unknown> }
|
||||
| undefined;
|
||||
{ offering?: Record<string, unknown> } | undefined;
|
||||
if (subscription?.offering) {
|
||||
delete subscription.offering.group;
|
||||
delete subscription.offering.benefits;
|
||||
|
||||
@@ -52,8 +52,8 @@ const APP_READ_LIMIT = {
|
||||
/**
|
||||
* Unauthenticated icon serving; no actor to key on, so the bucket is the
|
||||
* address — and an address is a NAT, a campus or a carrier gateway that can
|
||||
* hold hundreds of desktops. Each desktop boot pulls the taskbar's icons at
|
||||
* once, so a single burst from one network is already thousands of requests.
|
||||
* hold hundreds of desktops. Each dashboard load pulls its app icons at once,
|
||||
* so a single burst from one network is already thousands of requests.
|
||||
* Responses are publicly cacheable and usually a redirect, so the ceiling is
|
||||
* not protecting bandwidth; it is there so a client looping on a broken icon
|
||||
* can't spin unbounded.
|
||||
@@ -523,7 +523,7 @@ export class AppController extends PuterController {
|
||||
};
|
||||
|
||||
// Icons are <img src> targets from the GUI (root) AND resolved via
|
||||
// api_base_url in taskbar payloads. Register on both so either origin
|
||||
// api_base_url in app summaries. Register on both so either origin
|
||||
// works without a cross-subdomain redirect.
|
||||
router.get(
|
||||
'/app-icon/:app_uid',
|
||||
|
||||
@@ -1271,7 +1271,7 @@ describe('AppController app-icon rate limit', () => {
|
||||
it('sizes the icon bucket for a whole network rather than one desktop', () => {
|
||||
// Icons are unauthenticated `<img src>` targets, so the only key is
|
||||
// the address — which one NAT shares across every desktop behind it,
|
||||
// and each boot pulls the taskbar's icons in a burst.
|
||||
// and each dashboard load pulls its app icons in a burst.
|
||||
for (const path of ['/app-icon/:app_uid', '/app-icon/:app_uid/:size']) {
|
||||
const route = router.routes.find(
|
||||
(r) => r.method === 'get' && r.path === path,
|
||||
|
||||
@@ -1416,7 +1416,7 @@ describe('AuthController.handleLogin', () => {
|
||||
is_user_token: true,
|
||||
});
|
||||
expect(body.user.directories).toBeTypeOf('object');
|
||||
expect(Array.isArray(body.user.taskbar_items)).toBe(true);
|
||||
expect(body.user).not.toHaveProperty('taskbar_items');
|
||||
});
|
||||
|
||||
// A deleted row still answering from cache reaches the session INSERT.
|
||||
|
||||
@@ -25,18 +25,13 @@ const ALLOWED_SORT_BY = ['name', 'size', 'modified', 'type'];
|
||||
const ALLOWED_SORT_ORDER = ['asc', 'desc'];
|
||||
|
||||
/**
|
||||
* Desktop/UI preference routes.
|
||||
*
|
||||
* Two categories:
|
||||
*
|
||||
* - User-level: desktop background, taskbar items (UserStore)
|
||||
* - Folder-level: layout, sort_by/sort_order (fsentries table)
|
||||
* Folder view preference routes: layout and sort_by/sort_order, stored on the
|
||||
* folder's fsentries row.
|
||||
*/
|
||||
/**
|
||||
* Desktop preference writes — background, taskbar, layout, sort order. All four
|
||||
* persist to the user row on every call, and the GUI fires them on direct user
|
||||
* action, so a per-minute ceiling well above human speed is enough to catch a
|
||||
* stuck client.
|
||||
* Desktop preference writes — layout, sort order. Each persists on every call,
|
||||
* and the GUI fires them on direct user action, so a per-minute ceiling well
|
||||
* above human speed is enough to catch a stuck client.
|
||||
*/
|
||||
const PREFERENCE_WRITE_LIMIT = {
|
||||
scope: 'desktop-preference',
|
||||
@@ -47,89 +42,6 @@ const PREFERENCE_WRITE_LIMIT = {
|
||||
|
||||
export class DesktopController extends PuterController {
|
||||
registerRoutes(router) {
|
||||
// -- Desktop background --------------------------------------
|
||||
|
||||
router.post(
|
||||
'/set-desktop-bg',
|
||||
{
|
||||
subdomain: 'api',
|
||||
requireUserActor: true,
|
||||
allowFullAccessToken: true,
|
||||
rateLimit: PREFERENCE_WRITE_LIMIT,
|
||||
},
|
||||
async (req, res) => {
|
||||
const { url, color, fit } = req.body ?? {};
|
||||
|
||||
const patch = {};
|
||||
if (url !== undefined) {
|
||||
if (url !== null && typeof url !== 'string') {
|
||||
throw new HttpError(
|
||||
400,
|
||||
'`url` must be a string or null',
|
||||
{ legacyCode: 'bad_request' },
|
||||
);
|
||||
}
|
||||
patch.desktop_bg_url = url;
|
||||
}
|
||||
if (color !== undefined) {
|
||||
if (color !== null && typeof color !== 'string') {
|
||||
throw new HttpError(
|
||||
400,
|
||||
'`color` must be a string or null',
|
||||
{ legacyCode: 'bad_request' },
|
||||
);
|
||||
}
|
||||
patch.desktop_bg_color = color;
|
||||
}
|
||||
if (fit !== undefined) {
|
||||
if (fit !== null && typeof fit !== 'string') {
|
||||
throw new HttpError(
|
||||
400,
|
||||
'`fit` must be a string or null',
|
||||
{ legacyCode: 'bad_request' },
|
||||
);
|
||||
}
|
||||
patch.desktop_bg_fit = fit;
|
||||
}
|
||||
|
||||
if (Object.keys(patch).length === 0) {
|
||||
throw new HttpError(400, 'No fields provided', {
|
||||
legacyCode: 'bad_request',
|
||||
});
|
||||
}
|
||||
|
||||
await this.stores.user.update(req.actor.user.id, patch);
|
||||
res.json({});
|
||||
},
|
||||
);
|
||||
|
||||
// -- Taskbar items -------------------------------------------
|
||||
|
||||
router.post(
|
||||
'/update-taskbar-items',
|
||||
{
|
||||
subdomain: 'api',
|
||||
requireUserActor: true,
|
||||
allowFullAccessToken: true,
|
||||
rateLimit: PREFERENCE_WRITE_LIMIT,
|
||||
},
|
||||
async (req, res) => {
|
||||
const { items } = req.body ?? {};
|
||||
if (!Array.isArray(items)) {
|
||||
throw new HttpError(
|
||||
400,
|
||||
'Missing or invalid `items` array',
|
||||
{ legacyCode: 'bad_request' },
|
||||
);
|
||||
}
|
||||
|
||||
await this.stores.user.update(req.actor.user.id, {
|
||||
taskbar_items: JSON.stringify(items),
|
||||
});
|
||||
res.json({});
|
||||
},
|
||||
);
|
||||
|
||||
// -- Folder layout -------------------------------------------
|
||||
|
||||
router.post(
|
||||
|
||||
@@ -130,147 +130,17 @@ const callRoute = async (
|
||||
});
|
||||
};
|
||||
|
||||
// ── /set-desktop-bg ─────────────────────────────────────────────────
|
||||
|
||||
describe('DesktopController POST /set-desktop-bg', () => {
|
||||
it('persists url/color/fit on the user row', async () => {
|
||||
const { actor, userId } = await makeUser();
|
||||
const { res, captured } = makeRes();
|
||||
await callRoute(
|
||||
'post',
|
||||
'/set-desktop-bg',
|
||||
makeReq({
|
||||
body: {
|
||||
url: 'https://cdn.test/wallpaper.png',
|
||||
color: '#000000',
|
||||
fit: 'cover',
|
||||
},
|
||||
actor,
|
||||
}),
|
||||
res,
|
||||
);
|
||||
expect(captured.body).toEqual({});
|
||||
|
||||
const refreshed = await server.stores.user.getById(userId);
|
||||
expect(refreshed?.desktop_bg_url).toBe(
|
||||
'https://cdn.test/wallpaper.png',
|
||||
);
|
||||
expect(refreshed?.desktop_bg_color).toBe('#000000');
|
||||
expect(refreshed?.desktop_bg_fit).toBe('cover');
|
||||
describe('DesktopController routes', () => {
|
||||
it('has no desktop background route', () => {
|
||||
expect(
|
||||
router.routes.some((r) => r.path === '/set-desktop-bg'),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('persists only the supplied fields (partial update)', async () => {
|
||||
const { actor, userId } = await makeUser();
|
||||
const { res } = makeRes();
|
||||
await callRoute(
|
||||
'post',
|
||||
'/set-desktop-bg',
|
||||
makeReq({ body: { color: '#ffffff' }, actor }),
|
||||
res,
|
||||
);
|
||||
const refreshed = await server.stores.user.getById(userId);
|
||||
expect(refreshed?.desktop_bg_color).toBe('#ffffff');
|
||||
// Untouched fields remain at their column default.
|
||||
expect(refreshed?.desktop_bg_url).toBeFalsy();
|
||||
expect(refreshed?.desktop_bg_fit).toBeFalsy();
|
||||
});
|
||||
|
||||
it('passes through `null` to clear a field', async () => {
|
||||
const { actor, userId } = await makeUser();
|
||||
// First populate, then clear.
|
||||
const populate = makeRes();
|
||||
await callRoute(
|
||||
'post',
|
||||
'/set-desktop-bg',
|
||||
makeReq({
|
||||
body: { url: 'https://cdn.test/x.png' },
|
||||
actor,
|
||||
}),
|
||||
populate.res,
|
||||
);
|
||||
const before = await server.stores.user.getById(userId);
|
||||
expect(before?.desktop_bg_url).toBe('https://cdn.test/x.png');
|
||||
|
||||
const clear = makeRes();
|
||||
await callRoute(
|
||||
'post',
|
||||
'/set-desktop-bg',
|
||||
makeReq({ body: { url: null }, actor }),
|
||||
clear.res,
|
||||
);
|
||||
const after = await server.stores.user.getById(userId);
|
||||
expect(after?.desktop_bg_url).toBeNull();
|
||||
});
|
||||
|
||||
it('throws 400 when url is not a string or null', async () => {
|
||||
const { actor } = await makeUser();
|
||||
const { res } = makeRes();
|
||||
await expect(
|
||||
callRoute(
|
||||
'post',
|
||||
'/set-desktop-bg',
|
||||
makeReq({ body: { url: 123 }, actor }),
|
||||
res,
|
||||
),
|
||||
).rejects.toMatchObject({ statusCode: 400 });
|
||||
});
|
||||
|
||||
it('throws 400 when no fields are provided', async () => {
|
||||
const { actor } = await makeUser();
|
||||
const { res } = makeRes();
|
||||
await expect(
|
||||
callRoute(
|
||||
'post',
|
||||
'/set-desktop-bg',
|
||||
makeReq({ body: {}, actor }),
|
||||
res,
|
||||
),
|
||||
).rejects.toMatchObject({ statusCode: 400 });
|
||||
});
|
||||
});
|
||||
|
||||
// ── /update-taskbar-items ───────────────────────────────────────────
|
||||
|
||||
describe('DesktopController POST /update-taskbar-items', () => {
|
||||
it('persists items as a JSON-encoded string', async () => {
|
||||
const { actor, userId } = await makeUser();
|
||||
const items = [{ name: 'editor' }, { name: 'browser' }];
|
||||
const { res } = makeRes();
|
||||
await callRoute(
|
||||
'post',
|
||||
'/update-taskbar-items',
|
||||
makeReq({ body: { items }, actor }),
|
||||
res,
|
||||
);
|
||||
|
||||
const refreshed = await server.stores.user.getById(userId);
|
||||
expect(refreshed?.taskbar_items).toBe(JSON.stringify(items));
|
||||
});
|
||||
|
||||
it('throws 400 when items is missing', async () => {
|
||||
const { actor } = await makeUser();
|
||||
const { res } = makeRes();
|
||||
await expect(
|
||||
callRoute(
|
||||
'post',
|
||||
'/update-taskbar-items',
|
||||
makeReq({ body: {}, actor }),
|
||||
res,
|
||||
),
|
||||
).rejects.toMatchObject({ statusCode: 400 });
|
||||
});
|
||||
|
||||
it('throws 400 when items is not an array', async () => {
|
||||
const { actor } = await makeUser();
|
||||
const { res } = makeRes();
|
||||
await expect(
|
||||
callRoute(
|
||||
'post',
|
||||
'/update-taskbar-items',
|
||||
makeReq({ body: { items: 'oops' }, actor }),
|
||||
res,
|
||||
),
|
||||
).rejects.toMatchObject({ statusCode: 400 });
|
||||
it('has no taskbar items route', () => {
|
||||
expect(
|
||||
router.routes.some((r) => r.path === '/update-taskbar-items'),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5111,9 +5111,8 @@ describe('LegacyFSController.updateFsentryThumbnail', () => {
|
||||
// ── GET /get-launch-apps ────────────────────────────────────────────
|
||||
//
|
||||
// `recent` is a launch-metadata producer: it returns `index_url` for
|
||||
// apps the user has opened before. The taskbar happens to launch these
|
||||
// by name (so AppDriver's hosted-backing guard applies), but the field
|
||||
// is served regardless, so the guard is applied here too.
|
||||
// apps the user has opened before. The field is served whoever reads it,
|
||||
// so AppDriver's hosted-backing guard is applied here too.
|
||||
|
||||
describe('LegacyFSController GET /get-launch-apps', () => {
|
||||
const hostedUrl = (sub: string) => `https://${sub}.site.puter.localhost/`;
|
||||
|
||||
@@ -401,11 +401,9 @@ export class LegacyFSController extends PuterController {
|
||||
.filter(Boolean);
|
||||
|
||||
// Don't hand out an index_url whose puter-hosted backing is
|
||||
// gone or reclaimed. The taskbar launches recents by name (so
|
||||
// AppDriver's guard applies), but this list is a
|
||||
// launch-metadata producer like any other — a future consumer
|
||||
// reading index_url straight off it shouldn't inherit a stale
|
||||
// origin.
|
||||
// gone or reclaimed. This list is a launch-metadata producer
|
||||
// like any other — a consumer reading index_url straight off
|
||||
// it shouldn't inherit a stale origin.
|
||||
let backingGoneFlags: boolean[];
|
||||
try {
|
||||
backingGoneFlags =
|
||||
@@ -950,9 +948,7 @@ export class LegacyFSController extends PuterController {
|
||||
// Trash, and `null`/`{}` when restoring. See
|
||||
// `src/gui/src/helpers.js` → `window.move_items`.
|
||||
newMetadata: (body.new_metadata ?? undefined) as
|
||||
| Record<string, unknown>
|
||||
| null
|
||||
| undefined,
|
||||
Record<string, unknown> | null | undefined,
|
||||
});
|
||||
const overwrittenEntry = replacedByMove(overwriteTarget, moved, name);
|
||||
const oldPath = source.path;
|
||||
@@ -1426,8 +1422,7 @@ export class LegacyFSController extends PuterController {
|
||||
}
|
||||
|
||||
type SignedOrEmpty =
|
||||
| (SignedFile & { path?: string })
|
||||
| Record<string, never>;
|
||||
(SignedFile & { path?: string }) | Record<string, never>;
|
||||
const result: { signatures: SignedOrEmpty[]; token?: string } = {
|
||||
signatures: [],
|
||||
};
|
||||
@@ -2054,10 +2049,7 @@ export class LegacyFSController extends PuterController {
|
||||
const subjectRef = body.subject;
|
||||
const appRef = body.app;
|
||||
const mode = (getString(body, 'mode') ?? 'read') as
|
||||
| 'see'
|
||||
| 'list'
|
||||
| 'read'
|
||||
| 'write';
|
||||
'see' | 'list' | 'read' | 'write';
|
||||
if (!subjectRef || !appRef)
|
||||
throw new HttpError(400, '`subject` and `app` are required', {
|
||||
legacyCode: 'bad_request',
|
||||
|
||||
@@ -2864,7 +2864,7 @@ export class AuthService extends PuterService {
|
||||
|
||||
#actorUserFromRow(user: UserRow) {
|
||||
// Strip the password hash; pass everything else through so callers
|
||||
// can read metadata, desktop_bg_*, otp_enabled, etc. without
|
||||
// can read metadata, otp_enabled, etc. without
|
||||
// re-fetching the user. Mirrors what /whoami exposes off of UserRow.
|
||||
const { password: _password, ...rest } = user;
|
||||
return {
|
||||
|
||||
@@ -517,8 +517,6 @@ export class PuterHomepageService extends PuterService {
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
${bundled ? `<link rel="stylesheet" href="${guiCss}">` : ''}
|
||||
|
||||
<link rel="preload" as="image" href="https://puter-assets.b-cdn.net/wallpaper.webp">
|
||||
|
||||
<script>
|
||||
if ( ! window.service_script ) {
|
||||
window.service_script_api_promise = (() => {
|
||||
|
||||
@@ -159,8 +159,8 @@ export interface AppSummaryOptions {
|
||||
|
||||
/**
|
||||
* The summary the desktop launches from directly, without re-reading the app
|
||||
* (suggested, recommended, recent and taskbar apps), so every launch-relevant
|
||||
* flag belongs here.
|
||||
* (suggested, recommended and recent apps), so every launch-relevant flag
|
||||
* belongs here.
|
||||
*/
|
||||
export function toAppSummary(
|
||||
app: AppRow,
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
|
||||
import { PuterServer } from '../server.ts';
|
||||
import { setupTestServer } from '../testUtil.ts';
|
||||
import { getTaskbarItems } from './taskbarItems.ts';
|
||||
|
||||
describe('getTaskbarItems', () => {
|
||||
let server: PuterServer;
|
||||
let deps: Parameters<typeof getTaskbarItems>[1];
|
||||
|
||||
beforeAll(async () => {
|
||||
server = await setupTestServer();
|
||||
deps = {
|
||||
clients: server.clients,
|
||||
stores: server.stores,
|
||||
apiBaseUrl: 'https://api.puter.com',
|
||||
config: { static_hosting_domain: 'puter.site' },
|
||||
} as unknown as Parameters<typeof getTaskbarItems>[1];
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await server?.shutdown();
|
||||
});
|
||||
|
||||
const makeUser = async (taskbarItems?: unknown) => {
|
||||
const username = `tb-${Math.random().toString(36).slice(2, 10)}`;
|
||||
const created = await server.stores.user.create({
|
||||
username,
|
||||
uuid: uuidv4(),
|
||||
password: null,
|
||||
email: `${username}@test.local`,
|
||||
} as never);
|
||||
if (taskbarItems !== undefined) {
|
||||
await server.stores.user.update(created.id, {
|
||||
taskbar_items:
|
||||
typeof taskbarItems === 'string'
|
||||
? taskbarItems
|
||||
: JSON.stringify(taskbarItems),
|
||||
});
|
||||
}
|
||||
return (await server.stores.user.getById(created.id))!;
|
||||
};
|
||||
|
||||
const makeApp = async (ownerUserId: number, overrides = {}) => {
|
||||
const name = `tb-app-${Math.random().toString(36).slice(2, 10)}`;
|
||||
return server.stores.app.create(
|
||||
{
|
||||
name,
|
||||
title: `Title ${name}`,
|
||||
index_url: `https://${name}.example.com/`,
|
||||
description: 'a test app',
|
||||
...overrides,
|
||||
},
|
||||
{ ownerUserId },
|
||||
);
|
||||
};
|
||||
|
||||
it('seeds the default taskbar for a user that has none, and persists it', async () => {
|
||||
const user = await makeUser();
|
||||
expect(user.taskbar_items).toBeFalsy();
|
||||
|
||||
await getTaskbarItems(user as never, deps);
|
||||
|
||||
const [row] = await server.clients.db.read(
|
||||
'SELECT `taskbar_items` FROM `user` WHERE `id` = ?',
|
||||
[user.id],
|
||||
);
|
||||
const stored = JSON.parse(String(row.taskbar_items));
|
||||
expect(stored.map((e: { name: string }) => e.name)).toContain(
|
||||
'app-center',
|
||||
);
|
||||
// The cached row must not keep serving the pre-seed value.
|
||||
expect(
|
||||
(await server.stores.user.getById(user.id))?.taskbar_items,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('resolves entries by name, uid and id', async () => {
|
||||
const owner = await makeUser([]);
|
||||
const byName = await makeApp(owner.id);
|
||||
const byUid = await makeApp(owner.id);
|
||||
const byId = await makeApp(owner.id);
|
||||
|
||||
const user = await makeUser([
|
||||
{ name: byName.name, type: 'app' },
|
||||
{ uid: byUid.uid, type: 'app' },
|
||||
{ id: byId.id, type: 'app' },
|
||||
]);
|
||||
|
||||
const items = await getTaskbarItems(user as never, deps);
|
||||
expect(items.map((i) => i.uid)).toEqual([
|
||||
byName.uid,
|
||||
byUid.uid,
|
||||
byId.uid,
|
||||
]);
|
||||
expect(items[0]).toMatchObject({
|
||||
uuid: byName.uid,
|
||||
name: byName.name,
|
||||
title: byName.title,
|
||||
godmode: false,
|
||||
maximize_on_start: false,
|
||||
description: 'a test app',
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves the whole taskbar with one batched lookup per key, not one per entry', async () => {
|
||||
const owner = await makeUser([]);
|
||||
const apps = await Promise.all([0, 1, 2].map(() => makeApp(owner.id)));
|
||||
const byUid = await makeApp(owner.id);
|
||||
const user = await makeUser([
|
||||
...apps.map((app) => ({ name: app.name, type: 'app' })),
|
||||
{ uid: byUid.uid, type: 'app' },
|
||||
]);
|
||||
const appStore = server.stores.app;
|
||||
const spies = {
|
||||
getByName: vi.spyOn(appStore, 'getByName'),
|
||||
getByUid: vi.spyOn(appStore, 'getByUid'),
|
||||
getById: vi.spyOn(appStore, 'getById'),
|
||||
getByNames: vi.spyOn(appStore, 'getByNames'),
|
||||
getByUids: vi.spyOn(appStore, 'getByUids'),
|
||||
};
|
||||
try {
|
||||
const items = await getTaskbarItems(user as never, deps);
|
||||
|
||||
expect(items.map((i) => i.uid)).toEqual([
|
||||
...apps.map((app) => app.uid),
|
||||
byUid.uid,
|
||||
]);
|
||||
expect(spies.getByName).not.toHaveBeenCalled();
|
||||
expect(spies.getByUid).not.toHaveBeenCalled();
|
||||
expect(spies.getById).not.toHaveBeenCalled();
|
||||
expect(spies.getByNames).toHaveBeenCalledTimes(1);
|
||||
expect(spies.getByUids).toHaveBeenCalledTimes(1);
|
||||
} finally {
|
||||
for (const spy of Object.values(spies)) spy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('skips non-app entries, the explorer pin, and unresolvable apps', async () => {
|
||||
const owner = await makeUser([]);
|
||||
const app = await makeApp(owner.id);
|
||||
const user = await makeUser([
|
||||
{ name: 'explorer', type: 'app' },
|
||||
{ name: 'nothing-here', type: 'app' },
|
||||
{ name: app.name, type: 'separator' },
|
||||
{ type: 'app' },
|
||||
{ name: app.name, type: 'app' },
|
||||
]);
|
||||
|
||||
const items = await getTaskbarItems(user as never, deps);
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0].name).toBe(app.name);
|
||||
});
|
||||
|
||||
it('falls back to an empty taskbar when the stored JSON is corrupt', async () => {
|
||||
const user = await makeUser('{not json');
|
||||
expect(await getTaskbarItems(user as never, deps)).toEqual([]);
|
||||
});
|
||||
|
||||
it('accepts an already-parsed taskbar array on the user row', async () => {
|
||||
const owner = await makeUser([]);
|
||||
const app = await makeApp(owner.id);
|
||||
const user = await makeUser([]);
|
||||
|
||||
const items = await getTaskbarItems(
|
||||
{ ...user, taskbar_items: [{ name: app.name, type: 'app' }] },
|
||||
deps,
|
||||
);
|
||||
expect(items.map((i) => i.name)).toEqual([app.name]);
|
||||
});
|
||||
|
||||
it('builds a sized icon URL by default and drops the icon on request', async () => {
|
||||
const owner = await makeUser([]);
|
||||
const app = await makeApp(owner.id);
|
||||
const user = await makeUser([{ name: app.name, type: 'app' }]);
|
||||
|
||||
const withIcon = await getTaskbarItems(user as never, deps, {
|
||||
iconSize: 64,
|
||||
});
|
||||
expect(withIcon[0].icon).toBe(
|
||||
`https://api.puter.com/app-icon/${app.uid}/64`,
|
||||
);
|
||||
|
||||
const withoutIcon = await getTaskbarItems(user as never, deps, {
|
||||
noIcons: true,
|
||||
});
|
||||
expect('icon' in withoutIcon[0]).toBe(false);
|
||||
});
|
||||
|
||||
it('pairs the icon URL with the direct subdomain URL when one exists', async () => {
|
||||
const owner = await makeUser([]);
|
||||
// An http(s) icon column is what the resize pipeline leaves behind, so
|
||||
// the sized PNG is on the subdomain.
|
||||
const app = await makeApp(owner.id, {
|
||||
icon: 'https://api.puter.com/app-icon/x',
|
||||
});
|
||||
const user = await makeUser([{ name: app.name, type: 'app' }]);
|
||||
|
||||
const items = await getTaskbarItems(user as never, deps, {
|
||||
iconSize: 64,
|
||||
});
|
||||
expect(items[0].icon).toBe(
|
||||
`https://api.puter.com/app-icon/${app.uid}/64`,
|
||||
);
|
||||
expect(items[0].iconCdnUrl).toBe(
|
||||
`https://puter-app-icons.puter.site/${app.uid}-64.png`,
|
||||
);
|
||||
});
|
||||
|
||||
it('reports no subdomain URL for an app whose icon was never generated', async () => {
|
||||
const owner = await makeUser([]);
|
||||
const app = await makeApp(owner.id);
|
||||
const user = await makeUser([{ name: app.name, type: 'app' }]);
|
||||
|
||||
const items = await getTaskbarItems(user as never, deps, {
|
||||
iconSize: 64,
|
||||
});
|
||||
expect(items[0].iconCdnUrl).toBeNull();
|
||||
});
|
||||
|
||||
it('falls back to the raw icon column when no API base URL is configured', async () => {
|
||||
const owner = await makeUser([]);
|
||||
const app = await makeApp(owner.id, {
|
||||
icon: 'https://cdn.example.com/i.png',
|
||||
});
|
||||
const user = await makeUser([{ name: app.name, type: 'app' }]);
|
||||
|
||||
const items = await getTaskbarItems(
|
||||
user as never,
|
||||
{
|
||||
...deps,
|
||||
apiBaseUrl: undefined,
|
||||
} as never,
|
||||
);
|
||||
expect(items[0].icon).toBe('https://cdn.example.com/i.png');
|
||||
});
|
||||
|
||||
it('reports null for an app with no icon and no API base URL', async () => {
|
||||
const owner = await makeUser([]);
|
||||
const app = await makeApp(owner.id);
|
||||
const user = await makeUser([{ name: app.name, type: 'app' }]);
|
||||
|
||||
const items = await getTaskbarItems(
|
||||
user as never,
|
||||
{
|
||||
...deps,
|
||||
apiBaseUrl: undefined,
|
||||
} as never,
|
||||
);
|
||||
expect(items[0].icon).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type { AppIconHostConfig } from './appIcon.js';
|
||||
import { toAppSummary, type AppRow } from './appView.js';
|
||||
|
||||
interface TaskbarEntry {
|
||||
name?: string;
|
||||
id?: number;
|
||||
uid?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
interface TaskbarOptions {
|
||||
iconSize?: number;
|
||||
noIcons?: boolean;
|
||||
}
|
||||
|
||||
interface TaskbarDeps {
|
||||
apiBaseUrl?: string;
|
||||
config?: AppIconHostConfig;
|
||||
stores: {
|
||||
app: {
|
||||
getByNames: (names: string[]) => Promise<Map<string, AppRow>>;
|
||||
getByUids: (uids: string[]) => Promise<Map<string, AppRow>>;
|
||||
getByIds: (ids: number[]) => Promise<Map<number, AppRow>>;
|
||||
};
|
||||
user: {
|
||||
update: (
|
||||
id: number,
|
||||
patch: Record<string, unknown>,
|
||||
) => Promise<unknown>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const DEFAULT_TASKBAR_ITEMS: TaskbarEntry[] = [
|
||||
{ name: 'app-center', type: 'app' },
|
||||
{ name: 'builder', type: 'app' },
|
||||
{ name: 'dev-center', type: 'app' },
|
||||
{ name: 'editor', type: 'app' },
|
||||
{ name: 'code', type: 'app' },
|
||||
{ name: 'camera', type: 'app' },
|
||||
{ name: 'recorder', type: 'app' },
|
||||
{ name: 'browser', type: 'app' },
|
||||
];
|
||||
|
||||
export async function getTaskbarItems(
|
||||
user: Record<string, unknown>,
|
||||
deps: TaskbarDeps,
|
||||
options: TaskbarOptions = {},
|
||||
): Promise<Array<Record<string, unknown>>> {
|
||||
let raw: TaskbarEntry[];
|
||||
|
||||
if (!user.taskbar_items) {
|
||||
raw = DEFAULT_TASKBAR_ITEMS;
|
||||
await deps.stores.user.update(user.id as number, {
|
||||
taskbar_items: JSON.stringify(raw),
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
raw =
|
||||
typeof user.taskbar_items === 'string'
|
||||
? JSON.parse(user.taskbar_items as string)
|
||||
: (user.taskbar_items as TaskbarEntry[]);
|
||||
} catch {
|
||||
raw = [];
|
||||
}
|
||||
}
|
||||
|
||||
const entries = raw.filter(
|
||||
(entry) => entry.type === 'app' && entry.name !== 'explorer',
|
||||
);
|
||||
const [byName, byUid, byId] = await Promise.all([
|
||||
deps.stores.app.getByNames(
|
||||
entries.flatMap((entry) => (entry.name ? [entry.name] : [])),
|
||||
),
|
||||
deps.stores.app.getByUids(
|
||||
entries.flatMap((entry) =>
|
||||
!entry.name && entry.uid ? [entry.uid] : [],
|
||||
),
|
||||
),
|
||||
deps.stores.app.getByIds(
|
||||
entries.flatMap((entry) =>
|
||||
!entry.name && !entry.uid && entry.id ? [entry.id] : [],
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
const items: Array<Record<string, unknown>> = [];
|
||||
for (const entry of entries) {
|
||||
const app = entry.name
|
||||
? byName.get(entry.name)
|
||||
: entry.uid
|
||||
? byUid.get(entry.uid)
|
||||
: entry.id
|
||||
? byId.get(entry.id)
|
||||
: undefined;
|
||||
if (!app) continue;
|
||||
|
||||
const item: Record<string, unknown> = {
|
||||
uid: app.uid,
|
||||
...toAppSummary(app, {
|
||||
apiBaseUrl: deps.apiBaseUrl,
|
||||
config: deps.config,
|
||||
iconSize: options.iconSize,
|
||||
}),
|
||||
description: app.description,
|
||||
};
|
||||
if (options.noIcons) {
|
||||
delete item.icon;
|
||||
delete item.iconCdnUrl;
|
||||
}
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
@@ -85,7 +85,17 @@ describe('buildUserDetails', () => {
|
||||
[`/${user.username}/Desktop`]: user.desktop_uuid,
|
||||
[`/${user.username}/Trash`]: user.trash_uuid,
|
||||
});
|
||||
expect(Array.isArray(details.taskbar_items)).toBe(true);
|
||||
});
|
||||
|
||||
it('leaves out taskbar items saved on the account', async () => {
|
||||
const user = {
|
||||
...(await makeUser()),
|
||||
taskbar_items: JSON.stringify([{ type: 'app', name: 'editor' }]),
|
||||
};
|
||||
const details = await buildUserDetails(user, deps(), {
|
||||
isUser: true,
|
||||
});
|
||||
expect(details).not.toHaveProperty('taskbar_items');
|
||||
});
|
||||
|
||||
it('leaves the desktop-only parts out for an app actor', async () => {
|
||||
@@ -93,7 +103,6 @@ describe('buildUserDetails', () => {
|
||||
const details = await buildUserDetails(user, deps(), {
|
||||
isUser: false,
|
||||
});
|
||||
expect(details).not.toHaveProperty('taskbar_items');
|
||||
expect(details).not.toHaveProperty('directories');
|
||||
expect(details.card_fallback_available).toBe(false);
|
||||
});
|
||||
@@ -124,7 +133,7 @@ describe('buildUserDetails lookups', () => {
|
||||
return keys.slice(keys.indexOf('hasDevAccountAccess') + 1);
|
||||
};
|
||||
|
||||
it('reads the linked provider, taskbar and seat together, listed in a fixed order', async () => {
|
||||
it('reads the linked provider and seat together, listed in a fixed order', async () => {
|
||||
const user = {
|
||||
...(await makeUser()),
|
||||
password: null,
|
||||
@@ -162,7 +171,6 @@ describe('buildUserDetails lookups', () => {
|
||||
);
|
||||
expect(tailKeys(details)).toEqual([
|
||||
'oidc_revalidate_url',
|
||||
'taskbar_items',
|
||||
'directories',
|
||||
'team',
|
||||
'last_activity_ts',
|
||||
@@ -177,7 +185,6 @@ describe('buildUserDetails lookups', () => {
|
||||
const user = {
|
||||
...(await makeUser()),
|
||||
password: null,
|
||||
taskbar_items: JSON.stringify([{ type: 'app', name: 'editor' }]),
|
||||
last_activity_ts: '2026-01-02 03:04:05',
|
||||
};
|
||||
const events: string[] = [];
|
||||
@@ -199,15 +206,6 @@ describe('buildUserDetails lookups', () => {
|
||||
},
|
||||
},
|
||||
stores: Object.assign(Object.create(d.stores), {
|
||||
app: {
|
||||
getByNames: () =>
|
||||
timed(events, 'taskbar', 20, () => {
|
||||
throw new Error('taskbar down');
|
||||
}),
|
||||
getByUids: async () => new Map(),
|
||||
getByIds: async () => new Map(),
|
||||
},
|
||||
// Fails first, but is reported after the taskbar.
|
||||
team: {
|
||||
getOrgSeat: () =>
|
||||
timed(events, 'seat', 5, () => {
|
||||
@@ -219,18 +217,12 @@ describe('buildUserDetails lookups', () => {
|
||||
{ isUser: true },
|
||||
);
|
||||
expect(warn.mock.calls).toEqual([
|
||||
['[user-details] taskbar_items failed:', expect.any(Error)],
|
||||
['[user-details] team lookup failed:', expect.any(Error)],
|
||||
]);
|
||||
} finally {
|
||||
warn.mockRestore();
|
||||
}
|
||||
expect(tailKeys(details)).toEqual([
|
||||
'taskbar_items',
|
||||
'directories',
|
||||
'last_activity_ts',
|
||||
]);
|
||||
expect(details.taskbar_items).toEqual([]);
|
||||
expect(tailKeys(details)).toEqual(['directories', 'last_activity_ts']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -27,18 +27,12 @@ import {
|
||||
isCardFallbackEligible,
|
||||
type CardFallbackClients,
|
||||
} from './cardFallback.js';
|
||||
import { getTaskbarItems } from './taskbarItems.js';
|
||||
import { isEmailGateLast } from './verificationOrder.js';
|
||||
|
||||
type TaskbarDeps = Parameters<typeof getTaskbarItems>[1];
|
||||
|
||||
export interface UserDetailsDeps {
|
||||
config: IConfig;
|
||||
// `db.write` backs the taskbar's first-read default row.
|
||||
clients: CardFallbackClients & {
|
||||
db: { write: (query: string, params?: unknown[]) => Promise<unknown> };
|
||||
};
|
||||
stores: TaskbarDeps['stores'] & {
|
||||
clients: CardFallbackClients;
|
||||
stores: {
|
||||
kv: Pick<SystemKVStore, 'get'>;
|
||||
team?: Pick<TeamStoreContract, 'getOrgSeat'>;
|
||||
};
|
||||
@@ -78,8 +72,8 @@ const SENSITIVE_KEYS: ReadonlySet<string> = new Set([
|
||||
]);
|
||||
|
||||
/**
|
||||
* Delete every sensitive key at any depth (`metadata` and `taskbar_items` are
|
||||
* nested). Depth-limited and cycle-safe. Runs last over a whole payload.
|
||||
* Delete every sensitive key at any depth (`metadata` is nested). Depth-limited
|
||||
* and cycle-safe. Runs last over a whole payload.
|
||||
*/
|
||||
export const scrubSensitive = (
|
||||
value: unknown,
|
||||
@@ -124,7 +118,7 @@ export const toUnixSeconds = (value: unknown): number | undefined => {
|
||||
export async function buildUserDetails(
|
||||
user: UserRow,
|
||||
deps: UserDetailsDeps,
|
||||
opts: { isUser: boolean; iconSize?: number; noIcons?: boolean },
|
||||
opts: { isUser: boolean },
|
||||
): Promise<Record<string, unknown>> {
|
||||
const { config, clients, stores, services } = deps;
|
||||
const { isUser } = opts;
|
||||
@@ -170,9 +164,6 @@ export async function buildUserDetails(
|
||||
cardFallbackDepsFrom(clients),
|
||||
)
|
||||
: false,
|
||||
desktop_bg_url: user.desktop_bg_url,
|
||||
desktop_bg_color: user.desktop_bg_color,
|
||||
desktop_bg_fit: user.desktop_bg_fit,
|
||||
is_temp: user.password === null && user.email === null,
|
||||
is_user_token: true,
|
||||
// Null until the account asks for a code; never minted here.
|
||||
@@ -185,14 +176,6 @@ export async function buildUserDetails(
|
||||
hasDevAccountAccess: !!user.metadata?.hasDevAccountAccess,
|
||||
};
|
||||
|
||||
// Not an inline literal, so a `TaskbarDeps` without `clients` still
|
||||
// accepts it.
|
||||
const taskbarDeps = {
|
||||
clients,
|
||||
stores,
|
||||
apiBaseUrl: String(config.api_base_url ?? ''),
|
||||
config,
|
||||
};
|
||||
const linkedProvider = async () => {
|
||||
if (!oidcOnly) return null;
|
||||
try {
|
||||
@@ -202,19 +185,6 @@ export async function buildUserDetails(
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const taskbarItems = async () => {
|
||||
if (!isUser) return null;
|
||||
try {
|
||||
return {
|
||||
items: await getTaskbarItems(user, taskbarDeps, {
|
||||
iconSize: opts.iconSize,
|
||||
noIcons: opts.noIcons,
|
||||
}),
|
||||
};
|
||||
} catch (error) {
|
||||
return { error };
|
||||
}
|
||||
};
|
||||
// The team that pays for the account; every seat restriction keys on it.
|
||||
const orgSeat = async () => {
|
||||
if (!isUser || config.teams_enabled !== true) return null;
|
||||
@@ -226,26 +196,14 @@ export async function buildUserDetails(
|
||||
};
|
||||
// Independent and each best-effort, so read together; the results land
|
||||
// below in the order the payload has always listed them.
|
||||
const [provider, taskbar, team] = await Promise.all([
|
||||
linkedProvider(),
|
||||
taskbarItems(),
|
||||
orgSeat(),
|
||||
]);
|
||||
const [provider, team] = await Promise.all([linkedProvider(), orgSeat()]);
|
||||
|
||||
if (provider) {
|
||||
const origin = (config.origin ?? '').replace(/\/$/, '');
|
||||
details.oidc_revalidate_url = `${origin}/auth/oidc/${provider}/start?flow=revalidate&user_uuid=${encodeURIComponent(user.uuid)}`;
|
||||
}
|
||||
|
||||
if (taskbar) {
|
||||
// Best-effort: the account still works without them.
|
||||
if ('error' in taskbar) {
|
||||
console.warn('[user-details] taskbar_items failed:', taskbar.error);
|
||||
details.taskbar_items = [];
|
||||
} else {
|
||||
details.taskbar_items = taskbar.items;
|
||||
}
|
||||
|
||||
if (isUser) {
|
||||
const directories: Record<string, unknown> = {};
|
||||
const nameToProp: Record<string, string> = {
|
||||
desktop_uuid: `/${user.username}/Desktop`,
|
||||
|
||||
@@ -27,7 +27,7 @@ A set of key/value pairs that configure the window.
|
||||
| `is_resizable` | `Boolean` | If set to `true`, the user can resize the window. |
|
||||
| `has_head` | `Boolean` | If set to `true`, the window has a head which contains the icon and the close, minimize, and maximize buttons. |
|
||||
| `disable_parent_window` | `Boolean` | If set to `true`, the parent window is blocked until the current window is closed. |
|
||||
| `show_in_taskbar` | `Boolean` | If set to `true`, the window is represented in the taskbar. |
|
||||
| `show_in_taskbar` | `Boolean` | Deprecated. It has no effect. |
|
||||
|
||||
## Return value
|
||||
A `Promise` that resolves to a window handle object with an `id` (String) property identifying the created window. This `id` can be passed as the `window_id` argument to the `setWindow*` methods.
|
||||
@@ -50,7 +50,6 @@ A `Promise` that resolves to a window handle object with an `id` (String) proper
|
||||
is_resizable: false,
|
||||
has_head: true,
|
||||
center: true,
|
||||
show_in_taskbar: false,
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -36,12 +36,11 @@ Without `background`, Puter creates and shows the window before the app's own co
|
||||
runs, so a service app cannot avoid briefly appearing on screen.
|
||||
|
||||
A background instance stays private to your app for as long as it is hidden: it has no
|
||||
taskbar item and no running mark on its icon, and opening the app from the
|
||||
taskbar or from Puter's app list starts a separate, ordinary instance for the
|
||||
user rather than handing them the one you are talking to. It can show itself at
|
||||
any time with [`puter.ui.showWindow()`](/UI/showWindow), and from that moment it
|
||||
is an ordinary window. It takes its place in the taskbar, and the user can
|
||||
return to it, hide it, or close it like any other.
|
||||
running mark on its icon, and opening the app from Puter's app list starts a
|
||||
separate, ordinary instance for the user rather than handing them the one you
|
||||
are talking to. It can show itself at any time with
|
||||
[`puter.ui.showWindow()`](/UI/showWindow), and from that moment it is an
|
||||
ordinary window that the user can return to, hide, or close like any other.
|
||||
|
||||
A background app closes when the app that launched it closes: it was launched to
|
||||
serve that app, and the user never saw it. Once it has shown itself it keeps
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
## Credits
|
||||
|
||||
The default wallpaper is created by [Milad Fakurian](https://unsplash.com/photos/blue-orange-and-yellow-wallpaper-E8Ufcyxz514) and published on [Unsplash](https://unsplash.com/).
|
||||
|
||||
Icons by [Papirus](https://github.com/PapirusDevelopmentTeam/papirus-icon-theme) under GPL-3.0 license.
|
||||
|
||||
Icons by [Iconoir](https://iconoir.com/) under MIT license.
|
||||
|
||||
+4
-5
@@ -484,7 +484,6 @@ const ipc_listener = async (event, handled) => {
|
||||
is_resizable: event.data.options.is_resizable,
|
||||
has_head: event.data.options.has_head,
|
||||
center: event.data.options.center,
|
||||
show_in_taskbar: event.data.options.show_in_taskbar,
|
||||
iframe_srcdoc: event.data.options.content,
|
||||
parent_uuid: event.data.appInstanceID,
|
||||
});
|
||||
@@ -1149,9 +1148,9 @@ const ipc_listener = async (event, handled) => {
|
||||
if ( event.data.y !== undefined ) {
|
||||
event.data.y = parseFloat(event.data.y);
|
||||
// we don't want the window to go off the top edge of the screen
|
||||
if ( event.data.y < window.taskbar_height )
|
||||
if ( event.data.y < 0 )
|
||||
{
|
||||
event.data.y = window.taskbar_height;
|
||||
event.data.y = 0;
|
||||
}
|
||||
// we don't want the window to go off the bottom edge of the screen
|
||||
if ( event.data.y > window.innerHeight - 100 )
|
||||
@@ -1217,9 +1216,9 @@ const ipc_listener = async (event, handled) => {
|
||||
if ( event.data.y !== undefined ) {
|
||||
event.data.y = parseFloat(event.data.y);
|
||||
// we don't want the window to go off the top edge of the screen
|
||||
if ( event.data.y < window.taskbar_height )
|
||||
if ( event.data.y < 0 )
|
||||
{
|
||||
event.data.y = window.taskbar_height;
|
||||
event.data.y = 0;
|
||||
}
|
||||
// we don't want the window to go off the bottom edge of the screen
|
||||
if ( event.data.y > window.innerHeight - 100 )
|
||||
|
||||
@@ -75,8 +75,7 @@ async function UIDashboard (options) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
options = options ?? {};
|
||||
|
||||
// Mark dashboard mode on <body> so window chrome can adapt — e.g. app
|
||||
// windows keep their minimize button even though fullpage-mode hides it.
|
||||
// Mark dashboard mode on <body> so window chrome and notifications can adapt.
|
||||
$('body').addClass('dashboard-mode');
|
||||
|
||||
// Create mutable tabs array from built-in tabs
|
||||
|
||||
@@ -79,7 +79,6 @@ async function UIWindowDisable2FA (options) {
|
||||
width: 350,
|
||||
height: 'auto',
|
||||
dominant: true,
|
||||
show_in_taskbar: false,
|
||||
on_before_exit: async () => {
|
||||
if ( ! disabled_successfully ) {
|
||||
promise.resolve(false);
|
||||
|
||||
@@ -136,7 +136,6 @@ function UIAlert (options) {
|
||||
selectable_body: false,
|
||||
draggable_body: options.draggable_body ?? true,
|
||||
allow_context_menu: false,
|
||||
show_in_taskbar: false,
|
||||
window_class: 'window-alert',
|
||||
dominant: true,
|
||||
body_content: h,
|
||||
|
||||
@@ -568,9 +568,9 @@ function UIContextMenu (options) {
|
||||
|
||||
// Y position
|
||||
// is the menu going to go out of the window from the bottom?
|
||||
if ( (start_y + menu_height) > (window.innerHeight - window.taskbar_height - 10) )
|
||||
if ( (start_y + menu_height) > (window.innerHeight - 10) )
|
||||
{
|
||||
y_pos = window.innerHeight - menu_height - window.taskbar_height - 10;
|
||||
y_pos = window.innerHeight - menu_height - 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -801,9 +801,6 @@ function UIContextMenu (options) {
|
||||
$(options.parent_element).css('overflow', 'scroll');
|
||||
|
||||
$(options.parent_element).removeClass('has-open-contextmenu');
|
||||
if ( $(options.parent_element).hasClass('taskbar-item') ) {
|
||||
window.make_taskbar_sortable();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import UIWindowSaveAccount from './UIWindowSaveAccount.js';
|
||||
import UIAuthLogin from './Auth/UIAuthLogin.js';
|
||||
|
||||
import UIWindowProgress from './UIWindowProgress.js';
|
||||
import UITaskbar from './UITaskbar.js';
|
||||
import new_context_menu_item from '../helpers/newContextMenuItem.js';
|
||||
import refresh_item_container from '../helpers/refreshItemContainer.js';
|
||||
import { atPath, retargetNavHistory, retargetPaths, underPath } from '../helpers/pathElements.js';
|
||||
@@ -759,9 +758,6 @@ async function UIDesktop (options) {
|
||||
// Append to <body>
|
||||
$('body').append(h);
|
||||
|
||||
// Set desktop height based on taskbar height
|
||||
$('.desktop').css('height', `calc(100vh - ${window.taskbar_height + window.toolbar_height}px)`);
|
||||
|
||||
// Initialize the preference early
|
||||
puter.kv.get('desktop_icons_hidden').then(async (val) => {
|
||||
window.desktop_icons_hidden = (val === 'true' || val === true);
|
||||
@@ -772,18 +768,6 @@ async function UIDesktop (options) {
|
||||
}
|
||||
}).catch(err => console.warn('Could not load desktop_icons_hidden:', err));
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Taskbar
|
||||
// In fullpage mode the desktop only hosts a single app, so there's
|
||||
// nothing for the taskbar to do; skip building it entirely.
|
||||
// ---------------------------------------------------------------
|
||||
if ( !window.is_fullpage_mode ) {
|
||||
UITaskbar();
|
||||
}
|
||||
|
||||
// Update desktop dimensions after taskbar is initialized with position
|
||||
window.update_desktop_dimensions_for_taskbar();
|
||||
|
||||
const el_desktop = document.querySelector('.desktop');
|
||||
|
||||
window.active_element = el_desktop;
|
||||
@@ -1581,155 +1565,7 @@ async function UIDesktop (options) {
|
||||
}
|
||||
}
|
||||
|
||||
$(document).on('contextmenu taphold', '.taskbar', function (event) {
|
||||
// dismiss taphold on regular devices
|
||||
if ( event.type === 'taphold' && !isMobile.phone && !isMobile.tablet )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Get current taskbar position
|
||||
const currentPosition = window.taskbar_position || 'bottom';
|
||||
|
||||
// Create base menu items
|
||||
let menuItems = [];
|
||||
|
||||
// Only show position submenu on desktop devices
|
||||
if ( !isMobile.phone && !isMobile.tablet ) {
|
||||
menuItems.push({
|
||||
html: i18n('desktop_position'),
|
||||
items: [
|
||||
{
|
||||
html: i18n('desktop_position_left'),
|
||||
checked: currentPosition === 'left',
|
||||
onClick: function () {
|
||||
window.update_taskbar_position('left');
|
||||
},
|
||||
},
|
||||
{
|
||||
html: i18n('desktop_position_bottom'),
|
||||
checked: currentPosition === 'bottom',
|
||||
onClick: function () {
|
||||
window.update_taskbar_position('bottom');
|
||||
},
|
||||
},
|
||||
{
|
||||
html: i18n('desktop_position_right'),
|
||||
checked: currentPosition === 'right',
|
||||
onClick: function () {
|
||||
window.update_taskbar_position('right');
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
menuItems.push('-'); // divider
|
||||
}
|
||||
|
||||
// Add the "Show open windows" option for all devices
|
||||
menuItems.push({
|
||||
html: i18n('desktop_show_open_windows'),
|
||||
onClick: function () {
|
||||
$('.window').showWindow();
|
||||
},
|
||||
});
|
||||
|
||||
// Add the "Show the desktop" option for all devices
|
||||
menuItems.push({
|
||||
html: i18n('desktop_show_desktop'),
|
||||
onClick: function () {
|
||||
$('.window').hideWindow();
|
||||
},
|
||||
});
|
||||
|
||||
UIContextMenu({
|
||||
parent_element: $('.taskbar'),
|
||||
items: menuItems,
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
window.set_desktop_background = function (options) {
|
||||
if ( options.fit ) {
|
||||
let fit = options.fit;
|
||||
if ( fit === 'cover' || fit === 'contain' ) {
|
||||
$('body').css('background-size', fit);
|
||||
$('body').css('background-repeat', 'no-repeat');
|
||||
$('body').css('background-position', 'center center');
|
||||
}
|
||||
else if ( fit === 'center' ) {
|
||||
$('body').css('background-size', 'auto');
|
||||
$('body').css('background-repeat', 'no-repeat');
|
||||
$('body').css('background-position', 'center center');
|
||||
}
|
||||
|
||||
else if ( fit === 'repeat' ) {
|
||||
$('body').css('background-size', 'auto');
|
||||
$('body').css('background-repeat', 'repeat');
|
||||
}
|
||||
window.desktop_bg_fit = fit;
|
||||
}
|
||||
|
||||
if ( options.url ) {
|
||||
$('body').css('background-image', `url(${options.url})`);
|
||||
window.desktop_bg_url = options.url;
|
||||
window.desktop_bg_color = undefined;
|
||||
}
|
||||
else if ( options.color ) {
|
||||
$('body').css({
|
||||
'background-image': 'none',
|
||||
'background-color': options.color,
|
||||
});
|
||||
window.desktop_bg_color = options.color;
|
||||
window.desktop_bg_url = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
window.update_taskbar = function () {
|
||||
let items = [];
|
||||
$('.taskbar-item-sortable[data-keep-in-taskbar="true"]').each(function (index) {
|
||||
items.push({
|
||||
name: $(this).attr('data-app'),
|
||||
type: 'app',
|
||||
});
|
||||
});
|
||||
|
||||
// update taskbar in the server-side
|
||||
$.ajax({
|
||||
url: `${window.api_origin }/update-taskbar-items`,
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
items: items,
|
||||
}),
|
||||
async: true,
|
||||
contentType: 'application/json',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${ window.auth_token}`,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
window.remove_taskbar_item = function (item) {
|
||||
$(item).find('*').fadeOut(100, function () {
|
||||
|
||||
});
|
||||
|
||||
$(item).animate({ width: 0 }, 200, function () {
|
||||
$(item).remove();
|
||||
|
||||
// Adjust taskbar item sizes after removing an item
|
||||
if ( window.adjust_taskbar_item_sizes ) {
|
||||
setTimeout(() => {
|
||||
window.adjust_taskbar_item_sizes();
|
||||
}, 10);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.enter_fullpage_mode = (el_window) => {
|
||||
$('.taskbar').hide();
|
||||
$(el_window).find('.window-head').hide();
|
||||
$('body').addClass('fullpage-mode');
|
||||
$(el_window).css({
|
||||
@@ -1743,26 +1579,12 @@ window.enter_fullpage_mode = (el_window) => {
|
||||
|
||||
window.exit_fullpage_mode = (el_window) => {
|
||||
$('body').removeClass('fullpage-mode');
|
||||
window.taskbar_height = window.default_taskbar_height;
|
||||
// In fullpage mode the taskbar is never built, so create it on exit; otherwise just restore it.
|
||||
if ( $('.taskbar').length === 0 ) {
|
||||
UITaskbar();
|
||||
} else {
|
||||
$('.taskbar').css('height', window.taskbar_height);
|
||||
$('.taskbar').show();
|
||||
}
|
||||
refresh_item_container($('.desktop.item-container'), { fadeInItems: true });
|
||||
$(el_window).removeAttr('data-is_fullpage');
|
||||
if ( el_window ) {
|
||||
window.reset_window_size_and_position(el_window);
|
||||
$(el_window).find('.window-head').show();
|
||||
}
|
||||
|
||||
// reset dektop height to take into account the taskbar height
|
||||
$('.desktop').css('height', `calc(100vh - ${window.taskbar_height + window.toolbar_height}px)`);
|
||||
|
||||
// refresh desktop background
|
||||
window.refresh_desktop_background();
|
||||
};
|
||||
|
||||
window.reset_window_size_and_position = (el_window) => {
|
||||
|
||||
@@ -26,7 +26,6 @@ import truncate_filename from '../helpers/truncateFilename.js';
|
||||
import launch_app from '../helpers/launchApp.js';
|
||||
import open_item from '../helpers/openItem.js';
|
||||
import publish_as_website from '../helpers/publishAsWebsite.js';
|
||||
import mime from '../lib/mime.js';
|
||||
import { isWeblinkName, weblinkChangeIconMenuItem } from '../helpers/weblink.js';
|
||||
import { is_owned_by_me } from '../helpers/pathOwner.js';
|
||||
import { can_rename, can_restructure, invalidate_shared_roots } from '../helpers/sharedAccess.js';
|
||||
@@ -415,8 +414,8 @@ async function UIItem (options) {
|
||||
// the desktop itself, not a window showing the Desktop folder
|
||||
if ( $(el_item).closest('.item-container').hasClass('desktop') &&
|
||||
!window.is_auto_arrange_enabled && $(el_item).attr('data-path') !== window.trash_path && !ui.helper.data('dropped') &&
|
||||
// Item must be dropped on the Desktop and not on the taskbar
|
||||
window.mouseover_window === undefined && ui.position.top <= window.desktop_height - window.taskbar_height - 15 ) {
|
||||
// .desktop clips its overflow, so the whole item has to fit above the bottom edge
|
||||
window.mouseover_window === undefined && ui.position.top + $(el_item).outerHeight(true) <= window.desktop_height ) {
|
||||
|
||||
el_item.style.position = 'absolute';
|
||||
el_item.style.left = `${ui.position.left }px`;
|
||||
@@ -1311,48 +1310,6 @@ async function UIItem (options) {
|
||||
});
|
||||
}
|
||||
// -------------------------------------------
|
||||
// Set as Wallpaper
|
||||
// -------------------------------------------
|
||||
const mime_type = mime.getType($(el_item).attr('data-name')) ??
|
||||
'application/octet-stream';
|
||||
if ( !is_trashed && !is_trash && !options.is_dir && mime_type.startsWith('image/') ) {
|
||||
menu_items.push({
|
||||
html: i18n('set_as_background'),
|
||||
onClick: async function () {
|
||||
const read_url = await puter.fs.sign(undefined, { uid: $(el_item).attr('data-uid'), action: 'read' });
|
||||
window.set_desktop_background({
|
||||
url: read_url.items.read_url,
|
||||
fit: window.desktop_bg_fit,
|
||||
});
|
||||
try {
|
||||
$.ajax({
|
||||
url: `${window.api_origin }/set-desktop-bg`,
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
url: window.desktop_bg_url,
|
||||
color: window.desktop_bg_color,
|
||||
fit: window.desktop_bg_fit,
|
||||
}),
|
||||
async: true,
|
||||
contentType: 'application/json',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${window.auth_token}`,
|
||||
},
|
||||
statusCode: {
|
||||
401: function (xhr) {
|
||||
window.handle401(xhr);
|
||||
},
|
||||
},
|
||||
});
|
||||
$(el_window).close();
|
||||
resolve(true);
|
||||
} catch ( err ) {
|
||||
// Ignore
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
// -------------------------------------------
|
||||
// Zip
|
||||
// -------------------------------------------
|
||||
if ( !is_trash && !is_trashed && !$(el_item).attr('data-path').endsWith('.zip') ) {
|
||||
@@ -1597,7 +1554,7 @@ async function UIItem (options) {
|
||||
left = left > (window.innerWidth - window_width) ? (window.innerWidth - window_width) : left;
|
||||
|
||||
let top = $(el_item).position().top + $(el_item).height();
|
||||
top = top > (window.innerHeight - (window_height + window.taskbar_height + window.toolbar_height)) ? (window.innerHeight - (window_height + window.taskbar_height + window.toolbar_height)) : top;
|
||||
top = top > (window.innerHeight - (window_height + window.toolbar_height)) ? (window.innerHeight - (window_height + window.toolbar_height)) : top;
|
||||
|
||||
UIWindowItemProperties(
|
||||
$(el_item).attr('data-name'),
|
||||
|
||||
@@ -175,3 +175,53 @@ describe('UIItem Publish as Worker', () => {
|
||||
expect(labels).not.toContain('publish_as_serverless_worker');
|
||||
});
|
||||
});
|
||||
|
||||
describe('UIItem dropped on the desktop', () => {
|
||||
let stop;
|
||||
|
||||
beforeEach(async () => {
|
||||
document.body.innerHTML = '<div class="item-container desktop"></div>';
|
||||
Object.assign(window, {
|
||||
user: { username: 'alice' },
|
||||
trash_path: '/alice/Trash',
|
||||
desktop_height: 800,
|
||||
is_auto_arrange_enabled: false,
|
||||
mouseover_window: undefined,
|
||||
desktop_item_positions: {},
|
||||
save_desktop_item_positions: vi.fn(),
|
||||
});
|
||||
const draggable = jQuery.fn.draggable;
|
||||
jQuery.fn.draggable = function (options) {
|
||||
if ( typeof options === 'object' ) stop = options.stop;
|
||||
return this;
|
||||
};
|
||||
try {
|
||||
await UIItem({
|
||||
appendTo: $('.item-container'),
|
||||
uid: 'u-2',
|
||||
path: '/alice/Desktop/Notes',
|
||||
name: 'Notes',
|
||||
is_dir: true,
|
||||
icon: { image: '', type: 'icon' },
|
||||
});
|
||||
} finally {
|
||||
jQuery.fn.draggable = draggable;
|
||||
}
|
||||
// jsdom has no stylesheet, so give the item the size style.css does.
|
||||
$('.item').css({ height: '70px', margin: '15px 5px 30px 5px' });
|
||||
});
|
||||
|
||||
const dropAt = (top) => stop({}, { position: { top, left: 300 }, helper: $('<div>') });
|
||||
|
||||
it('moves there when the whole item fits on screen', () => {
|
||||
dropAt(685);
|
||||
expect($('.item')[0].style.top).toBe('685px');
|
||||
expect(window.save_desktop_item_positions).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('stays put when the item would hang off the bottom edge', () => {
|
||||
dropAt(735);
|
||||
expect($('.item')[0].style.top).toBe('');
|
||||
expect(window.save_desktop_item_positions).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -62,47 +62,17 @@ function UIPopover (options) {
|
||||
function position_popover () {
|
||||
// X position
|
||||
const popover_width = options.width ?? $(el_popover).width();
|
||||
if ( options.center_horizontally ) {
|
||||
// Check taskbar position to determine popover positioning
|
||||
const taskbar_position = window.taskbar_position || 'bottom';
|
||||
|
||||
if ( taskbar_position === 'left' ) {
|
||||
// Position in top-left corner for left taskbar
|
||||
x_pos = window.taskbar_height + 10; // Just to the right of the taskbar
|
||||
} else if ( taskbar_position === 'right' ) {
|
||||
// Position in top-right corner for right taskbar
|
||||
x_pos = window.innerWidth - popover_width - window.taskbar_height - 40; // Just to the left of the taskbar
|
||||
} else {
|
||||
// Default bottom taskbar behavior - center horizontally
|
||||
x_pos = window.innerWidth / 2 - popover_width / 2 - 15;
|
||||
}
|
||||
} else {
|
||||
if ( options.position === 'bottom' || options.position === 'top' )
|
||||
{
|
||||
x_pos = options.left ?? ($(options.snapToElement).offset().left - (popover_width / 2) + 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
x_pos = options.left ?? ($(options.snapToElement).offset().left + 5);
|
||||
}
|
||||
if ( options.position === 'bottom' || options.position === 'top' )
|
||||
{
|
||||
x_pos = options.left ?? ($(options.snapToElement).offset().left - (popover_width / 2) + 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
x_pos = options.left ?? ($(options.snapToElement).offset().left + 5);
|
||||
}
|
||||
|
||||
// Y position
|
||||
const popover_height = options.height ?? $(el_popover).height();
|
||||
if ( options.center_horizontally ) {
|
||||
// Check taskbar position to determine popover positioning
|
||||
const taskbar_position = window.taskbar_position || 'bottom';
|
||||
|
||||
if ( taskbar_position === 'left' || taskbar_position === 'right' ) {
|
||||
// Position at top for left/right taskbars
|
||||
y_pos = window.toolbar_height + 10; // Just below the toolbar
|
||||
} else {
|
||||
// Default bottom taskbar behavior - position above taskbar
|
||||
y_pos = options.top ?? (window.innerHeight - (window.taskbar_height + popover_height + 10));
|
||||
}
|
||||
} else {
|
||||
y_pos = options.top ?? ($(options.snapToElement).offset().top + $(options.snapToElement).height() + 5);
|
||||
}
|
||||
y_pos = options.top ?? ($(options.snapToElement).offset().top + $(options.snapToElement).height() + 5);
|
||||
|
||||
$(el_popover).css({
|
||||
left: `${x_pos }px`,
|
||||
|
||||
@@ -71,7 +71,6 @@ function UIPrompt (options) {
|
||||
selectable_body: false,
|
||||
draggable_body: true,
|
||||
allow_context_menu: false,
|
||||
show_in_taskbar: false,
|
||||
window_class: 'window-alert',
|
||||
dominant: true,
|
||||
body_content: h,
|
||||
|
||||
@@ -1,468 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import UITaskbarItem from './UITaskbarItem.js';
|
||||
import launch_app from '../helpers/launchApp.js';
|
||||
import { appIconSrc } from '../helpers/appIcon.js';
|
||||
|
||||
const TASKBAR_POSITIONS = ['left', 'bottom', 'right'];
|
||||
|
||||
async function UITaskbar (options) {
|
||||
window.global_element_id++;
|
||||
|
||||
options = options ?? {};
|
||||
options.content = options.content ?? '';
|
||||
|
||||
let taskbar_position;
|
||||
|
||||
// if first visit ever, set taskbar position to left
|
||||
if ( window.first_visit_ever ) {
|
||||
puter.kv.set('taskbar_position', 'left').catch(err => console.warn('Could not save taskbar_position:', err));
|
||||
taskbar_position = 'left';
|
||||
} else {
|
||||
taskbar_position = await puter.kv.get('taskbar_position').catch(() => null);
|
||||
// if this is not first visit, set taskbar position to bottom since it's from a user that
|
||||
// used puter before customizing taskbar position was added and the taskbar position was set to bottom
|
||||
// Unknown values are discarded too: this ends up in markup and stored values may be poisoned.
|
||||
if ( ! TASKBAR_POSITIONS.includes(taskbar_position) ) {
|
||||
taskbar_position = 'bottom'; // default position
|
||||
puter.kv.set('taskbar_position', taskbar_position).catch(err => console.warn('Could not save taskbar_position:', err));
|
||||
}
|
||||
}
|
||||
|
||||
// Force bottom position on mobile devices
|
||||
if ( isMobile.phone || isMobile.tablet ) {
|
||||
taskbar_position = 'bottom';
|
||||
}
|
||||
|
||||
// Set global taskbar position
|
||||
window.taskbar_position = taskbar_position;
|
||||
|
||||
let h = '';
|
||||
h += `<div id="ui-taskbar_${window.global_element_id}" class="taskbar taskbar-position-${taskbar_position}" style="height:${window.taskbar_height}px;">`;
|
||||
h += '<div class="taskbar-sortable" style="display: flex; justify-content: center; z-index: 99999;"></div>';
|
||||
h += '</div>';
|
||||
|
||||
if ( taskbar_position === 'left' || taskbar_position === 'right' ) {
|
||||
$('.desktop').addClass(`desktop-taskbar-position-${taskbar_position}`);
|
||||
}
|
||||
|
||||
$('.desktop').append(h);
|
||||
|
||||
//---------------------------------------------
|
||||
// add `Explorer` to the taskbar
|
||||
//---------------------------------------------
|
||||
UITaskbarItem({
|
||||
icon: window.icons['folders.svg'],
|
||||
app: 'explorer',
|
||||
name: 'Explorer',
|
||||
sortable: false,
|
||||
keep_in_taskbar: true,
|
||||
lock_keep_in_taskbar: true,
|
||||
onClick: function () {
|
||||
let open_window_count = parseInt($('.taskbar-item[data-app="explorer"]').attr('data-open-windows'));
|
||||
if ( open_window_count === 0 ) {
|
||||
launch_app({ name: 'explorer', path: window.home_path });
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
//---------------------------------------------
|
||||
// add separator before trash
|
||||
//---------------------------------------------
|
||||
UITaskbarItem({
|
||||
icon: '', // No icon for separator
|
||||
name: 'separator',
|
||||
app: 'separator',
|
||||
sortable: false,
|
||||
keep_in_taskbar: true,
|
||||
lock_keep_in_taskbar: true,
|
||||
disable_context_menu: true,
|
||||
style: 'pointer-events: none;', // Make it non-interactive
|
||||
onClick: function () {
|
||||
// Separator is non-interactive
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
//---------------------------------------------
|
||||
// Add other useful apps to the taskbar
|
||||
//---------------------------------------------
|
||||
if ( window.user.taskbar_items && window.user.taskbar_items.length > 0 ) {
|
||||
for ( let index = 0; index < window.user.taskbar_items.length; index++ ) {
|
||||
const app_info = window.user.taskbar_items[index];
|
||||
const icon = appIconSrc(app_info);
|
||||
// add taskbar item for each app
|
||||
UITaskbarItem({
|
||||
icon: icon.src,
|
||||
iconFallback: icon.fallback,
|
||||
app: app_info.name,
|
||||
name: app_info.title,
|
||||
keep_in_taskbar: true,
|
||||
onClick: function () {
|
||||
let open_window_count = parseInt($(`.taskbar-item[data-app="${app_info.name}"]`).attr('data-open-windows'));
|
||||
if ( open_window_count === 0 ) {
|
||||
launch_app({
|
||||
name: app_info.name,
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------
|
||||
// add `Trash` to the taskbar
|
||||
//---------------------------------------------
|
||||
const trash = await puter.fs.stat({ path: window.trash_path, consistency: 'eventual' });
|
||||
if ( window.socket ) {
|
||||
window.socket.emit('trash.is_empty', { is_empty: trash.is_empty });
|
||||
}
|
||||
|
||||
UITaskbarItem({
|
||||
icon: trash.is_empty ? window.icons['trash.svg'] : window.icons['trash-full.svg'],
|
||||
app: 'trash',
|
||||
name: `${i18n('trash')}`,
|
||||
sortable: false,
|
||||
keep_in_taskbar: true,
|
||||
lock_keep_in_taskbar: true,
|
||||
onClick: function () {
|
||||
let open_windows = $(`.window[data-path="${html_encode(window.trash_path)}"]`);
|
||||
if ( open_windows.length === 0 ) {
|
||||
launch_app({ name: 'explorer', path: window.trash_path });
|
||||
} else {
|
||||
open_windows.focusWindow();
|
||||
}
|
||||
},
|
||||
onItemsDrop: function (items) {
|
||||
window.move_items(items, window.trash_path);
|
||||
},
|
||||
});
|
||||
|
||||
//---------------------------------------------
|
||||
// add separator before trash
|
||||
//---------------------------------------------
|
||||
UITaskbarItem({
|
||||
icon: '', // No icon for separator
|
||||
name: 'separator',
|
||||
app: 'separator',
|
||||
sortable: false,
|
||||
keep_in_taskbar: true,
|
||||
lock_keep_in_taskbar: true,
|
||||
disable_context_menu: true,
|
||||
style: 'pointer-events: none;', // Make it non-interactive
|
||||
onClick: function () {
|
||||
// Separator is non-interactive
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
window.make_taskbar_sortable();
|
||||
}
|
||||
|
||||
//-------------------------------------------
|
||||
// Taskbar is sortable
|
||||
//-------------------------------------------
|
||||
window.make_taskbar_sortable = function () {
|
||||
const position = window.taskbar_position || 'bottom';
|
||||
const axis = position === 'bottom' ? 'x' : 'y';
|
||||
|
||||
$('.taskbar-sortable').sortable({
|
||||
axis: axis,
|
||||
items: '.taskbar-item-sortable:not(.has-open-contextmenu):not([data-app="separator"])',
|
||||
cancel: '.has-open-contextmenu',
|
||||
placeholder: 'taskbar-item-sortable-placeholder',
|
||||
helper: 'clone',
|
||||
distance: 5,
|
||||
revert: 10,
|
||||
update: function (event, ui) {
|
||||
// only update the DB if the sorted item is pinned
|
||||
if ( $(ui.item).attr('data-keep-in-taskbar') === 'true' ) {
|
||||
window.update_taskbar();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Function to update taskbar position
|
||||
window.update_taskbar_position = async function (new_position) {
|
||||
// Prevent position changes on mobile devices - always keep bottom
|
||||
if ( isMobile.phone || isMobile.tablet ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! TASKBAR_POSITIONS.includes(new_position) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the new position
|
||||
puter.kv.set('taskbar_position', new_position).catch(err => console.warn('Could not save taskbar_position:', err));
|
||||
window.taskbar_position = new_position;
|
||||
|
||||
// Remove old position classes and add new one
|
||||
$('.taskbar').removeClass('taskbar-position-left taskbar-position-bottom taskbar-position-right');
|
||||
$('.taskbar').addClass(`taskbar-position-${new_position}`);
|
||||
|
||||
// update desktop class, if left or right, add `desktop-taskbar-position-left` or `desktop-taskbar-position-right`
|
||||
$('.desktop').removeClass('desktop-taskbar-position-left');
|
||||
$('.desktop').removeClass('desktop-taskbar-position-right');
|
||||
$('.desktop').addClass(`desktop-taskbar-position-${new_position}`);
|
||||
|
||||
// Update desktop height/width calculations based on new position
|
||||
window.update_desktop_dimensions_for_taskbar();
|
||||
|
||||
// Update window positions if needed (for maximized windows)
|
||||
$('.window[data-is_maximized="1"]').each(function () {
|
||||
const el_window = this;
|
||||
window.update_maximized_window_for_taskbar(el_window);
|
||||
});
|
||||
|
||||
// Re-initialize sortable with correct axis
|
||||
$('.taskbar-sortable').sortable('destroy');
|
||||
window.make_taskbar_sortable();
|
||||
|
||||
// Adjust taskbar item sizes for the new position
|
||||
setTimeout(() => {
|
||||
window.adjust_taskbar_item_sizes();
|
||||
}, 10);
|
||||
|
||||
// Reinitialize all taskbar item tooltips with new position
|
||||
$('.taskbar-item').each(function () {
|
||||
const $item = $(this);
|
||||
// Destroy existing tooltip
|
||||
if ( $item.data('ui-tooltip') ) {
|
||||
$item.tooltip('destroy');
|
||||
}
|
||||
|
||||
// Helper function to get tooltip position based on taskbar position
|
||||
function getTooltipPosition () {
|
||||
const taskbarPosition = window.taskbar_position || 'bottom';
|
||||
|
||||
if ( taskbarPosition === 'bottom' ) {
|
||||
return {
|
||||
my: 'center bottom-20',
|
||||
at: 'center top',
|
||||
};
|
||||
} else if ( taskbarPosition === 'top' ) {
|
||||
return {
|
||||
my: 'center top+20',
|
||||
at: 'center bottom',
|
||||
};
|
||||
} else if ( taskbarPosition === 'left' ) {
|
||||
return {
|
||||
my: 'left+20 center',
|
||||
at: 'right center',
|
||||
};
|
||||
} else if ( taskbarPosition === 'right' ) {
|
||||
return {
|
||||
my: 'right-20 center',
|
||||
at: 'left center',
|
||||
};
|
||||
}
|
||||
return {
|
||||
my: 'center bottom-20',
|
||||
at: 'center top',
|
||||
}; // fallback
|
||||
}
|
||||
|
||||
const tooltipPosition = getTooltipPosition();
|
||||
|
||||
// Reinitialize tooltip with new position
|
||||
$item.tooltip({
|
||||
items: ".taskbar:not(.children-have-open-contextmenu) .taskbar-item:not([data-app='separator'])",
|
||||
position: {
|
||||
my: tooltipPosition.my,
|
||||
at: tooltipPosition.at,
|
||||
using: function ( position, feedback ) {
|
||||
$(this).css( position);
|
||||
$('<div>')
|
||||
.addClass( 'arrow')
|
||||
.addClass( feedback.vertical)
|
||||
.addClass( feedback.horizontal)
|
||||
.appendTo( this);
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Function to update desktop dimensions based on taskbar position
|
||||
window.update_desktop_dimensions_for_taskbar = function () {
|
||||
const position = window.taskbar_position || 'bottom';
|
||||
|
||||
if ( position === 'bottom' ) {
|
||||
$('.desktop').css({
|
||||
'height': `calc(100vh - ${window.taskbar_height + window.toolbar_height}px)`,
|
||||
'width': '100%',
|
||||
'left': '0',
|
||||
'top': `${window.toolbar_height}px`,
|
||||
});
|
||||
} else if ( position === 'left' ) {
|
||||
$('.desktop').css({
|
||||
'height': `calc(100vh - ${window.toolbar_height}px)`,
|
||||
'width': `calc(100% - ${window.taskbar_height}px)`,
|
||||
'left': `${window.taskbar_height}px`,
|
||||
'top': `${window.toolbar_height}px`,
|
||||
});
|
||||
} else if ( position === 'right' ) {
|
||||
$('.desktop').css({
|
||||
'height': `calc(100vh - ${window.toolbar_height}px)`,
|
||||
'width': `calc(100% - ${window.taskbar_height}px)`,
|
||||
'left': '0',
|
||||
'top': `${window.toolbar_height}px`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//-------------------------------------------
|
||||
// Dynamic taskbar item resizing for left/right positions
|
||||
//-------------------------------------------
|
||||
window.adjust_taskbar_item_sizes = function () {
|
||||
const position = window.taskbar_position || 'bottom';
|
||||
|
||||
// Only apply to left and right positions
|
||||
if ( position !== 'left' && position !== 'right' ) {
|
||||
// Reset to default sizes for bottom position
|
||||
$('.taskbar .taskbar-item').css({
|
||||
'width': '40px',
|
||||
'height': '40px',
|
||||
'min-width': '40px',
|
||||
'min-height': '40px',
|
||||
});
|
||||
$('.taskbar-icon').css('height', '40px');
|
||||
return;
|
||||
}
|
||||
|
||||
const taskbar = $('.taskbar')[0];
|
||||
const taskbarItems = $('.taskbar .taskbar-item:visible');
|
||||
|
||||
if ( !taskbar || taskbarItems.length === 0 ) return;
|
||||
|
||||
// Get available height (minus padding)
|
||||
const totalItemsNeeded = taskbarItems.length;
|
||||
const taskbarHeight = taskbar.clientHeight;
|
||||
const paddingTop = 20; // from CSS
|
||||
const paddingBottom = 20; // from CSS
|
||||
const availableHeight = taskbarHeight - paddingTop - paddingBottom - 180;
|
||||
|
||||
// Calculate space needed with default sizes
|
||||
const defaultItemSize = 40;
|
||||
const defaultMargin = 5;
|
||||
const spaceNeededDefault = (totalItemsNeeded * defaultItemSize) + ((totalItemsNeeded - 1) * defaultMargin);
|
||||
|
||||
if ( spaceNeededDefault <= availableHeight ) {
|
||||
// No overflow, use default sizes
|
||||
taskbarItems.css({
|
||||
'width': '40px',
|
||||
'height': '40px',
|
||||
'min-width': '40px',
|
||||
'min-height': '40px',
|
||||
'padding': '6px 5px 10px 5px', // default padding
|
||||
});
|
||||
$('.taskbar-icon').css('height', `${defaultItemSize }px`);
|
||||
$('.taskbar-icon').css('width', '40px');
|
||||
$('.taskbar-icon > img').css('width', 'auto');
|
||||
$('.taskbar-icon > img').css('margin', 'auto');
|
||||
$('.taskbar-icon > img').css('display', 'block');
|
||||
|
||||
// Reset margins to default
|
||||
taskbarItems.css('margin-bottom', '5px');
|
||||
taskbarItems.last().css('margin-bottom', '0px');
|
||||
} else {
|
||||
// Overflow detected, calculate smaller sizes
|
||||
// Reserve some margin space (minimum 2px between items)
|
||||
const minMargin = 2;
|
||||
const marginSpace = (totalItemsNeeded - 1) * minMargin;
|
||||
const availableForItems = availableHeight - marginSpace;
|
||||
const newItemSize = Math.floor(availableForItems / totalItemsNeeded);
|
||||
|
||||
// Ensure minimum size of 20px
|
||||
const finalItemSize = Math.max(20, newItemSize);
|
||||
|
||||
// Calculate proportional padding based on size ratio
|
||||
const sizeRatio = finalItemSize / defaultItemSize;
|
||||
const paddingTop = Math.max(1, Math.floor(6 * sizeRatio));
|
||||
const paddingRight = Math.max(1, Math.floor(5 * sizeRatio));
|
||||
const paddingBottom = Math.max(1, Math.floor(10 * sizeRatio));
|
||||
const paddingLeft = Math.max(1, Math.floor(5 * sizeRatio));
|
||||
|
||||
// Apply new sizes and padding
|
||||
taskbarItems.css({
|
||||
'width': '40px',
|
||||
'height': `${finalItemSize }px`,
|
||||
'min-width': '40px',
|
||||
'min-height': `${finalItemSize }px`,
|
||||
'padding': `${paddingTop}px ${paddingRight}px ${paddingBottom}px ${paddingLeft}px`,
|
||||
});
|
||||
$('.taskbar-icon').css('height', `${finalItemSize }px`);
|
||||
$('.taskbar-icon').css('width', '40px');
|
||||
$('.taskbar-icon > img').css('width', 'auto');
|
||||
$('.taskbar-icon > img').css('margin', 'auto');
|
||||
$('.taskbar-icon > img').css('display', 'block');
|
||||
// Adjust margins
|
||||
taskbarItems.css('margin-bottom', `${minMargin }px`);
|
||||
taskbarItems.last().css('margin-bottom', '0px');
|
||||
}
|
||||
};
|
||||
|
||||
// Hook into existing taskbar functionality
|
||||
$(document).ready(function () {
|
||||
// Watch for taskbar item changes
|
||||
const observer = new MutationObserver(function (mutations) {
|
||||
mutations.forEach(function (mutation) {
|
||||
if ( mutation.type === 'childList' || mutation.type === 'attributes' ) {
|
||||
// Delay to ensure DOM updates are complete
|
||||
setTimeout(() => {
|
||||
window.adjust_taskbar_item_sizes();
|
||||
}, 10);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Start observing when taskbar is available
|
||||
const checkTaskbar = setInterval(() => {
|
||||
const taskbar = document.querySelector('.taskbar-sortable');
|
||||
if ( taskbar ) {
|
||||
observer.observe(taskbar, {
|
||||
childList: true,
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
});
|
||||
clearInterval(checkTaskbar);
|
||||
|
||||
// Initial call
|
||||
setTimeout(() => {
|
||||
window.adjust_taskbar_item_sizes();
|
||||
}, 100);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Also watch for window resize events
|
||||
window.addEventListener('resize', () => {
|
||||
setTimeout(() => {
|
||||
window.adjust_taskbar_item_sizes();
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
|
||||
export default UITaskbar;
|
||||
@@ -1,75 +0,0 @@
|
||||
// @vitest-environment jsdom
|
||||
/*
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, expect, it, vi } from 'vitest';
|
||||
import jQuery from '../lib/jquery-3.6.1/jquery-3.6.1.min.js';
|
||||
|
||||
vi.mock('../helpers/launchApp.js', () => ({ default: vi.fn() }));
|
||||
|
||||
globalThis.$ = globalThis.jQuery = jQuery;
|
||||
globalThis.i18n = (key) => key;
|
||||
globalThis.html_encode = (value) => String(value ?? '');
|
||||
globalThis.isMobile = { phone: false, tablet: false };
|
||||
await import('../lib/jquery-ui-1.13.2/jquery-ui.js');
|
||||
const { default: UITaskbar } = await import('./UITaskbar.js');
|
||||
|
||||
let requests;
|
||||
|
||||
afterEach(() => {
|
||||
$('.taskbar').remove();
|
||||
$(document).off();
|
||||
$(window).off();
|
||||
document.body.innerHTML = '';
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
document.body.innerHTML = '<div class="desktop"></div>';
|
||||
Object.assign(window, {
|
||||
global_element_id: 0,
|
||||
taskbar_height: 50,
|
||||
toolbar_height: 0,
|
||||
api_origin: 'http://api.test',
|
||||
auth_token: 'test-token',
|
||||
icons: {},
|
||||
user: { taskbar_items: [{ name: 'notes', title: 'Notes' }] },
|
||||
trash_path: '/test/Trash',
|
||||
});
|
||||
vi.stubGlobal('puter', {
|
||||
kv: { get: async () => 'bottom' },
|
||||
fs: { stat: async () => ({ is_empty: true }) },
|
||||
});
|
||||
requests = [];
|
||||
$.ajaxTransport('+*', (options) => {
|
||||
requests.push(options.url);
|
||||
return { send () {}, abort () {} };
|
||||
});
|
||||
await UITaskbar();
|
||||
});
|
||||
|
||||
it('starts the taskbar at Explorer, followed by pinned apps and Trash', () => {
|
||||
const apps = $('.taskbar .taskbar-item').map((_, el) => el.dataset.app).get();
|
||||
expect(apps).toEqual(['explorer', 'separator', 'notes', 'separator', 'trash']);
|
||||
});
|
||||
|
||||
it('does not fetch launch apps when the taskbar loads', () => {
|
||||
expect(requests.filter(url => url.includes('/get-launch-apps'))).toEqual([]);
|
||||
});
|
||||
@@ -1,531 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import UIContextMenu from './UIContextMenu.js';
|
||||
import path from '../lib/path.js';
|
||||
import launch_app from '../helpers/launchApp.js';
|
||||
import { user_facing_windows } from '../helpers/windowVisibility.js';
|
||||
import { appIconFallbackAttr } from '../helpers/appIcon.js';
|
||||
|
||||
let tray_item_id = 1;
|
||||
|
||||
function UITaskbarItem (options) {
|
||||
let h = '';
|
||||
tray_item_id++;
|
||||
options.sortable = options.sortable ?? true;
|
||||
options.open_windows_count = options.open_windows_count ?? 0;
|
||||
options.lock_keep_in_taskbar = options.lock_keep_in_taskbar ?? false;
|
||||
options.before_trash = options.before_trash ?? false;
|
||||
|
||||
// The windows this item stands for: the user's own. An instance another
|
||||
// app launched in the background is not counted in data-open-windows and
|
||||
// has no item of its own (see UIWindow's taskbar block) — it must not be
|
||||
// shown, hidden, or closed through the item its app's OTHER windows own.
|
||||
const item_windows = () => $(user_facing_windows($(`.window[data-app="${options.app}"]`)));
|
||||
|
||||
const element_id = window.global_element_id++;
|
||||
|
||||
h += `<div class = "taskbar-item ${options.sortable ? 'taskbar-item-sortable' : ''} disable-user-select"
|
||||
id = "taskbar-item-${tray_item_id}"
|
||||
data-taskbar-item-id = "${tray_item_id}"
|
||||
data-element-id = "${html_encode(element_id)}"
|
||||
data-name = "${html_encode(options.name)}"
|
||||
data-app = "${html_encode(options.app)}"
|
||||
data-keep-in-taskbar = "${html_encode(options.keep_in_taskbar ?? 'false')}"
|
||||
data-open-windows="${(options.open_windows_count)}"
|
||||
title = "${html_encode(options.name)}"
|
||||
style= "${options.style ? html_encode(options.style) : ''}"
|
||||
>`;
|
||||
let icon = options.icon ? options.icon : window.icons['app.svg'];
|
||||
// App icons carry a retry URL (see helpers/appIcon.js); the bundled icons
|
||||
// below never need one.
|
||||
let icon_fallback = options.iconFallback ?? '';
|
||||
if ( options.app === 'explorer' )
|
||||
{
|
||||
icon = window.icons['folders.svg'];
|
||||
icon_fallback = '';
|
||||
}
|
||||
|
||||
// taskbar icon
|
||||
h += '<div class="taskbar-icon">';
|
||||
// Don't add img tag for separator
|
||||
if ( options.app !== 'separator' ) {
|
||||
h += `<img src="${html_encode(icon)}"${appIconFallbackAttr(icon_fallback)} style="${options.group === 'apps' ? 'filter:none;' : ''}">`;
|
||||
}
|
||||
h += '</div>';
|
||||
|
||||
// active indicator
|
||||
if ( options.app !== 'apps' )
|
||||
{
|
||||
h += '<span class="active-taskbar-indicator"></span>';
|
||||
}
|
||||
h += '</div>';
|
||||
|
||||
if ( options.before_trash ) {
|
||||
$('.taskbar-sortable').append(h);
|
||||
} else {
|
||||
if ( options.sortable )
|
||||
{
|
||||
$('.taskbar-sortable').append(h);
|
||||
}
|
||||
else {
|
||||
// if taskbar-sortable is empty then append before it
|
||||
if ( $('.taskbar-sortable').children().length === 0 )
|
||||
{
|
||||
$('.taskbar').find('.taskbar-sortable').before(h);
|
||||
}
|
||||
else
|
||||
{
|
||||
$('.taskbar').find('.taskbar-sortable').after(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const el_taskbar_item = document.querySelector(`#taskbar-item-${tray_item_id}`);
|
||||
|
||||
// fade in the taskbar item
|
||||
$(el_taskbar_item).show(50);
|
||||
|
||||
// Adjust taskbar item sizes after adding new item
|
||||
if ( window.adjust_taskbar_item_sizes ) {
|
||||
setTimeout(() => {
|
||||
window.adjust_taskbar_item_sizes();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
$(el_taskbar_item).on('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Don't handle clicks for separators
|
||||
if ( options.app === 'separator' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this item has an open context menu, don't do anything
|
||||
if ( $(el_taskbar_item).hasClass('has-open-contextmenu') )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( options.onClick === undefined || options.onClick(el_taskbar_item) === false ) {
|
||||
// re-show each window in this app group
|
||||
item_windows().showWindow();
|
||||
}
|
||||
});
|
||||
|
||||
$(el_taskbar_item).on('contextmenu taphold', function (e) {
|
||||
// seems like the only way to stop sortable is to destroy it
|
||||
if ( options.sortable ) {
|
||||
$('.taskbar-sortable').sortable('destroy');
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Don't show context menu for separators
|
||||
if ( options.app === 'separator' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If context menu is disabled on this item, return
|
||||
if ( options.disable_context_menu )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// don't allow context menu to open if it's already open
|
||||
if ( $(el_taskbar_item).hasClass('has-open-contextmenu') )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const menu_items = [];
|
||||
const open_windows = parseInt($(el_taskbar_item).attr('data-open-windows'));
|
||||
// -------------------------------------------
|
||||
// List of open windows belonging to this app
|
||||
// -------------------------------------------
|
||||
item_windows().each(function () {
|
||||
menu_items.push({
|
||||
html: $(this).find('.window-head-title').html(),
|
||||
val: $(this).attr('data-id'),
|
||||
onClick: function (e) {
|
||||
$(`.window[data-id="${e.value}"]`).showWindow();
|
||||
},
|
||||
});
|
||||
});
|
||||
// -------------------------------------------
|
||||
// divider
|
||||
// -------------------------------------------
|
||||
if ( menu_items.length > 0 )
|
||||
{
|
||||
menu_items.push('-');
|
||||
}
|
||||
//------------------------------------------
|
||||
// New Window
|
||||
//------------------------------------------
|
||||
if ( options.app && options.app !== 'trash' ) {
|
||||
menu_items.push({
|
||||
html: i18n('new_window'),
|
||||
val: $(this).attr('data-id'),
|
||||
onClick: function () {
|
||||
// is trash?
|
||||
launch_app({
|
||||
name: options.app,
|
||||
maximized: (isMobile.phone || isMobile.tablet),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
//------------------------------------------
|
||||
// Open Trash
|
||||
//------------------------------------------
|
||||
else if ( options.app && options.app === 'trash' ) {
|
||||
menu_items.push({
|
||||
html: i18n('open_trash'),
|
||||
val: $(this).attr('data-id'),
|
||||
onClick: function () {
|
||||
launch_app({
|
||||
name: options.app,
|
||||
path: window.trash_path,
|
||||
maximized: (isMobile.phone || isMobile.tablet),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
//------------------------------------------
|
||||
// Empty Trash
|
||||
//------------------------------------------
|
||||
if ( options.app && options.app === 'trash' ) {
|
||||
// divider
|
||||
menu_items.push('-');
|
||||
|
||||
// Empty Trash menu item
|
||||
menu_items.push({
|
||||
html: i18n('empty_trash'),
|
||||
val: $(this).attr('data-id'),
|
||||
onClick: async function () {
|
||||
window.empty_trash();
|
||||
},
|
||||
});
|
||||
}
|
||||
//------------------------------------------
|
||||
// Remove from Taskbar
|
||||
//------------------------------------------
|
||||
if ( options.keep_in_taskbar && !options.lock_keep_in_taskbar ) {
|
||||
menu_items.push({
|
||||
html: i18n('remove_from_taskbar'),
|
||||
val: $(this).attr('data-id'),
|
||||
onClick: function () {
|
||||
$(el_taskbar_item).attr('data-keep-in-taskbar', 'false');
|
||||
if ( $(el_taskbar_item).attr('data-open-windows') === '0' ) {
|
||||
window.remove_taskbar_item(el_taskbar_item);
|
||||
}
|
||||
window.update_taskbar();
|
||||
options.keep_in_taskbar = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
//------------------------------------------
|
||||
// Keep in Taskbar
|
||||
//------------------------------------------
|
||||
else if ( ! options.keep_in_taskbar ) {
|
||||
menu_items.push({
|
||||
html: i18n('keep_in_taskbar'),
|
||||
val: $(this).attr('data-id'),
|
||||
onClick: function () {
|
||||
$(el_taskbar_item).attr('data-keep-in-taskbar', 'true');
|
||||
window.update_taskbar();
|
||||
options.keep_in_taskbar = true;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if ( open_windows > 0 ) {
|
||||
// -------------------------------------------
|
||||
// divider
|
||||
// -------------------------------------------
|
||||
menu_items.push('-');
|
||||
// -------------------------------------------
|
||||
// Show All Windows
|
||||
// -------------------------------------------
|
||||
menu_items.push({
|
||||
html: i18n('show_all_windows'),
|
||||
onClick: function () {
|
||||
item_windows().showWindow();
|
||||
},
|
||||
});
|
||||
// -------------------------------------------
|
||||
// Hide All Windows
|
||||
// -------------------------------------------
|
||||
menu_items.push({
|
||||
html: i18n('hide_all_windows'),
|
||||
onClick: function () {
|
||||
if ( open_windows > 0 )
|
||||
{
|
||||
item_windows().hideWindow();
|
||||
}
|
||||
},
|
||||
});
|
||||
// -------------------------------------------
|
||||
// Close All Windows
|
||||
// -------------------------------------------
|
||||
menu_items.push({
|
||||
html: i18n('close_all_windows'),
|
||||
onClick: function () {
|
||||
// "All" means the windows this item lists — a background
|
||||
// helper belongs to the app running it, and goes when
|
||||
// that app does (UIWindow's close path).
|
||||
item_windows().close();
|
||||
},
|
||||
});
|
||||
}
|
||||
const pos = el_taskbar_item.getBoundingClientRect();
|
||||
UIContextMenu({
|
||||
parent_element: el_taskbar_item,
|
||||
position: getContextMenuPosition(pos),
|
||||
items: menu_items,
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Helper function to get tooltip position based on taskbar position
|
||||
function getTooltipPosition () {
|
||||
const taskbarPosition = window.taskbar_position || 'bottom';
|
||||
|
||||
if ( taskbarPosition === 'bottom' ) {
|
||||
return {
|
||||
my: 'center bottom-20',
|
||||
at: 'center top',
|
||||
};
|
||||
} else if ( taskbarPosition === 'top' ) {
|
||||
return {
|
||||
my: 'center top+20',
|
||||
at: 'center bottom',
|
||||
};
|
||||
} else if ( taskbarPosition === 'left' ) {
|
||||
return {
|
||||
my: 'left+20 center',
|
||||
at: 'right center',
|
||||
};
|
||||
} else if ( taskbarPosition === 'right' ) {
|
||||
return {
|
||||
my: 'right-20 center',
|
||||
at: 'left center',
|
||||
};
|
||||
}
|
||||
return {
|
||||
my: 'center bottom-20',
|
||||
at: 'center top',
|
||||
}; // fallback
|
||||
}
|
||||
|
||||
// Helper function to get context menu position based on taskbar position
|
||||
function getContextMenuPosition (pos) {
|
||||
const taskbarPosition = window.taskbar_position || 'bottom';
|
||||
|
||||
if ( taskbarPosition === 'bottom' ) {
|
||||
return {
|
||||
top: pos.top - 15,
|
||||
left: pos.left + 5,
|
||||
};
|
||||
} else if ( taskbarPosition === 'top' ) {
|
||||
return {
|
||||
top: pos.bottom + 15,
|
||||
left: pos.left + 5,
|
||||
};
|
||||
} else if ( taskbarPosition === 'left' ) {
|
||||
return {
|
||||
top: pos.top + 5,
|
||||
left: pos.right + 5,
|
||||
};
|
||||
} else if ( taskbarPosition === 'right' ) {
|
||||
return {
|
||||
top: pos.top + 5,
|
||||
left: pos.left - 20,
|
||||
};
|
||||
}
|
||||
return {
|
||||
top: pos.top - 15,
|
||||
left: pos.left + 5,
|
||||
}; // fallback
|
||||
}
|
||||
|
||||
const tooltipPosition = getTooltipPosition();
|
||||
|
||||
$(el_taskbar_item).tooltip({
|
||||
// only show tooltip if desktop is not selectable active
|
||||
items: ".desktop:not(.desktop-selectable-active) .taskbar:not(.children-have-open-contextmenu) .taskbar-item:not([data-app='separator'])",
|
||||
position: {
|
||||
my: tooltipPosition.my,
|
||||
at: tooltipPosition.at,
|
||||
using: function ( position, feedback ) {
|
||||
$(this).css( position);
|
||||
$('<div>')
|
||||
.addClass( 'arrow')
|
||||
.addClass( feedback.vertical)
|
||||
.addClass( feedback.horizontal)
|
||||
.appendTo( this);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Droppable
|
||||
// --------------------------------------------------------
|
||||
// Don't make separators droppable
|
||||
if ( options.app !== 'separator' ) {
|
||||
$(el_taskbar_item).droppable({
|
||||
accept: '.item',
|
||||
// 'pointer' is very important because of active window tracking is based on the position of cursor.
|
||||
tolerance: 'pointer',
|
||||
drop: async function ( event, ui ) {
|
||||
// Check if hovering over an item that is VISIBILE
|
||||
if ( $(event.target).closest('.window').attr('data-id') !== $(window.mouseover_window).attr('data-id') )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If ctrl is pressed and source is Trashed, cancel whole operation
|
||||
if ( event.ctrlKey && path.dirname($(ui.draggable).attr('data-path')) === window.trash_path )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const items_to_move = [];
|
||||
|
||||
// First item
|
||||
items_to_move.push(ui.draggable);
|
||||
|
||||
// All subsequent items
|
||||
const cloned_items = document.getElementsByClassName('item-selected-clone');
|
||||
for ( let i = 0; i < cloned_items.length; i++ ) {
|
||||
const source_item = document.getElementById(`item-${ $(cloned_items[i]).attr('data-id')}`);
|
||||
if ( source_item !== null )
|
||||
{
|
||||
items_to_move.push(source_item);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// If `options.onItemsDrop` is set, call it with the items to move
|
||||
//--------------------------------------------------------
|
||||
if ( options.onItemsDrop && typeof options.onItemsDrop === 'function' ) {
|
||||
options.onItemsDrop(items_to_move);
|
||||
return;
|
||||
}
|
||||
// --------------------------------------------------------
|
||||
// If dropped on an app, open the app with the dropped item as an argument
|
||||
//--------------------------------------------------------
|
||||
else if ( options.app ) {
|
||||
// an array that hold the items to sign
|
||||
const items_to_sign = [];
|
||||
|
||||
// prepare items to sign
|
||||
for ( let i = 0; i < items_to_move.length; i++ ) {
|
||||
items_to_sign.push({
|
||||
name: $(items_to_move[i]).attr('data-name'),
|
||||
uid: $(items_to_move[i]).attr('data-uid'),
|
||||
action: 'write',
|
||||
path: $(items_to_move[i]).attr('data-path'),
|
||||
});
|
||||
}
|
||||
|
||||
// open each item
|
||||
for ( let i = 0; i < items_to_sign.length; i++ ) {
|
||||
const item = items_to_sign[i];
|
||||
launch_app({
|
||||
name: options.app,
|
||||
file_path: item.path,
|
||||
// app_obj: open_item_meta.suggested_apps[0],
|
||||
window_title: item.name,
|
||||
file_uid: item.uid,
|
||||
// file_signature: item,
|
||||
});
|
||||
}
|
||||
|
||||
// deselect dragged item
|
||||
for ( let i = 0; i < items_to_move.length; i++ )
|
||||
{
|
||||
$(items_to_move[i]).removeClass('item-selected');
|
||||
}
|
||||
}
|
||||
|
||||
// Unselect directory/app if item is dropped
|
||||
if ( options.is_dir || options.app ) {
|
||||
$(el_taskbar_item).removeClass('active');
|
||||
$(el_taskbar_item).tooltip('close');
|
||||
$('.ui-draggable-dragging .item-name, .item-selected-clone .item-name').css('opacity', 'initial');
|
||||
$('.item-container').removeClass('item-container-transparent-border');
|
||||
}
|
||||
|
||||
// Re-enable droppable on all item-container
|
||||
$('.item-container').droppable('enable');
|
||||
|
||||
return false;
|
||||
},
|
||||
over: function (event, ui) {
|
||||
// Check hovering over an item that is VISIBILE
|
||||
const $event_parent_win = $(event.target).closest('.window');
|
||||
if ( $event_parent_win.length > 0 && $event_parent_win.attr('data-id') !== $(window.mouseover_window).attr('data-id') )
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Don't do anything if the dragged item is NOT a UIItem
|
||||
if ( ! $(ui.draggable).hasClass('item') )
|
||||
{
|
||||
return;
|
||||
}
|
||||
// If this is a directory or an app, and an item was dragged over it, highlight it.
|
||||
if ( options.is_dir || options.app ) {
|
||||
$(el_taskbar_item).addClass('active');
|
||||
// show tooltip of this item
|
||||
$(el_taskbar_item).tooltip().mouseover();
|
||||
// make item name partially transparent
|
||||
$('.ui-draggable-dragging .item-name, .item-selected-clone .item-name').css('opacity', 0.1);
|
||||
// remove all item-container active borders
|
||||
$('.item-container').addClass('item-container-transparent-border');
|
||||
}
|
||||
// Disable all window bodies
|
||||
$('.item-container').droppable( 'disable');
|
||||
},
|
||||
out: function (event, ui) {
|
||||
// Don't do anything if the dragged item is NOT a UIItem
|
||||
if ( ! $(ui.draggable).hasClass('item') )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Unselect directory/app if item is dragged out
|
||||
if ( options.is_dir || options.app ) {
|
||||
$(el_taskbar_item).removeClass('active');
|
||||
$(el_taskbar_item).tooltip('close');
|
||||
$('.ui-draggable-dragging .item-name, .item-selected-clone .item-name').css('opacity', 'initial');
|
||||
$('.item-container').removeClass('item-container-transparent-border');
|
||||
}
|
||||
$('.item-container').droppable( 'enable');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return el_taskbar_item;
|
||||
}
|
||||
|
||||
export default UITaskbarItem;
|
||||
@@ -0,0 +1,123 @@
|
||||
// @vitest-environment jsdom
|
||||
/*
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import jQuery from '../lib/jquery-3.6.1/jquery-3.6.1.min.js';
|
||||
|
||||
vi.mock('../helpers/launchApp.js', () => ({ default: vi.fn() }));
|
||||
|
||||
globalThis.$ = globalThis.jQuery = jQuery;
|
||||
globalThis.i18n = (key) => key;
|
||||
globalThis.html_encode = (value) => String(value ?? '');
|
||||
globalThis.isMobile = { phone: false, tablet: false };
|
||||
globalThis.def = globalThis.def ?? ((value) => value);
|
||||
await import('../lib/jquery-ui-1.13.2/jquery-ui.js');
|
||||
// The page loads this plugin from a vendored script; nothing here drops files.
|
||||
$.fn.dragster = function () {
|
||||
return this;
|
||||
};
|
||||
const { default: UIWindow } = await import('./UIWindow.js');
|
||||
|
||||
const openWindow = (options = {}) => UIWindow({
|
||||
title: 'Notes',
|
||||
app: 'notes',
|
||||
body_content: '<p>hello</p>',
|
||||
width: 400,
|
||||
height: 300,
|
||||
...options,
|
||||
});
|
||||
|
||||
/** Opens the window head's context menu and returns its item labels. */
|
||||
const headMenuLabels = (el_window) => {
|
||||
$(el_window).find('.window-head').trigger('contextmenu');
|
||||
return $('.context-menu .contextmenu-label').map((_, el) => $(el).text().toLowerCase()).get();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = '<div class="desktop"></div>';
|
||||
Object.assign(window, {
|
||||
global_element_id: 0,
|
||||
window_counter: 0,
|
||||
last_window_zindex: 1,
|
||||
window_stack: [],
|
||||
toolbar_height: 0,
|
||||
icons: {},
|
||||
uuidv4: () => crypto.randomUUID(),
|
||||
is_dashboard_mode: false,
|
||||
is_fullpage_mode: false,
|
||||
is_embedded: false,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
$(document).off();
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
describe('minimize', () => {
|
||||
it('is not offered on the desktop, where nothing could restore the window', async () => {
|
||||
const el_window = await openWindow();
|
||||
|
||||
expect($(el_window).find('.window-minimize-btn').length).toBe(0);
|
||||
|
||||
const labels = headMenuLabels(el_window);
|
||||
expect(labels.some((label) => label === 'maximize')).toBe(true);
|
||||
expect(labels.some((label) => label === 'minimize')).toBe(false);
|
||||
});
|
||||
|
||||
it('is offered on the dashboard, which restores the window from its tile', async () => {
|
||||
window.is_dashboard_mode = true;
|
||||
const el_window = await openWindow();
|
||||
|
||||
expect($(el_window).find('.window-minimize-btn').length).toBe(1);
|
||||
expect(headMenuLabels(el_window).some((label) => label === 'minimize')).toBe(true);
|
||||
});
|
||||
|
||||
it('hides the window in place and brings it back', async () => {
|
||||
window.is_dashboard_mode = true;
|
||||
const el_window = await openWindow();
|
||||
const before = { top: el_window.style.top, left: el_window.style.left };
|
||||
|
||||
await $(el_window).hideWindow();
|
||||
expect($(el_window).attr('data-is_minimized')).toBe('true');
|
||||
expect($(el_window).attr('data-minimized_in_place')).toBe('1');
|
||||
|
||||
await $(el_window).showWindow({ no_history: true });
|
||||
expect($(el_window).attr('data-is_minimized')).toBe('false');
|
||||
expect({ top: el_window.style.top, left: el_window.style.left }).toEqual(before);
|
||||
});
|
||||
});
|
||||
|
||||
describe('backdrop', () => {
|
||||
const backdropZ = (el_window) => Number($(el_window).closest('.window-backdrop').css('z-index'));
|
||||
|
||||
it('stays above ordinary windows on the desktop', async () => {
|
||||
const el_window = await openWindow({ backdrop: true });
|
||||
|
||||
expect(backdropZ(el_window)).toBeGreaterThan(window.last_window_zindex + 1000);
|
||||
});
|
||||
|
||||
it('stacks at the window counter on the dashboard', async () => {
|
||||
window.is_dashboard_mode = true;
|
||||
const el_window = await openWindow({ backdrop: true });
|
||||
|
||||
expect(backdropZ(el_window)).toBeLessThanOrEqual(window.last_window_zindex);
|
||||
});
|
||||
});
|
||||
+248
-505
@@ -20,7 +20,6 @@
|
||||
import UIAlert from './UIAlert.js';
|
||||
import UIContextMenu from './UIContextMenu.js';
|
||||
import path from '../lib/path.js';
|
||||
import UITaskbarItem from './UITaskbarItem.js';
|
||||
import UIAuthLogin from './Auth/UIAuthLogin.js';
|
||||
import UIWindowItemProperties from './UIWindowItemProperties.js';
|
||||
import new_context_menu_item from '../helpers/newContextMenuItem.js';
|
||||
@@ -38,6 +37,8 @@ import { is_window_hidden, is_unseen_background_window, user_facing_windows } fr
|
||||
|
||||
const el_body = document.getElementsByTagName('body')[0];
|
||||
const SNAP_PLACEHOLDER_DELAY_MS = 600; // delay before showing placeholder in any snap zone
|
||||
// Above any z-index an ordinary desktop window reaches in practice.
|
||||
const DESKTOP_BACKDROP_MIN_ZINDEX = 99999;
|
||||
|
||||
async function UIWindow (options) {
|
||||
const win_id = window.global_element_id++;
|
||||
@@ -105,7 +106,7 @@ async function UIWindow (options) {
|
||||
options.is_resizable = false;
|
||||
}
|
||||
|
||||
// In the embedded/fullpage mode every window is on top since there is no taskbar to switch between windows
|
||||
// In the embedded/fullpage mode every window is on top since there is nothing to switch between windows
|
||||
// if user has specifically asked for this window to NOT stay on top, honor it.
|
||||
if ( (window.is_embedded || window.is_fullpage_mode) && !options.parent_uuid && options.stay_on_top !== false )
|
||||
{
|
||||
@@ -115,11 +116,11 @@ async function UIWindow (options) {
|
||||
options.stay_on_top = options.stay_on_top ?? false;
|
||||
|
||||
options.is_saveFileDialog = options.is_saveFileDialog ?? false;
|
||||
options.show_minimize_button = options.show_minimize_button ?? true;
|
||||
// Only the dashboard can restore a minimized window, from the app's tile.
|
||||
options.show_minimize_button = (options.show_minimize_button ?? true) && !!window.is_dashboard_mode;
|
||||
options.on_close = options.on_close ?? undefined;
|
||||
options.parent_uuid = options.parent_uuid ?? null;
|
||||
options.selectable_body = (options.selectable_body === undefined || options.selectable_body === true) ? true : false;
|
||||
options.show_in_taskbar = options.show_in_taskbar ?? true;
|
||||
options.show_maximize_button = options.show_maximize_button ?? true;
|
||||
options.single_instance = options.single_instance ?? false;
|
||||
options.sort_by = options.sort_by ?? 'name';
|
||||
@@ -539,18 +540,11 @@ async function UIWindow (options) {
|
||||
|
||||
// backdrop
|
||||
if ( options.backdrop ) {
|
||||
let backdrop_zindex;
|
||||
// backdrop should also cover over taskbar
|
||||
let taskbar_zindex = $('.taskbar').css('z-index');
|
||||
if ( taskbar_zindex === null || taskbar_zindex === undefined )
|
||||
{
|
||||
backdrop_zindex = zindex;
|
||||
}
|
||||
else {
|
||||
taskbar_zindex = parseInt(taskbar_zindex);
|
||||
backdrop_zindex = taskbar_zindex > zindex ? taskbar_zindex : zindex;
|
||||
}
|
||||
|
||||
// On the desktop a backdrop stays above every ordinary window, even one
|
||||
// focused after it opened, so the dialog it holds stays modal.
|
||||
const backdrop_zindex = (options.stay_on_top || window.is_dashboard_mode || window.is_fullpage_mode)
|
||||
? zindex
|
||||
: Math.max(zindex, DESKTOP_BACKDROP_MIN_ZINDEX);
|
||||
h = `<div class="window-backdrop" style="z-index:${backdrop_zindex};">${ h }</div>`;
|
||||
}
|
||||
|
||||
@@ -593,56 +587,6 @@ async function UIWindow (options) {
|
||||
const el_directorypicker_select_btn = document.querySelector(`#window-${win_id} .directorypicker-select-btn`);
|
||||
const el_window_filedialog_upload_here = document.querySelector(`#window-${win_id} .window-filedialog-upload-here`);
|
||||
|
||||
// Add Taskbar Item
|
||||
// data-in_taskbar records that this window is one of the item's
|
||||
// data-open-windows, so the close path only decrements a count this
|
||||
// window actually added to (see the close handler) — the two must stay
|
||||
// in step, and a window that never took an item must not take one away.
|
||||
const add_taskbar_item = () => {
|
||||
$(el_window).attr('data-in_taskbar', '1');
|
||||
// add icon if there is no similar app already open
|
||||
if ( $(`.taskbar-item[data-app="${options.app}"]`).length === 0 ) {
|
||||
UITaskbarItem({
|
||||
icon: options.icon,
|
||||
name: options.title,
|
||||
app: options.app,
|
||||
open_windows_count: 1,
|
||||
before_trash: true,
|
||||
onClick: function () {
|
||||
let open_window_count = parseInt($(`.taskbar-item[data-app="${options.app}"]`).attr('data-open-windows'));
|
||||
if ( open_window_count === 0 ) {
|
||||
launch_app({
|
||||
name: options.app,
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
if ( options.app )
|
||||
{
|
||||
$(`.taskbar-item[data-app="${options.app}"] .active-taskbar-indicator`).show();
|
||||
}
|
||||
} else {
|
||||
if ( options.app ) {
|
||||
$(`.taskbar-item[data-app="${options.app}"]`).attr('data-open-windows', parseInt($(`.taskbar-item[data-app="${options.app}"]`).attr('data-open-windows')) + 1);
|
||||
$(`.taskbar-item[data-app="${options.app}"] .active-taskbar-indicator`).show();
|
||||
}
|
||||
}
|
||||
};
|
||||
if ( !options.is_openFileDialog && !options.is_saveFileDialog && !options.is_directoryPicker && options.show_in_taskbar ) {
|
||||
// A window started hidden by a background launch gets no taskbar item
|
||||
// yet: it runs for the app that launched it, and an item would both
|
||||
// advertise and hand out an instance the user never asked for. The
|
||||
// item is what the window earns the moment it first becomes visible
|
||||
// — makeWindowVisible calls this, whether the app showed itself with
|
||||
// puter.ui.showWindow() or something else revealed it. An app that is
|
||||
// ALWAYS windowless never reaches here: launch_app leaves
|
||||
// show_in_taskbar false for it, so it keeps having no item at all.
|
||||
if ( options.launched_hidden ) el_window._add_taskbar_item = add_taskbar_item;
|
||||
else add_taskbar_item();
|
||||
}
|
||||
|
||||
if ( el_window_filedialog_upload_here ) {
|
||||
el_window_filedialog_upload_here.addEventListener('click', function () {
|
||||
window.init_upload_using_dialog(el_window_body, `${$(el_window).attr('data-path') }/`);
|
||||
@@ -667,8 +611,7 @@ async function UIWindow (options) {
|
||||
// shrink icon
|
||||
$(el_window).find('.window-scale-btn>img').attr('src', window.icons['scale-down-3.svg']);
|
||||
|
||||
// Use taskbar position-aware window positioning
|
||||
window.update_maximized_window_for_taskbar(el_window);
|
||||
window.update_maximized_window(el_window);
|
||||
}
|
||||
|
||||
// when a window is created, focus is brought to it and
|
||||
@@ -2046,7 +1989,6 @@ async function UIWindow (options) {
|
||||
$(window_snap_placeholder).css('padding', 0);
|
||||
|
||||
setTimeout(function () {
|
||||
// Get taskbar-aware snap dimensions for final positioning
|
||||
const snapDims = getSnapDimensions();
|
||||
|
||||
// snap to w
|
||||
@@ -2133,39 +2075,22 @@ async function UIWindow (options) {
|
||||
}
|
||||
|
||||
// if window is dropped outside the available area, move it back in
|
||||
// Bottom boundary (account for taskbar position)
|
||||
const taskbar_position = window.taskbar_position || 'bottom';
|
||||
let maxTop;
|
||||
if ( taskbar_position === 'bottom' ) {
|
||||
maxTop = window.innerHeight - window.taskbar_height - 30;
|
||||
} else {
|
||||
maxTop = window.innerHeight - 30;
|
||||
}
|
||||
// the lst '- 30' is to account for the window head
|
||||
// the '- 30' is to account for the window head
|
||||
const maxTop = window.innerHeight - 30;
|
||||
if ( $(el_window).position().top > maxTop && !window_will_snap ) {
|
||||
$(el_window).animate({
|
||||
top: maxTop - 30,
|
||||
}, 100);
|
||||
}
|
||||
// if window is dropped too far to the right, move it left
|
||||
let maxLeft;
|
||||
if ( taskbar_position === 'right' ) {
|
||||
maxLeft = window.innerWidth - window.taskbar_height - 50;
|
||||
} else {
|
||||
maxLeft = window.innerWidth - 50;
|
||||
}
|
||||
const maxLeft = window.innerWidth - 50;
|
||||
if ( $(el_window).position().left > maxLeft && !window_will_snap ) {
|
||||
$(el_window).animate({
|
||||
left: maxLeft,
|
||||
}, 100);
|
||||
}
|
||||
// if window is dropped too far to the left, move it right
|
||||
let minLeft;
|
||||
if ( taskbar_position === 'left' ) {
|
||||
minLeft = window.taskbar_height - $(el_window).width() + 150;
|
||||
} else {
|
||||
minLeft = -$(el_window).width() + 150;
|
||||
}
|
||||
const minLeft = -$(el_window).width() + 150;
|
||||
if ( $(el_window).position().left < minLeft && !window_will_snap ) {
|
||||
$(el_window).animate({
|
||||
left: minLeft,
|
||||
@@ -2384,12 +2309,14 @@ async function UIWindow (options) {
|
||||
window.scale_window(el_window);
|
||||
},
|
||||
});
|
||||
menu_items.push({
|
||||
html: i18n('minimize'),
|
||||
onClick: function () {
|
||||
minimize_window(el_window);
|
||||
},
|
||||
});
|
||||
if ( window.is_dashboard_mode ) {
|
||||
menu_items.push({
|
||||
html: i18n('minimize'),
|
||||
onClick: function () {
|
||||
minimize_window(el_window);
|
||||
},
|
||||
});
|
||||
}
|
||||
menu_items.push({
|
||||
html: 'Advanced',
|
||||
items: [
|
||||
@@ -2665,7 +2592,7 @@ async function UIWindow (options) {
|
||||
left = left > (window.innerWidth - window_width) ? (window.innerWidth - window_width) : left;
|
||||
|
||||
let top = window.mouseY;
|
||||
top = top > (window.innerHeight - (window_height + window.taskbar_height + window.toolbar_height)) ? (window.innerHeight - (window_height + window.taskbar_height + window.toolbar_height)) : top;
|
||||
top = top > (window.innerHeight - (window_height + window.toolbar_height)) ? (window.innerHeight - (window_height + window.toolbar_height)) : top;
|
||||
|
||||
UIWindowItemProperties(
|
||||
options.title,
|
||||
@@ -2889,10 +2816,6 @@ $(document).on('click', '.window-sidebar-item', async function (e) {
|
||||
// todo
|
||||
// sort_by: $(el_item).attr('data-sort_by'),
|
||||
app: 'explorer',
|
||||
// top: options.maximized ? 0 : undefined,
|
||||
// left: options.maximized ? 0 : undefined,
|
||||
// height: options.maximized ? `calc(100% - ${window.taskbar_height + 1}px)` : undefined,
|
||||
// width: options.maximized ? `100%` : undefined,
|
||||
});
|
||||
}
|
||||
// update window path only if it's a new path AND no ctrl/cmd key pressed
|
||||
@@ -2953,10 +2876,6 @@ $(document).on('contextmenu taphold', '.window-sidebar-item', function (event) {
|
||||
// todo
|
||||
// sort_by: $(el_item).attr('data-sort_by'),
|
||||
app: 'explorer',
|
||||
// top: options.maximized ? 0 : undefined,
|
||||
// left: options.maximized ? 0 : undefined,
|
||||
// height: options.maximized ? `calc(100% - ${window.taskbar_height + 1}px)` : undefined,
|
||||
// width: options.maximized ? `100%` : undefined,
|
||||
});
|
||||
},
|
||||
},
|
||||
@@ -2969,7 +2888,7 @@ $(document).on('dblclick', '.window .ui-resizable-handle', function (e) {
|
||||
let el_window = $(this).closest('.window');
|
||||
// bottom
|
||||
if ( $(this).hasClass('ui-resizable-s') ) {
|
||||
let height = window.innerHeight - $(el_window).position().top - window.taskbar_height - 6;
|
||||
let height = window.innerHeight - $(el_window).position().top - 6;
|
||||
$(el_window).height(height);
|
||||
}
|
||||
|
||||
@@ -2999,7 +2918,7 @@ $(document).on('dblclick', '.window .ui-resizable-handle', function (e) {
|
||||
// bottom left
|
||||
else if ( $(this).hasClass('ui-resizable-sw') ) {
|
||||
let width = $(el_window).width() + $(el_window).position().left;
|
||||
let height = window.innerHeight - $(el_window).position().top - window.taskbar_height - 6;
|
||||
let height = window.innerHeight - $(el_window).position().top - 6;
|
||||
$(el_window).css({
|
||||
width: width,
|
||||
height: height,
|
||||
@@ -3009,7 +2928,7 @@ $(document).on('dblclick', '.window .ui-resizable-handle', function (e) {
|
||||
// bottom right
|
||||
else if ( $(this).hasClass('ui-resizable-se') ) {
|
||||
let width = window.innerWidth - $(el_window).position().left;
|
||||
let height = window.innerHeight - $(el_window).position().top - window.taskbar_height - 6;
|
||||
let height = window.innerHeight - $(el_window).position().top - 6;
|
||||
$(el_window).css({
|
||||
width: width,
|
||||
height: height,
|
||||
@@ -3665,30 +3584,7 @@ $.fn.close = async function (options) {
|
||||
let window_uuid = $(this).attr('data-element_uuid');
|
||||
// remove all instances of win_id from window.window_stack
|
||||
window.window_stack = window.window_stack.filter(id => id !== win_id);
|
||||
// taskbar update — only for a window that IS one of the item's
|
||||
// open windows (data-in_taskbar, stamped when it took its place
|
||||
// in the count). A window that never counted must not decrement:
|
||||
// an unseen background instance closing alongside the user's own
|
||||
// window would otherwise zero the count and take the item away
|
||||
// while that window is still open.
|
||||
if ( $(this).attr('data-in_taskbar') === '1' ) {
|
||||
let open_window_count = parseInt($(`.taskbar-item[data-app="${$(this).attr('data-app')}"]`).attr('data-open-windows'));
|
||||
// update open window count of corresponding taskbar item
|
||||
if ( open_window_count > 0 ) {
|
||||
$(`.taskbar-item[data-app="${$(this).attr('data-app')}"]`).attr('data-open-windows', open_window_count - 1);
|
||||
}
|
||||
// decide whether to remove taskbar item
|
||||
if ( open_window_count === 1 ) {
|
||||
$(`.taskbar-item[data-app="${$(this).attr('data-app')}"] .active-taskbar-indicator`).hide();
|
||||
window.remove_taskbar_item($(`.taskbar-item[data-app="${$(this).attr('data-app')}"][data-keep-in-taskbar="false"]`));
|
||||
}
|
||||
// if no more windows of this app are open, remove taskbar item
|
||||
if ( open_window_count - 1 === 0 )
|
||||
{
|
||||
$(`.taskbar-item[data-app="${$(this).attr('data-app')}"] .active-taskbar-indicator`).hide();
|
||||
}
|
||||
}
|
||||
// if a fullpage window is closed, show desktop and taskbar
|
||||
// if a fullpage window is closed, show the desktop
|
||||
if ( $(this).attr('data-is_fullpage') === '1' ) {
|
||||
window.exit_fullpage_mode();
|
||||
}
|
||||
@@ -3836,8 +3732,7 @@ window.scale_window = (el_window) => {
|
||||
// shrink icon
|
||||
$(el_window).find('.window-scale-btn>img').attr('src', window.icons['scale-down-3.svg']);
|
||||
|
||||
// Use taskbar position-aware window positioning
|
||||
window.update_maximized_window_for_taskbar(el_window);
|
||||
window.update_maximized_window(el_window);
|
||||
}
|
||||
//shrink
|
||||
else {
|
||||
@@ -3961,19 +3856,12 @@ $.fn.makeWindowVisible = function (options) {
|
||||
if ( $(this).hasClass('window') ) {
|
||||
// Seen by the user, so no longer a window that exists purely to
|
||||
// serve whoever launched it: it outlives its launcher from here on
|
||||
// (see the close path's cleanup of background children), and it
|
||||
// becomes a window the user can act on — hence the taskbar item
|
||||
// its launch deferred, and the dashboard tile's running dot.
|
||||
// Both happen BEFORE the window shows: focusWindow() below marks
|
||||
// the app's taskbar item active, which needs the item to exist.
|
||||
// (see the close path's cleanup of background children), and the
|
||||
// dashboard tile shows it as running.
|
||||
const was_launched_hidden = $(this).attr('data-launched_hidden') === '1';
|
||||
$(this).removeAttr('data-launched_hidden');
|
||||
if ( was_launched_hidden ) {
|
||||
this._add_taskbar_item?.();
|
||||
delete this._add_taskbar_item;
|
||||
if ( window.is_dashboard_mode && $(this).attr('data-app') ) {
|
||||
document.dispatchEvent(new CustomEvent('dashboard-app-windows-changed'));
|
||||
}
|
||||
if ( was_launched_hidden && window.is_dashboard_mode && $(this).attr('data-app') ) {
|
||||
document.dispatchEvent(new CustomEvent('dashboard-app-windows-changed'));
|
||||
}
|
||||
|
||||
$(this).show();
|
||||
@@ -4020,13 +3908,8 @@ $.fn.makeWindowInvisible = async function (options) {
|
||||
$.fn.showWindow = async function (options) {
|
||||
$(this).each(async function () {
|
||||
if ( $(this).hasClass('window') ) {
|
||||
// A window hidden by puter.ui.hideWindow() is not minimized: it
|
||||
// kept its geometry and has no data-orig-* to restore, so every
|
||||
// path below (all of which un-minimize) would leave it hidden —
|
||||
// and stamp NaN geometry on it from the absent attributes. Simply
|
||||
// un-hiding it is the whole job, and the inverse of what hid it.
|
||||
// This is what makes the taskbar item a real handle on a window
|
||||
// the user cannot currently see.
|
||||
// A window hidden by puter.ui.hideWindow() is not minimized and
|
||||
// kept its geometry, so un-hiding it is the whole job.
|
||||
if ( is_window_hidden(this) ) {
|
||||
$(this).makeWindowVisible();
|
||||
return;
|
||||
@@ -4044,92 +3927,63 @@ $.fn.showWindow = async function (options) {
|
||||
? 99999999 + (++window.last_window_zindex)
|
||||
: ++window.last_window_zindex);
|
||||
|
||||
// A window minimized with no taskbar to animate toward (dashboard
|
||||
// mode) was hidden in place by hideWindow — its geometry was
|
||||
// never disturbed. If the app's tile is visible on the Apps tab's
|
||||
// current page, grow the window back out of it (the reverse of
|
||||
// hideWindow's zoom-to-tile morph); otherwise just fade it back
|
||||
// in as before. (The explicit flag is used because data-orig-*
|
||||
// can't distinguish the two minimize paths: drag/maximize
|
||||
// handlers set it too.)
|
||||
if ( $(el_window).attr('data-minimized_in_place') === '1' ) {
|
||||
$(el_window).attr({
|
||||
'data-is_minimized': false,
|
||||
'data-minimized_in_place': '0',
|
||||
});
|
||||
// On screen again (Back, tile click, Forward), so it is no
|
||||
// longer minimized-because-a-child-took-over: closing that
|
||||
// child must not now send the user to the dashboard.
|
||||
$(el_window).removeAttr('data-minimized_for_child');
|
||||
$(el_window).css('z-index', raised_zindex());
|
||||
const reduce_motion = window.matchMedia
|
||||
&& window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
// Skip the morph while another morph still owns the window's
|
||||
// inline styles (reopen mid-minimize-zoom): clearing the
|
||||
// flags above already makes that animation's cleanup leave
|
||||
// the window visible and restored, same as before.
|
||||
const tile = (reduce_motion
|
||||
|| $(el_window).attr('data-window_morphing') === '1')
|
||||
? null
|
||||
: dashboard_tile_in_view($(el_window).attr('data-app'));
|
||||
let morphed = false;
|
||||
if ( tile ) {
|
||||
// The morph needs the window displayed and laid out to
|
||||
// measure it; no paint happens between this show() and
|
||||
// the morph's start state, so nothing flashes.
|
||||
const was_hidden = ! $(el_window).is(':visible');
|
||||
if ( was_hidden ) $(el_window).show();
|
||||
// The lighter cut of the launch morph — window half
|
||||
// only, on a shorter path — so un-minimizing reads as
|
||||
// the window coming back out of the tile, not the app
|
||||
// launching again (no icon flourish, no launch pacing).
|
||||
morphed = morph_window_from_tile(el_window, tile, {
|
||||
icon_half: false,
|
||||
duration: 0.3,
|
||||
});
|
||||
if ( ! morphed && was_hidden ) $(el_window).hide();
|
||||
}
|
||||
if ( ! morphed ) $(el_window).fadeIn(150);
|
||||
// Restores come back with the control drawer collapsed —
|
||||
// whatever state it was minimized in (headless dashboard
|
||||
// windows only; no-op otherwise).
|
||||
el_window._dashboard_drawer_collapse?.();
|
||||
// A restore re-claims the URL for this app — except when
|
||||
// the restore was DRIVEN by a history traversal (popstate
|
||||
// passes no_history: the entry is already current).
|
||||
if ( ! options?.no_history
|
||||
&& $(el_window).attr('data-update_window_url') === 'true'
|
||||
&& $(el_window).attr('data-app') !== 'explorer' ) {
|
||||
push_dashboard_app_url($(el_window).attr('data-app'), $(el_window).attr('data-name'));
|
||||
}
|
||||
setTimeout(() => {
|
||||
$(this).focusWindow();
|
||||
}, 80);
|
||||
return;
|
||||
}
|
||||
|
||||
$(el_window).css({
|
||||
'transition': 'top 0.2s, left 0.2s, bottom 0.2s, right 0.2s, width 0.2s, height 0.2s',
|
||||
top: `${$(el_window).attr('data-orig-top') }px`,
|
||||
left: `${$(el_window).attr('data-orig-left') }px`,
|
||||
width: `${$(el_window).attr('data-orig-width') }px`,
|
||||
height: `${$(el_window).attr('data-orig-height') }px`,
|
||||
});
|
||||
$(el_window).css('z-index', raised_zindex());
|
||||
|
||||
// hideWindow hid the window in place, so its geometry was never
|
||||
// disturbed. If the app's tile is visible on the Apps tab's current
|
||||
// page, grow the window back out of it (the reverse of hideWindow's
|
||||
// zoom-to-tile morph); otherwise just fade it back in.
|
||||
if ( $(el_window).attr('data-minimized_in_place') !== '1' ) return;
|
||||
$(el_window).attr({
|
||||
'data-is_minimized': false,
|
||||
'data-minimized_in_place': '0',
|
||||
});
|
||||
|
||||
// On screen again (Back, tile click, Forward), so it is no
|
||||
// longer minimized-because-a-child-took-over: closing that
|
||||
// child must not now send the user to the dashboard.
|
||||
$(el_window).removeAttr('data-minimized_for_child');
|
||||
$(el_window).css('z-index', raised_zindex());
|
||||
const reduce_motion = window.matchMedia
|
||||
&& window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
// Skip the morph while another morph still owns the window's
|
||||
// inline styles (reopen mid-minimize-zoom): clearing the
|
||||
// flags above already makes that animation's cleanup leave
|
||||
// the window visible and restored, same as before.
|
||||
const tile = (reduce_motion
|
||||
|| $(el_window).attr('data-window_morphing') === '1')
|
||||
? null
|
||||
: dashboard_tile_in_view($(el_window).attr('data-app'));
|
||||
let morphed = false;
|
||||
if ( tile ) {
|
||||
// The morph needs the window displayed and laid out to
|
||||
// measure it; no paint happens between this show() and
|
||||
// the morph's start state, so nothing flashes.
|
||||
const was_hidden = ! $(el_window).is(':visible');
|
||||
if ( was_hidden ) $(el_window).show();
|
||||
// The lighter cut of the launch morph — window half
|
||||
// only, on a shorter path — so un-minimizing reads as
|
||||
// the window coming back out of the tile, not the app
|
||||
// launching again (no icon flourish, no launch pacing).
|
||||
morphed = morph_window_from_tile(el_window, tile, {
|
||||
icon_half: false,
|
||||
duration: 0.3,
|
||||
});
|
||||
if ( ! morphed && was_hidden ) $(el_window).hide();
|
||||
}
|
||||
if ( ! morphed ) $(el_window).fadeIn(150);
|
||||
// Restores come back with the control drawer collapsed —
|
||||
// whatever state it was minimized in (headless dashboard
|
||||
// windows only; no-op otherwise).
|
||||
el_window._dashboard_drawer_collapse?.();
|
||||
// A restore re-claims the URL for this app — except when
|
||||
// the restore was DRIVEN by a history traversal (popstate
|
||||
// passes no_history: the entry is already current).
|
||||
if ( ! options?.no_history
|
||||
&& $(el_window).attr('data-update_window_url') === 'true'
|
||||
&& $(el_window).attr('data-app') !== 'explorer' ) {
|
||||
push_dashboard_app_url($(el_window).attr('data-app'), $(el_window).attr('data-name'));
|
||||
}
|
||||
setTimeout(() => {
|
||||
$(this).focusWindow();
|
||||
}, 80);
|
||||
|
||||
// remove `transitions` a good while after setting css to make sure
|
||||
// it doesn't interfere with an ongoing animation
|
||||
setTimeout(() => {
|
||||
$(el_window).css('transition', 'none');
|
||||
}, 250);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
@@ -4251,7 +4105,6 @@ $.fn.focusWindow = function (event) {
|
||||
document.title = $(this).attr('data-name');
|
||||
}
|
||||
}
|
||||
$(`.taskbar .taskbar-item[data-app="${$(this).attr('data-app')}"]`).addClass('taskbar-item-active');
|
||||
} else {
|
||||
$('.window').find('.item-selected').addClass('item-blurred');
|
||||
$('.desktop').find('.item-blurred').removeClass('item-blurred');
|
||||
@@ -5233,230 +5086,167 @@ function morph_window_from_tile (el_window, tile, morph_options = {}) {
|
||||
$.fn.hideWindow = async function (options) {
|
||||
$(this).each(async function () {
|
||||
if ( $(this).hasClass('window') ) {
|
||||
// get taskbar item location
|
||||
let taskbar_item_pos = $(`.taskbar .taskbar-item[data-app="${$(this).attr('data-app')}"]`).position();
|
||||
// If the app's tile is visible on the dashboard's Apps tab, shrink
|
||||
// the window into it so the user sees where the app went; otherwise
|
||||
// simply hide the window in place. Either way the geometry is
|
||||
// untouched afterwards and showWindow() un-hides via the
|
||||
// data-minimized_in_place flag.
|
||||
const el_window = this;
|
||||
const $win = $(this);
|
||||
const reduce_motion = window.matchMedia
|
||||
&& window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
// (A morph already in flight — either direction — owns the
|
||||
// window's inline transform/opacity and will restore them at
|
||||
// its end, so don't start another on top; fall back to the
|
||||
// plain fade below, exactly as before the morphs existed.)
|
||||
const tile = (reduce_motion
|
||||
|| $win.attr('data-window_morphing') === '1')
|
||||
? null
|
||||
: dashboard_tile_in_view($win.attr('data-app'));
|
||||
|
||||
// No taskbar item to animate toward (e.g. dashboard mode, which
|
||||
// has no taskbar): if the app's tile is visible on the Apps tab's
|
||||
// current page, shrink the window into it so the user sees where
|
||||
// the app went; otherwise simply hide the window in place. Either
|
||||
// way the geometry is untouched afterwards and showWindow()
|
||||
// un-hides via the data-minimized_in_place flag.
|
||||
if ( ! taskbar_item_pos ) {
|
||||
const el_window = this;
|
||||
const $win = $(this);
|
||||
const reduce_motion = window.matchMedia
|
||||
&& window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
// (A morph already in flight — either direction — owns the
|
||||
// window's inline transform/opacity and will restore them at
|
||||
// its end, so don't start another on top; fall back to the
|
||||
// plain fade below, exactly as before the morphs existed.)
|
||||
const tile = (reduce_motion
|
||||
|| $win.attr('data-window_morphing') === '1')
|
||||
? null
|
||||
: dashboard_tile_in_view($win.attr('data-app'));
|
||||
if ( tile ) {
|
||||
// iOS-style zoom-to-icon morph, two congruent layers on
|
||||
// the same 450ms decelerating path so it reads as ONE
|
||||
// object transforming:
|
||||
// 1. the LIVE window scales onto the tile's icon box,
|
||||
// its corner radius growing toward the squircle,
|
||||
// fading OUT once it has mostly shrunk;
|
||||
// 2. an enlarged clone of the icon ("ghost") starts
|
||||
// covering the window and flies/shrinks with it,
|
||||
// fading IN as the window fades out, landing exactly
|
||||
// on the real icon's slot (the real icon is hidden
|
||||
// until the ghost lands, so nothing doubles).
|
||||
// Transform + opacity animate on the compositor (no
|
||||
// per-frame reflow of the app's iframe); the window's
|
||||
// geometry (top/left/width/height) is never touched, so
|
||||
// restore is trivial.
|
||||
const icon = tile.querySelector('.myapps-tile-icon') || tile;
|
||||
const win_rect = el_window.getBoundingClientRect();
|
||||
const icon_rect = icon.getBoundingClientRect();
|
||||
if ( win_rect.width > 0 && win_rect.height > 0
|
||||
&& icon_rect.width > 0 && icon_rect.height > 0 ) {
|
||||
// Everything touched is restored from these exact
|
||||
// inline values once the animation ends.
|
||||
const orig_style = {
|
||||
transform: el_window.style.transform,
|
||||
transformOrigin: el_window.style.transformOrigin,
|
||||
opacity: el_window.style.opacity,
|
||||
borderRadius: el_window.style.borderRadius,
|
||||
overflow: el_window.style.overflow,
|
||||
willChange: el_window.style.willChange,
|
||||
pointerEvents: el_window.style.pointerEvents,
|
||||
transition: el_window.style.transition,
|
||||
};
|
||||
const icon_orig_visibility = icon.style.visibility;
|
||||
// Map the window rect exactly onto the icon rect (and
|
||||
// the ghost the exact inverse: icon rect onto window
|
||||
// rect), so the two layers stay congruent throughout.
|
||||
const scale_x = icon_rect.width / win_rect.width;
|
||||
const scale_y = icon_rect.height / win_rect.height;
|
||||
const dx = icon_rect.left - win_rect.left;
|
||||
const dy = icon_rect.top - win_rect.top;
|
||||
|
||||
if ( tile ) {
|
||||
// iOS-style zoom-to-icon morph, two congruent layers on
|
||||
// the same 450ms decelerating path so it reads as ONE
|
||||
// object transforming:
|
||||
// 1. the LIVE window scales onto the tile's icon box,
|
||||
// its corner radius growing toward the squircle,
|
||||
// fading OUT once it has mostly shrunk;
|
||||
// 2. an enlarged clone of the icon ("ghost") starts
|
||||
// covering the window and flies/shrinks with it,
|
||||
// fading IN as the window fades out, landing exactly
|
||||
// on the real icon's slot (the real icon is hidden
|
||||
// until the ghost lands, so nothing doubles).
|
||||
// Transform + opacity animate on the compositor (no
|
||||
// per-frame reflow of the app's iframe); the window's
|
||||
// geometry (top/left/width/height) is never touched, so
|
||||
// restore is trivial.
|
||||
const icon = tile.querySelector('.myapps-tile-icon') || tile;
|
||||
const win_rect = el_window.getBoundingClientRect();
|
||||
const icon_rect = icon.getBoundingClientRect();
|
||||
if ( win_rect.width > 0 && win_rect.height > 0
|
||||
&& icon_rect.width > 0 && icon_rect.height > 0 ) {
|
||||
// Everything touched is restored from these exact
|
||||
// inline values once the animation ends.
|
||||
const orig_style = {
|
||||
transform: el_window.style.transform,
|
||||
transformOrigin: el_window.style.transformOrigin,
|
||||
opacity: el_window.style.opacity,
|
||||
borderRadius: el_window.style.borderRadius,
|
||||
overflow: el_window.style.overflow,
|
||||
willChange: el_window.style.willChange,
|
||||
pointerEvents: el_window.style.pointerEvents,
|
||||
transition: el_window.style.transition,
|
||||
};
|
||||
const icon_orig_visibility = icon.style.visibility;
|
||||
// Map the window rect exactly onto the icon rect (and
|
||||
// the ghost the exact inverse: icon rect onto window
|
||||
// rect), so the two layers stay congruent throughout.
|
||||
const scale_x = icon_rect.width / win_rect.width;
|
||||
const scale_y = icon_rect.height / win_rect.height;
|
||||
const dx = icon_rect.left - win_rect.left;
|
||||
const dy = icon_rect.top - win_rect.top;
|
||||
const ghost = icon.cloneNode(true);
|
||||
ghost.style.position = 'fixed';
|
||||
ghost.style.left = `${icon_rect.left}px`;
|
||||
ghost.style.top = `${icon_rect.top}px`;
|
||||
ghost.style.width = `${icon_rect.width}px`;
|
||||
ghost.style.height = `${icon_rect.height}px`;
|
||||
ghost.style.margin = '0';
|
||||
ghost.style.zIndex = (parseInt($win.css('z-index'), 10) || 1) + 1;
|
||||
ghost.style.pointerEvents = 'none';
|
||||
ghost.style.transformOrigin = 'top left';
|
||||
ghost.style.willChange = 'transform, opacity';
|
||||
ghost.style.opacity = '0';
|
||||
// UNIFORM start scale so the icon stays square (a
|
||||
// rect-onto-rect mapping stretches it), sized to fit
|
||||
// INSIDE the window (contain, centered) — a covering
|
||||
// square would protrude past the card's short edge
|
||||
// and the protruding halo pops in mid-crossfade
|
||||
// (visible jitter). Contained, the ghost's side
|
||||
// tracks the card's short edge exactly for the whole
|
||||
// flight (both interpolate linearly between the same
|
||||
// start and end sizes), so the card's rectangular
|
||||
// flanks simply melt away around a steady square.
|
||||
const ghost_scale = Math.min(
|
||||
win_rect.width / icon_rect.width,
|
||||
win_rect.height / icon_rect.height,
|
||||
);
|
||||
const ghost_left = win_rect.left + (win_rect.width - icon_rect.width * ghost_scale) / 2;
|
||||
const ghost_top = win_rect.top + (win_rect.height - icon_rect.height * ghost_scale) / 2;
|
||||
ghost.style.transform = `translate(${ghost_left - icon_rect.left}px, ${ghost_top - icon_rect.top}px) scale(${ghost_scale})`;
|
||||
document.body.appendChild(ghost);
|
||||
icon.style.visibility = 'hidden';
|
||||
|
||||
const ghost = icon.cloneNode(true);
|
||||
ghost.style.position = 'fixed';
|
||||
ghost.style.left = `${icon_rect.left}px`;
|
||||
ghost.style.top = `${icon_rect.top}px`;
|
||||
ghost.style.width = `${icon_rect.width}px`;
|
||||
ghost.style.height = `${icon_rect.height}px`;
|
||||
ghost.style.margin = '0';
|
||||
ghost.style.zIndex = (parseInt($win.css('z-index'), 10) || 1) + 1;
|
||||
ghost.style.pointerEvents = 'none';
|
||||
ghost.style.transformOrigin = 'top left';
|
||||
ghost.style.willChange = 'transform, opacity';
|
||||
ghost.style.opacity = '0';
|
||||
// UNIFORM start scale so the icon stays square (a
|
||||
// rect-onto-rect mapping stretches it), sized to fit
|
||||
// INSIDE the window (contain, centered) — a covering
|
||||
// square would protrude past the card's short edge
|
||||
// and the protruding halo pops in mid-crossfade
|
||||
// (visible jitter). Contained, the ghost's side
|
||||
// tracks the card's short edge exactly for the whole
|
||||
// flight (both interpolate linearly between the same
|
||||
// start and end sizes), so the card's rectangular
|
||||
// flanks simply melt away around a steady square.
|
||||
const ghost_scale = Math.min(
|
||||
win_rect.width / icon_rect.width,
|
||||
win_rect.height / icon_rect.height,
|
||||
);
|
||||
const ghost_left = win_rect.left + (win_rect.width - icon_rect.width * ghost_scale) / 2;
|
||||
const ghost_top = win_rect.top + (win_rect.height - icon_rect.height * ghost_scale) / 2;
|
||||
ghost.style.transform = `translate(${ghost_left - icon_rect.left}px, ${ghost_top - icon_rect.top}px) scale(${ghost_scale})`;
|
||||
document.body.appendChild(ghost);
|
||||
icon.style.visibility = 'hidden';
|
||||
|
||||
$win.attr({
|
||||
'data-is_minimized': true,
|
||||
'data-minimized_in_place': '1',
|
||||
'data-window_morphing': '1',
|
||||
});
|
||||
el_window.style.transformOrigin = 'top left';
|
||||
el_window.style.overflow = 'hidden'; // let the radius clip the content
|
||||
el_window.style.willChange = 'transform, opacity';
|
||||
el_window.style.pointerEvents = 'none';
|
||||
// Flush the starting state (both layers) so the
|
||||
// transitions animate from it rather than snapping.
|
||||
void el_window.offsetWidth;
|
||||
// The handoff: by ~130ms the decelerating curve has
|
||||
// done ~80% of the shrink — the ghost fades in just
|
||||
// before (80→240ms) and the window fades out just
|
||||
// after (120→320ms). The overlap means there is never
|
||||
// a hole, and since both ride the same path the swap
|
||||
// is invisible: the window "becomes" the icon.
|
||||
el_window.style.transition = [
|
||||
'transform 0.45s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||
'border-radius 0.45s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||
'opacity 0.2s ease-out 0.12s',
|
||||
].join(', ');
|
||||
el_window.style.transform = `translate(${dx}px, ${dy}px) scale(${scale_x}, ${scale_y})`;
|
||||
// Percentage radius stays proportional under the
|
||||
// scale — ~22% is the icon-squircle look.
|
||||
el_window.style.borderRadius = '22%';
|
||||
el_window.style.opacity = '0';
|
||||
ghost.style.transition = [
|
||||
'transform 0.45s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||
'opacity 0.16s ease-in 0.08s',
|
||||
].join(', ');
|
||||
ghost.style.transform = 'none';
|
||||
ghost.style.opacity = '1';
|
||||
$win.attr({
|
||||
'data-is_minimized': true,
|
||||
'data-minimized_in_place': '1',
|
||||
'data-window_morphing': '1',
|
||||
});
|
||||
el_window.style.transformOrigin = 'top left';
|
||||
el_window.style.overflow = 'hidden'; // let the radius clip the content
|
||||
el_window.style.willChange = 'transform, opacity';
|
||||
el_window.style.pointerEvents = 'none';
|
||||
// Flush the starting state (both layers) so the
|
||||
// transitions animate from it rather than snapping.
|
||||
void el_window.offsetWidth;
|
||||
// The handoff: by ~130ms the decelerating curve has
|
||||
// done ~80% of the shrink — the ghost fades in just
|
||||
// before (80→240ms) and the window fades out just
|
||||
// after (120→320ms). The overlap means there is never
|
||||
// a hole, and since both ride the same path the swap
|
||||
// is invisible: the window "becomes" the icon.
|
||||
el_window.style.transition = [
|
||||
'transform 0.45s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||
'border-radius 0.45s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||
'opacity 0.2s ease-out 0.12s',
|
||||
].join(', ');
|
||||
el_window.style.transform = `translate(${dx}px, ${dy}px) scale(${scale_x}, ${scale_y})`;
|
||||
// Percentage radius stays proportional under the
|
||||
// scale — ~22% is the icon-squircle look.
|
||||
el_window.style.borderRadius = '22%';
|
||||
el_window.style.opacity = '0';
|
||||
ghost.style.transition = [
|
||||
'transform 0.45s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||
'opacity 0.16s ease-in 0.08s',
|
||||
].join(', ');
|
||||
ghost.style.transform = 'none';
|
||||
ghost.style.opacity = '1';
|
||||
|
||||
setTimeout(() => {
|
||||
// End of the zoom: the ghost has landed on the
|
||||
// real icon's slot — swap them back in the same
|
||||
// frame, hide the window, and put every touched
|
||||
// inline property back while nothing is visible.
|
||||
// If the user re-opened the window mid-animation
|
||||
// (tile click → showWindow cleared the flags),
|
||||
// restore but leave it visible.
|
||||
ghost.remove();
|
||||
icon.style.visibility = icon_orig_visibility;
|
||||
$win.attr('data-window_morphing', '0');
|
||||
const minimized = $win.attr('data-is_minimized');
|
||||
if ( minimized === '1' || minimized === 'true' ) $win.hide();
|
||||
el_window.style.transition = 'none';
|
||||
el_window.style.transform = orig_style.transform;
|
||||
el_window.style.transformOrigin = orig_style.transformOrigin;
|
||||
el_window.style.opacity = orig_style.opacity;
|
||||
el_window.style.borderRadius = orig_style.borderRadius;
|
||||
el_window.style.overflow = orig_style.overflow;
|
||||
el_window.style.willChange = orig_style.willChange;
|
||||
el_window.style.pointerEvents = orig_style.pointerEvents;
|
||||
setTimeout(() => {
|
||||
// End of the zoom: the ghost has landed on the
|
||||
// real icon's slot — swap them back in the same
|
||||
// frame, hide the window, and put every touched
|
||||
// inline property back while nothing is visible.
|
||||
// If the user re-opened the window mid-animation
|
||||
// (tile click → showWindow cleared the flags),
|
||||
// restore but leave it visible.
|
||||
ghost.remove();
|
||||
icon.style.visibility = icon_orig_visibility;
|
||||
$win.attr('data-window_morphing', '0');
|
||||
const minimized = $win.attr('data-is_minimized');
|
||||
if ( minimized === '1' || minimized === 'true' ) $win.hide();
|
||||
el_window.style.transition = 'none';
|
||||
el_window.style.transform = orig_style.transform;
|
||||
el_window.style.transformOrigin = orig_style.transformOrigin;
|
||||
el_window.style.opacity = orig_style.opacity;
|
||||
el_window.style.borderRadius = orig_style.borderRadius;
|
||||
el_window.style.overflow = orig_style.overflow;
|
||||
el_window.style.willChange = orig_style.willChange;
|
||||
el_window.style.pointerEvents = orig_style.pointerEvents;
|
||||
setTimeout(() => {
|
||||
el_window.style.transition = orig_style.transition;
|
||||
}, 50);
|
||||
}, 480);
|
||||
return;
|
||||
}
|
||||
el_window.style.transition = orig_style.transition;
|
||||
}, 50);
|
||||
}, 480);
|
||||
return;
|
||||
}
|
||||
|
||||
$win.attr({
|
||||
'data-is_minimized': true,
|
||||
'data-minimized_in_place': '1',
|
||||
});
|
||||
$win.fadeOut(150);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate animation target based on taskbar position
|
||||
let animationTarget = {};
|
||||
const taskbarPosition = window.taskbar_position || 'bottom';
|
||||
|
||||
if ( taskbarPosition === 'bottom' ) {
|
||||
// taskbar position is center of window minus half of taskbar item width
|
||||
taskbar_item_pos.left = taskbar_item_pos.left + ($(window).width() / 2) - ($('.taskbar').width() / 2);
|
||||
animationTarget = {
|
||||
top: 'calc(100% - 60px)',
|
||||
left: taskbar_item_pos.left + 14.5,
|
||||
};
|
||||
} else if ( taskbarPosition === 'left' ) {
|
||||
animationTarget = {
|
||||
top: taskbar_item_pos.top + ($(window).height() / 2) - ($('.taskbar').height() / 2) + 14.5,
|
||||
left: '5px',
|
||||
};
|
||||
} else if ( taskbarPosition === 'right' ) {
|
||||
animationTarget = {
|
||||
top: taskbar_item_pos.top + ($(window).height() / 2) - ($('.taskbar').height() / 2) + 14.5,
|
||||
left: 'calc(100% - 60px)',
|
||||
};
|
||||
}
|
||||
|
||||
$(this).attr({
|
||||
'data-orig-width': $(this).width(),
|
||||
'data-orig-height': $(this).height(),
|
||||
'data-orig-top': $(this).position().top,
|
||||
'data-orig-left': $(this).position().left,
|
||||
$win.attr({
|
||||
'data-is_minimized': true,
|
||||
'data-minimized_in_place': '1',
|
||||
});
|
||||
|
||||
$(this).css({
|
||||
...(!isMobile.phone ? {
|
||||
'transition': 'top 0.2s, left 0.2s, bottom 0.2s, right 0.2s, width 0.2s, height 0.2s',
|
||||
} : {}),
|
||||
width: '0',
|
||||
height: '0',
|
||||
...animationTarget,
|
||||
});
|
||||
|
||||
// remove transitions a good while after setting css to make sure
|
||||
// it doesn't interfere with an ongoing animation
|
||||
setTimeout(() => {
|
||||
$(this).css({
|
||||
'transition': 'none',
|
||||
'transform': 'none',
|
||||
});
|
||||
}, 250);
|
||||
|
||||
// reset the tab title (the URL is not touched — apps never
|
||||
// rewrite it on the desktop) — only if this window owned it
|
||||
const update_window_url = $(this).attr('data-update_window_url');
|
||||
if ( ! window.is_dashboard_mode && (update_window_url === 'true' || update_window_url === null) ) {
|
||||
document.title = i18n('window_title_puter');
|
||||
}
|
||||
$win.fadeOut(150);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
@@ -5598,11 +5388,7 @@ async function saveSidebarOrder (order) {
|
||||
}
|
||||
}
|
||||
|
||||
// Function to update maximized window positioning based on taskbar position
|
||||
window.update_maximized_window_for_taskbar = function (el_window) {
|
||||
const position = window.taskbar_position || 'bottom';
|
||||
|
||||
// Handle fullpage mode differently
|
||||
window.update_maximized_window = function (el_window) {
|
||||
if ( window.is_fullpage_mode ) {
|
||||
$(el_window).css({
|
||||
'top': `${window.toolbar_height }px`,
|
||||
@@ -5613,63 +5399,20 @@ window.update_maximized_window_for_taskbar = function (el_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( position === 'bottom' ) {
|
||||
let height = window.innerHeight - window.taskbar_height - window.toolbar_height - 6;
|
||||
let width = '100%';
|
||||
|
||||
$(el_window).css({
|
||||
'top': `${window.toolbar_height }px`,
|
||||
'left': '0',
|
||||
'width': width,
|
||||
'height': `${height }px`,
|
||||
});
|
||||
} else if ( position === 'left' ) {
|
||||
let width = window.innerWidth - window.taskbar_height - 1;
|
||||
|
||||
$(el_window).css({
|
||||
'top': `${window.toolbar_height }px`,
|
||||
'left': `${window.taskbar_height + 1 }px`,
|
||||
'width': width,
|
||||
'height': `calc(100% - ${window.toolbar_height}px)`,
|
||||
});
|
||||
} else if ( position === 'right' ) {
|
||||
$(el_window).css({
|
||||
'top': `${window.toolbar_height }px`,
|
||||
'left': '0',
|
||||
'width': `calc(100% - ${window.taskbar_height + 1}px)`,
|
||||
'height': `calc(100% - ${window.toolbar_height}px)`,
|
||||
});
|
||||
}
|
||||
$(el_window).css({
|
||||
'top': `${window.toolbar_height }px`,
|
||||
'left': '0',
|
||||
'width': '100%',
|
||||
'height': `${window.innerHeight - window.toolbar_height - 6 }px`,
|
||||
});
|
||||
};
|
||||
|
||||
// Function to get snap dimensions and positions based on taskbar position
|
||||
function getSnapDimensions () {
|
||||
const taskbar_position = window.taskbar_position || 'bottom';
|
||||
|
||||
let available_width, available_height, start_x, start_y;
|
||||
|
||||
if ( taskbar_position === 'left' ) {
|
||||
available_width = window.innerWidth - window.taskbar_height;
|
||||
available_height = window.innerHeight - window.toolbar_height;
|
||||
start_x = window.taskbar_height;
|
||||
start_y = window.toolbar_height;
|
||||
} else if ( taskbar_position === 'right' ) {
|
||||
available_width = window.innerWidth - window.taskbar_height;
|
||||
available_height = window.innerHeight - window.toolbar_height;
|
||||
start_x = 0;
|
||||
start_y = window.toolbar_height;
|
||||
} else { // bottom (default)
|
||||
available_width = window.innerWidth;
|
||||
available_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
|
||||
start_x = 0;
|
||||
start_y = window.toolbar_height;
|
||||
}
|
||||
|
||||
return {
|
||||
available_width,
|
||||
available_height,
|
||||
start_x,
|
||||
start_y,
|
||||
available_width: window.innerWidth,
|
||||
available_height: window.innerHeight - window.toolbar_height,
|
||||
start_x: 0,
|
||||
start_y: window.toolbar_height,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -782,7 +782,6 @@ const UIWindow2FASetup = async function UIWindow2FASetup () {
|
||||
width: Math.min(480, window.innerWidth - 24),
|
||||
height: 'auto',
|
||||
dominant: true,
|
||||
show_in_taskbar: false,
|
||||
is_draggable: false,
|
||||
backdrop: true,
|
||||
on_before_exit: async () => {
|
||||
|
||||
@@ -215,7 +215,6 @@ async function UIWindowAuthMe (options = {}) {
|
||||
width: 400,
|
||||
height: 'auto',
|
||||
dominant: true,
|
||||
show_in_taskbar: false,
|
||||
window_class: 'window-authme',
|
||||
body_css: {
|
||||
width: 'initial',
|
||||
|
||||
@@ -87,7 +87,6 @@ async function UIWindowBlockedSenders (options) {
|
||||
width: 420,
|
||||
height: 'auto',
|
||||
dominant: true,
|
||||
show_in_taskbar: false,
|
||||
onAppend: function (this_window) {
|
||||
$(this_window).find('.blocked-username').get(0)?.focus({ preventScroll: true });
|
||||
},
|
||||
|
||||
@@ -68,7 +68,6 @@ async function UIWindowChangeUsername (options) {
|
||||
width: 350,
|
||||
height: 'auto',
|
||||
dominant: true,
|
||||
show_in_taskbar: false,
|
||||
onAppend: function (this_window) {
|
||||
$(this_window).find('.new-username').get(0)?.focus({ preventScroll: true });
|
||||
const oidc_only = !!(window.user && window.user.oidc_only);
|
||||
|
||||
@@ -114,7 +114,6 @@ function UIWindowCopyToken (options = {}) {
|
||||
width: 450,
|
||||
height: 'auto',
|
||||
dominant: true,
|
||||
show_in_taskbar: false,
|
||||
window_class: 'window-copy-auth-token',
|
||||
body_css: {
|
||||
width: 'initial',
|
||||
|
||||
@@ -57,7 +57,6 @@ async function UIWindowLoginInProgress (options) {
|
||||
width: 350,
|
||||
height: 'auto',
|
||||
dominant: true,
|
||||
show_in_taskbar: false,
|
||||
backdrop: true,
|
||||
stay_on_top: true,
|
||||
window_class: 'window-login-progress',
|
||||
|
||||
@@ -100,7 +100,6 @@ async function UIWindowNewPassword (options) {
|
||||
width: 350,
|
||||
height: 'auto',
|
||||
dominant: true,
|
||||
show_in_taskbar: false,
|
||||
onAppend: function (this_window) {
|
||||
$(this_window).find('.new-password').get(0)?.focus({ preventScroll: true });
|
||||
},
|
||||
|
||||
@@ -71,7 +71,6 @@ function UIWindowPasswordChangeRequired (options) {
|
||||
width: 390,
|
||||
height: 'auto',
|
||||
dominant: true,
|
||||
show_in_taskbar: false,
|
||||
onAppend: function (this_window) {
|
||||
$(this_window).find('.pcr-current').get(0)?.focus({ preventScroll: true });
|
||||
},
|
||||
|
||||
@@ -91,7 +91,6 @@ async function UIWindowSaveAccount (options) {
|
||||
allow_user_select: true,
|
||||
width: 350,
|
||||
dominant: true,
|
||||
show_in_taskbar: false,
|
||||
...options.window_options,
|
||||
onAppend: function (this_window) {
|
||||
if ( options.default_username )
|
||||
|
||||
@@ -8691,13 +8691,6 @@ body.myapps-reordering .myapps-tile {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* Dashboard mode runs fullpage (which normally hides the minimize button
|
||||
because there's no taskbar to restore from), but here a minimized app can
|
||||
be brought back from its Apps-tab tile — so keep the button available. */
|
||||
body.dashboard-mode .window-minimize-btn {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Fullpage mode sizes .window-body-app to 100% because fullpage windows have
|
||||
no titlebar — but dashboard-mode app windows WITH a head still lose 29px to
|
||||
it, so compensate for those. Headless dashboard app windows
|
||||
|
||||
+18
-511
@@ -104,12 +104,6 @@ pre {
|
||||
--window-sidebar-alpha: calc(min(1, 0.11 + var(--primary-alpha)));
|
||||
--window-sidebar-color: var(--primary-color);
|
||||
|
||||
--taskbar-hue: var(--primary-hue);
|
||||
--taskbar-saturation: var(--primary-saturation);
|
||||
--taskbar-lightness: var(--primary-lightness);
|
||||
--taskbar-alpha: calc(0.73 * var(--primary-alpha));
|
||||
--taskbar-color: var(--primary-color);
|
||||
|
||||
--select-hue: 213.05;
|
||||
--select-saturation: 74.22%;
|
||||
--select-lightness: 55.88%;
|
||||
@@ -122,9 +116,6 @@ html, body {
|
||||
}
|
||||
|
||||
body {
|
||||
background: no-repeat center center fixed;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-color: #ffffff;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -372,8 +363,9 @@ input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, sel
|
||||
.desktop {
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
height: calc(100vh - 60px) !important;
|
||||
height: 100vh !important;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
grid-template-rows: repeat(auto-fill, 109px);
|
||||
grid-auto-flow: column;
|
||||
@@ -385,28 +377,9 @@ input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, sel
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.desktop.desktop-taskbar-position-left {
|
||||
margin-left: 50px;
|
||||
padding-right: 0;
|
||||
padding-bottom: 0;
|
||||
height: calc(100vh) !important;
|
||||
}
|
||||
|
||||
.desktop.desktop-taskbar-position-right {
|
||||
margin-right: 50px;
|
||||
padding-left: 0;
|
||||
padding-bottom: 0;
|
||||
height: calc(100vh) !important;
|
||||
}
|
||||
|
||||
.fullpage-mode .window-minimize-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.device-phone .desktop {
|
||||
height: calc(100vh - 90px) !important;
|
||||
height: calc(100dvh - 90px) !important;
|
||||
/* Ensure no left/right padding on mobile, regardless of taskbar position classes */
|
||||
height: 100vh !important;
|
||||
height: 100dvh !important;
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
@@ -911,6 +884,18 @@ span.header-sort-icon img {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
/* The desktop is a plain page color, so its labels need text that reads on it. */
|
||||
.desktop .item .item-name {
|
||||
color: rgb(73, 73, 73);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.desktop .item .item-name {
|
||||
color: #e6e6e6;
|
||||
}
|
||||
}
|
||||
|
||||
.device-phone .item-container-list .item .item-name {
|
||||
line-height: 42px;
|
||||
border-bottom: 1px solid #e3e3e3;
|
||||
@@ -2509,253 +2494,6 @@ label {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
* Taskbar
|
||||
*****************************************************/
|
||||
|
||||
.taskbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: hsla(var(--taskbar-hue),
|
||||
var(--taskbar-saturation),
|
||||
var(--taskbar-lightness),
|
||||
calc(0.5 + 0.5*var(--taskbar-alpha)));
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
z-index: 99999;
|
||||
overflow: hidden !important;
|
||||
|
||||
height: 50px;
|
||||
border-radius: 10px;
|
||||
bottom: calc(5px + env(safe-area-inset-bottom, 0px));
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
width: auto;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
/* that sweet sweet subtle shadow */
|
||||
box-shadow:
|
||||
inset 0 0 0 0.5px rgba(255, 255, 255, 0.2),
|
||||
0 0 0 0.5px rgba(0, 0, 0, 0.2),
|
||||
0 4px 16px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* Bottom positioned taskbar (default) */
|
||||
.taskbar.taskbar-position-bottom {
|
||||
bottom: 5px;
|
||||
left: 50%;
|
||||
right: auto;
|
||||
top: auto;
|
||||
width: auto;
|
||||
height: 50px;
|
||||
transform: translateX(-50%);
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
writing-mode: initial;
|
||||
}
|
||||
|
||||
/* Left positioned taskbar */
|
||||
.taskbar.taskbar-position-left {
|
||||
left: 0 !important;
|
||||
top: 0;
|
||||
width: 50px;
|
||||
transform: none;
|
||||
height: 100% !important;
|
||||
flex-direction: column;
|
||||
justify-content: normal;
|
||||
writing-mode: initial;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Right positioned taskbar */
|
||||
.taskbar.taskbar-position-right {
|
||||
right: 0 !important;
|
||||
top: 0;
|
||||
left: auto;
|
||||
bottom: auto;
|
||||
width: 50px;
|
||||
height: 100% !important;
|
||||
transform: none;
|
||||
flex-direction: column;
|
||||
justify-content: normal;
|
||||
writing-mode: initial;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.taskbar.taskbar-position-left .taskbar-sortable,
|
||||
.taskbar.taskbar-position-right .taskbar-sortable {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* Taskbar items for left/right positioning */
|
||||
.taskbar.taskbar-position-left .taskbar-item,
|
||||
.taskbar.taskbar-position-right .taskbar-item {
|
||||
margin-bottom: 5px;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.taskbar.taskbar-position-left .taskbar-item:last-child,
|
||||
.taskbar.taskbar-position-right .taskbar-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.taskbar .taskbar-item {
|
||||
float: left;
|
||||
position: relative;
|
||||
overflow: hidden !important;
|
||||
transition: background-color 0.2s;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.taskbar .taskbar-item-sortable-placeholder, .taskbar .taskbar-item {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 6px 5px 10px 5px;
|
||||
}
|
||||
|
||||
.taskbar .taskbar-item .taskbar-icon {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.taskbar .taskbar-item, .taskbar .taskbar-item * {
|
||||
-webkit-user-drag: none;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
.taskbar-item.ui-sortable-helper {
|
||||
margin-left: 25px;
|
||||
z-index: 999999999 !important;
|
||||
}
|
||||
|
||||
.desktop:not(.desktop-selectable-active) .taskbar .taskbar-item:hover .taskbar-icon {
|
||||
background-color: rgb(255 255 255 / 40%);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.taskbar .taskbar-item:active .taskbar-icon,
|
||||
.taskbar .taskbar-item:focus-within .taskbar-icon,
|
||||
.taskbar .taskbar-item:focus-visible .taskbar-icon,
|
||||
.taskbar .taskbar-item:focus .taskbar-icon,
|
||||
.taskbar-item.has-open-contextmenu .taskbar-icon,
|
||||
.taskbar-item.has-open-popover .taskbar-icon,
|
||||
.taskbar .taskbar-item.active .taskbar-icon,
|
||||
.taskbar-item.ui-sortable-helper .taskbar-icon {
|
||||
background-color: rgb(255 255 255 / 80%) !important;
|
||||
transition: background-color 0.2s;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.active-taskbar-indicator {
|
||||
font-size: 18px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
-webkit-transform: translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
bottom: -6px;
|
||||
display: none;
|
||||
width: 9px;
|
||||
height: 3px;
|
||||
background-color: #686868;
|
||||
bottom: 8px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.device-phone .active-taskbar-indicator {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.taskbar .taskbar-icon img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
filter: drop-shadow(0px 0px 0.2px rgb(51, 51, 51));
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.taskbar-icon {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
/* Taskbar separator styling */
|
||||
.taskbar-item[data-app="separator"] {
|
||||
pointer-events: none !important;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.taskbar-item[data-app="separator"] .taskbar-icon {
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
}
|
||||
|
||||
/* Vertical separator for bottom taskbar */
|
||||
.taskbar.taskbar-position-bottom .taskbar-item[data-app="separator"] .taskbar-icon::after {
|
||||
content: '';
|
||||
width: 1px;
|
||||
height: 35px;
|
||||
max-height: 35px;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 0.5px;
|
||||
}
|
||||
|
||||
/* Horizontal separator for left/right taskbar */
|
||||
.taskbar.taskbar-position-left .taskbar-item[data-app="separator"] .taskbar-icon::after,
|
||||
.taskbar.taskbar-position-right .taskbar-item[data-app="separator"] .taskbar-icon::after {
|
||||
content: '';
|
||||
width: 35px;
|
||||
height: 1px;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 0.5px;
|
||||
}
|
||||
|
||||
/* Hide separator on mobile devices */
|
||||
.device-phone .taskbar-item[data-app="separator"],
|
||||
.device-tablet .taskbar-item[data-app="separator"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.taskbar.taskbar-position-bottom .taskbar-item[data-app="separator"]{
|
||||
max-width: 10px;
|
||||
min-width: 10px !important;
|
||||
}
|
||||
|
||||
.taskbar.taskbar-position-bottom .taskbar-item[data-app="separator"] .taskbar-icon{
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.taskbar.taskbar-position-left .taskbar-item[data-app="separator"],
|
||||
.taskbar.taskbar-position-right .taskbar-item[data-app="separator"]{
|
||||
max-height: 10px;
|
||||
min-height: 10px !important;
|
||||
padding: 5px 3px 5px 7px !important;
|
||||
}
|
||||
.taskbar.taskbar-position-left .taskbar-item[data-app="separator"] .taskbar-icon,
|
||||
.taskbar.taskbar-position-right .taskbar-item[data-app="separator"] .taskbar-icon{
|
||||
max-height: 10px;
|
||||
min-height: 10px !important;
|
||||
padding-bottom: 5px !important;
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
* Captcha
|
||||
*****************************************************/
|
||||
@@ -2883,20 +2621,6 @@ label {
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@supports ((backdrop-filter: blur())) {
|
||||
.taskbar {
|
||||
background-color: hsla(var(--taskbar-hue),
|
||||
var(--taskbar-saturation),
|
||||
var(--taskbar-lightness),
|
||||
var(--taskbar-alpha));
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.taskbar .taskbar-icon img {
|
||||
filter: drop-shadow(0px 0px 0.5px rgb(51, 51, 51));
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.window-copy-auth-token {
|
||||
width: 100% !important;
|
||||
@@ -2914,172 +2638,6 @@ label {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.taskbar {
|
||||
justify-content: center;
|
||||
overflow: visible !important;
|
||||
overflow-x: scroll !important;
|
||||
overflow-y: hidden !important;
|
||||
max-width: calc(100% - 40px);
|
||||
}
|
||||
|
||||
.taskbar .taskbar-item, .taskbar .taskbar-item-sortable-placeholder {
|
||||
width: 40px !important;
|
||||
height: 40px !important;
|
||||
margin-right: 5px;
|
||||
overflow: visible !important;
|
||||
padding: 5px 5px 10px 5px;
|
||||
}
|
||||
|
||||
.taskbar-icon {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
.taskbar ::-webkit-scrollbar {
|
||||
width: 0 !important;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
.taskbar {
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
* Tooltip
|
||||
*****************************************************/
|
||||
|
||||
.ui-tooltip, .arrow:after {
|
||||
background-color: rgba(231, 238, 245, .92);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ui-tooltip {
|
||||
padding: 7px 11px;
|
||||
border-radius: 2px;
|
||||
font: 14px "Helvetica Neue", Sans-Serif;
|
||||
border: none !important;
|
||||
backdrop-filter: blur(3px);
|
||||
filter: drop-shadow(0 0 3px rgba(0, 0, 0, .455));
|
||||
}
|
||||
|
||||
/* Base arrow styles */
|
||||
.arrow {
|
||||
width: 70px;
|
||||
height: 16px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-left: -35px;
|
||||
bottom: -16px;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.arrow:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: -20px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
background-color: rgba(231, 238, 245, .92);
|
||||
}
|
||||
|
||||
/* Arrow pointing up (tooltip below taskbar item) */
|
||||
.arrow.bottom {
|
||||
bottom: auto;
|
||||
top: 31px !important;
|
||||
transform: scaleY(-1);
|
||||
left: calc(50% + 2px) !important;
|
||||
}
|
||||
|
||||
.arrow.bottom:after {
|
||||
bottom: -20px;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
/* Arrow pointing down (tooltip above taskbar item) */
|
||||
.arrow.top {
|
||||
top: auto;
|
||||
bottom: -16px;
|
||||
}
|
||||
|
||||
.arrow.top:after {
|
||||
top: -20px;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
/* Arrow pointing right (tooltip to the right of taskbar item) */
|
||||
.arrow.left {
|
||||
width: 16px;
|
||||
height: 70px;
|
||||
left: -16px;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
margin-left: 0;
|
||||
margin-top: -37px;
|
||||
transform: scaleX(-1);
|
||||
top:18px !important;
|
||||
}
|
||||
|
||||
.arrow.left:after {
|
||||
left: -20px;
|
||||
top: 20px;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
/* Arrow pointing left (tooltip to the left of taskbar item) */
|
||||
.arrow.right {
|
||||
width: 16px;
|
||||
height: 70px;
|
||||
right: -16px !important;
|
||||
left: auto;
|
||||
margin-left: 0;
|
||||
margin-top: 35px;
|
||||
transform: scaleX(-1);
|
||||
position: absolute;
|
||||
top:18px !important;
|
||||
}
|
||||
|
||||
.arrow.right:after {
|
||||
right: -20px !important;
|
||||
left: auto !important;
|
||||
top: 20px;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
/* Center positioning adjustments */
|
||||
.arrow.center {
|
||||
left: 50%;
|
||||
margin-left: -35px;
|
||||
}
|
||||
|
||||
.arrow.middle {
|
||||
top: 50%;
|
||||
margin-top: -35px;
|
||||
}
|
||||
|
||||
/* Horizontal center adjustments for left/right arrows */
|
||||
.arrow.left.middle,
|
||||
.arrow.right.middle {
|
||||
margin-top: -35px;
|
||||
}
|
||||
|
||||
/* Vertical center adjustments for top/bottom arrows */
|
||||
.arrow.top.center,
|
||||
.arrow.bottom.center {
|
||||
margin-left: -35px;
|
||||
}
|
||||
|
||||
/******************************************************/
|
||||
@@ -3156,8 +2714,8 @@ label {
|
||||
}
|
||||
|
||||
.device-phone .popover {
|
||||
height: calc(100vh - 65px);
|
||||
height: calc(100dvh - 65px);
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100%;
|
||||
@@ -5608,57 +5166,6 @@ html.dark-mode .usage-table-fade-overlay {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Taskbar container */
|
||||
.device-phone .taskbar {
|
||||
/* Force taskbar to bottom on mobile devices, overriding any position classes */
|
||||
position: fixed !important;
|
||||
bottom: 5px !important;
|
||||
left: 50% !important;
|
||||
right: auto !important;
|
||||
top: auto !important;
|
||||
width: auto !important;
|
||||
height: 50px !important;
|
||||
transform: translateX(-50%) !important;
|
||||
flex-direction: row !important;
|
||||
justify-content: left !important;
|
||||
writing-mode: initial !important;
|
||||
padding: 0 7px !important;
|
||||
border-radius: 10px !important;
|
||||
|
||||
/* Enable smooth scrolling */
|
||||
-webkit-overflow-scrolling: touch;
|
||||
/* Allow horizontal touch scrolling */
|
||||
touch-action: pan-x;
|
||||
/* Enable horizontal scroll */
|
||||
overflow-x: auto;
|
||||
/* Hide scrollbars while keeping functionality */
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
/* Base styling */
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Hide scrollbar while keeping functionality */
|
||||
.device-phone .taskbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Taskbar items */
|
||||
.device-phone .taskbar .taskbar-item {
|
||||
/* Allow dragging while preventing unwanted touch actions */
|
||||
touch-action: pan-x pinch-zoom;
|
||||
/* Ensure items can be dragged */
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
cursor: grab;
|
||||
|
||||
/* Base styling */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.device-phone .desktop {
|
||||
position: relative;
|
||||
|
||||
|
||||
@@ -114,8 +114,6 @@ if ( window.user_preferences === null ) {
|
||||
|
||||
window.window_stack = [];
|
||||
window.toolbar_height = 0;
|
||||
window.default_taskbar_height = 50;
|
||||
window.taskbar_height = window.default_taskbar_height;
|
||||
window.upload_progress_hide_delay = 500;
|
||||
window.active_uploads = {};
|
||||
window.copy_progress_hide_delay = 1000;
|
||||
@@ -159,14 +157,12 @@ window.child_launch_callbacks = {};
|
||||
// Is puter being loaded inside an iframe?
|
||||
if ( window.location !== window.parent.location ) {
|
||||
window.is_embedded = true;
|
||||
// taskbar is not needed in embedded mode
|
||||
window.taskbar_height = 0;
|
||||
} else {
|
||||
window.is_embedded = false;
|
||||
}
|
||||
|
||||
// calculate desktop height and width
|
||||
window.desktop_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
|
||||
window.desktop_height = window.innerHeight - window.toolbar_height;
|
||||
window.desktop_width = window.innerWidth;
|
||||
|
||||
// {id: {left: 0, top: 0}}
|
||||
@@ -180,7 +176,7 @@ $(window).on( 'resize', function () {
|
||||
if ( window.a_window_is_resizing ) return;
|
||||
if ( window.a_window_sidebar_is_resizing ) return;
|
||||
|
||||
const new_desktop_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
|
||||
const new_desktop_height = window.innerHeight - window.toolbar_height;
|
||||
const new_desktop_width = window.innerWidth;
|
||||
|
||||
window.desktop_height = new_desktop_height;
|
||||
@@ -188,7 +184,7 @@ $(window).on( 'resize', function () {
|
||||
|
||||
// Update all maximized windows to fit the new viewport
|
||||
$('.window[data-is_maximized="1"]').each(function () {
|
||||
window.update_maximized_window_for_taskbar(this);
|
||||
window.update_maximized_window(this);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+4
-35
@@ -615,7 +615,7 @@ window.check_fsentry_against_allowed_file_types_string = function (fsentry, allo
|
||||
window.refresh_user_data = async (auth_token) => {
|
||||
let whoami;
|
||||
try {
|
||||
whoami = await puter.os.user({ query: 'icon_size=64' });
|
||||
whoami = await puter.os.user();
|
||||
} catch (e) {
|
||||
// Ignored
|
||||
}
|
||||
@@ -677,6 +677,7 @@ window.update_auth_data = async (auth_token, user) => {
|
||||
|
||||
const to_storable_user = user => {
|
||||
const storable_user = { ...user };
|
||||
// Older sessions stored taskbar_items, with app icons, in these copies.
|
||||
delete storable_user.taskbar_items;
|
||||
return storable_user;
|
||||
};
|
||||
@@ -1726,14 +1727,13 @@ window.trigger_download = (paths) => {
|
||||
|
||||
/**
|
||||
* Updates every Trash icon in the UI to reflect whether the trash is empty:
|
||||
* the taskbar item, desktop items (and shortcuts to Trash), open explorer
|
||||
* window head icons, and the Dashboard's Files-tab sidebar.
|
||||
* desktop items (and shortcuts to Trash), open explorer window head icons,
|
||||
* and the Dashboard's Files-tab sidebar.
|
||||
*
|
||||
* @param {boolean} is_empty - Whether the trash is empty
|
||||
*/
|
||||
window.update_trash_icons = function (is_empty) {
|
||||
const icon = is_empty ? window.icons['trash.svg'] : window.icons['trash-full.svg'];
|
||||
$('[data-app="trash"]').find('.taskbar-icon > img').attr('src', icon);
|
||||
$(`.item[data-path="${html_encode(window.trash_path)}" i], .item[data-shortcut_to_path="${html_encode(window.trash_path)}" i]`).find('.item-icon > img').attr('src', icon);
|
||||
$(`.window[data-path="${html_encode(window.trash_path)}" i]`).find('.window-head-icon').attr('src', icon);
|
||||
$('.directories [data-folder="Trash"] img').attr('src', icon);
|
||||
@@ -2189,37 +2189,6 @@ window.move_items = async function (el_items, dest_path, is_undo = false) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Refreshes the desktop background based on the user's settings.
|
||||
* If the user has set a custom desktop background URL or color, it will use that.
|
||||
* If not, it defaults to a specific wallpaper image.
|
||||
*
|
||||
* @global
|
||||
* @function
|
||||
* @fires set_desktop_background - Calls this global function to set the desktop background.
|
||||
*
|
||||
* @example
|
||||
* // This will refresh the desktop background according to the user's preference or defaults.
|
||||
* window.refresh_desktop_background();
|
||||
*/
|
||||
window.refresh_desktop_background = function () {
|
||||
if ( window.user && (window.user.desktop_bg_url !== null || window.user.desktop_bg_color !== null) ) {
|
||||
window.set_desktop_background({
|
||||
url: window.user.desktop_bg_url,
|
||||
fit: window.user.desktop_bg_fit,
|
||||
color: window.user.desktop_bg_color,
|
||||
});
|
||||
}
|
||||
// default background
|
||||
else {
|
||||
let wallpaper = (window.gui_env === 'prod') ? 'https://puter-assets.b-cdn.net/wallpaper.webp' : '/src/images/wallpaper.webp';
|
||||
window.set_desktop_background({
|
||||
url: wallpaper,
|
||||
fit: 'cover',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
window.determine_website_url = function (fsentry_path) {
|
||||
// The address an item has under a site serving a folder above it. The
|
||||
// site's folder and address come with its root_dir.
|
||||
|
||||
@@ -29,8 +29,8 @@ const FALLBACK_ATTR = 'data-icon-fallback';
|
||||
/**
|
||||
* Pick the URL to load an app's icon from, plus the one URL to retry with.
|
||||
*
|
||||
* @param {Object} app - App record from the backend (taskbar item, launch app,
|
||||
* installedApps row). Reads `iconCdnUrl` and `iconUrl`/`icon`.
|
||||
* @param {Object} app - App record from the backend (launch app, installedApps
|
||||
* row). Reads `iconCdnUrl` and `iconUrl`/`icon`.
|
||||
* @param {string} [defaultIcon] - Used when the app carries no icon at all.
|
||||
* @returns {{ src: string; fallback: string }} `fallback` is empty when there
|
||||
* is nothing left to try.
|
||||
|
||||
@@ -465,7 +465,7 @@ const generate_file_context_menu = async function (options) {
|
||||
left = left > (window.innerWidth - window_width) ? (window.innerWidth - window_width) : left;
|
||||
|
||||
let top = $(el_item).position().top + $(el_item).height();
|
||||
top = top > (window.innerHeight - (window_height + window.taskbar_height + window.toolbar_height)) ? (window.innerHeight - (window_height + window.taskbar_height + window.toolbar_height)) : top;
|
||||
top = top > (window.innerHeight - (window_height + window.toolbar_height)) ? (window.innerHeight - (window_height + window.toolbar_height)) : top;
|
||||
|
||||
UIWindowItemProperties(
|
||||
$(el_item).attr('data-name'),
|
||||
|
||||
@@ -625,7 +625,7 @@ const runLaunch = async (options) => {
|
||||
window_height = parseFloat(app_info.metadata.window_size.height);
|
||||
} if ( options.maximized )
|
||||
{
|
||||
window_height = `calc(100% - ${window.taskbar_height + window.toolbar_height + 1}px)`;
|
||||
window_height = `calc(100% - ${window.toolbar_height + 1}px)`;
|
||||
}
|
||||
|
||||
// top
|
||||
@@ -678,18 +678,6 @@ const runLaunch = async (options) => {
|
||||
title = options.file_path ? path.basename(options.file_path) : title;
|
||||
}
|
||||
|
||||
// show_in_taskbar
|
||||
// Deliberately keyed on the app's own `background`, not on this launch's:
|
||||
// an app that always runs windowless has nothing to put in the taskbar,
|
||||
// ever. A background *launch* is a normal app that happens to start
|
||||
// hidden, so it keeps asking for an item — UIWindow just holds it back
|
||||
// until the window is first shown (see its taskbar block).
|
||||
let show_in_taskbar = app_info.background ? false : window_options?.show_in_taskbar;
|
||||
if ( window_options?.show_in_taskbar !== undefined )
|
||||
{
|
||||
show_in_taskbar = window_options.show_in_taskbar;
|
||||
}
|
||||
|
||||
// has_head
|
||||
let has_head = app_info.metadata?.has_head !== undefined ? app_info.metadata.has_head : window_options?.has_head;
|
||||
if ( app_info.metadata?.hide_titlebar !== undefined && typeof app_info.metadata.hide_titlebar === 'boolean' && app_info.metadata.hide_titlebar === true )
|
||||
@@ -748,7 +736,6 @@ const runLaunch = async (options) => {
|
||||
...window_options,
|
||||
is_resizable: window_resizable,
|
||||
has_head: has_head,
|
||||
show_in_taskbar: show_in_taskbar,
|
||||
update_window_url: update_window_url,
|
||||
custom_path: custom_path,
|
||||
});
|
||||
|
||||
@@ -276,9 +276,9 @@ Please try recreating the link.`);
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------
|
||||
// Dashboard: is this file already open in an app window? In
|
||||
// dashboard mode there is no taskbar — the file's row is where a
|
||||
// user goes looking for a window they minimized — so restore that
|
||||
// Dashboard: is this file already open in an app window? The
|
||||
// file's row is where a user goes looking for a window they
|
||||
// minimized, so restore that
|
||||
// window (edits intact) instead of minting a second instance.
|
||||
//----------------------------------------------------------------
|
||||
// The user's own windows only: a file opened in an instance another app
|
||||
@@ -323,7 +323,7 @@ Please try recreating the link.`);
|
||||
app: 'explorer',
|
||||
top: options.maximized ? 0 : undefined,
|
||||
left: options.maximized ? 0 : undefined,
|
||||
height: options.maximized ? `calc(100% - ${window.taskbar_height + window.toolbar_height + 1}px)` : undefined,
|
||||
height: options.maximized ? `calc(100% - ${window.toolbar_height + 1}px)` : undefined,
|
||||
width: options.maximized ? '100%' : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,10 +23,9 @@
|
||||
* for one (`puter.ui.launchApp(name, args, { background: true })`), which lets a
|
||||
* dual-purpose app serve another app without a window flashing on screen.
|
||||
*
|
||||
* Either way the window starts out of sight and out of reach: no taskbar item,
|
||||
* no running dot, and not what reopening the app finds (see
|
||||
* user_facing_windows) — it earns all three the moment it first becomes
|
||||
* visible, and an always-windowless app never asks for a taskbar item at all.
|
||||
* Either way the window starts out of sight and out of reach: no running dot,
|
||||
* and not what reopening the app finds (see user_facing_windows). It earns both
|
||||
* the moment it first becomes visible.
|
||||
*
|
||||
* @param {{ background?: boolean }} [appInfo] the app record being launched
|
||||
* @param {{ background?: boolean }} [options] this launch's options
|
||||
|
||||
@@ -83,7 +83,7 @@ export const is_unseen_background_window = (el_window) => {
|
||||
* Of the windows an app has open, the ones that are the USER's. An instance
|
||||
* another app launched in the background and never showed exists to serve that
|
||||
* app alone, so everything the user drives — reopening the app, its running
|
||||
* dot, its taskbar item, the dashboard's Back/Forward — has to look straight
|
||||
* dot, the dashboard's Back/Forward — has to look straight
|
||||
* past it and act on the user's own instances instead, launching a fresh one
|
||||
* when there are none. (Paths that act on the APP rather than on a window it
|
||||
* happens to have open — uninstall, and the launcher's close taking its
|
||||
|
||||
@@ -132,7 +132,7 @@ describe('user_facing_windows', () => {
|
||||
|
||||
it('keeps a background instance once it has been shown', () => {
|
||||
// makeWindowVisible drops the marker: from then on the window is the
|
||||
// user's, and the tile/taskbar/history paths act on it again.
|
||||
// user's, and the tile and history paths act on it again.
|
||||
const shown = win({ visible: '1', parent: 'parent-uuid' });
|
||||
expect(user_facing_windows([shown])).toEqual([shown]);
|
||||
});
|
||||
|
||||
@@ -38,7 +38,6 @@ const ar = {
|
||||
change_password: 'تغيير كلمة المرور',
|
||||
change_username: 'تغيير اسم المستخدم',
|
||||
close: 'إغلاق',
|
||||
close_all_windows: 'إغلاق جميع النوافذ',
|
||||
close_all_windows_and_log_out: 'إغلاق النوافذ وتسجيل الخروج',
|
||||
change_always_open_with: 'هل تريد دائمًا فتح هذا النوع من الملفات باستخدام',
|
||||
confirm: 'تأكيد',
|
||||
@@ -132,7 +131,6 @@ const ar = {
|
||||
general: 'عام',
|
||||
get_a_copy_of_on_puter: "Puter.com! :احصل على نسخة من '%%' على",
|
||||
get_copy_link: 'احصل على رابط النسخ',
|
||||
hide_all_windows: 'إخفاء جميع النوافذ',
|
||||
home: 'الصفحة الرئيسية',
|
||||
html_document: 'HTML مستند',
|
||||
hue: 'درجة اللون',
|
||||
@@ -142,7 +140,6 @@ const ar = {
|
||||
items_in_trash_cannot_be_renamed:
|
||||
'.لا يمكن إعادة تسمية هذا العنصر لأنه في سلة المهملات. لإعادة تسمية هذا العنصر، اسحبه أولاً خارج سلة المهملات',
|
||||
jpeg_image: 'JPEG صورة',
|
||||
keep_in_taskbar: 'الاحتفاظ في شريط المهام',
|
||||
language: 'اللغة',
|
||||
license: 'رخصة',
|
||||
lightness: 'إضاءة',
|
||||
@@ -215,7 +212,6 @@ const ar = {
|
||||
refer_friends_social_media_c2a:
|
||||
'Puter.com! احصل على 1 جيجابايت من التخزين المجاني على',
|
||||
refresh: 'تحديث',
|
||||
remove_from_taskbar: 'إزالة من شريط المهام',
|
||||
rename: 'إعادة تسمية',
|
||||
replace: 'استبدال',
|
||||
replace_all: 'استبدال الكل',
|
||||
@@ -243,7 +239,6 @@ const ar = {
|
||||
share: 'مشاركة',
|
||||
share_to: 'مشاركة إلى',
|
||||
shortcut_to: 'اختصار إلى',
|
||||
show_all_windows: 'عرض جميع النوافذ',
|
||||
show_hidden: 'إظهار المخفي',
|
||||
sign_in_with_puter: 'Puter تسجيل الدخول باستخدام',
|
||||
sign_up: 'تسجيل',
|
||||
@@ -390,8 +385,6 @@ const ar = {
|
||||
create_shortcut_s: 'إنشاء اختصارات', // In English: "Create Shortcuts"
|
||||
minimize: 'تصغير', // In English: "Minimize"
|
||||
reload_app: 'إعادة تحميل التطبيق', // In English: "Reload App"
|
||||
new_window: 'نافذة جديدة', // In English: "New Window"
|
||||
open_trash: 'فتح سلة المهملات', // In English: "Open Trash"
|
||||
publish_as_serverless_worker: 'النشر كعامل', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'وضع ملء الشاشة', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -424,14 +417,8 @@ const ar = {
|
||||
window_title_publish_worker: 'نشر العامل',
|
||||
window_title_authenticating: '…جارٍ التحقق',
|
||||
window_title_refer_friend: '!أوصِ صديقًا',
|
||||
desktop_show_desktop: 'عرض سطح المكتب',
|
||||
desktop_show_open_windows: 'عرض النوافذ المفتوحة',
|
||||
desktop_exit_full_screen: 'الخروج من وضع ملء الشاشة',
|
||||
desktop_enter_full_screen: 'وضع ملء الشاشة',
|
||||
desktop_position: 'الموضع',
|
||||
desktop_position_left: 'اليسار',
|
||||
desktop_position_bottom: 'الأسفل',
|
||||
desktop_position_right: 'اليمين',
|
||||
item_shared_with_you: '.قام مستخدم بمشاركة هذا العنصر معك',
|
||||
item_shared_by_you: '.لقد قمت بمشاركة هذا العنصر مع مستخدم آخر على الأقل',
|
||||
item_shortcut: 'اختصار',
|
||||
|
||||
@@ -40,7 +40,6 @@ const bg = {
|
||||
change_password: 'Промяна на паролата',
|
||||
change_username: 'Промяна на потребителското име',
|
||||
close: 'Затвори',
|
||||
close_all_windows: 'Затвори всички прозорци',
|
||||
close_all_windows_and_log_out: 'Затвори прозорците и излез',
|
||||
confirm: 'Потвърди',
|
||||
confirm_2fa_setup: 'Добавих кода към приложението си за удостоверяване',
|
||||
@@ -139,7 +138,6 @@ const bg = {
|
||||
general: 'Общи',
|
||||
get_a_copy_of_on_puter: 'Вземете копие на %% в Puter.com!',
|
||||
get_copy_link: 'Вземи връзка за копие',
|
||||
hide_all_windows: 'Скрий всички прозорци',
|
||||
home: 'Начало',
|
||||
html_document: 'HTML документ',
|
||||
hue: 'Нюанс',
|
||||
@@ -149,7 +147,6 @@ const bg = {
|
||||
items_in_trash_cannot_be_renamed:
|
||||
'Този елемент не може да бъде преименуван, защото е в Кошчето. За да го преименувате, първо го извадете от Кошчето.',
|
||||
jpeg_image: 'JPEG изображение',
|
||||
keep_in_taskbar: 'Задръж в лентата на задачите',
|
||||
language: 'Език',
|
||||
license: 'Лиценз',
|
||||
lightness: 'Светлина',
|
||||
@@ -179,10 +176,8 @@ const bg = {
|
||||
no: 'Не',
|
||||
ok: 'ОК',
|
||||
open: 'Отвори',
|
||||
new_window: 'Нов прозорец',
|
||||
open_in_new_tab: 'Отвори в нов раздел',
|
||||
open_in_new_window: 'Отвори в нов прозорец',
|
||||
open_trash: 'Отвори Кошчето',
|
||||
open_with: 'Отвори с',
|
||||
original_name: 'Оригинално име',
|
||||
original_path: 'Оригинален път',
|
||||
@@ -228,7 +223,6 @@ const bg = {
|
||||
refer_friends_social_media_c2a:
|
||||
'Получете 1 GB безплатно пространство в Puter.com!',
|
||||
refresh: 'Опресни',
|
||||
remove_from_taskbar: 'Премахни от лентата на задачите',
|
||||
rename: 'Преименувай',
|
||||
replace: 'Замени',
|
||||
replace_all: 'Замени всички',
|
||||
@@ -258,7 +252,6 @@ const bg = {
|
||||
share: 'Сподели',
|
||||
share_to: 'Сподели с',
|
||||
shortcut_to: 'Пряк път до',
|
||||
show_all_windows: 'Покажи всички прозорци',
|
||||
show_hidden: 'Покажи скритите',
|
||||
sign_in_with_puter: 'Влез с Puter',
|
||||
sign_up: 'Регистрирай се',
|
||||
@@ -453,14 +446,8 @@ const bg = {
|
||||
window_title_refer_friend: 'Препоръчайте приятел!',
|
||||
|
||||
// Desktop UI
|
||||
desktop_show_desktop: 'Покажи работния плот',
|
||||
desktop_show_open_windows: 'Покажи отворените прозорци',
|
||||
desktop_exit_full_screen: 'Изход от режим на цял екран',
|
||||
desktop_enter_full_screen: 'Вход в режим на цял екран',
|
||||
desktop_position: 'Позиция',
|
||||
desktop_position_left: 'Ляво',
|
||||
desktop_position_bottom: 'Долу',
|
||||
desktop_position_right: 'Дясно',
|
||||
|
||||
// Item UI
|
||||
item_shared_with_you: 'Потребител е споделил този елемент с вас.',
|
||||
@@ -480,9 +467,6 @@ const bg = {
|
||||
window_title_pictures: 'Снимки',
|
||||
window_title_puter: 'Puter',
|
||||
window_folder_empty: 'Тази папка е празна',
|
||||
|
||||
// Website Management
|
||||
set_as_background: 'Задай като фон на работния плот',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ const bn = {
|
||||
change_password: 'পাসওয়ার্ড পরিবর্তন করুন',
|
||||
change_username: 'ইউজারনেম পরিবর্তন করুন',
|
||||
close: 'বন্ধ করুন',
|
||||
close_all_windows: 'সমস্ত উইন্ডো বন্ধ করুন',
|
||||
close_all_windows_and_log_out: 'উইন্ডো বন্ধ এবং লগ আউট করুন',
|
||||
change_always_open_with: 'আপনি কি এই ধরনের ফাইলটি সবসময় এই সাথে খোলা রাখতে চান',
|
||||
confirm: 'অনুমোদন',
|
||||
@@ -121,7 +120,6 @@ const bn = {
|
||||
general: 'সাধারণ',
|
||||
get_a_copy_of_on_puter: 'পিউটার ডটকমে \'%%\' এর একটি অনুলিপি পান!',
|
||||
get_copy_link: 'কপি লিংক নিন',
|
||||
hide_all_windows: 'সমস্ত উইন্ডো লুকান',
|
||||
home: 'হোম',
|
||||
html_document: 'এইচটিএমএল ডকুমেন্ট',
|
||||
hue: 'হিউ',
|
||||
@@ -130,7 +128,6 @@ const bn = {
|
||||
item: 'আইটেম',
|
||||
items_in_trash_cannot_be_renamed: 'এই আইটেমটি নাম পরিবর্তন করা যাবে না কারণ এটি ট্র্যাশে রয়েছে। এই আইটেমটির নাম পরিবর্তন করতে, প্রথমে এটি ট্র্যাশ থেকে তুলে নিন।',
|
||||
jpeg_image: 'জেপিইজি ইমেজ',
|
||||
keep_in_taskbar: 'টাস্কবারে রাখুন',
|
||||
language: 'ভাষা',
|
||||
license: 'লাইসেন্স',
|
||||
lightness: 'উজ্জ্বলতা',
|
||||
@@ -199,7 +196,6 @@ const bn = {
|
||||
refer_friends_c2a: 'Puter তে অ্যাকাউন্ট তৈরি এবং নিশ্চিতকরণ করে ১ জিবি পান। আপনার বন্ধুও ১ জিবি পাবে!',
|
||||
refer_friends_social_media_c2a: 'Puter.com এ 1 GB বিনামূল্যের সংরক্ষণ পান!',
|
||||
refresh: 'রিফ্রেশ',
|
||||
remove_from_taskbar: 'টাস্কবার থেকে সরান',
|
||||
rename: 'পুনঃনামকরণ',
|
||||
replace: 'বদলান',
|
||||
replace_all: 'সকল কিছু বদলান',
|
||||
@@ -227,7 +223,6 @@ const bn = {
|
||||
share: 'শেয়ার করুন',
|
||||
share_to: 'শেয়ার করুন প্রতি',
|
||||
shortcut_to: 'শর্টকাট',
|
||||
show_all_windows: 'সমস্ত উইন্ডো দেখান',
|
||||
show_hidden: 'লুকানো দেখান',
|
||||
sign_in_with_puter: 'Puter দিয়ে সাইন ইন করুন',
|
||||
sign_up: 'নিবন্ধন করুন',
|
||||
@@ -370,8 +365,6 @@ const bn = {
|
||||
'create_shortcut_s': 'শর্টকাটগুলো তৈরি করুন',
|
||||
'minimize': 'আড়াল করুন',
|
||||
'reload_app': 'অ্যাপ রিলোড করুন',
|
||||
'new_window': 'নতুন উইন্ডো',
|
||||
'open_trash': 'আবর্জনা বক্স খুলুন',
|
||||
'publish_as_serverless_worker': 'কর্মী হিসেবে পাবলিশ করুন',
|
||||
'toolbar.enter_fullscreen': 'পরিপূর্ণ পর্দায় প্রবেশ করুন',
|
||||
'toolbar.github': 'গিটহাব',
|
||||
@@ -403,14 +396,8 @@ const bn = {
|
||||
'window_title_publish_worker': 'কর্মী পাবলিশ করুন',
|
||||
'window_title_authenticating': 'প্রমানীকরণ চলমান...',
|
||||
'window_title_refer_friend': 'বন্ধুকে রেফার করুন!',
|
||||
'desktop_show_desktop': 'ডেস্কটপ দেখান',
|
||||
'desktop_show_open_windows': 'খোলা উইন্ডোগুলো দেখান',
|
||||
'desktop_exit_full_screen': 'পরিপূর্ণ পর্দা থেকে বের হয়ে যান',
|
||||
'desktop_enter_full_screen': 'পরিপূর্ণ পর্দায় প্রবেশ করুন',
|
||||
'desktop_position': 'অবস্থান',
|
||||
'desktop_position_left': 'বামে',
|
||||
'desktop_position_bottom': 'নিচে',
|
||||
'desktop_position_right': 'ডানে',
|
||||
'item_shared_with_you': 'একজন ব্যবহারকারী এটি আপনার সাথে শেয়ার করেছে।',
|
||||
'item_shared_by_you': 'আপনি এই বস্তুটি কমপক্ষে একজন ব্যবহারকারীর সাথে শেয়ার করেছেন।',
|
||||
'item_shortcut': 'শর্টকাট',
|
||||
|
||||
@@ -38,7 +38,6 @@ const br = {
|
||||
change_password: 'Mudar Senha',
|
||||
change_username: 'Mudar Nome de Usuário',
|
||||
close: 'Fechar',
|
||||
close_all_windows: 'Fechar Todas as Janelas',
|
||||
close_all_windows_and_log_out: 'Fechar Janelas e Sair',
|
||||
change_always_open_with: 'Deseja sempre abrir este tipo de arquivo com',
|
||||
confirm: 'Confirmar',
|
||||
@@ -121,7 +120,6 @@ const br = {
|
||||
general: 'Geral',
|
||||
get_a_copy_of_on_puter: 'Obtenha uma cópia de \'%%\' em Puter.com!',
|
||||
get_copy_link: 'Obter Link de Cópia',
|
||||
hide_all_windows: 'Ocultar Todas as Janelas',
|
||||
home: 'Início',
|
||||
html_document: 'Documento HTML',
|
||||
hue: 'Matiz',
|
||||
@@ -130,7 +128,6 @@ const br = {
|
||||
item: 'item',
|
||||
items_in_trash_cannot_be_renamed: 'Este item não pode ser renomeado porque está na lixeira. Para renomear este item, primeiro arraste-o para fora da Lixeira.',
|
||||
jpeg_image: 'Imagem JPEG',
|
||||
keep_in_taskbar: 'Manter na Barra de Tarefas',
|
||||
language: 'Idioma',
|
||||
license: 'Licença',
|
||||
lightness: 'Luminosidade',
|
||||
@@ -196,7 +193,6 @@ const br = {
|
||||
'refer_friends_c2a': 'Ganhe 1 GB para cada amigo que criar e confirmar uma conta no Puter. Seu amigo também ganhará 1 GB!',
|
||||
'refer_friends_social_media_c2a': 'Ganhe 1 GB de armazenamento gratuito no Puter.com!',
|
||||
'refresh': 'Atualizar',
|
||||
'remove_from_taskbar': 'Remover da Barra de Tarefas',
|
||||
'rename': 'Renomear',
|
||||
'replace': 'Substituir',
|
||||
'replace_all': 'Substituir Todos',
|
||||
@@ -225,7 +221,6 @@ const br = {
|
||||
'share_to': 'Compartilhar para',
|
||||
'share_with': 'Compartilhar com:',
|
||||
'shortcut_to': 'Atalho para',
|
||||
'show_all_windows': 'Mostrar Todas as Janelas',
|
||||
'show_hidden': 'Mostrar ocultos',
|
||||
'sign_in_with_puter': 'Entrar com Puter',
|
||||
'sign_up': 'Cadastrar-se',
|
||||
@@ -364,8 +359,6 @@ const br = {
|
||||
'create_shortcut_s': 'Criar atalhos', // In English: "Create Shortcuts"
|
||||
'minimize': 'Minimizar', // In English: "Minimize"
|
||||
'reload_app': 'Recarregar App', // In English: "Reload App"
|
||||
'new_window': 'Nova Janela', // In English: "New Window"
|
||||
'open_trash': 'Abrir Lixeira', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'Publicar como Worker', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Entrar em Tela Cheia', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -397,14 +390,8 @@ const br = {
|
||||
'window_title_publish_worker': 'Publicar Worker', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'Autenticando...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'Indicar um amigo!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'Mostrar Área de Trabalho', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'Mostrar Janelas Abertas', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'Sair de Tela Cheia', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'Entrar em Tela Cheia', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'Posição', // In English: "Position"
|
||||
'desktop_position_left': 'Esquerda', // In English: "Left"
|
||||
'desktop_position_bottom': 'Fundo', // In English: "Bottom"
|
||||
'desktop_position_right': 'Direita', // In English: "Right"
|
||||
'item_shared_with_you': 'Um usuário compartilhou este item com você.', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'Você compartilhou este item com pelo menos um usuário.', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'Atalho', // In English: "Shortcut"
|
||||
|
||||
@@ -72,7 +72,6 @@ const cs = {
|
||||
"revalidate_flow_notice": "Až budete pokračovat, budete požádáni o přihlášení pomocí propojeného účtu.",
|
||||
"reauth_required_message": "Vaše relace byla z důvodu zabezpečení aktualizována – přihlaste se prosím znovu.",
|
||||
"close": "Zavřít",
|
||||
"close_all_windows": "Zavřete všechna okna",
|
||||
"change_email": "Změnit e-mail",
|
||||
"revalidated": "Znovu ověřeno.",
|
||||
"color_depth": "Barevná hloubka",
|
||||
@@ -187,8 +186,6 @@ const cs = {
|
||||
"keep_both": "Nechte si obojí",
|
||||
"html_document": "HTML dokument",
|
||||
"invite_link": "Odkaz na pozvánku",
|
||||
"keep_in_taskbar": "Ponechat na hlavním panelu",
|
||||
"hide_all_windows": "Skrýt všechna okna",
|
||||
"log_in": "Přihlásit se",
|
||||
"log_out": "Odhlásit se",
|
||||
"looks_good": "Vypadá to dobře!",
|
||||
@@ -211,7 +208,6 @@ const cs = {
|
||||
"ok": "OK",
|
||||
"or": "nebo",
|
||||
"name_too_long": "Název nesmí být delší než %% znaků.",
|
||||
"new_window": "Nové okno",
|
||||
"open": "Otevřít",
|
||||
"open_in_new_tab": "Otevřít na nové kartě",
|
||||
"open_in_new_window": "Otevřít v novém okně",
|
||||
@@ -229,7 +225,6 @@ const cs = {
|
||||
"original_name": "Původní název",
|
||||
"password_required": "Heslo je povinné.",
|
||||
"open_with": "Otevřít pomocí",
|
||||
"open_trash": "Otevřít koš",
|
||||
"path": "Cesta",
|
||||
"personalization": "Personalizace",
|
||||
"paste": "Vložit",
|
||||
@@ -289,7 +284,6 @@ const cs = {
|
||||
"replace_all": "Nahradit vše",
|
||||
"recover_password": "Obnovit heslo",
|
||||
"publish_as_serverless_worker": "Publikovat jako pracovník",
|
||||
"remove_from_taskbar": "Odebrat z hlavního panelu",
|
||||
"save_session": "Uložit relaci",
|
||||
"save_account_to_get_copy_link": "Chcete-li pokračovat, vytvořte si účet.",
|
||||
"resend_confirmation_code": "Znovu odeslat potvrzovací kód",
|
||||
@@ -381,7 +375,6 @@ const cs = {
|
||||
"blocked_none": "Nikoho jste nezablokovali.",
|
||||
"blocked_failed": "Seznam blokovaných se nepodařilo aktualizovat.",
|
||||
"shortcut_to": "Zkratka k",
|
||||
"show_all_windows": "Zobrazit všechna okna",
|
||||
"share_with": "Sdílet s:",
|
||||
"share_inherited_via": "přes {{folder}}",
|
||||
"sign_in": "Přihlásit se",
|
||||
@@ -574,18 +567,12 @@ const cs = {
|
||||
"window_title_set_new_password": "Nastavit nové heslo",
|
||||
"window_title_authenticating": "Ověřování...",
|
||||
"window_title_publish_worker": "Publikovat pracovníka",
|
||||
"desktop_show_desktop": "Zobrazit plochu",
|
||||
"window_title_select_font": "Vybrat písmo…",
|
||||
"window_title_publish_website": "Publikovat webové stránky",
|
||||
"desktop_show_open_windows": "Zobrazit otevřená okna",
|
||||
"sign_up_with_email": "Zaregistrujte se pomocí e-mailu",
|
||||
"window_title_refer_friend": "Doporučte příteli!",
|
||||
"window_title_session_list": "Seznam relací!",
|
||||
"desktop_position_right": "Vpravo",
|
||||
"desktop_position_left": "Vlevo",
|
||||
"desktop_position_bottom": "Dole",
|
||||
"desktop_enter_full_screen": "Přejít na celou obrazovku",
|
||||
"desktop_position": "Pozice",
|
||||
"item_shortcut": "Zkratka",
|
||||
"item_shared_by_you": "Tuto položku jste sdíleli s alespoň jedním dalším uživatelem.",
|
||||
"item_shared_with_you": "Tuto položku s vámi sdílí uživatel.",
|
||||
@@ -601,7 +588,6 @@ const cs = {
|
||||
"window_title_puter": "Puter",
|
||||
"item_associated_websites_plural": "Přidružené webové stránky",
|
||||
"window_click_to_go_forward": "Kliknutím přejdete vpřed.",
|
||||
"set_as_background": "Nastavit jako pozadí plochy",
|
||||
"perm_folder_pictures": "vaší složce Obrázky",
|
||||
"perm_folder_documents": "vaší složce Dokumenty",
|
||||
"perm_folder_videos": "vaší složce Videa",
|
||||
|
||||
@@ -38,7 +38,6 @@ const da = {
|
||||
change_password: 'Ændre adgangskode',
|
||||
change_username: 'Ændre brugernavn',
|
||||
close: 'Luk',
|
||||
close_all_windows: 'Luk alle vinduer',
|
||||
close_all_windows_and_log_out: 'Luk vinduer og log ud',
|
||||
change_always_open_with: 'Vil du altid åbne denne filtype med',
|
||||
confirm: 'Bekræft',
|
||||
@@ -121,7 +120,6 @@ const da = {
|
||||
general: 'Generelt',
|
||||
get_a_copy_of_on_puter: 'Få en kopi af \'%%\' på Puter.com!',
|
||||
get_copy_link: 'Få kopi-link',
|
||||
hide_all_windows: 'Skjul alle vinduer',
|
||||
home: 'Hjem',
|
||||
html_document: 'HTML-dokument',
|
||||
hue: 'Farvetone',
|
||||
@@ -130,7 +128,6 @@ const da = {
|
||||
item: 'element',
|
||||
items_in_trash_cannot_be_renamed: 'Dette element kan ikke omdøbes, fordi det er i papirkurven. For at omdøbe dette element, skal du først trække det ud af papirkurven.',
|
||||
jpeg_image: 'JPEG-billede',
|
||||
keep_in_taskbar: 'Behold i proceslinjen',
|
||||
language: 'Sprog',
|
||||
license: 'Licens',
|
||||
lightness: 'Lysstyrke',
|
||||
@@ -196,7 +193,6 @@ const da = {
|
||||
refer_friends_c2a: 'Få 1 GB for hver ven, der opretter og bekræfter en konto på Puter. Din ven vil også få 1 GB!',
|
||||
refer_friends_social_media_c2a: 'Få 1 GB gratis lagerplads på Puter.com!',
|
||||
refresh: 'Opdater',
|
||||
remove_from_taskbar: 'Fjern fra proceslinje',
|
||||
rename: 'Omdøb',
|
||||
replace: 'Erstat',
|
||||
replace_all: 'Erstat alle',
|
||||
@@ -224,7 +220,6 @@ const da = {
|
||||
share: 'Del',
|
||||
share_to: 'Del til',
|
||||
shortcut_to: 'Genvej til',
|
||||
show_all_windows: 'Vis alle vinduer',
|
||||
show_hidden: 'Vis skjulte',
|
||||
sign_in_with_puter: 'Log ind med Puter',
|
||||
sign_up: 'Tilmeld dig',
|
||||
@@ -364,8 +359,6 @@ const da = {
|
||||
'create_shortcut_s': undefined, // In English: "Create Shortcuts"
|
||||
'minimize': undefined, // In English: "Minimize"
|
||||
'reload_app': undefined, // In English: "Reload App"
|
||||
'new_window': undefined, // In English: "New Window"
|
||||
'open_trash': undefined, // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': undefined, // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': undefined, // In English: "Enter Full Screen"
|
||||
'toolbar.github': undefined, // In English: "GitHub"
|
||||
@@ -448,14 +441,8 @@ const da = {
|
||||
'window_title_publish_worker': undefined, // In English: "Publish Worker"
|
||||
'window_title_authenticating': undefined, // In English: "Authenticating..."
|
||||
'window_title_refer_friend': undefined, // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': undefined, // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': undefined, // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': undefined, // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': undefined, // In English: "Enter Full Screen"
|
||||
'desktop_position': undefined, // In English: "Position"
|
||||
'desktop_position_left': undefined, // In English: "Left"
|
||||
'desktop_position_bottom': undefined, // In English: "Bottom"
|
||||
'desktop_position_right': undefined, // In English: "Right"
|
||||
'item_shared_with_you': undefined, // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': undefined, // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': undefined, // In English: "Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const de = {
|
||||
change_password: 'Passwort ändern',
|
||||
change_username: 'Benutzername ändern',
|
||||
close: 'Schließen',
|
||||
close_all_windows: 'Alle Fenster schließen',
|
||||
close_all_windows_and_log_out: 'Fenster schließen und abmelden',
|
||||
change_always_open_with: 'Öffnen Sie diesen Dateityp immer mit',
|
||||
confirm_2fa_setup: 'Ich habe den Code in meine Authentifizierungs-App eingegeben',
|
||||
@@ -120,7 +119,6 @@ const de = {
|
||||
general: 'Allgemein',
|
||||
get_a_copy_of_on_puter: 'Holen Sie sich eine Kopie von \'%%\' auf Puter.com!',
|
||||
get_copy_link: 'Kopierlink erhalten',
|
||||
hide_all_windows: 'Alle Fenster verstecken',
|
||||
home: 'Startseite',
|
||||
html_document: 'HTML-Dokument',
|
||||
hue: 'Farbton',
|
||||
@@ -129,7 +127,6 @@ const de = {
|
||||
item: 'Element',
|
||||
items_in_trash_cannot_be_renamed: 'Dieses Element kann nicht umbenannt werden, da es sich im Papierkorb befindet. Um dieses Element umzubenennen, ziehen Sie es zunächst aus dem Papierkorb.',
|
||||
jpeg_image: 'JPEG-Bild',
|
||||
keep_in_taskbar: 'In Taskleiste behalten',
|
||||
language: 'Sprache',
|
||||
license: 'Lizenz',
|
||||
lightness: 'Helligkeit',
|
||||
@@ -195,7 +192,6 @@ const de = {
|
||||
refer_friends_c2a: 'Erhalten Sie 1 GB für jeden Freund, der ein Konto auf Puter erstellt und bestätigt. Ihr Freund erhält ebenfalls 1 GB!',
|
||||
refer_friends_social_media_c2a: 'Erhalten Sie 1 GB kostenlosen Speicherplatz auf Puter.com!',
|
||||
refresh: 'Aktualisieren',
|
||||
remove_from_taskbar: 'Von Taskleiste entfernen',
|
||||
rename: 'Umbenennen',
|
||||
replace: 'Ersetzen',
|
||||
replace_all: 'Alles ersetzen',
|
||||
@@ -223,7 +219,6 @@ const de = {
|
||||
share: 'Teilen',
|
||||
share_to: 'Teilen mit',
|
||||
shortcut_to: 'Verknüpfung zu',
|
||||
show_all_windows: 'Alle Fenster anzeigen',
|
||||
show_hidden: 'Zeige versteckte',
|
||||
sign_in_with_puter: 'Mit Puter anmelden',
|
||||
sign_up: 'Registrieren',
|
||||
@@ -362,8 +357,6 @@ const de = {
|
||||
'create_shortcut_s': 'Verknüpfungen erstellen', // In English: "Create Shortcuts"
|
||||
'minimize': 'Minimieren', // In English: "Minimize"
|
||||
'reload_app': 'App neu laden', // In English: "Reload App"
|
||||
'new_window': 'Neues Fenster', // In English: "New Window"
|
||||
'open_trash': 'Papierkorb öffnen', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'Als Worker veröffentlichen', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Vollbildmodus aktivieren', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -395,14 +388,8 @@ const de = {
|
||||
'window_title_publish_worker': 'Veröffentlichungsmitarbeiter', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'Authentifizierung...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'Empfehlen Sie uns weiter!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'Desktop anzeigen', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'Offene Fenster anzeigen', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'Vollbildmodus beenden', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'Vollbildmodus aktivieren', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'Position', // In English: "Position"
|
||||
'desktop_position_left': 'Links', // In English: "Left"
|
||||
'desktop_position_bottom': 'Unten', // In English: "Bottom"
|
||||
'desktop_position_right': 'Rechts', // In English: "Right"
|
||||
'item_shared_with_you': 'Ein Benutzer hat diesen Artikel mit Ihnen geteilt.', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'Sie haben diesen Artikel mit mindestens einem anderen Benutzer geteilt.', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'Verknüpfung', // In English: "Shortcut"
|
||||
|
||||
@@ -31,7 +31,6 @@ const emoji = {
|
||||
change_language: '🔄🌐',
|
||||
change_password: '🔑🔄',
|
||||
change_username: '👤🔄',
|
||||
close_all_windows: '🖼️❌🖼️',
|
||||
close_all_windows_and_log_out: '❌🔄🖼️🖼️🔚',
|
||||
confirm_account_for_free_referral_storage_c2a: '📧🆓👤📂📦🆓',
|
||||
confirm_delete_multiple_items: '❓❌📂❓', // folder emoji indicates plurality
|
||||
@@ -71,13 +70,11 @@ const emoji = {
|
||||
general: '⚙️',
|
||||
get_a_copy_of_on_puter: '📩🔄📂',
|
||||
get_copy_link: '🔗🔄',
|
||||
hide_all_windows: '🔚🔄🖼️🖼️',
|
||||
html_document: '📄🌐',
|
||||
invite_link: '🔗📩',
|
||||
item: '📂',
|
||||
items_in_trash_cannot_be_renamed: '🗑️🆓❌',
|
||||
jpeg_image: '🖼️',
|
||||
keep_in_taskbar: '📌📁',
|
||||
loading: '🔄',
|
||||
log_in: '👤🔓',
|
||||
log_out: '👤🔚',
|
||||
@@ -118,7 +115,6 @@ const emoji = {
|
||||
refer_friends_c2a: '👤📞📧👤',
|
||||
refer_friends_social_media_c2a: '📲👤🆓',
|
||||
refresh: '🔄🔄',
|
||||
remove_from_taskbar: '📌❌📁',
|
||||
rename: '🔄📛',
|
||||
replace: '🔄🔄',
|
||||
replace_all: '🔄🔄',
|
||||
@@ -136,7 +132,6 @@ const emoji = {
|
||||
session_saved: '👤💾🔄',
|
||||
set_new_password: '🔑🆕',
|
||||
share_to: '🔁➡️',
|
||||
show_all_windows: '🖼️🔓🖼️',
|
||||
show_hidden: '👁️🔄',
|
||||
sign_in_with_puter: '👤🆔',
|
||||
sign_up: '👤🆕',
|
||||
|
||||
@@ -91,7 +91,6 @@ const en = {
|
||||
reauth_required_message: 'Your session was updated for security — please sign in again.',
|
||||
color_depth: 'Color Depth',
|
||||
close: 'Close',
|
||||
close_all_windows: 'Close All Windows',
|
||||
close_all_windows_and_log_out: 'Close Windows and Log Out',
|
||||
close_menu: 'Close menu',
|
||||
collapse_sidebar: 'Collapse sidebar',
|
||||
@@ -269,7 +268,6 @@ const en = {
|
||||
general: 'General',
|
||||
get_a_copy_of_on_puter: 'Get a copy of \'%%\' on Puter.com!',
|
||||
get_copy_link: 'Get Copy Link',
|
||||
hide_all_windows: 'Hide All Windows',
|
||||
hide_password: 'Hide password',
|
||||
home: 'Home',
|
||||
html_document: 'HTML document',
|
||||
@@ -293,7 +291,6 @@ const en = {
|
||||
items_in_trash_cannot_be_renamed: 'This item can\'t be renamed because it\'s in the trash. To rename this item, first drag it out of the Trash.',
|
||||
jpeg_image: 'JPEG image',
|
||||
keep_both: 'Keep Both',
|
||||
keep_in_taskbar: 'Keep in Taskbar',
|
||||
language: 'Language',
|
||||
language_save_failed: 'Your language could not be saved. Please try again.',
|
||||
license: 'License',
|
||||
@@ -349,7 +346,6 @@ const en = {
|
||||
ok: 'OK',
|
||||
or: 'or',
|
||||
open: 'Open',
|
||||
new_window: 'New Window',
|
||||
open_with_ai: 'Open with AI',
|
||||
open_in_new_tab: 'Open in New Tab',
|
||||
app_pages: 'App pages',
|
||||
@@ -374,7 +370,6 @@ const en = {
|
||||
uninstall_named: 'Uninstall {{name}}',
|
||||
open_in_new_window: 'Open in New Window',
|
||||
open_menu: 'Open menu',
|
||||
open_trash: 'Open Trash',
|
||||
open_with: 'Open With',
|
||||
original_name: 'Original Name',
|
||||
original_path: 'Original Path',
|
||||
@@ -507,7 +502,6 @@ const en = {
|
||||
recover_password: 'Recover Password',
|
||||
refer_friends_social_media_c2a: 'Get 1 GB of free storage on Puter.com!',
|
||||
refresh: 'Refresh',
|
||||
remove_from_taskbar: 'Remove from Taskbar',
|
||||
remove_profile_picture: 'Remove photo',
|
||||
rename: 'Rename',
|
||||
replace: 'Replace',
|
||||
@@ -694,7 +688,6 @@ const en = {
|
||||
share_copy_link: 'Copy link',
|
||||
share_link_copied: 'Link copied',
|
||||
shortcut_to: 'Shortcut to',
|
||||
show_all_windows: 'Show All Windows',
|
||||
show_hidden: 'Show hidden',
|
||||
show_password: 'Show password',
|
||||
sign_in: 'Sign in',
|
||||
@@ -984,14 +977,8 @@ const en = {
|
||||
'window_title_refer_friend': 'Refer a friend!',
|
||||
|
||||
// Desktop UI
|
||||
'desktop_show_desktop': 'Show Desktop',
|
||||
'desktop_show_open_windows': 'Show Open Windows',
|
||||
'desktop_exit_full_screen': 'Exit Full Screen',
|
||||
'desktop_enter_full_screen': 'Enter Full Screen',
|
||||
'desktop_position': 'Position',
|
||||
'desktop_position_left': 'Left',
|
||||
'desktop_position_bottom': 'Bottom',
|
||||
'desktop_position_right': 'Right',
|
||||
// Item UI
|
||||
'item_shared_with_you': 'A user has shared this item with you.',
|
||||
'item_shared_by_you': 'You have shared this item with at least one other user.',
|
||||
@@ -1017,11 +1004,6 @@ const en = {
|
||||
'window_title_puter': 'Puter',
|
||||
'window_folder_empty': 'This folder is empty',
|
||||
|
||||
// Website Management
|
||||
|
||||
|
||||
'set_as_background': 'Set as Desktop Background',
|
||||
|
||||
// Permission Descriptions
|
||||
'perm_fs_see': 'see that {{name}} exists in {{path}}',
|
||||
'perm_fs_dir_see': 'see that the folder {{name}} in {{path}} exists, and whether anything exists in it or its subfolders',
|
||||
|
||||
@@ -45,7 +45,6 @@ const es = {
|
||||
change_password: 'Cambiar Contraseña',
|
||||
change_username: 'Cambiar Nombre de Usuario',
|
||||
close: 'Cerrar',
|
||||
close_all_windows: 'Cerrar todas las ventanas',
|
||||
close_all_windows_and_log_out: 'Cerrar ventanas y cerrar sesión',
|
||||
change_always_open_with: '¿Quieres abrir siempre este tipo de archivos con',
|
||||
confirm: 'Confirmar',
|
||||
@@ -141,7 +140,6 @@ const es = {
|
||||
general: 'General',
|
||||
get_a_copy_of_on_puter: '¡Consigue una copia de \'%%\' en Puter.com!',
|
||||
get_copy_link: 'Copiar el enlace',
|
||||
hide_all_windows: 'Ocultar todas las ventanas',
|
||||
home: 'Inicio',
|
||||
html_document: 'Documento HTML',
|
||||
hue: 'Hue',
|
||||
@@ -150,7 +148,6 @@ const es = {
|
||||
item: 'elemento',
|
||||
items_in_trash_cannot_be_renamed: 'Este elemento no se puede renombrar porque está en la papelera. Para cambiar el nombre de este archivo, primero extráelo fuera de la misma.',
|
||||
jpeg_image: 'Imagen JPEG',
|
||||
keep_in_taskbar: 'Mantener en la barra de tareas',
|
||||
language: 'Lenguage',
|
||||
license: 'Licencia',
|
||||
lightness: 'Claridad',
|
||||
@@ -223,7 +220,6 @@ const es = {
|
||||
'Consigue 1 GB por cada amigo que cree y confirme una cuenta en Puter ¡Tu amigo recibirá 1GB también!',
|
||||
refer_friends_social_media_c2a: '¡Consigue 1 GB de almacenamiento gratuito en Puter.com!',
|
||||
refresh: 'Refrescar',
|
||||
remove_from_taskbar: 'Eliminar de la barra de tareas',
|
||||
rename: 'Renombrar',
|
||||
replace: 'Remplazar',
|
||||
replace_all: 'Replace All',
|
||||
@@ -253,7 +249,6 @@ const es = {
|
||||
share: 'Compartir',
|
||||
share_to: 'Compartir a',
|
||||
shortcut_to: 'Acceso directo a',
|
||||
show_all_windows: 'Mostrar todas las ventanas',
|
||||
show_hidden: 'Mostrar ocultos',
|
||||
sign_in_with_puter: 'Inicia sesión con Puter',
|
||||
sign_up: 'Registrarse',
|
||||
@@ -410,8 +405,6 @@ const es = {
|
||||
'create_shortcut_s': 'Crear atajos', // In English: "Create Shortcuts"
|
||||
'minimize': 'Minimizar', // In English: "Minimize"
|
||||
'reload_app': 'Recargar la Aplicación', // In English: "Reload App"
|
||||
'new_window': 'Nueva Ventana', // In English: "New Window"
|
||||
'open_trash': 'Abrir Papelera', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'Publicar como un Worker', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Abrir pantalla completa', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -443,14 +436,8 @@ const es = {
|
||||
'window_title_publish_worker': 'Publicar Worker', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'Autenticando...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': '¡Invitar a un amigo!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'Mostrar Escritorio', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'Mostrar Ventanas Abiertas', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'Salir de Pantalla Completa', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'Abrir Pantalla Completa', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'Posición', // In English: "Position"
|
||||
'desktop_position_left': 'Izquierda', // In English: "Left"
|
||||
'desktop_position_bottom': 'Parte inferior', // In English: "Bottom"
|
||||
'desktop_position_right': 'Derecha', // In English: "Right"
|
||||
'item_shared_with_you': 'Un usuario compartió este elemento contigo.', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'Has compartido este elemento con otro usuario', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'Atajo', // In English: "Shortcut"
|
||||
|
||||
@@ -40,7 +40,6 @@ const fa = {
|
||||
change_password: 'تغییر رمز عبور',
|
||||
change_username: 'تغییر نام کاربری',
|
||||
close: 'بستن',
|
||||
close_all_windows: 'بستن همه پنجره ها',
|
||||
close_all_windows_and_log_out: 'بستن پنجرهها و خروج',
|
||||
confirm: 'تایید',
|
||||
confirm_2fa_setup: 'کد را به برنامه تأیید هویت خود اضافه کردهام',
|
||||
@@ -136,7 +135,6 @@ const fa = {
|
||||
general: 'عمومی',
|
||||
get_a_copy_of_on_puter: 'یک نسخه از \'%%\' را در Puter.com بگیرید!',
|
||||
get_copy_link: 'گرفتن لینک کپی',
|
||||
hide_all_windows: 'پنهان کردن همه پنجره ها',
|
||||
home: 'خانه',
|
||||
html_document: 'سند HTML',
|
||||
hue: 'رنگ',
|
||||
@@ -145,7 +143,6 @@ const fa = {
|
||||
item: 'مورد',
|
||||
items_in_trash_cannot_be_renamed: 'این مورد نمی تواند تغییر نام دهد زیرا در سطل زباله است. برای تغییر نام این مورد، ابتدا آن را از سطل زباله بیرون بکشید.',
|
||||
jpeg_image: 'تصویر JPEG',
|
||||
keep_in_taskbar: 'در نوار وظایف نگه دارید',
|
||||
language: 'زبان',
|
||||
license: 'مجوز',
|
||||
lightness: 'روشنایی',
|
||||
@@ -219,7 +216,6 @@ const fa = {
|
||||
'برای هر دوستی که حساب کاربری Puter ایجاد و تأیید کند، 1 گیگابایت دریافت کنید. دوست شما هم 1 گیگابایت دریافت خواهد کرد!',
|
||||
refer_friends_social_media_c2a: '1 گیگابایت فضای ذخیره سازی رایگان را در Puter.com بگیرید!',
|
||||
refresh: 'تازه کردن',
|
||||
remove_from_taskbar: 'از نوار وظایف حذف کن',
|
||||
rename: 'تغییر نام',
|
||||
replace: 'جایگزین کردن',
|
||||
replace_all: 'جایگزینی همه',
|
||||
@@ -248,7 +244,6 @@ const fa = {
|
||||
share: 'به اشتراک گذاری',
|
||||
share_to: 'اشتراک گذاری به',
|
||||
shortcut_to: 'میانبر به',
|
||||
show_all_windows: 'نمایش همه پنجره ها',
|
||||
show_hidden: 'نمایش مخفی',
|
||||
sign_in_with_puter: 'ورود با Puter',
|
||||
sign_up: 'ثبت نام',
|
||||
@@ -384,8 +379,6 @@ const fa = {
|
||||
'create_shortcut_s': 'ایجاد میانبرها',
|
||||
'minimize': 'کوچککردن',
|
||||
'reload_app': 'بارگذاری دوباره برنامه',
|
||||
'new_window': 'پنجره جدید',
|
||||
'open_trash': 'باز کردن سطل زباله',
|
||||
'publish_as_serverless_worker': 'انتشار بهعنوان کارگر',
|
||||
'toolbar.enter_fullscreen': 'ورود به تمامصفحه',
|
||||
'toolbar.github': 'گیتهاب',
|
||||
@@ -417,14 +410,8 @@ const fa = {
|
||||
'window_title_publish_worker': 'انتشار کارگر',
|
||||
'window_title_authenticating': 'در حال احراز هویت...',
|
||||
'window_title_refer_friend': 'معرفی یک دوست!',
|
||||
'desktop_show_desktop': 'نمایش دسکتاپ',
|
||||
'desktop_show_open_windows': 'نمایش پنجرههای باز',
|
||||
'desktop_exit_full_screen': 'خروج از تمامصفحه',
|
||||
'desktop_enter_full_screen': 'ورود به تمامصفحه',
|
||||
'desktop_position': 'موقعیت',
|
||||
'desktop_position_left': 'چپ',
|
||||
'desktop_position_bottom': 'پایین',
|
||||
'desktop_position_right': 'راست',
|
||||
'item_shared_with_you': 'یک کاربر این مورد را با شما به اشتراک گذاشته است.',
|
||||
'item_shared_by_you': 'شما این مورد را با حداقل یک کاربر دیگر به اشتراک گذاشتهاید.',
|
||||
'item_shortcut': 'میانبر',
|
||||
|
||||
@@ -44,7 +44,6 @@ const fi = {
|
||||
change_password: 'Vaihda salasana',
|
||||
change_username: 'Vaihda käyttäjänimi',
|
||||
close: 'Sulje',
|
||||
close_all_windows: 'Sulje kaikki ikkunat',
|
||||
close_all_windows_and_log_out: 'Sulje ikkunat ja kirjaudu ulos',
|
||||
change_always_open_with:
|
||||
'Haluatko aina avata tämän tyyppisen tiedoston sovelluksella',
|
||||
@@ -167,7 +166,6 @@ const fi = {
|
||||
// 'get a copy of a link' => 'Ota Kopio Linkkiin' or
|
||||
// 'get a link to the copy' => 'Ota Linkki Kopioon' - More probable, just want to be sure
|
||||
|
||||
hide_all_windows: 'Piilota kaikki ikkunat',
|
||||
home: 'Koti',
|
||||
html_document: 'HTML-dokumentti',
|
||||
hue: 'Sävy',
|
||||
@@ -176,7 +174,6 @@ const fi = {
|
||||
item: 'kohde',
|
||||
items_in_trash_cannot_be_renamed: 'Tätä kohdetta ei voi nimetä uudelleen, koska se on roskakorissa. Jos haluat nimetä kohteen uudelleen, palauta se ensin roskakorista.',
|
||||
jpeg_image: 'JPEG-kuva',
|
||||
keep_in_taskbar: 'Pidä tehtäväpalkissa',
|
||||
language: 'Kieli',
|
||||
license: 'Lisenssi',
|
||||
lightness: 'Valoisuus',
|
||||
@@ -263,7 +260,6 @@ const fi = {
|
||||
// "get rid of" => "Oletko varma, että haluat luovuttaa tämän osoitteen?" or
|
||||
// "publish" => "Oletko varma, että haluat julkaista tämän osoitteen?"
|
||||
|
||||
remove_from_taskbar: 'Poista tehtäväpalkista',
|
||||
rename: 'Nimeä uudelleen',
|
||||
replace: 'Replace',
|
||||
replace_all: 'Korvaa kaikki',
|
||||
@@ -296,7 +292,6 @@ const fi = {
|
||||
// If "share to" is followed by the name of a user, it will not make any sense, as the name can be suffixed by for example "Jaa %%lle".
|
||||
|
||||
shortcut_to: 'Pikakuvake',
|
||||
show_all_windows: 'Näytä kaikki ikkunat',
|
||||
show_hidden: 'Näytä piilotetut',
|
||||
sign_in_with_puter: 'Kirjaudu sisään Puterilla',
|
||||
sign_up: 'Rekisteröidy',
|
||||
@@ -450,8 +445,6 @@ const fi = {
|
||||
'create_shortcut_s': 'Luo pikakuvakkeita',
|
||||
'minimize': 'Pienennä',
|
||||
'reload_app': 'Lataa sovellus uudelleen',
|
||||
'new_window': 'Uusi ikkuna',
|
||||
'open_trash': 'Avaa roskakori',
|
||||
'publish_as_serverless_worker': 'Julkaise työntekijänä',
|
||||
'toolbar.enter_fullscreen': 'Koko näyttöön',
|
||||
'toolbar.github': 'GitHub',
|
||||
@@ -483,14 +476,8 @@ const fi = {
|
||||
'window_title_publish_worker': 'Julkaise työntekijä',
|
||||
'window_title_authenticating': 'Todennetaan...',
|
||||
'window_title_refer_friend': 'Suosittele ystävälle!',
|
||||
'desktop_show_desktop': 'Näytä työpöytä',
|
||||
'desktop_show_open_windows': 'Näytä avoimet ikkunat',
|
||||
'desktop_exit_full_screen': 'Poistu koko näytöstä',
|
||||
'desktop_enter_full_screen': 'Koko näyttöön',
|
||||
'desktop_position': 'Sijainti',
|
||||
'desktop_position_left': 'Vasen',
|
||||
'desktop_position_bottom': 'Ala',
|
||||
'desktop_position_right': 'Oikea',
|
||||
'item_shared_with_you': 'Käyttäjä on jakanut tämän kohteen kanssasi.',
|
||||
'item_shared_by_you': 'Olet jakanut tämän kohteen ainakin yhden muun käyttäjän kanssa.',
|
||||
'item_shortcut': 'Pikakuvake',
|
||||
|
||||
@@ -38,7 +38,6 @@ const fil = {
|
||||
change_password: 'Baguhin ang password',
|
||||
change_username: 'Baguhin ang username',
|
||||
close: 'Isara',
|
||||
close_all_windows: 'Isara ang lahat ng window',
|
||||
close_all_windows_and_log_out: 'Isara ang mga window at mag-log out',
|
||||
change_always_open_with: 'Gusto mo bang laging buksan ang ganitong file gamit ang',
|
||||
confirm: 'Kumpirmahin',
|
||||
@@ -130,7 +129,6 @@ const fil = {
|
||||
general: 'Pangkalahatan',
|
||||
get_a_copy_of_on_puter: 'Kumuha ng kopya ng \'%%\' sa Puter.com!',
|
||||
get_copy_link: 'Kunin ang copy link',
|
||||
hide_all_windows: 'Itago ang lahat ng window',
|
||||
home: 'Home',
|
||||
html_document: 'HTML na dokumento',
|
||||
hue: 'Hue',
|
||||
@@ -139,7 +137,6 @@ const fil = {
|
||||
item: 'item',
|
||||
items_in_trash_cannot_be_renamed: 'Hindi mapalitan ang pangalan ng item na ito dahil nasa Trash. Ilabas muna ito sa Trash.',
|
||||
jpeg_image: 'JPEG na larawan',
|
||||
keep_in_taskbar: 'Panatilihin sa taskbar',
|
||||
language: 'Wika',
|
||||
license: 'Lisensya',
|
||||
lightness: 'Liwanag',
|
||||
@@ -207,7 +204,6 @@ const fil = {
|
||||
recover_password: 'I-recover ang password',
|
||||
refer_friends_social_media_c2a: 'Makakuha ng 1 GB na libreng storage sa Puter.com!',
|
||||
refresh: 'I-refresh',
|
||||
remove_from_taskbar: 'Alisin sa taskbar',
|
||||
rename: 'Palitan ang pangalan',
|
||||
replace: 'Palitan',
|
||||
replace_all: 'Palitan lahat',
|
||||
@@ -236,7 +232,6 @@ const fil = {
|
||||
share: 'Ibahagi',
|
||||
share_to: 'Ibahagi kay',
|
||||
shortcut_to: 'Shortcut papunta sa',
|
||||
show_all_windows: 'Ipakita ang lahat ng window',
|
||||
show_hidden: 'Ipakita ang nakatago',
|
||||
sign_in_with_puter: 'Mag-sign in gamit ang Puter',
|
||||
sign_up: 'Mag-sign up',
|
||||
@@ -377,8 +372,6 @@ const fil = {
|
||||
'create_shortcut_s': 'Gumawa ng mga shortcut',
|
||||
'minimize': 'I-minimize',
|
||||
'reload_app': 'I-reload ang app',
|
||||
'new_window': 'Bagong window',
|
||||
'open_trash': 'Buksan ang Trash',
|
||||
'publish_as_serverless_worker': 'I-publish bilang worker',
|
||||
'toolbar.enter_fullscreen': 'Pumunta sa full screen',
|
||||
'toolbar.github': 'GitHub',
|
||||
@@ -410,14 +403,8 @@ const fil = {
|
||||
'window_title_publish_worker': 'I-publish ang worker',
|
||||
'window_title_authenticating': 'Nag-a-authenticate...',
|
||||
'window_title_refer_friend': 'Mag-refer ng kaibigan!',
|
||||
'desktop_show_desktop': 'Ipakita ang desktop',
|
||||
'desktop_show_open_windows': 'Ipakita ang mga bukas na window',
|
||||
'desktop_exit_full_screen': 'Lumabas sa full screen',
|
||||
'desktop_enter_full_screen': 'Pumunta sa full screen',
|
||||
'desktop_position': 'Posisyon',
|
||||
'desktop_position_left': 'Kaliwa',
|
||||
'desktop_position_bottom': 'Ibaba',
|
||||
'desktop_position_right': 'Kanan',
|
||||
'item_shared_with_you': 'May user na nagbahagi ng item na ito sa iyo.',
|
||||
'item_shared_by_you': 'Ibinahagi mo ang item na ito sa kahit isang ibang user.',
|
||||
'item_shortcut': 'Shortcut',
|
||||
|
||||
@@ -44,7 +44,6 @@ const fr = {
|
||||
change_username: "Changer le nom d'utilisateur",
|
||||
color_depth: 'Profondeur de couleur',
|
||||
close: 'Fermer',
|
||||
close_all_windows: 'Fermer toutes les fenêtres',
|
||||
close_all_windows_and_log_out: 'Fermer les fenêtres et se déconnecter',
|
||||
confirm: 'Confirmer',
|
||||
confirm_2fa_setup: "J'ai ajouté le code à mon application d'authentification",
|
||||
@@ -137,7 +136,6 @@ const fr = {
|
||||
general: 'Général',
|
||||
get_a_copy_of_on_puter: 'Obtenez une copie de \'%%\' sur Puter.com !',
|
||||
get_copy_link: 'Obtenir le lien de copie',
|
||||
hide_all_windows: 'Masquer toutes les fenêtres',
|
||||
home: 'Accueil',
|
||||
html_document: 'Document HTML',
|
||||
hue: 'Teinte',
|
||||
@@ -146,7 +144,6 @@ const fr = {
|
||||
item: 'élément',
|
||||
items_in_trash_cannot_be_renamed: 'Cet élément ne peut pas être renommé car il se trouve dans la corbeille. Pour renommer cet élément, faites-le d\'abord glisser hors de la corbeille.',
|
||||
jpeg_image: 'Image JPEG',
|
||||
keep_in_taskbar: 'Garder dans la barre des tâches',
|
||||
language: 'Langue',
|
||||
license: 'Licence',
|
||||
lightness: 'Luminosité',
|
||||
@@ -176,11 +173,9 @@ const fr = {
|
||||
no: 'Non',
|
||||
ok: 'OK',
|
||||
open: 'Ouvrir',
|
||||
new_window: 'Nouvelle fenêtre',
|
||||
open_with_ai: 'Ouvrir avec l\'IA',
|
||||
open_in_new_tab: 'Ouvrir dans un nouvel onglet',
|
||||
open_in_new_window: 'Ouvrir dans une nouvelle fenêtre',
|
||||
open_trash: 'Ouvrir la corbeille',
|
||||
open_with: 'Ouvrir avec',
|
||||
original_name: "Nom d'origine",
|
||||
original_path: "Chemin d'origine",
|
||||
@@ -223,7 +218,6 @@ const fr = {
|
||||
refer_friends_c2a: 'Obtenez 1 Go pour chaque ami qui crée et confirme un compte sur Puter. Votre ami recevra également 1 Go !',
|
||||
refer_friends_social_media_c2a: 'Obtenez 1 Go de stockage gratuit sur Puter.com !',
|
||||
refresh: 'Actualiser',
|
||||
remove_from_taskbar: 'Retirer de la barre des tâches',
|
||||
rename: 'Renommer',
|
||||
replace: 'Remplacer',
|
||||
replace_all: 'Tout remplacer',
|
||||
@@ -254,7 +248,6 @@ const fr = {
|
||||
share: 'Partager',
|
||||
share_to: 'Partager à',
|
||||
shortcut_to: 'Raccourci vers',
|
||||
show_all_windows: 'Afficher toutes les fenêtres',
|
||||
show_hidden: 'Afficher les fichiers cachés',
|
||||
sign_in_with_puter: 'Se connecter avec Puter',
|
||||
sign_up: "S'inscrire",
|
||||
@@ -436,14 +429,8 @@ const fr = {
|
||||
'window_title_refer_friend': 'Parrainer un ami !',
|
||||
|
||||
// Desktop UI
|
||||
'desktop_show_desktop': 'Afficher le bureau',
|
||||
'desktop_show_open_windows': 'Afficher les fenêtres ouvertes',
|
||||
'desktop_exit_full_screen': 'Quitter le mode plein écran',
|
||||
'desktop_enter_full_screen': 'Passer en plein écran',
|
||||
'desktop_position': 'Position',
|
||||
'desktop_position_left': 'Gauche',
|
||||
'desktop_position_bottom': 'Bas',
|
||||
'desktop_position_right': 'Droite',
|
||||
|
||||
// Item UI
|
||||
'item_shared_with_you': 'Un utilisateur a partagé cet élément avec vous.',
|
||||
@@ -463,10 +450,6 @@ const fr = {
|
||||
'window_title_puter': 'Puter',
|
||||
'window_folder_empty': 'Ce dossier est vide',
|
||||
|
||||
// Website Management
|
||||
|
||||
'set_as_background': 'Définir comme arrière-plan',
|
||||
|
||||
// Permission Descriptions
|
||||
'perm_email_read': 'voir votre adresse e-mail',
|
||||
'perm_folder_desktop': 'votre dossier Bureau',
|
||||
|
||||
@@ -38,7 +38,6 @@ const he = {
|
||||
change_password: 'שינוי סיסמה',
|
||||
change_username: 'שינוי שם משתמש',
|
||||
close: 'סגירה',
|
||||
close_all_windows: 'סגירת כל החלונות',
|
||||
close_all_windows_and_log_out: 'סגור את החלונות והתנתק',
|
||||
change_always_open_with: 'האם אתה רוצה תמיד לפתוח סוג זה של קובץ עם',
|
||||
confirm: 'לאשר',
|
||||
@@ -131,7 +130,6 @@ const he = {
|
||||
general: 'כללי',
|
||||
get_a_copy_of_on_puter: 'קבל עותק של \'%%\' ב Puter.com!',
|
||||
get_copy_link: 'קבל העתק קישור',
|
||||
hide_all_windows: 'הסתר את כל החלונות',
|
||||
home: 'בית',
|
||||
html_document: 'מסמך HTML',
|
||||
hue: 'דרגת צבע',
|
||||
@@ -140,7 +138,6 @@ const he = {
|
||||
item: 'פריט',
|
||||
items_in_trash_cannot_be_renamed: 'לא ניתן לשנות את שם הפריט הזה כי הוא נמצא באשפה. כדי לשנות את שם הפריט הזה, תחילה גרור אותו מחוץ לאשפה.',
|
||||
jpeg_image: 'JPEG תמונת',
|
||||
keep_in_taskbar: 'שמירה בשורת המשימות',
|
||||
language: 'שפה',
|
||||
license: 'רישיון',
|
||||
lightness: 'בהירות',
|
||||
@@ -210,7 +207,6 @@ const he = {
|
||||
'קבל 1 גיגה בייט עבור כל חבר שיוצר ומאשר חשבון ב Puter. גם החבר שלך יקבל 1 גיגה בייט!',
|
||||
refer_friends_social_media_c2a: 'קבל שטח אחסון של 1 גיגה בייט בחינם Puter.com!',
|
||||
refresh: 'רענן',
|
||||
remove_from_taskbar: 'הסרה משורת המשימות',
|
||||
rename: 'שנה שם',
|
||||
replace: 'החלף',
|
||||
replace_all: 'החלף הכל',
|
||||
@@ -239,7 +235,6 @@ const he = {
|
||||
share: 'שיתוף',
|
||||
share_to: 'שתף אל',
|
||||
shortcut_to: 'קיצור דרך אל',
|
||||
show_all_windows: 'הצג את כל החלונות',
|
||||
show_hidden: 'הצג מוסתר',
|
||||
sign_in_with_puter: 'להתחבר עם Puter',
|
||||
sign_up: 'הרשמה',
|
||||
@@ -378,8 +373,6 @@ const he = {
|
||||
'create_shortcut_s': 'צור קיצורי דרך', // In English: "Create Shortcuts"
|
||||
'minimize': 'מזער', // In English: "Minimize"
|
||||
'reload_app': 'טען מחדש', // In English: "Reload App"
|
||||
'new_window': 'חלון חדש', // In English: "New Window"
|
||||
'open_trash': 'פתח אשפה', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'פרסם כעובד', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'מסך מלא', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'גיטהאב', // In English: "GitHub"
|
||||
@@ -411,14 +404,8 @@ const he = {
|
||||
'window_title_publish_worker': 'פרסם עובד', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'מאמת', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': '!הפנה חבר', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'הראה שולחן עבודה', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'הראה חלונות פתוחים', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'בטל מסך מלא', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'הפעל מסך מלא', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'מיקום', // In English: "Position"
|
||||
'desktop_position_left': 'שמאל', // In English: "Left"
|
||||
'desktop_position_bottom': 'מטה', // In English: "Bottom"
|
||||
'desktop_position_right': 'ימין', // In English: "Right"
|
||||
'item_shared_with_you': 'משתמש שיתף עמך פריט זה', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'שיתפת פריט זה עם לפחות משתמש אחד נוסף', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'קיצור דרך', // In English: "Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const hi = {
|
||||
change_password: 'पासवर्ड बदलें',
|
||||
change_username: 'उपयोगकर्ता नाम बदलें',
|
||||
close: 'बंद ',
|
||||
close_all_windows: 'सभी विंडोज़ बंद करें',
|
||||
close_all_windows_and_log_out: 'विंडोज़ बंद करें और लॉग आउट करें',
|
||||
change_always_open_with: 'क्या आप इस प्रकार की फ़ाइल को हमेशा खोलना चाहते हैं?',
|
||||
confirm_2fa_setup: 'मैंने अपने प्रमाणक ऐप में कोड जोड़ दिया है',
|
||||
@@ -120,7 +119,6 @@ const hi = {
|
||||
general: 'सामान्य',
|
||||
get_a_copy_of_on_puter: "Purer.com पर '%%' की एक कॉपी प्राप्त करें!",
|
||||
get_copy_link: 'कॉपी लिंक प्राप्त करें',
|
||||
hide_all_windows: 'सभी विंडोज़ छिपाएँ',
|
||||
home: 'घर',
|
||||
html_document: 'एचटीएमएल दस्तावेज़',
|
||||
hue: 'रंग',
|
||||
@@ -129,7 +127,6 @@ const hi = {
|
||||
item: 'वस्तु',
|
||||
items_in_trash_cannot_be_renamed: 'इस वस्तु का नाम नहीं बदला जा सकता क्योंकि यह कूड़ेदान में है। इस वस्तु का नाम बदलने के लिए, पहले इसे ट्रैश से बाहर खींचें।',
|
||||
jpeg_image: 'जेपीईजी छवि',
|
||||
keep_in_taskbar: 'टास्कबार में रखें',
|
||||
language: 'भाषा',
|
||||
license: 'लाइसेंस',
|
||||
lightness: 'चमक',
|
||||
@@ -195,7 +192,6 @@ const hi = {
|
||||
refer_friends_c2a: 'पुटर पर खाता बनाने और पुष्टि करने वाले प्रत्येक मित्र के लिए 1 जीबी प्राप्त करें। आपके दोस्त को भी मिलेगा 1 जीबी!',
|
||||
refer_friends_social_media_c2a: 'Purer.com पर 1 जीबी निःशुल्क स्टोरेज प्राप्त करें!',
|
||||
refresh: 'ताजा करना ',
|
||||
remove_from_taskbar: 'टास्कबार से हटाएँ',
|
||||
rename: 'नाम बदलें',
|
||||
replace: 'प्रतिस्थापित करें',
|
||||
replace_all: 'सबको बदली करें',
|
||||
@@ -223,7 +219,6 @@ const hi = {
|
||||
share: 'आदान-प्रदान',
|
||||
share_to: 'साझा',
|
||||
shortcut_to: 'के लिए शॉर्टकट',
|
||||
show_all_windows: 'सभी विंडोज़ दिखाएँ',
|
||||
show_hidden: 'छिपा हुआ दिखाएं',
|
||||
sign_in_with_puter: 'पुटर के साथ साइन इन करें',
|
||||
sign_up: 'साइन अप करें',
|
||||
@@ -355,8 +350,6 @@ const hi = {
|
||||
'create_shortcut_s': 'शॉर्टकट्स बनाएँ', // In English: "Create Shortcuts"
|
||||
'minimize': 'मिनिमाइज़ करें', // In English: "Minimize"
|
||||
'reload_app': 'ऐप पुनः लोड करें', // In English: "Reload App"
|
||||
'new_window': 'नई विंडो', // In English: "New Window"
|
||||
'open_trash': 'ट्रैश खोलें', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'वर्कर के रूप में प्रकाशित करें', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'पूर्ण स्क्रीन में जाएँ', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -388,14 +381,8 @@ const hi = {
|
||||
'window_title_publish_worker': 'वर्कर प्रकाशित करें', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'प्रमाणित किया जा रहा है...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'मित्र को रेफ़र करें!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'डेस्कटॉप दिखाएँ', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'खुली विंडो दिखाएँ', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'पूर्ण स्क्रीन से बाहर निकलें', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'पूर्ण स्क्रीन में जाएँ', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'स्थिति', // In English: "Position"
|
||||
'desktop_position_left': 'बाएँ', // In English: "Left"
|
||||
'desktop_position_bottom': 'नीचे', // In English: "Bottom"
|
||||
'desktop_position_right': 'दाएँ', // In English: "Right"
|
||||
'item_shared_with_you': 'एक उपयोगकर्ता ने यह वस्तु आपके साथ साझा की है।', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'आपने यह वस्तु कम से कम एक अन्य उपयोगकर्ता के साथ साझा की है।', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'शॉर्टकट', // In English: "Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const hu = {
|
||||
change_password: 'Jelszó módosítása',
|
||||
change_username: 'Felhasználónév módosítása',
|
||||
close: 'Bezár',
|
||||
close_all_windows: 'Minden ablak bezárása',
|
||||
close_all_windows_and_log_out: 'Ablakok bezárása és kijelentkezés',
|
||||
change_always_open_with: 'Mindig ezzel az alkalmazással nyitja meg ezt a fájlt?',
|
||||
confirm: 'Megerősít',
|
||||
@@ -121,7 +120,6 @@ const hu = {
|
||||
general: 'Általános',
|
||||
get_a_copy_of_on_puter: "Szerezz egy példányt a(z) '%%' Puter.com-on!",
|
||||
get_copy_link: 'Másolási link megszerzése',
|
||||
hide_all_windows: 'Minden ablak elrejtése',
|
||||
home: 'Otthon',
|
||||
html_document: 'HTML dokumentum',
|
||||
hue: 'Színárnyalat',
|
||||
@@ -130,7 +128,6 @@ const hu = {
|
||||
item: 'elem',
|
||||
items_in_trash_cannot_be_renamed: 'Ez az elem nem nevezhető át, mert a kukában van. A név megváltoztatásához először húzd ki a Kukából.',
|
||||
jpeg_image: 'JPEG kép',
|
||||
keep_in_taskbar: 'Tartsa a tálcán',
|
||||
language: 'Nyelv',
|
||||
license: 'Licenc',
|
||||
lightness: 'Világosság',
|
||||
@@ -196,7 +193,6 @@ const hu = {
|
||||
refer_friends_c2a: 'Kapj 1 GB-ot minden barátodért, aki létrehoz és megerősít egy fiókot a Puteren. A barátod is kap 1 GB-ot!',
|
||||
refer_friends_social_media_c2a: 'Kapj 1 GB ingyenes tárhelyet a Puter.com-on!',
|
||||
refresh: 'Frissítés',
|
||||
remove_from_taskbar: 'Eltávolítás a tálcáról',
|
||||
rename: 'Átnevezés',
|
||||
replace: 'Csere',
|
||||
replace_all: 'Összes cseréje',
|
||||
@@ -224,7 +220,6 @@ const hu = {
|
||||
share: 'Megosztás',
|
||||
share_to: 'Megosztás ide:',
|
||||
shortcut_to: 'Parancsikon ide:',
|
||||
show_all_windows: 'Összes ablak megjelenítése',
|
||||
show_hidden: 'Rejtett megjelenítése',
|
||||
sign_in_with_puter: 'Bejelentkezés Puterrel',
|
||||
sign_up: 'Regisztráció',
|
||||
@@ -356,8 +351,6 @@ const hu = {
|
||||
'create_shortcut_s': 'Parancsikonok létrehozása', // English: "Create Shortcuts"
|
||||
'minimize': 'Minimalizálás', // English: "Minimize"
|
||||
'reload_app': 'Alkalmazás újratöltése', // English: "Reload App"
|
||||
'new_window': 'Új ablak', // English: "New Window"
|
||||
'open_trash': 'Szemetes megnyitása', // English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'Munkás közzététele', // English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Teljes képernyő', // English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // English: "GitHub"
|
||||
@@ -389,14 +382,8 @@ const hu = {
|
||||
'window_title_publish_worker': 'Munkás közzététele', // English: "Publish Worker"
|
||||
'window_title_authenticating': 'Hitelesítés...', // English: "Authenticating..."
|
||||
'window_title_refer_friend': 'Barát ajánlása!', // English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'Asztal megjelenítése', // English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'Megnyitott ablakok megjelenítése', // English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'Kilépés teljes képernyőből', // English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'Teljes képernyő', // English: "Enter Full Screen"
|
||||
'desktop_position': 'Pozíció', // English: "Position"
|
||||
'desktop_position_left': 'Bal', // English: "Left"
|
||||
'desktop_position_bottom': 'Alul', // English: "Bottom"
|
||||
'desktop_position_right': 'Jobb', // English: "Right"
|
||||
'item_shared_with_you': 'Egy felhasználó megosztotta Önnel ezt az elemet.', // English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'Legalább egy másik felhasználóval megosztotta ezt az elemet.', // English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'Parancsikon', // English: "Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const hy = {
|
||||
change_password: 'Փոխել գաղտնաբառը',
|
||||
change_username: 'Փոխել օգտանունը',
|
||||
close: 'Փակել',
|
||||
close_all_windows: 'Փակել բոլոր պատուհանները',
|
||||
close_all_windows_and_log_out: 'Փակել պատուհաններն ու դուրս գալ',
|
||||
change_always_open_with: 'Ցանկանու՞մ եք այս տեսակի ֆայլը միշտ բացել',
|
||||
confirm: 'Հաստատել',
|
||||
@@ -134,7 +133,6 @@ const hy = {
|
||||
general: 'Ընդհանուր',
|
||||
get_a_copy_of_on_puter: "Ստանալ '%%'-ի պատճենը Puter.com-ում!",
|
||||
get_copy_link: 'Ստանալ պատճենված հղումը',
|
||||
hide_all_windows: 'Թաքցնել բոլոր պատուհանները',
|
||||
home: 'Գլխավոր',
|
||||
html_document: 'HTML փաստաթուղթ',
|
||||
hue: 'Երանգ',
|
||||
@@ -144,7 +142,6 @@ const hy = {
|
||||
items_in_trash_cannot_be_renamed:
|
||||
'Այս տարրը չի կարող վերանվանվել, քանի որ այն աղբարկղում է: Այս տարրը վերանվանելու համար նախ տեղափոխեք այն աղբարկղից:',
|
||||
jpeg_image: 'JPEG պատկեր',
|
||||
keep_in_taskbar: 'Պահպանել խնդրագոտում',
|
||||
language: 'Լեզու',
|
||||
license: 'Լիցենզիա',
|
||||
lightness: 'Լուսավորություն',
|
||||
@@ -217,7 +214,6 @@ const hy = {
|
||||
'Ստացեք 1 ԳԲ յուրաքանչյուր ընկերոջ համար, ով ստեղծում և հաստատում է հաշիվ Փութերում: Ձեր ընկերը նույնպես կստանա 1 ԳԲ!',
|
||||
refer_friends_social_media_c2a: 'Ստացեք 1 ԳԲ անվճար պահեստ Puter.com-ում!',
|
||||
refresh: 'Թարմացնել',
|
||||
remove_from_taskbar: 'Հանել խնդրագոտուց',
|
||||
rename: 'Վերանվանել',
|
||||
replace: 'Փոխարինել',
|
||||
replace_all: 'Փոխարինել բոլորը',
|
||||
@@ -249,7 +245,6 @@ const hy = {
|
||||
share: 'Տարածել',
|
||||
share_to: 'Տարածել դեպի',
|
||||
shortcut_to: 'Դյուրանցում դեպի',
|
||||
show_all_windows: 'Ցույց տալ բոլոր պատուհանները',
|
||||
show_hidden: 'Ցույց տալ թաքնված տարրերը',
|
||||
sign_in_with_puter: 'Մուտք գործել Փութերի միջոցով',
|
||||
sign_up: 'Գրանցվել',
|
||||
@@ -405,8 +400,6 @@ const hy = {
|
||||
create_shortcut_s: 'Ստեղծել դյուրանցումներ',
|
||||
minimize: 'Փոքրացնել',
|
||||
reload_app: 'Վերաբեռնել հավելվածը',
|
||||
new_window: 'Նոր պատուհան',
|
||||
open_trash: 'Բացել Աղբամանը',
|
||||
publish_as_serverless_worker: 'Հրապարակել որպես Worker',
|
||||
'toolbar.enter_fullscreen': 'Լիաէկրան ռեժիմ',
|
||||
'toolbar.github': 'GitHub',
|
||||
@@ -440,14 +433,8 @@ const hy = {
|
||||
window_title_publish_worker: 'Հրապարակել Worker',
|
||||
window_title_authenticating: 'Նույնականացում…',
|
||||
window_title_refer_friend: 'Հրավիրել ընկերոջը',
|
||||
desktop_show_desktop: 'Ցուցադրել աշխատասեղանը',
|
||||
desktop_show_open_windows: 'Ցուցադրել բաց պատուհանները',
|
||||
desktop_exit_full_screen: 'Ելք լիաէկրան ռեժիմից',
|
||||
desktop_enter_full_screen: 'Լիաէկրան ռեժիմ',
|
||||
desktop_position: 'Դիրք',
|
||||
desktop_position_left: 'Ձախ',
|
||||
desktop_position_bottom: 'Ներքև',
|
||||
desktop_position_right: 'Աջ',
|
||||
item_shared_with_you: 'Օգտատերը կիսվել է այս տարրով ձեզ հետ.',
|
||||
item_shared_by_you:
|
||||
'Դուք կիսվել եք այս տարրով առնվազն մեկ այլ օգտատիրոջ հետ.',
|
||||
|
||||
@@ -38,7 +38,6 @@ const id = {
|
||||
change_password: 'Ubah Kata Sandi',
|
||||
change_username: 'Ubah Nama Pengguna',
|
||||
close: 'Tutup',
|
||||
close_all_windows: 'Tutup Semua Jendela',
|
||||
close_all_windows_and_log_out: 'Tutup Jendela dan Keluar',
|
||||
change_always_open_with: 'Apakah Anda ingin selalu membuka jenis file ini dengan',
|
||||
confirm: 'Konfirmasi',
|
||||
@@ -132,7 +131,6 @@ const id = {
|
||||
general: 'Umum',
|
||||
get_a_copy_of_on_puter: 'Dapatkan salinan \'%%\' di Puter.com!',
|
||||
get_copy_link: 'Dapatkan Salinan Tautan',
|
||||
hide_all_windows: 'Sembunyikan Semua Jendela',
|
||||
home: 'Beranda',
|
||||
html_document: 'Dokumen HTML',
|
||||
hue: 'Hue',
|
||||
@@ -141,7 +139,6 @@ const id = {
|
||||
item: 'barang',
|
||||
items_in_trash_cannot_be_renamed: 'Barang ini tidak dapat diganti namanya karena berada di sampah. Untuk mengganti nama barang ini, pindahkan dahulu dari Sampah.',
|
||||
jpeg_image: 'Gambar JPEG',
|
||||
keep_in_taskbar: 'Pertahankan di Bilah Tugas',
|
||||
language: 'Bahasa',
|
||||
license: 'Lisensi',
|
||||
lightness: 'Kecerahan',
|
||||
@@ -212,7 +209,6 @@ const id = {
|
||||
'Dapatkan 1 GB untuk setiap teman yang membuat dan mengonfirmasi akun di Puter. Teman Anda juga akan mendapatkan 1 GB!',
|
||||
refer_friends_social_media_c2a: 'Dapatkan 1 GB penyimpanan gratis di Puter.com!',
|
||||
refresh: 'Muat Ulang',
|
||||
remove_from_taskbar: 'Hapus dari Bilah Tugas',
|
||||
rename: 'Ganti Nama',
|
||||
replace: 'Ganti',
|
||||
replace_all: 'Ganti Semua',
|
||||
@@ -241,7 +237,6 @@ const id = {
|
||||
share: 'Bagikan',
|
||||
share_to: 'Bagikan ke',
|
||||
shortcut_to: 'Pintasan ke',
|
||||
show_all_windows: 'Tampilkan Semua Jendela',
|
||||
show_hidden: 'Tampilkan yang tersembunyi',
|
||||
sign_in_with_puter: 'Masuk dengan Puter',
|
||||
sign_up: 'Daftar',
|
||||
@@ -388,8 +383,6 @@ const id = {
|
||||
'create_shortcut_s': 'Buat Pintasan', // In English: "Create Shortcuts"
|
||||
'minimize': 'Kecilkan', // In English: "Minimize"
|
||||
'reload_app': 'Muat Ulang Aplikasi', // In English: "Reload App"
|
||||
'new_window': 'Jendela Baru', // In English: "New Window"
|
||||
'open_trash': 'Buka Tempat Sampah', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'Publikasikan sebagai Worker', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Masuk ke Layar Penuh', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'Github', // In English: "GitHub"
|
||||
@@ -421,14 +414,8 @@ const id = {
|
||||
'window_title_publish_worker': 'Publikasikan Worker', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'Proses Autentikasi...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'Ajak Teman!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'Tunjukkan Desktop', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'Tampilkan Jendela yang Terbuka', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'Keluar dari Layar Penuh', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'Masuk ke Layar Penuh', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'Posisi', // In English: "Position"
|
||||
'desktop_position_left': 'Kiri', // In English: "Left"
|
||||
'desktop_position_bottom': 'Bawah', // In English: "Bottom"
|
||||
'desktop_position_right': 'Kanan', // In English: "Right"
|
||||
'item_shared_with_you': 'Pengguna lain berbagi barang ini dengan Anda', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'Anda telah membagikan barang ini dengan setidaknya satu pengguna lain', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'Pintasan', // In English: "Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const ig = {
|
||||
change_password: 'ịgbanwe paswọọdụ',
|
||||
change_username: 'ịgbanwe aha njirimara',
|
||||
close: 'mmechi',
|
||||
close_all_windows: 'mmechi Windows niile',
|
||||
close_all_windows_and_log_out: 'mmechi Windows na wee pụọ',
|
||||
change_always_open_with: 'Ị chọrọ I na mepee ụdị faịlụ site na',
|
||||
confirm: 'gosi',
|
||||
@@ -133,7 +132,6 @@ const ig = {
|
||||
general: 'Izugbe',
|
||||
get_a_copy_of_on_puter: 'Nweta otu \'%%\' na Puter.com!',
|
||||
get_copy_link: 'Nweta njikọ nke',
|
||||
hide_all_windows: 'Ezo Windows niile',
|
||||
home: 'ụlọ',
|
||||
html_document: 'akwụkwọ HTML',
|
||||
hue: 'Hue',
|
||||
@@ -142,7 +140,6 @@ const ig = {
|
||||
item: 'ihe',
|
||||
items_in_trash_cannot_be_renamed: 'Enweghị ike ịnyegharị ihe a aha n\'ihi na ọ nọ na ahịhịa. Iji nyegharịa ihe a aha, buru ụzọ dọrọ ya na ahịhịa.',
|
||||
jpeg_image: 'Foto JPEG',
|
||||
keep_in_taskbar: 'Debe na Taskbar',
|
||||
language: 'Asụsụ',
|
||||
license: 'ikike',
|
||||
lightness: 'ìhè',
|
||||
@@ -214,7 +211,6 @@ const ig = {
|
||||
'Nweta 1 GB maka enyi ọ bụla mepụtara ma kwado akaụntụ na Puter. Enyi gị ga-enwetakwa 1 GB!',
|
||||
refer_friends_social_media_c2a: 'Nweta 1 GB nke nchekwa efu na Puter.com!',
|
||||
refresh: 'Weghachite ume',
|
||||
remove_from_taskbar: 'Wepu na Taskbar',
|
||||
rename: 'Nyegharịa aha',
|
||||
replace: 'Dochie',
|
||||
replace_all: 'Dochie ihe niile',
|
||||
@@ -243,7 +239,6 @@ const ig = {
|
||||
share: 'ike',
|
||||
share_to: 'ike nye',
|
||||
shortcut_to: 'Ụzọ mkpirisi ka',
|
||||
show_all_windows: 'Gosi Windows niile',
|
||||
show_hidden: 'Gosi ihe ezozo',
|
||||
sign_in_with_puter: 'Jiri Puter banye',
|
||||
sign_up: 'Debanye aha',
|
||||
@@ -390,8 +385,6 @@ const ig = {
|
||||
'create_shortcut_s': 'Mepụta ụzọ mkpirisi', // In English: "Create Shortcuts"
|
||||
'minimize': 'Wedata', // In English: "Minimize"
|
||||
'reload_app': 'Bugharịa ngwa', // In English: "Reload App"
|
||||
'new_window': 'Window ọhụrụ', // In English: "New Window"
|
||||
'open_trash': 'Mepee ahịhịa', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'Bipụta dị ka onye ọrụ', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Tinye ihuenyo zuru oke', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -423,14 +416,8 @@ const ig = {
|
||||
'window_title_publish_worker': 'Bipụta Onye Ọrụ', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'Na-achọpụta...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'Tụgharịa enyi!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'Gosi Desktọpụ', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'Gosi Windows mepere emepe', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'Wepụ ihuenyo zuru oke', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'Tinye ihuenyo zuru oke', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'Ọnọdụ', // In English: "Position"
|
||||
'desktop_position_left': 'Aka ekpe', // In English: "Left"
|
||||
'desktop_position_bottom': "N'okpuru", // In English: "Bottom"
|
||||
'desktop_position_right': 'Right', // In English: "Right"
|
||||
'item_shared_with_you': 'Onye ọrụ ekenyela gị ihe a.', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'Ị kekọrịtala ihe a na opekata mpe otu onye ọrụ ọzọ', // In English: " with at least one other user."
|
||||
'item_shortcut': 'Ụzọ mkpirisi', // In English: "Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const it = {
|
||||
change_password: 'Modifica password',
|
||||
change_username: 'Modifica Nome Utente',
|
||||
close: 'Chiudi',
|
||||
close_all_windows: 'Chiudi tutte le finestre',
|
||||
close_all_windows_and_log_out: 'Chiudi tutte le finestre e disconnettiti',
|
||||
change_always_open_with: 'Vuoi sempre aprire questo tipo di file con',
|
||||
confirm: 'Conferma',
|
||||
@@ -134,7 +133,6 @@ const it = {
|
||||
general: 'Generale',
|
||||
get_a_copy_of_on_puter: 'Ottieni una copia di \'%%\' su Puter.com!',
|
||||
get_copy_link: 'Ottieni link di copia',
|
||||
hide_all_windows: 'Nascondi tutte le finestre',
|
||||
home: 'Home',
|
||||
html_document: 'Documento HTML',
|
||||
hue: 'Tonalità',
|
||||
@@ -143,7 +141,6 @@ const it = {
|
||||
item: 'elemento',
|
||||
items_in_trash_cannot_be_renamed: 'Impossibile rinominare un elemento nel Cestino. Per rinominarlo, è necessario ripristinarlo.',
|
||||
jpeg_image: 'Immagine JPEG',
|
||||
keep_in_taskbar: 'Blocca nella barra delle applicazioni',
|
||||
language: 'Lingua',
|
||||
license: 'Licenza',
|
||||
lightness: 'Luminosità',
|
||||
@@ -215,7 +212,6 @@ const it = {
|
||||
'Ottieni 1 GB di spazio di archiviazione per ogni amico che crea un account e conferma l’email su Puter. Anche il tuo amico riceverà dello spazio extra!',
|
||||
refer_friends_social_media_c2a: 'Ottieni 1GB di spazio di spazio di archiviazione gratuito su Puter.com!',
|
||||
refresh: 'Ricarica',
|
||||
remove_from_taskbar: 'Sblocca dalla barra delle applicazioni',
|
||||
rename: 'Rinomina',
|
||||
replace: 'Sostituisci',
|
||||
replace_all: 'Sostituisci tutto',
|
||||
@@ -247,7 +243,6 @@ const it = {
|
||||
share: 'Condividi',
|
||||
share_to: 'Condividi con',
|
||||
shortcut_to: 'Scorciatoia per',
|
||||
show_all_windows: 'Mostra tutte le finestre',
|
||||
show_hidden: 'Mostra nascosti',
|
||||
sign_in_with_puter: 'Accedi con Puter',
|
||||
sign_up: 'Registrati',
|
||||
@@ -398,8 +393,6 @@ const it = {
|
||||
'create_shortcut_s': 'Crea scorciatoie',
|
||||
'minimize': 'Riduci',
|
||||
'reload_app': 'Ricarica app',
|
||||
'new_window': 'Nuova finestra',
|
||||
'open_trash': 'Apri cestino',
|
||||
'publish_as_serverless_worker': 'Pubblica come funzione serverless', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Visualizza a schermo intero',
|
||||
'toolbar.github': 'GitHub',
|
||||
@@ -431,14 +424,8 @@ const it = {
|
||||
'window_title_publish_worker': 'Pubblica worker', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'Autenticazione in corso...',
|
||||
'window_title_refer_friend': 'Invita un amico!',
|
||||
'desktop_show_desktop': 'Mostra desktop',
|
||||
'desktop_show_open_windows': 'Mostra finestre aperte',
|
||||
'desktop_exit_full_screen': 'Esci da schermo intero',
|
||||
'desktop_enter_full_screen': 'Visualizza a schermo intero',
|
||||
'desktop_position': 'Posizione',
|
||||
'desktop_position_left': 'Sinistra',
|
||||
'desktop_position_bottom': 'In basso',
|
||||
'desktop_position_right': 'Destra',
|
||||
'item_shared_with_you': 'Un utente ha condiviso questo elemento con te.',
|
||||
'item_shared_by_you': 'Hai condiviso questo elemento con almeno un altro utente',
|
||||
'item_shortcut': 'Scorciatoia',
|
||||
|
||||
@@ -38,7 +38,6 @@ const ja = {
|
||||
change_password: 'パスワードを変更',
|
||||
change_username: 'ユーザー名を変更',
|
||||
close: '閉じる',
|
||||
close_all_windows: 'すべてのウィンドウを閉じる',
|
||||
close_all_windows_and_log_out: 'ウィンドウを閉じてログアウト',
|
||||
change_always_open_with: 'この種類のファイルを常にこのアプリで開きますか?',
|
||||
confirm: '確認',
|
||||
@@ -121,7 +120,6 @@ const ja = {
|
||||
general: '一般',
|
||||
get_a_copy_of_on_puter: 'Puter.comで \'%%\' のコピーを取得!',
|
||||
get_copy_link: 'コピーリンクを取得',
|
||||
hide_all_windows: 'すべてのウィンドウを隠す',
|
||||
home: 'ホーム',
|
||||
html_document: 'HTML文書',
|
||||
hue: '色合い',
|
||||
@@ -130,7 +128,6 @@ const ja = {
|
||||
item: 'アイテム',
|
||||
items_in_trash_cannot_be_renamed: 'このアイテムはゴミ箱にあるため、名前を変更できません。このアイテムの名前を変更するには、まずゴミ箱からドラッグしてください。',
|
||||
jpeg_image: 'JPEG画像',
|
||||
keep_in_taskbar: 'タスクバーに保持',
|
||||
language: '言語',
|
||||
license: 'ライセンス',
|
||||
lightness: '明るさ',
|
||||
@@ -196,7 +193,6 @@ const ja = {
|
||||
refer_friends_c2a: '友達がPuterでアカウントを作成して確認すると、1GBを獲得できます。友達も1GBを獲得できます!',
|
||||
refer_friends_social_media_c2a: 'Puter.comで1GBの無料ストレージを手に入れよう!',
|
||||
refresh: '更新',
|
||||
remove_from_taskbar: 'タスクバーから削除',
|
||||
rename: '名前を変更',
|
||||
replace: '置換',
|
||||
replace_all: 'すべて置換',
|
||||
@@ -224,7 +220,6 @@ const ja = {
|
||||
share: '共有',
|
||||
share_to: '共有先',
|
||||
shortcut_to: 'ショートカット先',
|
||||
show_all_windows: 'すべてのウィンドウを表示',
|
||||
show_hidden: '隠しファイルを表示',
|
||||
sign_in_with_puter: 'Puterでサインイン',
|
||||
sign_up: 'サインアップ',
|
||||
@@ -369,8 +364,6 @@ const ja = {
|
||||
'create_shortcut_s': 'ショートカットを作成', // In English: "Create Shortcuts"
|
||||
'minimize': '最小化', // In English: "Minimize"
|
||||
'reload_app': 'アプリを再読み込み', // In English: "Reload App"
|
||||
'new_window': '新しいウィンドウ', // In English: "New Window"
|
||||
'open_trash': 'ゴミ箱を開く', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'ワーカーとして公開', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': '全画面表示にする', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -402,14 +395,8 @@ const ja = {
|
||||
'window_title_publish_worker': 'ワーカーを公開', // In English: "Publish Worker"
|
||||
'window_title_authenticating': '認証中...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': '友達を紹介しよう!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'デスクトップを表示', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': '開いているウィンドウを表示', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': '全画面表示を終了', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': '全画面表示にする', // In English: "Enter Full Screen"
|
||||
'desktop_position': '位置', // In English: "Position"
|
||||
'desktop_position_left': '左', // In English: "Left"
|
||||
'desktop_position_bottom': '下', // In English: "Bottom"
|
||||
'desktop_position_right': '右', // In English: "Right"
|
||||
'item_shared_with_you': 'ユーザーがこのアイテムをあなたと共有しました。', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'このアイテムは少なくとも1人以上のユーザーと共有されています。', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'ショートカット', // In English: "Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const ko = {
|
||||
change_password: '비밀번호 변경',
|
||||
change_username: '사용자 이름 변경',
|
||||
close: '닫기',
|
||||
close_all_windows: '모든 창 닫기',
|
||||
close_all_windows_and_log_out: '창을 닫고 로그아웃',
|
||||
change_always_open_with: '이 형식의 파일을 항상 이 앱으로 여시겠습니까?',
|
||||
confirm: '확인',
|
||||
@@ -134,7 +133,6 @@ const ko = {
|
||||
general: '일반',
|
||||
get_a_copy_of_on_puter: 'Puter.com에서 \'%%\'의 사본을 받으세요!',
|
||||
get_copy_link: '링크 복사',
|
||||
hide_all_windows: '모든 창 숨기기',
|
||||
home: '홈',
|
||||
html_document: 'HTML 문서',
|
||||
hue: '색조',
|
||||
@@ -143,7 +141,6 @@ const ko = {
|
||||
item: '항목', //
|
||||
items_in_trash_cannot_be_renamed: '이 항목은 휴지통에 있어 이름을 변경할 수 없습니다. 이름을 변경하려면 먼저 휴지통에서 꺼내야 합니다.',
|
||||
jpeg_image: 'JPEG 이미지',
|
||||
keep_in_taskbar: '작업 표시줄에 유지',
|
||||
language: '언어',
|
||||
license: '라이선스',
|
||||
lightness: '밝기',
|
||||
@@ -170,13 +167,11 @@ const ko = {
|
||||
new_folder: '새 폴더',
|
||||
new_password: '새 비밀번호',
|
||||
new_username: '새 사용자 이름',
|
||||
new_window: '새 창',
|
||||
no: '아니오',
|
||||
ok: '확인',
|
||||
open: '열기',
|
||||
open_in_new_tab: '새 탭에서 열기',
|
||||
open_in_new_window: '새 창에서 열기',
|
||||
open_trash: '휴지통 열기',
|
||||
open_with: '다른 앱으로 열기',
|
||||
original_name: '원본 이름',
|
||||
original_path: '원본 경로',
|
||||
@@ -221,7 +216,6 @@ const ko = {
|
||||
'Puter에 가입하고 이메일 인증을 완료하는 친구 한 명당 1GB의 저장 공간을 드립니다. 친구도 1GB를 받을 수 있습니다!',
|
||||
refer_friends_social_media_c2a: 'Puter.com에서 1GB의 무료 저장 공간을 받아보세요!',
|
||||
refresh: '새로 고침',
|
||||
remove_from_taskbar: '작업 표시줄에서 제거',
|
||||
rename: '이름 변경',
|
||||
replace: '교체',
|
||||
replace_all: '모두 교체',
|
||||
@@ -250,7 +244,6 @@ const ko = {
|
||||
share: '공유',
|
||||
share_to: '공유처',
|
||||
shortcut_to: '바로 가기',
|
||||
show_all_windows: '모든 창 표시',
|
||||
show_hidden: '숨김 항목 표시',
|
||||
sign_in_with_puter: 'Puter로 로그인',
|
||||
sign_up: '가입',
|
||||
@@ -426,14 +419,8 @@ const ko = {
|
||||
'window_title_refer_friend': '친구 추천!',
|
||||
|
||||
// Desktop UI
|
||||
'desktop_show_desktop': '바탕화면 보기',
|
||||
'desktop_show_open_windows': '열려있는 창 보기',
|
||||
'desktop_exit_full_screen': '전체 화면 종료',
|
||||
'desktop_enter_full_screen': '전체 화면 시작',
|
||||
'desktop_position': '위치',
|
||||
'desktop_position_left': '왼쪽',
|
||||
'desktop_position_bottom': '아래',
|
||||
'desktop_position_right': '오른쪽',
|
||||
// Item UI
|
||||
'item_shared_with_you': '다른 사용자가 이 항목을 당신과 공유했습니다.',
|
||||
'item_shared_by_you': '이 항목을 한 명 이상의 다른 사용자와 공유했습니다.',
|
||||
|
||||
@@ -38,7 +38,6 @@ const ku = {
|
||||
change_password: 'گۆڕینی وشەی تێپەڕ',
|
||||
change_username: 'گۆڕینی ناوی بەکارهێنەر',
|
||||
close: 'داخستن',
|
||||
close_all_windows: 'داخستنی هەموو پەنجەکان',
|
||||
close_all_windows_and_log_out: 'داخستن و چوونە دەرەوە',
|
||||
change_always_open_with: 'ئەتەوێت هەمیشە ئەم جۆرە فایلە بکرێتەوە بە',
|
||||
confirm: 'پشتڕاستکردنەوە',
|
||||
@@ -132,7 +131,6 @@ const ku = {
|
||||
general: 'گشتی',
|
||||
get_a_copy_of_on_puter: 'کۆپیێک وەرگرتنەوەی \'%%\' لە پوتەر.com!',
|
||||
get_copy_link: 'کۆپی بەستەر وەرگرتن',
|
||||
hide_all_windows: 'شاردنەوەی هەموو پەنجەکان',
|
||||
home: 'ماڵەوە',
|
||||
html_document: 'بەڵگەی HTML',
|
||||
hue: 'هیو',
|
||||
@@ -141,7 +139,6 @@ const ku = {
|
||||
item: 'بابەت',
|
||||
items_in_trash_cannot_be_renamed: 'ئەم بابەتە ناتوانرێت ناونوسێت چونکە لە کوڵەدا هەیە. بۆ ناونوسین، سەبارەکەی لە کوڵەکە دەر بکە.',
|
||||
jpeg_image: 'وێنەی JPEG',
|
||||
keep_in_taskbar: 'ڕێپێدان لە تاکسبار',
|
||||
language: 'زمان',
|
||||
license: 'لایسەنس',
|
||||
lightness: 'رووناکی',
|
||||
@@ -213,7 +210,6 @@ const ku = {
|
||||
'1 گیگابایت بۆ هەر هاوڕێک دەست بەرکە، پەیوەندیدانی هەژمارێک بۆ پوتەر دروست بکە و پشتڕاست بکە. هاوڕێتیش 1 گیگابایت وەرگرتن دەبێت!',
|
||||
refer_friends_social_media_c2a: '1 گیگابایت پارێزگاکانی پوتەر.com بۆ هەر هاوڕێک بکە!',
|
||||
refresh: 'بوژانەوە',
|
||||
remove_from_taskbar: 'لابردن لە تاکسبار',
|
||||
rename: 'ناونانەوە',
|
||||
replace: 'لە نوێکردنەوە',
|
||||
replace_all: 'لە نوێکردنەوەی هەموو',
|
||||
@@ -242,7 +238,6 @@ const ku = {
|
||||
share: 'هاوبەشکردن',
|
||||
share_to: 'هاوبەشکردن بۆ',
|
||||
shortcut_to: 'شارتی بۆ',
|
||||
show_all_windows: 'پیشاندانی هەموو پەنجەکان',
|
||||
show_hidden: 'پیشاندانی شاردراوەکان',
|
||||
sign_in_with_puter: 'چوونەژوورەوە بە پوتەر',
|
||||
sign_up: 'خۆتۆمارکردن',
|
||||
@@ -385,8 +380,6 @@ const ku = {
|
||||
'create_shortcut_s': 'دروستکردنی کورتەڕەوەکان', // In English: "Create Shortcuts"
|
||||
'minimize': 'بچووککردنەوە', // In English: "Minimize"
|
||||
'reload_app': 'ئەپلیکەیشن دووبارە بار بکە', // In English: "Reload App"
|
||||
'new_window': 'پەنجەرەی نوێ', // In English: "New Window"
|
||||
'open_trash': 'زبڵدانە بکەرەوە', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'بڵاوکردنەوە وەک کارمەند', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'بە تەواوی شاشە بچۆرە', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -418,14 +411,8 @@ const ku = {
|
||||
'window_title_publish_worker': 'بڵاوکردنەوەی کارمەند', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'پەسندکردنەوە', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'هاوڕێک بانگهێشت بکە!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'دێسکتۆپ پیشان بدە', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'پەنجەرە کراوەکان پیشان بدە', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'دەرچوون لە شاشەی پڕە', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'چوونە شاشەی پڕە', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'شوێن', // In English: "Position"
|
||||
'desktop_position_left': 'چەپ', // In English: "Left"
|
||||
'desktop_position_bottom': 'خوارەوە', // In English: "Bottom"
|
||||
'desktop_position_right': 'ڕاست', // In English: "Right"
|
||||
'item_shared_with_you': 'بەکارهێنەرێک ئەم شتەیە لەگەڵ تۆ هاوبەش کردووە.', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'تۆ ئەم شتە بە هەندێک بەکارهێنەرێکی تر هاوبەش کردووە.', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'کورتەڕەو', // In English: "Shortcut"
|
||||
|
||||
@@ -40,7 +40,6 @@ const ml = {
|
||||
change_password: 'പാസ്വേഡ് മാറ്റുക',
|
||||
change_username: 'ഉപയോക്തൃനാമം മാറ്റുക',
|
||||
close: 'അടയ്ക്കുക',
|
||||
close_all_windows: 'എല്ലാ വിൻഡോകളും അടയ്ക്കുക',
|
||||
close_all_windows_and_log_out: 'വിൻഡോകൾ അടച്ച് ലോഗ്ഔട്ട് ചെയ്യുക',
|
||||
confirm: 'സ്ഥിരീകരിക്കുക',
|
||||
confirm_2fa_setup: 'ഞാൻ കോഡ് എന്റെ ഓതന്റിക്കേറ്റർ ആപ്പിലേക്ക് ചേർത്തു',
|
||||
@@ -122,7 +121,6 @@ const ml = {
|
||||
general: 'പൊതുവായത്',
|
||||
get_a_copy_of_on_puter: 'Puter.com-ൽ \'%%\' എന്നതിന്റെ ഒരു പകർപ്പ് നേടുക!',
|
||||
get_copy_link: 'പകർപ്പ് ലിങ്ക് നേടുക',
|
||||
hide_all_windows: 'എല്ലാ വിൻഡോകളും മറയ്ക്കുക',
|
||||
home: 'ഹോം',
|
||||
html_document: 'HTML ഡോക്യുമെന്റ്',
|
||||
hue: 'ഹ്യൂ',
|
||||
@@ -131,7 +129,6 @@ const ml = {
|
||||
item: 'ഇനം',
|
||||
items_in_trash_cannot_be_renamed: 'ഈ ഇനം ട്രാഷിലായതിനാൽ പേരുമാറ്റാൻ കഴിയില്ല. ഈ ഇനത്തിന്റെ പേരുമാറ്റാൻ, ആദ്യം ഇത് ട്രാഷിൽ നിന്ന് പുറത്തെടുക്കുക.',
|
||||
jpeg_image: 'JPEG ചിത്രം',
|
||||
keep_in_taskbar: 'ടാസ്ക്ബാറിൽ നിലനിർത്തുക',
|
||||
language: 'ഭാഷ',
|
||||
license: 'ലൈസൻസ്',
|
||||
lightness: 'പ്രകാശം',
|
||||
@@ -198,7 +195,6 @@ const ml = {
|
||||
refer_friends_c2a: 'അക്കൗണ്ട് സൃഷ്ടിച്ച് സ്ഥിരീകരിക്കുന്ന ഓരോ സുഹൃത്തിനും 1 GB നേടുക. നിങ്ങളുടെ സുഹൃത്തിനും 1 GB ലഭിക്കും!',
|
||||
refer_friends_social_media_c2a: 'Puter.com-ൽ 1 GB സൗജന്യ സ്റ്റോറേജ് നേടുക!',
|
||||
refresh: 'പുതുക്കുക',
|
||||
remove_from_taskbar: 'ടാസ്ക്ബാറിൽ നിന്ന് നീക്കം ചെയ്യുക',
|
||||
rename: 'പേരുമാറ്റുക',
|
||||
replace: 'മാറ്റിസ്ഥാപിക്കുക',
|
||||
replace_all: 'എല്ലാം മാറ്റിസ്ഥാപിക്കുക',
|
||||
@@ -226,7 +222,6 @@ const ml = {
|
||||
share: 'പങ്കുവയ്ക്കുക',
|
||||
share_to: 'ഇതിലേക്ക് പങ്കുവയ്ക്കുക',
|
||||
shortcut_to: 'ഷോർട്ട്കട്ട്',
|
||||
show_all_windows: 'എല്ലാ വിൻഡോകളും കാണിക്കുക',
|
||||
show_hidden: 'മറഞ്ഞിരിക്കുന്നവ കാണിക്കുക',
|
||||
sign_in_with_puter: 'പ്യൂട്ടർ ഉപയോഗിച്ച് സൈൻ ഇൻ ചെയ്യുക',
|
||||
sign_up: 'സൈൻ അപ്പ്',
|
||||
|
||||
@@ -46,7 +46,6 @@ const my = {
|
||||
change_password: 'Tukar Kata Laluan',
|
||||
change_username: 'Tukar Nama Pengguna',
|
||||
close: 'Tutup',
|
||||
close_all_windows: 'Tutup Semua Tetingkap',
|
||||
close_all_windows_and_log_out: 'Tutup Tetingkap dan Keluar',
|
||||
change_always_open_with: 'Adakah anda mahu sentiasa membuka jenis fail ini dengan',
|
||||
confirm: 'Sahkan',
|
||||
@@ -142,7 +141,6 @@ const my = {
|
||||
general: 'Umum',
|
||||
get_a_copy_of_on_puter: 'Dapatkan salinan \'%%\' di Puter.com!',
|
||||
get_copy_link: 'Dapatkan Salinan Pautan',
|
||||
hide_all_windows: 'Sorok Semua Tetingkap',
|
||||
home: 'Laman Utama',
|
||||
html_document: 'Dokumen HTML',
|
||||
hue: 'Hue',
|
||||
@@ -151,7 +149,6 @@ const my = {
|
||||
item: 'item',
|
||||
items_in_trash_cannot_be_renamed: 'Item ini tidak boleh dinamakan semula kerana berada dalam tong sampah. Untuk menamakan semula item ini, tariknya keluar dari Tong Sampah terlebih dahulu.',
|
||||
jpeg_image: 'Imej JPEG',
|
||||
keep_in_taskbar: 'Kekal dalam Bar Tugas',
|
||||
language: 'Bahasa',
|
||||
license: 'Lesen',
|
||||
lightness: 'Kecerahan',
|
||||
@@ -181,10 +178,8 @@ const my = {
|
||||
no: 'Tidak',
|
||||
ok: 'OK',
|
||||
open: 'Buka',
|
||||
new_window: 'Tetingkap Baru',
|
||||
open_in_new_tab: 'Buka dalam Tab Baru',
|
||||
open_in_new_window: 'Buka dalam Tetingkap Baru',
|
||||
open_trash: 'Buka Tong Sampah',
|
||||
open_with: 'Buka Dengan',
|
||||
original_name: 'Nama Asal',
|
||||
original_path: 'Laluan Asal',
|
||||
@@ -223,7 +218,6 @@ const my = {
|
||||
refer_friends_c2a: 'Dapatkan 1 GB untuk setiap rakan yang membuat dan mengesahkan akaun di Puter. Rakan anda juga akan mendapat 1 GB!',
|
||||
refer_friends_social_media_c2a: 'Dapatkan 1 GB storan percuma di Puter.com!',
|
||||
refresh: 'Muat Semula',
|
||||
remove_from_taskbar: 'Keluarkan dari Bar Tugas',
|
||||
rename: 'Namakan Semula',
|
||||
replace: 'Ganti',
|
||||
replace_all: 'Ganti Semua',
|
||||
@@ -251,7 +245,6 @@ const my = {
|
||||
share: 'Kongsi',
|
||||
share_to: 'Kongsi kepada',
|
||||
shortcut_to: 'Pintasan ke',
|
||||
show_all_windows: 'Tunjukkan Semua Tetingkap',
|
||||
show_hidden: 'Tunjukkan yang tersembunyi',
|
||||
sign_in_with_puter: 'Daftar masuk dengan Puter',
|
||||
sign_up: 'Daftar',
|
||||
@@ -423,14 +416,8 @@ const my = {
|
||||
'window_title_refer_friend': 'Rujuk rakan!',
|
||||
|
||||
// Desktop UI
|
||||
'desktop_show_desktop': 'Tunjukkan Desktop',
|
||||
'desktop_show_open_windows': 'Tunjukkan Tetingkap Terbuka',
|
||||
'desktop_exit_full_screen': 'Keluar Skrin Penuh',
|
||||
'desktop_enter_full_screen': 'Masuk Skrin Penuh',
|
||||
'desktop_position': 'Kedudukan',
|
||||
'desktop_position_left': 'Kiri',
|
||||
'desktop_position_bottom': 'Bawah',
|
||||
'desktop_position_right': 'Kanan',
|
||||
|
||||
// Item UI
|
||||
'item_shared_with_you': 'Pengguna telah berkongsi item ini dengan anda.',
|
||||
|
||||
@@ -45,7 +45,6 @@ const nb = {
|
||||
change_password: 'Endre passord',
|
||||
change_username: 'Endre brukernavn',
|
||||
close: 'Lukk',
|
||||
close_all_windows: 'Lukk alle vinduer',
|
||||
close_all_windows_and_log_out: 'Lukk alle vinduer og logg ut',
|
||||
confirm: 'Bekrefte',
|
||||
confirm_2fa_setup: 'Jeg har lagt til koden i autentiseringsappen min',
|
||||
@@ -130,7 +129,6 @@ const nb = {
|
||||
general: 'Generelt',
|
||||
get_a_copy_of_on_puter: "Få en kopi av '%%' på Puter.com!",
|
||||
get_copy_link: 'Få kopilenke',
|
||||
hide_all_windows: 'Skjul alle vinduer',
|
||||
home: 'Hjem',
|
||||
html_document: 'HTML-dokument',
|
||||
hue: 'Fargetone',
|
||||
@@ -139,7 +137,6 @@ const nb = {
|
||||
item: 'element',
|
||||
items_in_trash_cannot_be_renamed: 'Dette elementet kan ikke omdøpes fordi det er i papirkurven. For å omdøpe dette elementet, dra det først ut av papirkurven.',
|
||||
jpeg_image: 'JPEG-bilde',
|
||||
keep_in_taskbar: 'Behold i oppgavelinjen',
|
||||
language: 'Språk',
|
||||
license: 'Lisens',
|
||||
lightness: 'Lysstyrke',
|
||||
@@ -167,7 +164,6 @@ const nb = {
|
||||
no: 'Nei',
|
||||
ok: 'OK',
|
||||
open: 'Åpne',
|
||||
new_window: 'Nytt vindu',
|
||||
open_in_new_tab: 'Åpne i ny fane',
|
||||
open_in_new_window: 'Åpne i nytt vindu',
|
||||
open_with: 'Åpne med',
|
||||
@@ -207,7 +203,6 @@ const nb = {
|
||||
refer_friends_c2a: 'Få 1 GB for hver venn som oppretter og bekrefter en konto på Puter. Vennen din får også 1 GB.',
|
||||
refer_friends_social_media_c2a: 'Få 1 GB gratis lagringsplass på Puter.com!',
|
||||
refresh: 'Oppdater',
|
||||
remove_from_taskbar: 'Fjern fra oppgavelinjen',
|
||||
rename: 'Gi nytt navn',
|
||||
replace: 'Erstatt',
|
||||
replace_all: 'Erstatt alle',
|
||||
@@ -235,7 +230,6 @@ const nb = {
|
||||
share: 'Dele',
|
||||
share_to: 'Del',
|
||||
shortcut_to: 'Snarvei til',
|
||||
show_all_windows: 'Vis alle vinduer',
|
||||
show_hidden: 'Vis skjulte',
|
||||
sign_in_with_puter: 'Logg inn med Puter',
|
||||
sign_up: 'Registrer deg',
|
||||
@@ -383,7 +377,6 @@ const nb = {
|
||||
'choose_publishing_option': 'Velg hvordan du vil publisere nettstedet ditt:',
|
||||
'minimize': 'Minimer',
|
||||
'reload_app': 'Last appen på nytt',
|
||||
'open_trash': 'Åpne papirkurv',
|
||||
'plural_suffix': 'er',
|
||||
'publish_as_serverless_worker': 'Publiser som Worker',
|
||||
'worker': 'Worker',
|
||||
@@ -411,14 +404,8 @@ const nb = {
|
||||
'window_title_publish_worker': 'Publiser Worker',
|
||||
'window_title_authenticating': 'Autentiserer...',
|
||||
'window_title_refer_friend': 'Henvis en venn!',
|
||||
'desktop_show_desktop': 'Vis skrivebord',
|
||||
'desktop_show_open_windows': 'Vis åpne vinduer',
|
||||
'desktop_exit_full_screen': 'Avslutt fullskjerm',
|
||||
'desktop_enter_full_screen': 'Gå til fullskjerm',
|
||||
'desktop_position': 'Posisjon',
|
||||
'desktop_position_left': 'Venstre',
|
||||
'desktop_position_bottom': 'Nederst',
|
||||
'desktop_position_right': 'Høyre',
|
||||
'item_shared_with_you': 'En bruker har delt dette elementet med deg.',
|
||||
'item_shared_by_you': 'Du har delt dette elementet med minst én annen bruker.',
|
||||
'item_shortcut': 'Snarvei',
|
||||
|
||||
@@ -38,7 +38,6 @@ const nl = {
|
||||
change_password: 'Wachtwoord veranderen',
|
||||
change_username: 'Gebruikersnaam veranderen',
|
||||
close: 'Sluiten',
|
||||
close_all_windows: 'Alle vensters sluiten',
|
||||
close_all_windows_and_log_out: 'Vensters sluiten en afmelden',
|
||||
change_always_open_with: 'Wilt u dit bestandstype altijd openen met',
|
||||
confirm: 'Bevestig',
|
||||
@@ -121,7 +120,6 @@ const nl = {
|
||||
general: 'Algemeen',
|
||||
get_a_copy_of_on_puter: 'Krijg een kopie van \'%%\' op Puter.com!',
|
||||
get_copy_link: 'Krijg Kopieerlink',
|
||||
hide_all_windows: 'Alle Vensters Verbergen',
|
||||
home: 'Home',
|
||||
html_document: 'HTML-document',
|
||||
hue: 'Tint',
|
||||
@@ -130,7 +128,6 @@ const nl = {
|
||||
item: 'item',
|
||||
items_in_trash_cannot_be_renamed: 'Dit item kan niet worden hernoemd omdat het in de prullenbak zit. Om dit item te hernoemen, sleept u het eerst uit de prullenbak.',
|
||||
jpeg_image: 'JPEG-afbeelding',
|
||||
keep_in_taskbar: 'In Taakbalk Houden',
|
||||
language: 'Taal',
|
||||
license: 'Licentie',
|
||||
lightness: 'Helderheid',
|
||||
@@ -197,7 +194,6 @@ const nl = {
|
||||
refer_friends_c2a: 'Krijg 1 GB voor elke vriend die een account aanmaakt en bevestigt op Puter. Je vriend krijgt ook 1 GB!',
|
||||
refer_friends_social_media_c2a: 'Krijg 1 GB gratis opslagruimte op Puter.com!',
|
||||
refresh: 'Vernieuwen',
|
||||
remove_from_taskbar: 'Verwijderen van taakbalk',
|
||||
rename: 'Hernoemen',
|
||||
replace: 'Vervangen',
|
||||
replace_all: 'Alles vervangen',
|
||||
@@ -225,7 +221,6 @@ const nl = {
|
||||
share: 'Delen',
|
||||
share_to: 'Delen met',
|
||||
shortcut_to: 'Snelkoppeling naar',
|
||||
show_all_windows: 'Toon alle vensters',
|
||||
show_hidden: 'Toon verborgen',
|
||||
sign_in_with_puter: 'Inloggen met Puter',
|
||||
sign_up: 'Aanmelden',
|
||||
@@ -367,8 +362,6 @@ const nl = {
|
||||
'create_shortcut_s': 'Snelkoppelingen maken', // In English: "Create Shortcuts"
|
||||
'minimize': 'Minimaliseren', // In English: "Minimize"
|
||||
'reload_app': 'App herladen', // In English: "Reload App"
|
||||
'new_window': 'Nieuw venster', // In English: "New Window"
|
||||
'open_trash': 'Prullenbak openen', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'Publiceer als Worker', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Naar volledig scherm', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -400,14 +393,8 @@ const nl = {
|
||||
'window_title_publish_worker': 'Worker publiceren', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'Authenticatie bezig...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'Verwijs een vriend!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'Toon bureaublad', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'Toon geopende vensters', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'Volledig scherm verlaten', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'Naar volledig scherm', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'Positie', // In English: "Position"
|
||||
'desktop_position_left': 'Links', // In English: "Left"
|
||||
'desktop_position_bottom': 'Onderkant', // In English: "Bottom"
|
||||
'desktop_position_right': 'Rechts', // In English: "Right"
|
||||
|
||||
'item_shared_with_you': 'Een gebruiker heeft dit item met u gedeeld.', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'U heeft dit item met ten minste één andere gebruiker gedeeld.', // In English: "You have shared this item with at least one other user."
|
||||
|
||||
@@ -30,7 +30,6 @@ const nn = {
|
||||
change_language: 'Endre språk',
|
||||
change_password: 'Endre passord',
|
||||
change_username: 'Endre brukarnamn',
|
||||
close_all_windows: 'Lukk alle vindauge',
|
||||
confirm_account_for_free_referral_storage_c2a:
|
||||
'Opprett ein konto og stadfest e-postadressa di for å få 1 GB gratis lagringsplass. Vennen din vil òg få 1 GB gratis lagringsplass.',
|
||||
confirm_new_password: 'Stadfest nytt passord',
|
||||
@@ -68,13 +67,11 @@ const nn = {
|
||||
general: 'Generelt',
|
||||
get_a_copy_of_on_puter: "Få ein kopi av '%%' på Puter.com!",
|
||||
get_copy_link: 'Få kopilenkje',
|
||||
hide_all_windows: 'Skjul alle vindauge',
|
||||
html_document: 'HTML-dokument',
|
||||
invite_link: 'Invitasjonslenkje',
|
||||
items_in_trash_cannot_be_renamed:
|
||||
'Dette elementet kan ikkje omdøypast fordi det er i papirkorga. For å omdøype dette elementet, dra det først ut av papirkorga.',
|
||||
jpeg_image: 'JPEG-bilete',
|
||||
keep_in_taskbar: 'Behald i oppgåvelinja',
|
||||
log_in: 'Logg inn',
|
||||
log_out: 'Logg ut',
|
||||
move: 'Flytt',
|
||||
@@ -112,7 +109,6 @@ const nn = {
|
||||
'Få 1 GB for kvar ven som opprettar og stadfestar ein konto på Puter. Vennen din får òg 1 GB.',
|
||||
refer_friends_social_media_c2a: 'Få 1 GB gratis lagringsplass på Puter.com!',
|
||||
refresh: 'Oppdater',
|
||||
remove_from_taskbar: 'Fjern frå oppgåvelinja',
|
||||
rename: 'Gje nytt namn',
|
||||
resend_confirmation_code: 'Send stadfestingskoden på nytt',
|
||||
restore: 'Gjenopprett',
|
||||
@@ -125,7 +121,6 @@ const nn = {
|
||||
session_saved: 'Takk for at du oppretta ein konto. Denne økta er lagra.',
|
||||
set_new_password: 'Set nytt passord',
|
||||
share_to: 'Del',
|
||||
show_all_windows: 'Vis alle vindauge',
|
||||
show_hidden: 'Vis skjulte',
|
||||
sign_in_with_puter: 'Logg inn med Puter',
|
||||
sign_up: 'Registrer deg',
|
||||
@@ -379,8 +374,6 @@ const nn = {
|
||||
create_shortcut_s: 'Lag snarvegar', // "Original English: Create Shortcuts"
|
||||
minimize: 'Minimer', // "Original English: Minimize"
|
||||
reload_app: 'Last inn app på nytt', // "Original English: Reload App"
|
||||
new_window: 'Nytt vindauge', // "Original English: New Window"
|
||||
open_trash: 'Opne papirkorg', // "Original English: Open Trash"
|
||||
publish_as_serverless_worker: 'Publiser som Worker', // "Original English: Publish as Worker - 'Worker' kept as technical term"
|
||||
'toolbar.enter_fullscreen': 'Gå til fullskjerm', // "Original English: Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // "Original English: GitHub - Brand name kept unchanged"
|
||||
@@ -412,14 +405,8 @@ const nn = {
|
||||
window_title_publish_worker: 'Publiser Worker', // "Original English: Publish Worker"
|
||||
window_title_authenticating: 'Autentiserer...', // "Original English: Authenticating..."
|
||||
window_title_refer_friend: 'Verv ein ven!', // "Original English: Refer a friend!"
|
||||
desktop_show_desktop: 'Vis skrivebord', // "Original English: Show Desktop"
|
||||
desktop_show_open_windows: 'Vis opne vindauge', // "Original English: Show Open Windows"
|
||||
desktop_exit_full_screen: 'Avslutt fullskjerm', // "Original English: Exit Full Screen"
|
||||
desktop_enter_full_screen: 'Gå til fullskjerm', // "Original English: Enter Full Screen"
|
||||
desktop_position: 'Posisjon', // "Original English: Position"
|
||||
desktop_position_left: 'Venstre', // "Original English: Left"
|
||||
desktop_position_bottom: 'Botn', // "Original English: Bottom"
|
||||
desktop_position_right: 'Høgre', // "Original English: Right"
|
||||
item_shared_with_you: 'Ein brukar har delt denne tingen med deg.', // "Original English: A user has shared this item with you."
|
||||
item_shared_by_you: 'Du har delt denne tingen med minst ein annan brukar.', // "Original English: You have shared this item with at least one other user."
|
||||
item_shortcut: 'Snarveg', // "Original English: Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const pl = {
|
||||
change_password: 'Zmień hasło',
|
||||
change_username: 'Zmień użytkownika',
|
||||
close: 'Zamknij',
|
||||
close_all_windows: 'Zamknij wszystkie okna',
|
||||
close_all_windows_and_log_out: 'Zamknij wszystkie okna i wyloguj',
|
||||
change_always_open_with: 'Czy chcesz zawsze otwierać ten typ pliku używając',
|
||||
confirm: 'Potwierdzam',
|
||||
@@ -121,7 +120,6 @@ const pl = {
|
||||
general: 'Ogólne',
|
||||
get_a_copy_of_on_puter: 'Pobierz kopię \'%%\' na Puter.com!',
|
||||
get_copy_link: 'Pobierz skopiowany link',
|
||||
hide_all_windows: 'Zamknij wszystkie okna',
|
||||
home: 'Folder domowy',
|
||||
html_document: 'dokument HTML',
|
||||
hue: 'Odcień',
|
||||
@@ -130,7 +128,6 @@ const pl = {
|
||||
item: 'przedmiot',
|
||||
items_in_trash_cannot_be_renamed: 'Nazwa tego przedmiotu nie może zostać zmieniona, ponieważ znajduje się on w koszu. Aby zmienić jego nazwę, wyciągnij go z kosza.',
|
||||
jpeg_image: 'Obraz JPEG',
|
||||
keep_in_taskbar: 'Zachowaj na pasku zadań',
|
||||
language: 'Język',
|
||||
license: 'Licencja',
|
||||
lightness: 'Jasność',
|
||||
@@ -196,7 +193,6 @@ const pl = {
|
||||
refer_friends_c2a: 'Zdobądź 1 GB za każdego znajomego, który założy konto na Puter! On również otrzyma 1 GB.',
|
||||
refer_friends_social_media_c2a: 'Zdobądź 1 GB darmowego miejsca na Puter.com!',
|
||||
refresh: 'Odśwież',
|
||||
remove_from_taskbar: 'Usuń z paska zadań',
|
||||
rename: 'Zmień nazwę',
|
||||
replace: 'Zamień',
|
||||
replace_all: 'Zamień wszystkie',
|
||||
@@ -224,7 +220,6 @@ const pl = {
|
||||
share: 'Udostępnij',
|
||||
share_to: 'Udostępnij do',
|
||||
shortcut_to: 'Skrót do',
|
||||
show_all_windows: 'Pokaż wszystkie okna',
|
||||
show_hidden: 'Pokaż ukryte',
|
||||
sign_in_with_puter: 'Zaloguj się z Puter',
|
||||
sign_up: 'Zarejestruj się',
|
||||
@@ -364,8 +359,6 @@ const pl = {
|
||||
'create_shortcut_s': 'Utwórz Skróty',
|
||||
'minimize': 'Zminimalizuj',
|
||||
'reload_app': 'Odśwież Aplikację',
|
||||
'new_window': 'Nowe Okno',
|
||||
'open_trash': 'Otwórz Kosz',
|
||||
'plural_suffix': undefined,
|
||||
'publish_as_serverless_worker': 'Opublikuj jako Worker',
|
||||
'toolbar.enter_fullscreen': 'Otwórz Pełen Ekran',
|
||||
@@ -398,14 +391,8 @@ const pl = {
|
||||
'window_title_publish_worker': 'Opublikuj Workera',
|
||||
'window_title_authenticating': 'Autentykowanie...',
|
||||
'window_title_refer_friend': 'Poleć znajomego!',
|
||||
'desktop_show_desktop': 'Pokaż Pulpit',
|
||||
'desktop_show_open_windows': 'Pokaż Otwarte Okna',
|
||||
'desktop_exit_full_screen': 'Zamknij Pełen Ekran',
|
||||
'desktop_enter_full_screen': 'Otwórz Pełne Okno',
|
||||
'desktop_position': 'Pozycja',
|
||||
'desktop_position_left': 'Lewo',
|
||||
'desktop_position_bottom': 'Dół',
|
||||
'desktop_position_right': 'Prawo',
|
||||
'item_shared_with_you': 'Użytkownik podzielił się tym przedmiotem z tobą.',
|
||||
'item_shared_by_you': 'Podzieliłeś się tym przedmiotem przynajmniej z jednym innym użytkownikiem.',
|
||||
'item_shortcut': 'Skrót',
|
||||
|
||||
@@ -39,7 +39,6 @@ const pt = {
|
||||
change_password: 'Alterar Senha',
|
||||
change_username: 'Alterar Nome de Utilizador',
|
||||
close: 'Fechar',
|
||||
close_all_windows: 'Fechar Todas as Janelas',
|
||||
close_all_windows_and_log_out: 'Fechar Janelas e Sair',
|
||||
change_always_open_with: 'Queres que ficheiros deste tipo abram sempre com',
|
||||
confirm: 'Confirmar',
|
||||
@@ -122,7 +121,6 @@ const pt = {
|
||||
general: 'Geral',
|
||||
get_a_copy_of_on_puter: 'Obter uma cópia de \'%%\' em Puter.com!',
|
||||
get_copy_link: 'Copiar Link',
|
||||
hide_all_windows: 'Ocultar Todas as Janelas',
|
||||
home: 'Home',
|
||||
html_document: 'Documento HTML',
|
||||
hue: 'Hue',
|
||||
@@ -131,7 +129,6 @@ const pt = {
|
||||
item: 'item',
|
||||
items_in_trash_cannot_be_renamed: 'Este item não pode ser renomeado porque está no lixo. Para alterar o nome, primeiro arrasta-o para fora do Lixo.',
|
||||
jpeg_image: 'Imagem JPEG',
|
||||
keep_in_taskbar: 'Manter na Barra de Tarefas',
|
||||
language: 'Língua',
|
||||
license: 'Licença',
|
||||
lightness: 'Lightness',
|
||||
@@ -199,7 +196,6 @@ const pt = {
|
||||
refer_friends_c2a: 'Ganha 1 GB por cada amigo que criar e confirmar uma conta Puter. Os teus amigos também ganham 1 GB!',
|
||||
refer_friends_social_media_c2a: 'Ganha 1 GB de armazenamento gratuito em Puter.com!',
|
||||
refresh: 'Atualizar',
|
||||
remove_from_taskbar: 'Remover da Barra de Tarefas',
|
||||
rename: 'Renomear',
|
||||
replace: 'Substituir',
|
||||
replace_all: 'Substituir Todos',
|
||||
@@ -227,7 +223,6 @@ const pt = {
|
||||
share: 'Partilhar',
|
||||
share_to: 'Partilhar com',
|
||||
shortcut_to: 'Atalho para',
|
||||
show_all_windows: 'Mostrar Todas as Janelas',
|
||||
show_hidden: 'Exibir janelas ocultas',
|
||||
sign_in_with_puter: 'Entrar em Puter',
|
||||
sign_up: 'Registar',
|
||||
@@ -365,8 +360,6 @@ const pt = {
|
||||
'create_shortcut_s': 'Criar Atalhos', // In English: "Create Shortcuts"
|
||||
'minimize': 'Minimizar', // In English: "Minimize"
|
||||
'reload_app': 'Recarregar Aplicação', // In English: "Reload App"
|
||||
'new_window': 'Nova Janela', // In English: "New Window"
|
||||
'open_trash': 'Abrir Lixeira', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'Publicar como Worker', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Entrar em Tela Cheia', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -398,14 +391,8 @@ const pt = {
|
||||
'window_title_publish_worker': 'Publicar Worker', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'Autenticando...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'Indicar um amigo!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'Mostrar Área de Trabalho', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'Mostrar Janelas Abertas', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'Sair da Tela Cheia', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'Entrar em Tela Cheia', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'Posição', // In English: "Position"
|
||||
'desktop_position_left': 'Esquerda', // In English: "Left"
|
||||
'desktop_position_bottom': 'Inferior', // In English: "Bottom"
|
||||
'desktop_position_right': 'Direita', // In English: "Right"
|
||||
'item_shared_with_you': 'Um usuário compartilhou este item com você.', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'Você compartilhou este item com pelo menos um outro usuário.', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'Atalho', // In English: "Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const ro = {
|
||||
change_password: 'Schimbă Parola',
|
||||
change_username: 'Schimbă User-ul',
|
||||
close: 'Închide',
|
||||
close_all_windows: 'Închide toate ferestrele',
|
||||
close_all_windows_and_log_out: 'Închide toate ferestrele și delogheză-mă',
|
||||
change_always_open_with: 'Doriți să deschideți acest tip de fișiere cu ',
|
||||
confirm: 'Confirmă',
|
||||
@@ -122,7 +121,6 @@ const ro = {
|
||||
general: 'General',
|
||||
get_a_copy_of_on_puter: 'Obțineți o copie a \'%%\' pe Puter.com!',
|
||||
get_copy_link: 'Obțineți link-ul copiei',
|
||||
hide_all_windows: 'Ascunde toate ferestrele',
|
||||
home: 'Acasă',
|
||||
html_document: 'Document HTML',
|
||||
hue: 'Hue',
|
||||
@@ -131,7 +129,6 @@ const ro = {
|
||||
item: 'Obiect',
|
||||
items_in_trash_cannot_be_renamed: 'Acest obiect nu poate fi redenumit deoarece este în coșul de gunoi. Pentru a redenumi acest obiect, mai întâi scoateți-l din Coșul de gunoi.',
|
||||
jpeg_image: 'Imagine JPEG',
|
||||
keep_in_taskbar: 'Păstrează în bara de activități',
|
||||
language: 'Limbă',
|
||||
license: 'Licență',
|
||||
lightness: 'Luminozitate',
|
||||
@@ -197,7 +194,6 @@ const ro = {
|
||||
refer_friends_c2a: 'Obțineți 1 GB pentru fiecare prieten care creează și confirmă un cont pe Puter. și prietenul tău va primi 1 GB!',
|
||||
refer_friends_social_media_c2a: 'Obțineți 1 GB de spațiu de stocare gratuit pe Puter.com!',
|
||||
refresh: 'Reîmprospătare',
|
||||
remove_from_taskbar: 'Eliminați din bara de activități',
|
||||
rename: 'Redenumește',
|
||||
replace: 'Înlocuiește',
|
||||
replace_all: 'Înlocuiește toate',
|
||||
@@ -225,7 +221,6 @@ const ro = {
|
||||
share: 'Distribuie',
|
||||
share_to: 'Distribuie către',
|
||||
shortcut_to: 'Comandă rapidă către',
|
||||
show_all_windows: 'Afișați toate ferestrele',
|
||||
show_hidden: 'Arată ascuns',
|
||||
sign_in_with_puter: 'Conectați-vă cu Puter',
|
||||
sign_up: 'Inscrie-te',
|
||||
@@ -360,8 +355,6 @@ const ro = {
|
||||
'create_shortcut_s': 'Creează Shortcut-uri',
|
||||
'minimize': 'Minimizează',
|
||||
'reload_app': 'Reîncarcă aplicația',
|
||||
'new_window': 'Fereastră nouă',
|
||||
'open_trash': 'Deschide Coșul de gunoi',
|
||||
'publish_as_serverless_worker': 'Publică ca muncitor',
|
||||
'toolbar.enter_fullscreen': 'Intră în modul tot ecranul',
|
||||
'toolbar.github': 'GitHub',
|
||||
@@ -393,14 +386,8 @@ const ro = {
|
||||
'window_title_publish_worker': 'Publică muncitorul',
|
||||
'window_title_authenticating': 'Se autentifică...',
|
||||
'window_title_refer_friend': 'Recomandă unui prieten!',
|
||||
'desktop_show_desktop': 'Arată desktopul',
|
||||
'desktop_show_open_windows': 'Arată ferestrele deschise',
|
||||
'desktop_exit_full_screen': 'Ieși din ecran complet',
|
||||
'desktop_enter_full_screen': 'Intră pe ecran complet',
|
||||
'desktop_position': 'Poziție',
|
||||
'desktop_position_left': 'Stânga',
|
||||
'desktop_position_bottom': 'Jos',
|
||||
'desktop_position_right': 'Dreapta',
|
||||
'item_shared_with_you': 'Un utilizator a partajat acest element cu tine.',
|
||||
'item_shared_by_you': 'Ai partajat acest element cu cel puțin un alt utilizator.',
|
||||
'item_shortcut': 'Scurtătură',
|
||||
|
||||
@@ -38,7 +38,6 @@ const ru = {
|
||||
change_password: 'Изменить пароль',
|
||||
change_username: 'Изменить имя пользователя',
|
||||
close: 'Закрыть',
|
||||
close_all_windows: 'Закрыть все окна',
|
||||
close_all_windows_and_log_out: 'Закрыть все окна и выйти',
|
||||
change_always_open_with:
|
||||
'Хотите всегда открывать файлы этого типа с помощью',
|
||||
@@ -136,7 +135,6 @@ const ru = {
|
||||
general: 'Общее',
|
||||
get_a_copy_of_on_puter: 'Получите копию \'%%\' на Puter.com!',
|
||||
get_copy_link: 'Получить ссылку для копирования',
|
||||
hide_all_windows: 'Скрыть все окна',
|
||||
home: 'Домой',
|
||||
html_document: 'HTML документ',
|
||||
hue: 'Оттенок',
|
||||
@@ -145,7 +143,6 @@ const ru = {
|
||||
item: 'элемент',
|
||||
items_in_trash_cannot_be_renamed: 'Этот элемент нельзя переименовать, потому что он находится в Корзине. Чтобы переименовать этот элемент, сначала перенесите его из Корзины.',
|
||||
jpeg_image: 'JPEG изображение',
|
||||
keep_in_taskbar: 'Сохранить на Панели Задач',
|
||||
language: 'Язык',
|
||||
license: 'Лицензия',
|
||||
lightness: 'Легкость', //нужен контекст
|
||||
@@ -218,7 +215,6 @@ const ru = {
|
||||
'Получите 1 ГБ за каждого друга, который создаст и подтвердит учетную запись на Puter. Ваш друг тоже получит 1 ГБ!',
|
||||
refer_friends_social_media_c2a: 'Получите 1 ГБ бесплатного хранилища на Puter.com!',
|
||||
refresh: 'Обновить',
|
||||
remove_from_taskbar: 'Удалить из панели задач',
|
||||
rename: 'Переименовать',
|
||||
replace: 'Заменить',
|
||||
replace_all: 'Заменить все',
|
||||
@@ -251,7 +247,6 @@ const ru = {
|
||||
share: 'Поделиться',
|
||||
share_to: 'Поделиться',
|
||||
shortcut_to: 'Ярлык для',
|
||||
show_all_windows: 'Показать Все Окна',
|
||||
show_hidden: 'Показать скрытые',
|
||||
sign_in_with_puter: 'Войти с Puter',
|
||||
sign_up: 'Зарегистрироваться',
|
||||
@@ -396,8 +391,6 @@ const ru = {
|
||||
'create_shortcut_s': 'Создать ярлыки', // In English: "Create Shortcuts"
|
||||
'minimize': 'Свернуть', // In English: "Minimize"
|
||||
'reload_app': 'Перезагрузить приложение', // In English: "Reload App"
|
||||
'new_window': 'Новое окно', // In English: "New Window"
|
||||
'open_trash': 'Открыть корзину', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'опубликовать как воркера', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Перейти в полноэкранный режим', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -429,14 +422,8 @@ const ru = {
|
||||
'window_title_publish_worker': 'Опубликовать воркер', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'Идёт проверка подлинности...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'Пригласить друга!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'Показать рабочий стол', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'Показать открытые окна', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'Выйти из полноэкранного режима', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'Перейти в полноэкранный режим', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'Положение', // In English: "Position"
|
||||
'desktop_position_left': 'Слева', // In English: "Left"
|
||||
'desktop_position_bottom': 'Внизу', // In English: "Bottom"
|
||||
'desktop_position_right': 'Справа', // In English: "Right"
|
||||
'item_shared_with_you': 'Пользователь поделился с вами этим элементом.', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'Вы поделились этим элементом как минимум с одним другим пользователем.', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'Ярлык', // In English: "Shortcut"
|
||||
|
||||
@@ -40,7 +40,6 @@ const sl = {
|
||||
change_password: 'Spremeni geslo',
|
||||
change_username: 'Spremeni uporabniško ime',
|
||||
close: 'Zapri',
|
||||
close_all_windows: 'Zapri vsa okna',
|
||||
close_all_windows_and_log_out: 'Zapri okna in se odjavi',
|
||||
confirm: 'Potrdi',
|
||||
confirm_2fa_setup: 'Kodo sem dodal v svojo aplikacijo za preverjanje pristnosti',
|
||||
@@ -125,7 +124,6 @@ const sl = {
|
||||
general: 'Splošno',
|
||||
get_a_copy_of_on_puter: 'Pridobite kopijo \'%%\' na Puter.com!',
|
||||
get_copy_link: 'Pridobi povezave kopije',
|
||||
hide_all_windows: 'Skrij vsa okna',
|
||||
home: 'Domov',
|
||||
html_document: 'HTML dokument',
|
||||
hue: 'Odtenek',
|
||||
@@ -134,7 +132,6 @@ const sl = {
|
||||
item: 'element',
|
||||
items_in_trash_cannot_be_renamed: 'Tega elementa ni mogoče preimenovati, ker je v smeteh. Če želite preimenovati ta element, ga najprej povlecite iz smeti.',
|
||||
jpeg_image: 'JPEG slika',
|
||||
keep_in_taskbar: 'Ohrani v opravilni vrstici',
|
||||
language: 'Jezik',
|
||||
license: 'Licenca',
|
||||
lightness: 'Svetloba',
|
||||
@@ -164,10 +161,8 @@ const sl = {
|
||||
no: 'Ne',
|
||||
ok: 'V redu',
|
||||
open: 'Odpri',
|
||||
new_window: 'Novo okno',
|
||||
open_in_new_tab: 'Odpri v novem zavihku',
|
||||
open_in_new_window: 'Odpri v novem oknu',
|
||||
open_trash: 'Odpri smeti',
|
||||
open_with: 'Odpri z',
|
||||
original_name: 'Izvirno ime',
|
||||
original_path: 'Izvirna pot',
|
||||
@@ -206,7 +201,6 @@ const sl = {
|
||||
refer_friends_c2a: 'Pridobite 1 GB za vsakega prijatelja, ki na Puterju ustvari in potrdi račun. Tudi vaš prijatelj dobi 1 GB!',
|
||||
refer_friends_social_media_c2a: 'Pridobite 1 GB brezplačnega prostora za shranjevanje na Puter.com!',
|
||||
refresh: 'Osveži',
|
||||
remove_from_taskbar: 'Odstrani iz opravilne vrstice',
|
||||
rename: 'Preimenuj',
|
||||
replace: 'Zamenjaj',
|
||||
replace_all: 'Zamenjaj vse',
|
||||
@@ -234,7 +228,6 @@ const sl = {
|
||||
share: 'Deli',
|
||||
share_to: 'Deli na',
|
||||
shortcut_to: 'Bližnjica do',
|
||||
show_all_windows: 'Pokaži vsa okna',
|
||||
show_hidden: 'Prikaži skrito',
|
||||
sign_in_with_puter: 'Prijavite se s Puterjem',
|
||||
sign_up: 'Ustvari račun',
|
||||
@@ -406,14 +399,8 @@ Shranite jih na varno mesto.
|
||||
'window_title_refer_friend': 'Priporoči prijatelju!',
|
||||
|
||||
// Desktop UI
|
||||
'desktop_show_desktop': 'Prikaži namizje',
|
||||
'desktop_show_open_windows': 'Prikaži odprta okna',
|
||||
'desktop_exit_full_screen': 'Izhod iz celozaslonskega načina',
|
||||
'desktop_enter_full_screen': 'Preklopi na celozaslonski način',
|
||||
'desktop_position': 'Položaj',
|
||||
'desktop_position_left': 'Levo',
|
||||
'desktop_position_bottom': 'Spodaj',
|
||||
'desktop_position_right': 'Desno',
|
||||
// Item UI
|
||||
'item_shared_with_you': 'Uporabnik je ta element delil z vami.',
|
||||
'item_shared_by_you': 'Ta element ste delili z vsaj enim drugim uporabnikom.',
|
||||
|
||||
@@ -38,7 +38,6 @@ const sv = {
|
||||
change_password: 'Byt lösenord',
|
||||
change_username: 'Byt användarnamn',
|
||||
close: 'Stäng',
|
||||
close_all_windows: 'Stäng alla fönster',
|
||||
close_all_windows_and_log_out: 'Stäng alla fönster och logga ut',
|
||||
change_always_open_with: 'Vill du alltid öppna den här filtypen med',
|
||||
confirm: 'Bekräfta',
|
||||
@@ -121,7 +120,6 @@ const sv = {
|
||||
general: 'Allmänt',
|
||||
get_a_copy_of_on_puter: "Få en kopia av '%%' på Puter.com!",
|
||||
get_copy_link: 'Få kopieringslänk',
|
||||
hide_all_windows: 'Dölj alla fönster',
|
||||
home: 'Hem',
|
||||
html_document: 'HTML-dokument',
|
||||
hue: 'Färgton',
|
||||
@@ -130,7 +128,6 @@ const sv = {
|
||||
item: 'Objekt',
|
||||
items_in_trash_cannot_be_renamed: 'Det här objektet kan inte byta namn eftersom det är i papperskorgen. För att byta namn på detta objekt, dra det först ur papperskorgen.',
|
||||
jpeg_image: 'JPEG-bild',
|
||||
keep_in_taskbar: 'Behåll i aktivitetsfältet',
|
||||
language: 'Språk',
|
||||
license: 'Licens',
|
||||
lightness: 'Ljus',
|
||||
@@ -196,7 +193,6 @@ const sv = {
|
||||
refer_friends_c2a: 'Få 1 GB för varje vän som skapar och bekräftar ett konto på Puter. Din vän får också 1 GB.',
|
||||
refer_friends_social_media_c2a: 'Få 1 GB gratis lagringsutrymme på Puter.com!',
|
||||
refresh: 'Uppdatera',
|
||||
remove_from_taskbar: 'Ta bort från aktivitetsfältet',
|
||||
rename: 'Byt namn',
|
||||
replace: 'Ersätt',
|
||||
replace_all: 'Ersätt alla',
|
||||
@@ -224,7 +220,6 @@ const sv = {
|
||||
share: 'Dela',
|
||||
share_to: 'Dela till',
|
||||
shortcut_to: 'Genväg till',
|
||||
show_all_windows: 'Visa alla fönster',
|
||||
show_hidden: 'Visa dolda',
|
||||
sign_in_with_puter: 'Logga in med Puter',
|
||||
sign_up: 'Skapa konto',
|
||||
@@ -365,8 +360,6 @@ const sv = {
|
||||
'create_shortcut_s': 'Skapa genvägar', // In English: "Create Shortcuts"
|
||||
'minimize': 'Minimera', // In English: "Minimize"
|
||||
'reload_app': 'Ladda om app', // In English: "Reload App"
|
||||
'new_window': 'Nytt fönster', // In English: "New Window"
|
||||
'open_trash': 'Öppna papperskorgen', // In English: "Open Trash"
|
||||
'plural_suffix': 'ar', // In English: "s"
|
||||
'publish_as_serverless_worker': 'Publicera som Worker', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Aktivera helskärm', // In English: "Enter Full Screen"
|
||||
@@ -399,14 +392,8 @@ const sv = {
|
||||
'window_title_publish_worker': 'Publicera Worker', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'Autentiserar...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'Rekommendera en vän!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'Visa skrivbord', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'Visa öppna fönster', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'Avsluta helskärm', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'Aktivera helskärm', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'Position', // In English: "Position"
|
||||
'desktop_position_left': 'Vänster', // In English: "Left"
|
||||
'desktop_position_bottom': 'Botten', // In English: "Bottom"
|
||||
'desktop_position_right': 'Höger', // In English: "Right"
|
||||
'item_shared_with_you': 'En användare har delat detta objekt med dig.', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'Du har delat detta objekt med minst en annan användare.', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'Genväg', // In English: "Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const ta = {
|
||||
change_password: 'கடவுச்சொல்லை மாற்றவும்',
|
||||
change_username: 'பயனர் பெயரை மாற்றவும்',
|
||||
close: 'மூடு',
|
||||
close_all_windows: 'அனைத்து விண்டோஸ் மூடு',
|
||||
close_all_windows_and_log_out: 'விண்டோஸை மூடிவிட்டு வெளியேறவும்',
|
||||
change_always_open_with:
|
||||
'இந்த வகையான கோப்பை எப்போதும் திறக்க விரும்புகிறீர்களா?',
|
||||
@@ -136,7 +135,6 @@ const ta = {
|
||||
general: 'பொது',
|
||||
get_a_copy_of_on_puter: 'Puter.com இல் \'%%\' நகலைப் பெறுங்கள்!',
|
||||
get_copy_link: 'நகல் இணைப்பைப் பெறவும்',
|
||||
hide_all_windows: 'அனைத்து விண்டோஸையும் மறைக்கவும்',
|
||||
home: 'வீடு',
|
||||
html_document: 'HTML ஆவணம்',
|
||||
hue: 'சாயல்',
|
||||
@@ -146,7 +144,6 @@ const ta = {
|
||||
items_in_trash_cannot_be_renamed:
|
||||
'இந்த உருப்படி குப்பையில் இருப்பதால் மறுபெயரிட முடியாது. இந்த உருப்படியை மறுபெயரிட, முதலில் அதை குப்பையிலிருந்து வெளியே இழுக்கவும்.',
|
||||
jpeg_image: 'JPEG படம்',
|
||||
keep_in_taskbar: 'பணிப்பட்டியில் வைக்கவும்',
|
||||
language: 'மொழி',
|
||||
license: 'உரிமம்',
|
||||
lightness: 'லேசான தன்மை',
|
||||
@@ -220,7 +217,6 @@ const ta = {
|
||||
refer_friends_social_media_c2a:
|
||||
'Puter.com இல் 1 GB இலவச சேமிப்பிடத்தைப் பெறுங்கள்!',
|
||||
refresh: 'புதுப்பிப்பு',
|
||||
remove_from_taskbar: 'பணிப்பட்டியில் இருந்து அகற்று',
|
||||
rename: 'மறுபெயரிடவும்',
|
||||
replace: 'மாற்றவும்',
|
||||
replace_all: 'அனைத்தையும் மாற்றவும்',
|
||||
@@ -251,7 +247,6 @@ const ta = {
|
||||
share: 'பகிர்',
|
||||
share_to: 'பகிரவும்',
|
||||
shortcut_to: 'குறுக்குவழி',
|
||||
show_all_windows: 'அனைத்து விண்டோஸையும் காட்டு',
|
||||
show_hidden: 'மறைக்கப்பட்டதைக் காட்டு',
|
||||
sign_in_with_puter: 'புட்டர் மூலம் உள்நுழையவும்',
|
||||
sign_up: 'பதிவு செய்யவும்',
|
||||
@@ -413,8 +408,6 @@ const ta = {
|
||||
create_shortcut_s: 'குறுக்குவழிகள் உருவாக்கு',
|
||||
minimize: 'சுருக்கு',
|
||||
reload_app: 'பயன்பாட்டை மீளேற்று',
|
||||
new_window: 'புதிய சாளரம்',
|
||||
open_trash: 'குப்பைத்தொட்டியைத் திற',
|
||||
publish_as_serverless_worker: 'வொர்க்கராக வெளியிடு',
|
||||
'toolbar.enter_fullscreen': 'முழுத்திரைக்கு செல்',
|
||||
'toolbar.github': 'GitHub',
|
||||
@@ -447,14 +440,8 @@ const ta = {
|
||||
window_title_publish_worker: 'வொர்க்கரை வெளியிடு',
|
||||
window_title_authenticating: 'அடையாளம் சரிபார்க்கப்படுகிறது...',
|
||||
window_title_refer_friend: 'நண்பரை அறிமுகப்படுத்து!',
|
||||
desktop_show_desktop: 'டெஸ்க்டாப் காண்பி',
|
||||
desktop_show_open_windows: 'திறந்த சாளரங்களை காண்பி',
|
||||
desktop_exit_full_screen: 'முழுத்திரையிலிருந்து வெளியேறு',
|
||||
desktop_enter_full_screen: 'முழுத்திரைக்கு செல்',
|
||||
desktop_position: 'இடம்',
|
||||
desktop_position_left: 'இடப்பு',
|
||||
desktop_position_bottom: 'கீழ்',
|
||||
desktop_position_right: 'வலப்பு',
|
||||
item_shared_with_you:
|
||||
'ஒரு பயனர் இந்த உருப்படியை உங்களுடன் பகிர்ந்துள்ளார்.',
|
||||
item_shared_by_you:
|
||||
|
||||
@@ -38,7 +38,6 @@ const th = {
|
||||
change_password: 'เปลี่ยนรหัสผ่าน',
|
||||
change_username: 'เปลี่ยนชื่อผู้ใช้',
|
||||
close: 'ปิด',
|
||||
close_all_windows: 'ปิดหน้าต่างทั้งหมด',
|
||||
close_all_windows_and_log_out: 'ปิดหน้าต่างและออกจากระบบ',
|
||||
change_always_open_with: 'คุณต้องการเปิดไฟล์ประเภทนี้ด้วย',
|
||||
confirm_2fa_setup: 'ฉันได้เพิ่มรหัสลงไปใน authenticator แอฟของฉันแล้ว',
|
||||
@@ -120,7 +119,6 @@ const th = {
|
||||
general: 'ทั่วไป',
|
||||
get_a_copy_of_on_puter: 'รับสำเนาของ "%%" ได้ที่ Puter.com!',
|
||||
get_copy_link: 'คัดลอกลิงก์',
|
||||
hide_all_windows: 'ซ่อนหน้าต่างทั้งหมด',
|
||||
home: 'บ้าน',
|
||||
html_document: 'เอกสาร HTML',
|
||||
hue: 'สี',
|
||||
@@ -129,7 +127,6 @@ const th = {
|
||||
item: 'รายการ',
|
||||
items_in_trash_cannot_be_renamed: 'ไม่สามารถเปลี่ยนชื่อรายการนี้ได้เนื่องจากอยู่ในถังขยะ หากต้องการเปลี่ยนชื่อรายการนี้ ให้ลากออกจากถังขยะก่อน',
|
||||
jpeg_image: 'ภาพ JPEG',
|
||||
keep_in_taskbar: 'คงไว้ในทาสก์บาร์',
|
||||
language: 'ภาษา',
|
||||
license: 'ใบอนุญาต',
|
||||
lightness: 'ความสว่าง',
|
||||
@@ -194,7 +191,6 @@ const th = {
|
||||
refer_friends_c2a: 'รับพื้นที่ 1 GB สำหรับเพื่อนทุกคนที่สร้าง และยืนยันบัญชีบน Puter เพื่อนของคุณจะได้รับพื้นที่ 1 GB เช่นกัน!',
|
||||
refer_friends_social_media_c2a: 'รับพื้นที่จัดเก็บข้อมูลฟรี 1 GB บน Puter.com!',
|
||||
refresh: 'รีเฟรช',
|
||||
remove_from_taskbar: 'นำออกจากทาสก์บาร์',
|
||||
rename: 'เปลี่ยนชื่อ',
|
||||
replace: 'แทนที่',
|
||||
replace_all: 'แทนที่ทั้งหมด',
|
||||
@@ -221,7 +217,6 @@ const th = {
|
||||
share: 'แชร์',
|
||||
share_to: 'แชร์ไปยัง',
|
||||
shortcut_to: 'ทางลัดไป',
|
||||
show_all_windows: 'แสดงหน้าต่างทั้งหมด',
|
||||
show_hidden: 'แสดงที่ซ่อนไว้',
|
||||
sign_in_with_puter: 'ลงชื่อเข้าใช้ด้วย Puter',
|
||||
sign_up: 'สมัครสมาชิก',
|
||||
@@ -363,8 +358,6 @@ const th = {
|
||||
'create_shortcut_s': 'สร้างทางลัด',
|
||||
'minimize': 'ย่อเล็กสุด',
|
||||
'reload_app': 'โหลดแอปใหม่',
|
||||
'new_window': 'หน้าต่างใหม่',
|
||||
'open_trash': 'เปิดถังขยะ',
|
||||
'publish_as_serverless_worker': 'เผยแพร่เป็น Worker',
|
||||
'toolbar.enter_fullscreen': 'เข้าสู่โหมดเต็มจอ',
|
||||
'toolbar.github': 'GitHub',
|
||||
@@ -396,14 +389,8 @@ const th = {
|
||||
'window_title_publish_worker': 'เผยแพร่ Worker',
|
||||
'window_title_authenticating': 'กำลังยืนยันตัวตน...',
|
||||
'window_title_refer_friend': 'แนะนำเพื่อน!',
|
||||
'desktop_show_desktop': 'แสดงเดสก์ท็อป',
|
||||
'desktop_show_open_windows': 'แสดงหน้าต่างที่เปิดอยู่',
|
||||
'desktop_exit_full_screen': 'ออกจากโหมดเต็มจอ',
|
||||
'desktop_enter_full_screen': 'เข้าสู่โหมดเต็มจอ',
|
||||
'desktop_position': 'ตำแหน่ง',
|
||||
'desktop_position_left': 'ซ้าย',
|
||||
'desktop_position_bottom': 'ล่าง',
|
||||
'desktop_position_right': 'ขวา',
|
||||
'item_shared_with_you': 'ผู้ใช้คนหนึ่งได้แชร์รายการนี้กับคุณ',
|
||||
'item_shared_by_you': 'คุณได้แชร์รายการนี้กับผู้ใช้อย่างน้อยหนึ่งคน',
|
||||
'item_shortcut': 'ทางลัด',
|
||||
|
||||
@@ -38,7 +38,6 @@ const tr = {
|
||||
change_password: 'Parolayı Değiştir',
|
||||
change_username: 'Kullanıcı Adını Değiştir',
|
||||
close: 'Kapat',
|
||||
close_all_windows: 'Tüm Pencereleri Kapat',
|
||||
close_all_windows_and_log_out: 'Pencereleri Kapat ve Çıkış Yap',
|
||||
change_always_open_with: 'Bu tür dosyaları her zaman şununla açmak istiyor musunuz',
|
||||
confirm_2fa_setup: 'Kodu kimlik doğrulama uygulamama ekledim',
|
||||
@@ -120,7 +119,6 @@ const tr = {
|
||||
general: 'Genel',
|
||||
get_a_copy_of_on_puter: "'%' kopyasını Puter.com'da edinin!",
|
||||
get_copy_link: 'Kopyalama Bağlantısını Al',
|
||||
hide_all_windows: 'Tüm Pencereleri Gizle',
|
||||
home: 'Anasayfa',
|
||||
html_document: 'HTML belgesi',
|
||||
hue: 'Renk Tonu',
|
||||
@@ -129,7 +127,6 @@ const tr = {
|
||||
item: 'öğe',
|
||||
items_in_trash_cannot_be_renamed: 'Bu öğe, çöp kutusunda olduğu için yeniden adlandırılamaz. Bu öğeyi yeniden adlandırmak için önce çöp kutusundan çıkarın.',
|
||||
jpeg_image: 'JPEG görüntüsü',
|
||||
keep_in_taskbar: 'Görev Çubuğunda Tut',
|
||||
language: 'Dil',
|
||||
license: 'Lisans',
|
||||
lightness: 'Aydınlık',
|
||||
@@ -195,7 +192,6 @@ const tr = {
|
||||
refer_friends_c2a: 'Hesap oluşturup onaylayan her arkadaşınız için 1 GB kazanın. Arkadaşınız da 1 GB kazanacak!',
|
||||
refer_friends_social_media_c2a: "Puter.com'da 1 GB ücretsiz depolama alanı kazanın!",
|
||||
refresh: 'Yenile',
|
||||
remove_from_taskbar: 'Görev Çubuğundan Kaldır',
|
||||
rename: 'Yeniden Adlandır',
|
||||
replace: 'Değiştir',
|
||||
replace_all: 'Tümünü Değiştir',
|
||||
@@ -223,7 +219,6 @@ const tr = {
|
||||
share: 'Paylaş',
|
||||
share_to: 'Şuna paylaş',
|
||||
shortcut_to: 'Şuna kısayol oluştur',
|
||||
show_all_windows: 'Tüm Pencereleri Göster',
|
||||
show_hidden: 'Gizli dosyaları göster',
|
||||
sign_in_with_puter: 'Puter ile giriş yap',
|
||||
sign_up: 'Kaydol',
|
||||
@@ -366,8 +361,6 @@ const tr = {
|
||||
'create_shortcut_s': 'Kısayollar Oluştur',
|
||||
'minimize': 'Küçült',
|
||||
'reload_app': 'Uygulamayı Yeniden Yükle',
|
||||
'new_window': 'Yeni Pencere',
|
||||
'open_trash': 'Çöp Kutusunu Aç',
|
||||
'publish_as_serverless_worker': 'Worker olarak Yayınla',
|
||||
'toolbar.enter_fullscreen': 'Tam Ekrana Geç',
|
||||
'toolbar.github': 'GitHub',
|
||||
@@ -399,14 +392,8 @@ const tr = {
|
||||
'window_title_publish_worker': 'Worker Yayınla',
|
||||
'window_title_authenticating': 'Kimlik doğrulanıyor...',
|
||||
'window_title_refer_friend': 'Bir arkadaşını yönlendir!',
|
||||
'desktop_show_desktop': 'Masaüstünü Göster',
|
||||
'desktop_show_open_windows': 'Açık Pencereleri Göster',
|
||||
'desktop_exit_full_screen': 'Tam Ekrandan Çık',
|
||||
'desktop_enter_full_screen': 'Tam Ekrana Geç',
|
||||
'desktop_position': 'Konum',
|
||||
'desktop_position_left': 'Sol',
|
||||
'desktop_position_bottom': 'Alt',
|
||||
'desktop_position_right': 'Sağ',
|
||||
'item_shared_with_you': 'Bir kullanıcı bu öğeyi sizinle paylaştı.',
|
||||
'item_shared_by_you': 'Bu öğeyi en az bir kullanıcıyla paylaştınız.',
|
||||
'item_shortcut': 'Kısayol',
|
||||
|
||||
@@ -39,7 +39,6 @@ const ua = {
|
||||
change_password: 'Змінити Пароль',
|
||||
change_username: "Змінити Ім\'я Користувача",
|
||||
close: 'Закрити',
|
||||
close_all_windows: 'Закрити всі Вікна',
|
||||
close_all_windows_and_log_out: 'Закрити Вікна і Вийти',
|
||||
change_always_open_with: 'Бажаєте завжди відкривати файли цього типу в',
|
||||
confirm: 'Підтвердити',
|
||||
@@ -122,7 +121,6 @@ const ua = {
|
||||
general: 'Загальний',
|
||||
get_a_copy_of_on_puter: 'Отримайте копію \'%%\' на Puter.com!',
|
||||
get_copy_link: 'Отримати Посилання для Копіювання',
|
||||
hide_all_windows: 'Приховати всі вікна',
|
||||
home: 'Додому',
|
||||
html_document: 'HTML документ',
|
||||
hue: 'Колірна Гама',
|
||||
@@ -132,7 +130,6 @@ const ua = {
|
||||
items_in_trash_cannot_be_renamed: `Цей елемент неможливо переіменувати тому що він знаходиться у кошику. Спочатку
|
||||
перемістіть його з корзини`,
|
||||
jpeg_image: 'JPEG зображення',
|
||||
keep_in_taskbar: 'Зберегти на Панелі Задач',
|
||||
language: 'Мова',
|
||||
license: 'Ліцензія',
|
||||
lightness: 'Яскравість',
|
||||
@@ -199,7 +196,6 @@ const ua = {
|
||||
refer_friends_c2a: 'Отримайте 1 ГБ за кожного друга, який створить і підтвердить обліковий запис на Puter. Ваш друг також отримає 1 ГБ!',
|
||||
refer_friends_social_media_c2a: 'Отримайте 1 ГБ безкоштовного сховища на Puter.com!',
|
||||
refresh: 'Оновити',
|
||||
remove_from_taskbar: 'Видалити з Панелі Завдань',
|
||||
rename: 'Перейменувати',
|
||||
replace: 'Замінити',
|
||||
replace_all: 'Замінити Все',
|
||||
@@ -227,7 +223,6 @@ const ua = {
|
||||
share: 'Поділитися',
|
||||
share_to: 'Поділитися з',
|
||||
shortcut_to: 'Ярлик для',
|
||||
show_all_windows: 'Показати Всі Вікна',
|
||||
show_hidden: 'Показати приховані',
|
||||
sign_in_with_puter: 'Увійти з Puter',
|
||||
sign_up: 'Зареєструватися',
|
||||
@@ -349,8 +344,6 @@ const ua = {
|
||||
'create_shortcut_s': 'Створити ярлики',
|
||||
'minimize': 'Згорнути',
|
||||
'reload_app': 'Перезавантажити додаток',
|
||||
'new_window': 'Нове вікно',
|
||||
'open_trash': 'Відкрити кошик',
|
||||
'publish_as_serverless_worker': 'Опублікувати як воркер',
|
||||
'toolbar.enter_fullscreen': 'Увійти в повноекранний режим',
|
||||
'toolbar.github': 'GitHub',
|
||||
@@ -382,14 +375,8 @@ const ua = {
|
||||
'window_title_publish_worker': 'Опублікувати воркер',
|
||||
'window_title_authenticating': 'Аутентифікація...',
|
||||
'window_title_refer_friend': 'Запросити друга!',
|
||||
'desktop_show_desktop': 'Показати робочий стіл',
|
||||
'desktop_show_open_windows': 'Показати відкриті вікна',
|
||||
'desktop_exit_full_screen': 'Вийти з повноекранного режиму',
|
||||
'desktop_enter_full_screen': 'Увійти в повноекранний режим',
|
||||
'desktop_position': 'Позиція',
|
||||
'desktop_position_left': 'Ліворуч',
|
||||
'desktop_position_bottom': 'Знизу',
|
||||
'desktop_position_right': 'Праворуч',
|
||||
'item_shared_with_you': 'Користувач поділився цим елементом з вами.',
|
||||
'item_shared_by_you': 'Ви поділилися цим елементом з принаймні одним іншим користувачем.',
|
||||
'item_shortcut': 'Ярлик',
|
||||
|
||||
@@ -36,7 +36,6 @@ const ur = {
|
||||
change_password: 'پاس ورڈ تبدیل کریں',
|
||||
change_username: 'صارف کا نام تبدیل کریں',
|
||||
close: 'بند کریں',
|
||||
close_all_windows: 'تمام ونڈوز بند کریں',
|
||||
close_all_windows_and_log_out: 'ونڈوز بند کریں اور لاگ آؤٹ کریں۔',
|
||||
change_always_open_with: 'کیا آپ ہمیشہ اس قسم کی فائل کو کھولنا چاہتے ہیں۔',
|
||||
confirm_2fa_setup: 'میں نے کوڈ کو اپنی تصدیق کنندہ ایپ میں شامل کر دیا ہے۔',
|
||||
@@ -124,7 +123,6 @@ const ur = {
|
||||
general: 'عام ',
|
||||
get_a_copy_of_on_puter: "Puter.com پر '%%' کی ایک کاپی حاصل کریں!",
|
||||
get_copy_link: 'کاپی لنک حاصل کریں',
|
||||
hide_all_windows: 'تمام ونڈوز چھپائیں ',
|
||||
home: 'گھر',
|
||||
html_document: 'ایچ ٹی ایم ایل دستاویز',
|
||||
hue: 'رنگت',
|
||||
@@ -134,7 +132,6 @@ const ur = {
|
||||
items_in_trash_cannot_be_renamed:
|
||||
'کچرے میں موجود اشیاء کا نام تبدیل نہیں کیا جا سکتا',
|
||||
jpeg_image: 'جے پی ای جی_تصویر',
|
||||
keep_in_taskbar: 'ٹاسک بار میں رکھیں ',
|
||||
language: 'زبان',
|
||||
license: 'License',
|
||||
lightness: 'لائسنس',
|
||||
@@ -202,7 +199,6 @@ const ur = {
|
||||
refer_friends_social_media_c2a:
|
||||
'پیوٹر ڈاٹ کام پر مفت 1 گیگابائٹ ذخیرہ حاصل کریں!',
|
||||
refresh: 'تازہ کریں ',
|
||||
remove_from_taskbar: 'ٹاسک بار سے ہٹائیں ',
|
||||
rename: 'دوبارہ نام دیں',
|
||||
replace: 'بدل دیں۔',
|
||||
replace_all: 'سبھی کو تبدیل کریں۔',
|
||||
@@ -227,7 +223,6 @@ const ur = {
|
||||
settings: 'ترتیبات',
|
||||
set_new_password: 'نیا پاس ورڈ مقرر کریں ',
|
||||
share_to: ' کے ساتھ شیئر کریں',
|
||||
show_all_windows: 'تمام ونڈوز دکھائیں ',
|
||||
show_hidden: 'پوشیدہ دکھائیں ',
|
||||
sign_in_with_puter: 'پیوٹر کے ساتھ سائن ان کریں',
|
||||
sign_up: 'سائن اپ کریں',
|
||||
@@ -390,8 +385,6 @@ const ur = {
|
||||
'create_shortcut_s': 'شارٹ کٹس بنائیں', // In English: "Create Shortcuts"
|
||||
'minimize': 'چھوٹا کریں', // In English: "Minimize"
|
||||
'reload_app': 'ایپ دوبارہ لوڈ کریں', // In English: "Reload App"
|
||||
'new_window': 'نیا ونڈو', // In English: "New Window"
|
||||
'open_trash': 'ری سائیکل بن کھولیں', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'ورکر کے طور پر شائع کریں', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'فل اسکرین پر جائیں', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'گیٹ ہب', // In English: "GitHub"
|
||||
@@ -423,14 +416,8 @@ const ur = {
|
||||
'window_title_publish_worker': 'ورکر شائع کریں', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'تصدیق جاری ہے...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'کسی دوست کو ریفر کریں!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'ڈیسک ٹاپ دکھائیں', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'کھلی ہوئی ونڈوز دکھائیں', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'فل اسکرین سے باہر نکلیں', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'فل اسکرین پر جائیں', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'پوزیشن', // In English: "Position"
|
||||
'desktop_position_left': 'بائیں', // In English: "Left"
|
||||
'desktop_position_bottom': 'نیچے', // In English: "Bottom"
|
||||
'desktop_position_right': 'دائیں', // In English: "Right"
|
||||
'item_shared_with_you': 'کسی صارف نے یہ آئٹم آپ کے ساتھ شیئر کیا ہے۔', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'آپ نے یہ آئٹم کم از کم ایک دوسرے صارف کے ساتھ شیئر کیا ہے۔', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'شارٹ کٹ', // In English: "Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const vi = {
|
||||
change_password: 'Thay đổi mật khẩu',
|
||||
change_username: 'Thay đổi tên người dùng',
|
||||
close: 'Đóng',
|
||||
close_all_windows: 'Đóng tất cả cửa sổ',
|
||||
close_all_windows_and_log_out: 'Đóng cửa sổ và đăng xuất',
|
||||
change_always_open_with: 'Bạn có muốn luôn mở loại tệp này bằng',
|
||||
confirm: 'Xác nhận',
|
||||
@@ -121,7 +120,6 @@ const vi = {
|
||||
general: 'Chung',
|
||||
get_a_copy_of_on_puter: 'Nhận một bản sao của \'%%\' trên Puter.com!',
|
||||
get_copy_link: 'Lấy liên kết sao chép',
|
||||
hide_all_windows: 'Ẩn tất cả cửa sổ',
|
||||
home: 'Trang chủ',
|
||||
html_document: 'Tài liệu HTML',
|
||||
hue: 'Màu sắc',
|
||||
@@ -130,7 +128,6 @@ const vi = {
|
||||
item: 'mục',
|
||||
items_in_trash_cannot_be_renamed: 'Mục này không thể đổi tên vì nó đang ở trong thùng rác. Để đổi tên mục này, trước tiên hãy kéo nó ra khỏi Thùng rác.',
|
||||
jpeg_image: 'Hình ảnh JPEG',
|
||||
keep_in_taskbar: 'Giữ trong thanh tác vụ',
|
||||
language: 'Ngôn ngữ',
|
||||
license: 'Giấy phép',
|
||||
lightness: 'Độ sáng',
|
||||
@@ -196,7 +193,6 @@ const vi = {
|
||||
refer_friends_c2a: 'Nhận 1 GB cho mỗi người bạn tạo và xác nhận tài khoản trên Puter. Bạn của bạn cũng sẽ nhận được 1 GB!',
|
||||
refer_friends_social_media_c2a: 'Nhận 1 GB dung lượng lưu trữ miễn phí trên Puter.com!',
|
||||
refresh: 'Làm mới',
|
||||
remove_from_taskbar: 'Xóa khỏi thanh tác vụ',
|
||||
rename: 'Đổi tên',
|
||||
replace: 'Thay thế',
|
||||
replace_all: 'Thay thế tất cả',
|
||||
@@ -224,7 +220,6 @@ const vi = {
|
||||
share: 'Chia sẻ',
|
||||
share_to: 'Chia sẻ đến',
|
||||
shortcut_to: 'Lối tắt đến',
|
||||
show_all_windows: 'Hiển thị tất cả cửa sổ',
|
||||
show_hidden: 'Hiển thị mục ẩn',
|
||||
sign_in_with_puter: 'Đăng nhập bằng Puter',
|
||||
sign_up: 'Đăng ký',
|
||||
@@ -363,8 +358,6 @@ const vi = {
|
||||
'create_shortcut_s': 'Tạo các lối tắt', // In English: "Create Shortcuts"
|
||||
'minimize': 'Thu nhỏ', // In English: "Minimize"
|
||||
'reload_app': 'Tải lại ứng dụng', // In English: "Reload App"
|
||||
'new_window': 'Cửa sổ mới', // In English: "New Window"
|
||||
'open_trash': 'Mở thùng rác', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': 'Xuất bản dưới dạng Worker', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': 'Vào chế độ toàn màn hình', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -396,14 +389,8 @@ const vi = {
|
||||
'window_title_publish_worker': 'Xuất bản Worker', // In English: "Publish Worker"
|
||||
'window_title_authenticating': 'Đang xác thực...', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': 'Giới thiệu bạn bè!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': 'Hiển thị Desktop', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': 'Hiển thị các cửa sổ đang mở', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': 'Thoát chế độ toàn màn hình', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': 'Vào chế độ toàn màn hình', // In English: "Enter Full Screen"
|
||||
'desktop_position': 'Vị trí', // In English: "Position"
|
||||
'desktop_position_left': 'Bên trái', // In English: "Left"
|
||||
'desktop_position_bottom': 'Phía dưới', // In English: "Bottom"
|
||||
'desktop_position_right': 'Bên phải', // In English: "Right"
|
||||
'item_shared_with_you': 'Một người dùng đã chia sẻ mục này với bạn.', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': 'Bạn đã chia sẻ mục này với ít nhất một người dùng khác.', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': 'Lối tắt', // In English: "Shortcut"
|
||||
|
||||
@@ -38,7 +38,6 @@ const zh = {
|
||||
change_password: '更改密码',
|
||||
change_username: '更改用户名',
|
||||
close: '关闭',
|
||||
close_all_windows: '关闭所有窗口',
|
||||
close_all_windows_and_log_out: '关闭窗口并退出',
|
||||
change_always_open_with: '以后都这样打开此类型的文件?',
|
||||
confirm: '确认',
|
||||
@@ -121,7 +120,6 @@ const zh = {
|
||||
general: '常用',
|
||||
get_a_copy_of_on_puter: '在 Puter.com 上获取 \'%%\' 的副本!',
|
||||
get_copy_link: '获取复制链接',
|
||||
hide_all_windows: '隐藏所有窗口',
|
||||
home: '主页',
|
||||
html_document: 'HTML 文档',
|
||||
hue: '色调',
|
||||
@@ -130,7 +128,6 @@ const zh = {
|
||||
item: '项目',
|
||||
items_in_trash_cannot_be_renamed: '此项目无法重命名,因为它在回收站中。要重命名此项目,请先将其拖出回收站。',
|
||||
jpeg_image: 'JPEG 图像',
|
||||
keep_in_taskbar: '固定在任务栏',
|
||||
language: '语言',
|
||||
license: '许可协议',
|
||||
lightness: '明亮',
|
||||
@@ -196,7 +193,6 @@ const zh = {
|
||||
refer_friends_c2a: '每个创建并确认 Puter 帐户的朋友都会为您获得 1 GB。您的朋友也将获得 1 GB!',
|
||||
refer_friends_social_media_c2a: '在 Puter.com 上获取 1 GB 的免费存储空间!',
|
||||
refresh: '刷新',
|
||||
remove_from_taskbar: '从任务栏中删除',
|
||||
rename: '重命名',
|
||||
replace: '替换',
|
||||
replace_all: '全部替换',
|
||||
@@ -224,7 +220,6 @@ const zh = {
|
||||
share: '分享',
|
||||
share_to: '分享到',
|
||||
shortcut_to: '快捷方式',
|
||||
show_all_windows: '显示所有窗口',
|
||||
show_hidden: '显示隐藏',
|
||||
sign_in_with_puter: '使用 Puter 登录',
|
||||
sign_up: '注册',
|
||||
@@ -364,8 +359,6 @@ const zh = {
|
||||
'create_shortcut_s': '创建快捷方式', // In English: "Create Shortcuts"
|
||||
'minimize': '最小化', // In English: "Minimize"
|
||||
'reload_app': '重新加载应用', // In English: "Reload App"
|
||||
'new_window': '新窗口', // In English: "New Window"
|
||||
'open_trash': '打开回收站', // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': '发布为 Worker', // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': '进入全屏', // In English: "Enter Full Screen"
|
||||
'toolbar.github': 'GitHub', // In English: "GitHub"
|
||||
@@ -397,14 +390,8 @@ const zh = {
|
||||
'window_title_publish_worker': '发布 Worker', // In English: "Publish Worker"
|
||||
'window_title_authenticating': '正在验证…', // In English: "Authenticating..."
|
||||
'window_title_refer_friend': '推荐朋友!', // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': '显示桌面', // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': '显示打开的窗口', // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': '退出全屏', // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': '进入全屏', // In English: "Enter Full Screen"
|
||||
'desktop_position': '位置', // In English: "Position"
|
||||
'desktop_position_left': '左侧', // In English: "Left"
|
||||
'desktop_position_bottom': '底部', // In English: "Bottom"
|
||||
'desktop_position_right': '右侧', // In English: "Right"
|
||||
'item_shared_with_you': '一位用户已与您共享此项目。', // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': '您已将此项目共享给至少一位用户。', // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': '快捷方式', // In English: "Shortcut"
|
||||
|
||||
@@ -56,7 +56,6 @@ const zhtw = {
|
||||
change_password: '更改密碼',
|
||||
change_username: '更改使用者名稱',
|
||||
close: '關閉',
|
||||
close_all_windows: '關閉所有視窗',
|
||||
close_all_windows_and_log_out: '關閉視窗並登出',
|
||||
change_always_open_with: '您是否要始終使用以下程式開啟此類型的檔案',
|
||||
confirm: '確認',
|
||||
@@ -139,7 +138,6 @@ const zhtw = {
|
||||
general: '一般',
|
||||
get_a_copy_of_on_puter: '在 Puter.com 上獲取 \'%%\' 的副本!',
|
||||
get_copy_link: '獲取副本連結',
|
||||
hide_all_windows: '隱藏所有視窗',
|
||||
home: '首頁',
|
||||
html_document: 'HTML 文件',
|
||||
hue: '色相',
|
||||
@@ -148,7 +146,6 @@ const zhtw = {
|
||||
item: '項目',
|
||||
items_in_trash_cannot_be_renamed: '此項目無法重新命名,因為它在垃圾桶中。要重新命名此項目,請先將其從垃圾桶中拖出。',
|
||||
jpeg_image: 'JPEG 圖片',
|
||||
keep_in_taskbar: '保留在工作列',
|
||||
language: '語言',
|
||||
license: '授權',
|
||||
lightness: '亮度',
|
||||
@@ -214,7 +211,6 @@ const zhtw = {
|
||||
refer_friends_c2a: '每邀請一位朋友在 Puter 上建立並確認帳戶,您就可獲得 1 GB 空間。您的朋友也會獲得 1 GB!',
|
||||
refer_friends_social_media_c2a: '在 Puter.com 上獲得 1 GB 免費儲存空間!',
|
||||
refresh: '重新整理',
|
||||
remove_from_taskbar: '從工作列移除',
|
||||
rename: '重新命名',
|
||||
replace: '取代',
|
||||
replace_all: '全部取代',
|
||||
@@ -242,7 +238,6 @@ const zhtw = {
|
||||
share: '分享',
|
||||
share_to: '分享到',
|
||||
shortcut_to: '捷徑到',
|
||||
show_all_windows: '顯示所有視窗',
|
||||
show_hidden: '顯示隱藏項目',
|
||||
sign_in_with_puter: '使用 Puter 登入',
|
||||
sign_up: '註冊',
|
||||
@@ -379,8 +374,6 @@ const zhtw = {
|
||||
'create_shortcut_s': "建立多個捷徑", // In English: "Create Shortcuts"
|
||||
'minimize': "最小化", // In English: "Minimize"
|
||||
'reload_app': "重新載入應用程式", // In English: "Reload App"
|
||||
'new_window': "新視窗", // In English: "New Window"
|
||||
'open_trash': "開啟垃圾桶", // In English: "Open Trash"
|
||||
'publish_as_serverless_worker': "發布為 Worker", // In English: "Publish as Worker"
|
||||
'toolbar.enter_fullscreen': "進入全螢幕", // In English: "Enter Full Screen"
|
||||
'toolbar.github': "GitHub", // In English: "GitHub"
|
||||
@@ -412,14 +405,8 @@ const zhtw = {
|
||||
'window_title_publish_worker': "發布 Worker", // In English: "Publish Worker"
|
||||
'window_title_authenticating': "正在驗證…", // In English: "Authenticating..."
|
||||
'window_title_refer_friend': "推薦好友!", // In English: "Refer a friend!"
|
||||
'desktop_show_desktop': "顯示桌面", // In English: "Show Desktop"
|
||||
'desktop_show_open_windows': "顯示開啟的視窗", // In English: "Show Open Windows"
|
||||
'desktop_exit_full_screen': "退出全螢幕", // In English: "Exit Full Screen"
|
||||
'desktop_enter_full_screen': "進入全螢幕", // In English: "Enter Full Screen"
|
||||
'desktop_position': "位置", // In English: "Position"
|
||||
'desktop_position_left': "左側", // In English: "Left"
|
||||
'desktop_position_bottom': "底部", // In English: "Bottom"
|
||||
'desktop_position_right': "右側", // In English: "Right"
|
||||
'item_shared_with_you': "有使用者與您分享了此項目。", // In English: "A user has shared this item with you."
|
||||
'item_shared_by_you': "您已將此項目分享給其他使用者。", // In English: "You have shared this item with at least one other user."
|
||||
'item_shortcut': "捷徑", // In English: "Shortcut"
|
||||
@@ -446,7 +433,6 @@ const zhtw = {
|
||||
'tar': "Tar 壓縮", // In English: "Tar"
|
||||
'download_as_tar': "下載為 Tar", // In English: "Download as Tar"
|
||||
'tarring': "正在建立 Tar 壓縮檔 %strong%", // In English: "Tarring %strong%"
|
||||
'set_as_background': "設為桌面背景", // In English: "Set as Desktop Background"
|
||||
'error_user_or_path_not_found': "找不到使用者或路徑。", // In English: "User or path not found."
|
||||
'error_invalid_username': "無效的使用者名稱。", // In English: "Invalid username."
|
||||
},
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 175 KiB |
+6
-55
@@ -1696,9 +1696,6 @@ window.initgui = async function (options) {
|
||||
(window.url_query_params.get('puter.fullpage') === 'true' ||
|
||||
window.url_query_params.get('puter.fullpage') === '1')
|
||||
) {
|
||||
// In fullpage mode, we want to hide the taskbar for better UX
|
||||
window.taskbar_height = 0;
|
||||
|
||||
// Puter is in fullpage mode.
|
||||
window.is_fullpage_mode = true;
|
||||
} else if (window.is_dashboard_mode) {
|
||||
@@ -1734,9 +1731,7 @@ window.initgui = async function (options) {
|
||||
if (tokenChanged) {
|
||||
// This will update the list of logged in users and set the current one
|
||||
try {
|
||||
const whoami = await puter.os.user({
|
||||
query: 'icon_size=64',
|
||||
});
|
||||
const whoami = await puter.os.user();
|
||||
if (whoami) await window.update_auth_data(token, whoami);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
@@ -1858,7 +1853,7 @@ window.initgui = async function (options) {
|
||||
let session_user = null;
|
||||
if (host_user && window.is_auth()) {
|
||||
try {
|
||||
session = await puter.os.user({ query: 'icon_size=64' });
|
||||
session = await puter.os.user();
|
||||
session_user = String(session?.uuid ?? '').toLowerCase();
|
||||
} catch (e) {
|
||||
session_user = null;
|
||||
@@ -1938,9 +1933,7 @@ window.initgui = async function (options) {
|
||||
// Ensure current user is in logged_in_users (e.g. after OIDC redirect we have token but no user in list)
|
||||
let currentUserUuid = window.user?.uuid ?? null;
|
||||
try {
|
||||
const whoami_popup = await puter.os.user({
|
||||
query: 'icon_size=64',
|
||||
});
|
||||
const whoami_popup = await puter.os.user();
|
||||
currentUserUuid = whoami_popup?.uuid ?? currentUserUuid;
|
||||
await window.update_auth_data(
|
||||
whoami_popup.token || window.auth_token,
|
||||
@@ -2027,7 +2020,6 @@ window.initgui = async function (options) {
|
||||
});
|
||||
if (app_info?.metadata?.fullpage_on_landing) {
|
||||
window.is_fullpage_mode = true;
|
||||
window.taskbar_height = 0;
|
||||
window.app_launched_from_url = app_info;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -2035,18 +2027,6 @@ window.initgui = async function (options) {
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Desktop background (early)
|
||||
// Set before action=login/signup so the signed-in dialog has the desktop behind it.
|
||||
// Skipped for the full-page form, which it would only flash behind before covering.
|
||||
// -------------------------------------------------------------------------------------
|
||||
const authActionIsPage =
|
||||
action === 'password-recovery' ||
|
||||
(!window.is_auth() && (action === 'login' || action === 'signup'));
|
||||
if (!window.is_fullpage_mode && !window.embedded_in_popup && !authActionIsPage) {
|
||||
window.refresh_desktop_background();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Action: Password recovery
|
||||
//--------------------------------------------------------------------------------------
|
||||
@@ -2169,7 +2149,7 @@ window.initgui = async function (options) {
|
||||
puter.setAuthToken(query_param_auth_token);
|
||||
|
||||
try {
|
||||
whoami = await puter.os.user({ query: 'icon_size=64' });
|
||||
whoami = await puter.os.user();
|
||||
} catch (e) {
|
||||
if (e.status === 401) {
|
||||
window.logout();
|
||||
@@ -2444,8 +2424,6 @@ window.initgui = async function (options) {
|
||||
$('.context-menu').remove();
|
||||
// remove desktop
|
||||
$('.desktop').remove();
|
||||
// remove taskbar
|
||||
$('.taskbar').remove();
|
||||
// disable native browser exit confirmation
|
||||
window.onbeforeunload = null;
|
||||
// go back to the interface the user was in: dashboard users to the root
|
||||
@@ -2492,7 +2470,7 @@ window.initgui = async function (options) {
|
||||
// try to get user data using /whoami, only if that data is missing
|
||||
if (!whoami) {
|
||||
try {
|
||||
whoami = await puter.os.user({ query: 'icon_size=64' });
|
||||
whoami = await puter.os.user();
|
||||
} catch (e) {
|
||||
if (e.status === 401) {
|
||||
bad_session_logout();
|
||||
@@ -2630,15 +2608,6 @@ window.initgui = async function (options) {
|
||||
!window.is_auth() &&
|
||||
(!window.first_visit_ever || window.disable_temp_users || is_app_landing_page || is_share_link);
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Desktop Background
|
||||
// If we're in fullpage/emebedded/Auth Popup mode, we don't want to load the custom background
|
||||
// because it's not visible anyway and it's a waste of bandwidth. Same behind the sign-in page.
|
||||
// -------------------------------------------------------------------------------------
|
||||
const showsSignInPage = needsSignIn && window.logged_in_users.length === 0;
|
||||
if (!window.is_fullpage_mode && !window.embedded_in_popup && !showsSignInPage) {
|
||||
window.refresh_desktop_background();
|
||||
}
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Un-authed but not first visit -> try to log in/sign up
|
||||
// -------------------------------------------------------------------------------------
|
||||
@@ -2692,12 +2661,6 @@ window.initgui = async function (options) {
|
||||
const needs_action = action === 'authme' || action === 'copyauth';
|
||||
const reload_on_success = needs_action;
|
||||
if (window.logged_in_users.length > 0) {
|
||||
// dashboard mode skips the wallpaper (fullpage), but the account
|
||||
// picker is a dialog, not a page — restore the wallpaper so it
|
||||
// isn't on a blank page
|
||||
if (window.is_dashboard_mode) {
|
||||
window.refresh_desktop_background();
|
||||
}
|
||||
await UIAuthAccountPicker({
|
||||
redirect_url: needs_action ? window.location.href : undefined,
|
||||
});
|
||||
@@ -3038,7 +3001,6 @@ window.initgui = async function (options) {
|
||||
});
|
||||
if (app_info?.metadata?.fullpage_on_landing) {
|
||||
window.is_fullpage_mode = true;
|
||||
window.taskbar_height = 0;
|
||||
window.app_launched_from_url = app_info;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -3097,9 +3059,6 @@ window.initgui = async function (options) {
|
||||
});
|
||||
}
|
||||
|
||||
// Close all tooltips
|
||||
$('.ui-tooltip').remove();
|
||||
|
||||
// rename items whose names were being edited
|
||||
if (!$(e.target).hasClass('item-name-editor')) {
|
||||
// blurring an Item Name Editor will automatically trigger renaming the item
|
||||
@@ -3134,13 +3093,6 @@ window.initgui = async function (options) {
|
||||
// Window Activation
|
||||
//--------------------------------------------------------
|
||||
$(document).on('mousedown', function (e) {
|
||||
// if taskbar or any parts of it is clicked, drop the event
|
||||
if (
|
||||
$(e.target).hasClass('taskbar') ||
|
||||
$(e.target).closest('.taskbar').length > 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// if close or minimize button clicked, drop the event
|
||||
if (
|
||||
document
|
||||
@@ -3233,8 +3185,7 @@ $(window).on('resize', function () {
|
||||
|
||||
const ratio = window.desktop_width / window.innerWidth;
|
||||
|
||||
window.desktop_height =
|
||||
window.innerHeight - window.toolbar_height - window.taskbar_height;
|
||||
window.desktop_height = window.innerHeight - window.toolbar_height;
|
||||
window.desktop_width = window.innerWidth;
|
||||
|
||||
// Re-center the login window
|
||||
|
||||
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user