@synopackageland/sdk 0.9.9 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +121 -0
  2. package/dist/i18n/bridge/de.json +6 -0
  3. package/dist/i18n/bridge/en.json +6 -0
  4. package/dist/i18n/bridge/es.json +6 -0
  5. package/dist/i18n/bridge/fr.json +6 -0
  6. package/dist/i18n/bridge/it.json +6 -0
  7. package/dist/i18n/bridge/ja.json +6 -0
  8. package/dist/i18n/bridge/ko.json +6 -0
  9. package/dist/i18n/bridge/zh-CN.json +6 -0
  10. package/dist/i18n/bridge/zh-TW.json +6 -0
  11. package/dist/i18n/shell/de.json +5 -1
  12. package/dist/i18n/shell/en.json +5 -1
  13. package/dist/i18n/shell/es.json +5 -1
  14. package/dist/i18n/shell/fr.json +5 -1
  15. package/dist/i18n/shell/it.json +5 -1
  16. package/dist/i18n/shell/ja.json +5 -1
  17. package/dist/i18n/shell/ko.json +5 -1
  18. package/dist/i18n/shell/zh-CN.json +5 -1
  19. package/dist/i18n/shell/zh-TW.json +5 -1
  20. package/dist/index.js +2492 -2338
  21. package/dist/src/bridge/i18n.d.ts +5 -0
  22. package/dist/src/context.d.ts +23 -2
  23. package/dist/src/delegate/root.d.ts +40 -0
  24. package/dist/src/index.d.ts +5 -2
  25. package/dist/src/protocol/v1.d.ts +49 -4
  26. package/dist/src/shell/AppShell.vue.d.ts +31 -10
  27. package/dist/src/shell/app-pages.d.ts +37 -0
  28. package/dist/src/shell/i18n.d.ts +4 -0
  29. package/dist/src/shell/pages.d.ts +7 -2
  30. package/dist/src/shell/slot-content.d.ts +2 -0
  31. package/dist/src/standalone.d.ts +5 -0
  32. package/dist/src/vue.d.ts +1 -0
  33. package/dist/style.css +1 -1
  34. package/dist/vue.js +1639 -1510
  35. package/package.json +4 -4
  36. package/spec/protocol/v1/README.md +154 -3
  37. package/spec/protocol/v1/errors.json +36 -4
  38. package/spec/protocol/v1/schema/root.jobs.get.json +19 -0
  39. package/spec/protocol/v1/schema/root.jobs.list.json +11 -0
  40. package/spec/protocol/v1/schema/root.jobs.stop.json +19 -0
  41. package/spec/protocol/v1/schema/root.run.json +6 -2
  42. package/spec/protocol/v1/schema/root.start.json +23 -0
  43. package/spec/protocol/v1/vectors/files.jsonl +11 -0
@@ -0,0 +1,5 @@
1
+ export declare const bridgeCatalogs: Record<string, Record<string, string>>;
2
+ /** The text of a code the bridge runtime adds (app_unavailable, and the
3
+ * services' limit_exceeded, read_failed, identity_unavailable) in a DSM or
4
+ * BCP-47 language; undefined for any other code. */
5
+ export declare function bridgeErrorCopy(e: unknown, locale: string): string | undefined;
@@ -90,8 +90,11 @@ export interface AuthorizationAccess {
90
90
  request(): Promise<AuthorizationStatus>;
91
91
  revoke(): Promise<AuthorizationStatus>;
92
92
  }
93
- export declare const DEFAULT_REQUEST_TIMEOUT = 20000;
93
+ export declare const DEFAULT_REQUEST_TIMEOUT = 60000;
94
94
  export declare function clientTimeout(requestTimeout?: number): number;
95
+ export interface CallOptions {
96
+ signal?: AbortSignal;
97
+ }
95
98
  export interface Context {
96
99
  requestTimeout?: number;
97
100
  authorization?: AuthorizationAccess;
@@ -101,17 +104,35 @@ export interface Context {
101
104
  token?: () => string | undefined;
102
105
  };
103
106
  displayName?: string;
107
+ appIcon?: string;
108
+ fullPage?: boolean;
104
109
  executor?: import('./delegate/executor-setup').ExecutorSetup;
105
110
  user?: {
106
111
  readonly name: string;
107
112
  readonly isAdmin: boolean;
108
113
  };
109
114
  portal?: PortalAccess;
110
- call<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T>;
115
+ call<T = unknown>(method: string, params?: Record<string, unknown>, options?: CallOptions): Promise<T>;
111
116
  reopen(fn: () => void): () => void;
112
117
  locale: string;
118
+ lang?: string;
119
+ t?: (key: string, params?: Record<string, unknown>) => string;
120
+ appWindow?: unknown;
113
121
  host: HostAdapter;
114
122
  }
123
+ /** What tells a full page apart: the launch, or the host when an older CLI omits it. */
124
+ export type LaunchContext = {
125
+ fullPage: boolean;
126
+ host?: Pick<HostAdapter, 'platform'>;
127
+ } | {
128
+ fullPage?: boolean;
129
+ host: Pick<HostAdapter, 'platform'>;
130
+ };
131
+ /** What AppShell reads: the launch, the brand, the signed-in account and, with app-root, the window to bring to the front. */
132
+ export type ShellContext = LaunchContext & Pick<Context, 'displayName' | 'appIcon' | 'user'> & {
133
+ host?: Partial<Pick<HostAdapter, 'activateWindow'>>;
134
+ };
135
+ export declare function isFullPage(ctx: LaunchContext): boolean;
115
136
  export interface AppStatus {
116
137
  package: string;
117
138
  version: string;
@@ -0,0 +1,40 @@
1
+ import type { RootJobsGetParams } from '../protocol/v1';
2
+ export type RootRunResult = {
3
+ request_id: string;
4
+ exit_code: number;
5
+ stderr: string;
6
+ } & ({
7
+ output: string;
8
+ } | {
9
+ staging: string;
10
+ size: number;
11
+ });
12
+ export type RootJobState = 'running' | 'stopping' | 'succeeded' | 'failed' | 'canceled' | 'timed-out' | 'interrupted';
13
+ export type RootJobRef = RootJobsGetParams;
14
+ export interface RootJobProgress {
15
+ percent?: number;
16
+ message?: string;
17
+ phase?: string;
18
+ }
19
+ export interface RootJob {
20
+ job_id: string;
21
+ name: string;
22
+ state: RootJobState;
23
+ requested_by: string;
24
+ started_at: string;
25
+ ended_at?: string;
26
+ exit_code?: number;
27
+ cancel_reason?: string;
28
+ progress?: RootJobProgress;
29
+ result?: unknown;
30
+ output: string;
31
+ }
32
+ export type RootStartResult = {
33
+ job_id: string;
34
+ state: 'running';
35
+ };
36
+ export type RootJobSummary = Omit<RootJob, 'progress' | 'result' | 'output'> & {
37
+ progress?: {
38
+ percent?: number;
39
+ };
40
+ };
@@ -1,7 +1,9 @@
1
1
  export type * from './context';
2
- export { DEFAULT_REQUEST_TIMEOUT, clientTimeout } from './context';
2
+ export { DEFAULT_REQUEST_TIMEOUT, clientTimeout, isFullPage } from './context';
3
+ export { createStandaloneHost, createStandaloneContext } from './standalone';
3
4
  export type * from './delegate/long-running';
4
5
  export type * from './delegate/executor-setup';
6
+ export type * from './delegate/root';
5
7
  export type * from './nas/contract';
6
8
  export type { AppSaveJob } from './delegate/facade';
7
9
  export type { FileTransport } from './files/transfers';
@@ -12,7 +14,7 @@ export { FILE_STATION_APP, fileStationPath, openFolderInFileStation, openFileInF
12
14
  export { createBackgroundPickerProvider } from './background/picker-provider';
13
15
  export { scopedPicker } from './delegate/scoped-picker';
14
16
  export { requestKey } from './background/request-key';
15
- export { shellPages, activePage, type ShellPage, type ShellFeatureFlags } from './shell/pages';
17
+ export { shellPages, activePage, type ShellPage, type ShellFeatureFlags, type PageRef } from './shell/pages';
16
18
  export { uiLocale, uiLocales, catalogs, mergeCatalogs, format, labelSeparator, type Message, type UiLocale } from './i18n';
17
19
  export { nasCopy, nasErrorCopy, nasCatalogs, type NasCopy } from './nas/locale';
18
20
  export { shellCopy, shellCatalogs, type ShellCopy } from './shell/i18n';
@@ -22,6 +24,7 @@ export { delegateCopy, errorCopy, delegateCatalogs, type DelegateCopy } from './
22
24
  export { saveCopy, saveError, saveCatalogs } from './delegate/save-copy';
23
25
  export { authorizationCopy, authorizationCatalogs } from './authorization/i18n';
24
26
  export { portalMessages, portalCatalogs } from './portal/i18n';
27
+ export { bridgeErrorCopy, bridgeCatalogs } from './bridge/i18n';
25
28
  export { createPortalAccess, type PortalOptions } from './portal/access';
26
29
  export * from './protocol/v1';
27
30
  export { encodeRequest, decodeResponse, type ProtocolAuth, type ProtocolResponse } from './protocol/codec';
@@ -108,12 +108,36 @@ export declare const protocolCodes: {
108
108
  readonly retryable: true;
109
109
  readonly httpStatus: 502;
110
110
  };
111
- /** The root operation exited with a non-zero status or failed to run. */
111
+ /** The root program could not run, ended by a signal, or its output could not be kept. */
112
112
  readonly ROOT_FAILED: {
113
113
  readonly kind: "refusal";
114
114
  readonly retryable: false;
115
115
  readonly httpStatus: 500;
116
116
  };
117
+ /** The root program ran past its policy timeout and was killed. */
118
+ readonly ROOT_TIMEOUT: {
119
+ readonly kind: "refusal";
120
+ readonly retryable: true;
121
+ readonly httpStatus: 504;
122
+ };
123
+ /** The package already runs as many root programs as it may. */
124
+ readonly ROOT_BUSY: {
125
+ readonly kind: "refusal";
126
+ readonly retryable: true;
127
+ readonly httpStatus: 429;
128
+ };
129
+ /** Root output waiting in the staging folder already fills its quota. */
130
+ readonly ROOT_STAGING_FULL: {
131
+ readonly kind: "refusal";
132
+ readonly retryable: true;
133
+ readonly httpStatus: 507;
134
+ };
135
+ /** No root job of the package has this job_id. */
136
+ readonly ROOT_JOB_NOT_FOUND: {
137
+ readonly kind: "refusal";
138
+ readonly retryable: false;
139
+ readonly httpStatus: 404;
140
+ };
117
141
  /** DSM did not accept the Application Portal change; status portal says whether the entry needs another removal. */
118
142
  readonly PORTAL_FAILED: {
119
143
  readonly kind: "refusal";
@@ -246,7 +270,7 @@ export declare const protocolCodes: {
246
270
  readonly retryable: true;
247
271
  readonly httpStatus: 429;
248
272
  };
249
- /** The file is larger than max_bytes. */
273
+ /** The file is larger than max_bytes, or root output is larger than its policy max_output. */
250
274
  readonly "limit-exceeded": {
251
275
  readonly kind: "refusal";
252
276
  readonly retryable: false;
@@ -344,7 +368,7 @@ export declare const protocolCodes: {
344
368
  };
345
369
  };
346
370
  export type ProtocolCode = keyof typeof protocolCodes;
347
- export type RefusalCode = "AUTH_DENIED" | "INELIGIBLE" | "APP_DENIED" | "ADMIN_REQUIRED" | "DELEGATION_DENIED" | "SCOPE_DENIED" | "POLICY_DENIED" | "PEER_DENIED" | "PERMISSION_DENIED" | "UNKNOWN_OP" | "INVALID_REQUEST" | "REQUEST_TOO_LARGE" | "EXECUTOR_PAUSED" | "STORAGE_ERROR" | "INTERNAL" | "WRITE_FAILED" | "READ_FAILED" | "DSM_FAILED" | "ROOT_FAILED" | "PORTAL_FAILED" | "NO_SPACE" | "SCOPE_APPROVAL_STALE" | "IDENTITY_CHANGED" | "needs-scope-confirmation" | "disabled" | "invalid-destination" | "invalid-size" | "operation-conflict" | "operation-not-found" | "destination-exists" | "invalid-path" | "path-too-deep" | "destination-not-directory" | "name-too-long" | "not-found" | "not-a-file" | "recursive-required" | "recycle-bin-disabled" | "task-not-found" | "too-many-tasks" | "queue-full" | "limit-exceeded";
371
+ export type RefusalCode = "AUTH_DENIED" | "INELIGIBLE" | "APP_DENIED" | "ADMIN_REQUIRED" | "DELEGATION_DENIED" | "SCOPE_DENIED" | "POLICY_DENIED" | "PEER_DENIED" | "PERMISSION_DENIED" | "UNKNOWN_OP" | "INVALID_REQUEST" | "REQUEST_TOO_LARGE" | "EXECUTOR_PAUSED" | "STORAGE_ERROR" | "INTERNAL" | "WRITE_FAILED" | "READ_FAILED" | "DSM_FAILED" | "ROOT_FAILED" | "ROOT_TIMEOUT" | "ROOT_BUSY" | "ROOT_STAGING_FULL" | "ROOT_JOB_NOT_FOUND" | "PORTAL_FAILED" | "NO_SPACE" | "SCOPE_APPROVAL_STALE" | "IDENTITY_CHANGED" | "needs-scope-confirmation" | "disabled" | "invalid-destination" | "invalid-size" | "operation-conflict" | "operation-not-found" | "destination-exists" | "invalid-path" | "path-too-deep" | "destination-not-directory" | "name-too-long" | "not-found" | "not-a-file" | "recursive-required" | "recycle-bin-disabled" | "task-not-found" | "too-many-tasks" | "queue-full" | "limit-exceeded";
348
372
  export type OutcomeCode = "OPERATION_CANCELED" | "CANCEL_REQUESTED" | "CLEANUP_PENDING";
349
373
  export type ReasonCode = "grant-revoked" | "destination-replaced" | "integration-disabled" | "validation-denied" | "scope-changed" | "storage-error" | "operation-canceled-by-user" | "operation-deadline-exceeded";
350
374
  export type ClientCode = "EXECUTOR_UNAVAILABLE" | "EXECUTOR_TIMEOUT" | "EXECUTOR_TOO_LARGE" | "EXECUTOR_PROTOCOL";
@@ -485,9 +509,26 @@ export interface PortalRegisterParams {
485
509
  /** portal.unregister: Administrator: remove the App's Application Portal entry, its saved alias and every nginx conf DSM generated for it. Since 0.9.6. */
486
510
  export interface PortalUnregisterParams {
487
511
  }
488
- /** root.run: Administrator: run a root operation pinned by digest in the policy. Since 0.9.0. */
512
+ /** root.jobs.get: Administrator whom Application Privileges allow to use the App: one root job with its state, latest progress and result. Since 0.10.0. */
513
+ export interface RootJobsGetParams {
514
+ job_id: string;
515
+ }
516
+ /** root.jobs.list: Administrator whom Application Privileges allow to use the App: list the package's root jobs, newest first. Since 0.10.0. */
517
+ export interface RootJobsListParams {
518
+ }
519
+ /** root.jobs.stop: Administrator whom Application Privileges allow to use the App: ask a running root job to cancel; it is killed after its grace. Since 0.10.0. */
520
+ export interface RootJobsStopParams {
521
+ job_id: string;
522
+ }
523
+ /** root.run: Administrator whom Application Privileges allow to use the App: run a root program pinned by digest in the policy. Params reach the program on stdin as JSON, checked against the entry's schema; the reply carries its exit code, stderr and output, or a staging file for large output. Since 0.9.0. */
489
524
  export interface RootRunParams {
490
525
  name: string;
526
+ params?: Record<string, unknown>;
527
+ }
528
+ /** root.start: Administrator whom Application Privileges allow to use the App: start a root job, a pinned program with job limits, in the background. Params reach it on stdin; it reports progress on fd 5 and is told to cancel on fd 4. Since 0.10.0. */
529
+ export interface RootStartParams {
530
+ name: string;
531
+ params?: Record<string, unknown>;
491
532
  }
492
533
  /** status: Executor readiness, protocol version, digests and the folder scope selection. Since 0.9.0. */
493
534
  export interface StatusParams {
@@ -517,7 +558,11 @@ export interface ProtocolParams {
517
558
  "files.write": FilesWriteParams;
518
559
  "portal.register": PortalRegisterParams;
519
560
  "portal.unregister": PortalUnregisterParams;
561
+ "root.jobs.get": RootJobsGetParams;
562
+ "root.jobs.list": RootJobsListParams;
563
+ "root.jobs.stop": RootJobsStopParams;
520
564
  "root.run": RootRunParams;
565
+ "root.start": RootStartParams;
521
566
  "status": StatusParams;
522
567
  }
523
568
  export type ProtocolOp = keyof ProtocolParams;
@@ -1,20 +1,41 @@
1
- import type { ShellPage } from './pages';
1
+ import { type WideLayout } from '@synopackageland/ui';
2
+ import { type ShellContext } from '../context';
3
+ import { type AppShellDenied, type AppShellPageEntry } from './app-pages';
2
4
  type __VLS_Props = {
3
5
  locale: string;
4
- modelValue: ShellPage;
5
- pages: readonly ShellPage[];
6
+ modelValue: string;
7
+ pages: readonly AppShellPageEntry[];
6
8
  description?: string;
9
+ ctx?: ShellContext;
10
+ wideLayout?: WideLayout;
11
+ sidebarStorageKey?: string;
12
+ brandName?: string;
13
+ brandIcon?: string;
14
+ keepScroll?: boolean;
15
+ pageWidth?: 'fill' | 'readable' | (string & {});
16
+ denied?: boolean | AppShellDenied;
17
+ appRoot?: boolean;
7
18
  };
8
- declare var __VLS_29: {}, __VLS_31: {};
9
- type __VLS_Slots = {} & {
10
- actions?: (props: typeof __VLS_29) => any;
11
- } & {
12
- default?: (props: typeof __VLS_31) => any;
19
+ type __VLS_Slots = {
20
+ default?: (scope: {
21
+ page: string;
22
+ }) => unknown;
23
+ actions?: (scope: {
24
+ page: string;
25
+ }) => unknown;
26
+ toolbar?: (scope: {
27
+ page: string;
28
+ }) => unknown;
29
+ notices?: (scope: {
30
+ page: string;
31
+ }) => unknown;
32
+ 'sidebar-footer'?: () => unknown;
33
+ denied?: () => unknown;
13
34
  };
14
35
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
15
- "update:modelValue": (value: ShellPage) => any;
36
+ "update:modelValue": (value: string) => any;
16
37
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
17
- "onUpdate:modelValue"?: ((value: ShellPage) => any) | undefined;
38
+ "onUpdate:modelValue"?: ((value: string) => any) | undefined;
18
39
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
40
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
20
41
  declare const _default: typeof __VLS_export;
@@ -0,0 +1,37 @@
1
+ import type { Component } from 'vue';
2
+ import type { AppSidebarGroup } from '@synopackageland/ui';
3
+ import type { ShellCopy } from './i18n';
4
+ import type { ShellPage } from './pages';
5
+ export interface AppShellPage {
6
+ id: string;
7
+ /** Sidebar label and page title, already in the App's language. */
8
+ title: string;
9
+ icon?: Component;
10
+ /** Pages that share a label sit together under it, in first-seen order. */
11
+ group?: string;
12
+ badge?: string | number;
13
+ /** Rendered for this page; without one the default slot renders it. */
14
+ component?: Component;
15
+ /** Reachable through v-model but not listed in the sidebar. */
16
+ hidden?: boolean;
17
+ /** Replaces the shell's description for this page. */
18
+ description?: string;
19
+ /** false: the page draws its own header. */
20
+ header?: boolean;
21
+ /**
22
+ * The body fills the height under the header and the page does not scroll:
23
+ * the content scrolls itself (a treemap, a table paged by height, a log).
24
+ */
25
+ fill?: boolean;
26
+ }
27
+ export type AppShellPageEntry = ShellPage | AppShellPage;
28
+ /** The page a denied shell shows; each field falls back to the shell's own words. */
29
+ export interface AppShellDenied {
30
+ title?: string;
31
+ /** The default body; the denied slot replaces it. */
32
+ message?: string;
33
+ /** false: the denied slot draws its own header. */
34
+ header?: boolean;
35
+ }
36
+ export declare function resolvePages(entries: readonly AppShellPageEntry[], copy: ShellCopy): AppShellPage[];
37
+ export declare function sidebarGroups(pages: readonly AppShellPage[]): AppSidebarGroup[];
@@ -3,5 +3,9 @@ export type ShellCopy = typeof en;
3
3
  export declare const shellCatalogs: Record<import("..").UiLocale, {
4
4
  status: string;
5
5
  background: string;
6
+ administrator: string;
7
+ dsmUser: string;
8
+ deniedTitle: string;
9
+ deniedMessage: string;
6
10
  }>;
7
11
  export declare function shellCopy(locale: string): ShellCopy;
@@ -3,5 +3,10 @@ export interface ShellFeatureFlags {
3
3
  background?: boolean;
4
4
  }
5
5
  export declare function shellPages(features: ShellFeatureFlags): ShellPage[];
6
- /** Keeps a selection that still exists; otherwise the first page wins. */
7
- export declare function activePage(current: ShellPage | undefined, pages: readonly ShellPage[]): ShellPage;
6
+ /** A page id, or an App's page with an id that may be left out of the sidebar. */
7
+ export type PageRef<T extends string = string> = T | {
8
+ readonly id: T;
9
+ readonly hidden?: boolean;
10
+ };
11
+ /** Keeps a selection that still exists; otherwise the first page the sidebar shows wins. */
12
+ export declare function activePage<T extends string>(current: string | undefined, pages: readonly PageRef<T>[]): T;
@@ -0,0 +1,2 @@
1
+ /** Whether a slot's vnodes show anything: not only v-if comments and whitespace, also inside fragments. */
2
+ export declare function hasContent(nodes: unknown): boolean;
@@ -0,0 +1,5 @@
1
+ import type { Context, HostAdapter, HostPlatformSnapshot } from './context';
2
+ /** A browser page's host; platform fields override the snapshot, say to stand in for a DSM window in a test. */
3
+ export declare function createStandaloneHost(platform?: Partial<HostPlatformSnapshot>): HostAdapter;
4
+ /** A full-page Context around the App's own call: English, a standalone host and no reopen events unless given. */
5
+ export declare function createStandaloneContext(fields: Pick<Context, 'call'> & Partial<Omit<Context, 'call'>>): Context;
package/dist/src/vue.d.ts CHANGED
@@ -3,6 +3,7 @@ export { ExecutorSetupKey, useExecutorSetup } from './delegate/use-executor-setu
3
3
  export { createSession, type IdentitySession, type SessionSubject } from './identity/session';
4
4
  export { useNasRequest } from './nas/use-request';
5
5
  export { default as AppShell } from './shell/AppShell.vue';
6
+ export type { AppShellDenied, AppShellPage, AppShellPageEntry } from './shell/app-pages';
6
7
  export { default as FilesPage } from './files/FilesPage.vue';
7
8
  export { default as FileBrowser } from './files/FileBrowser.vue';
8
9
  export { default as NasPicker } from './nas/NasPicker.vue';
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- .app-shell[data-v-05f63ca5]{height:100%;min-width:0;grid-template-rows:minmax(0,1fr)}.app-shell[data-v-05f63ca5]>div{min-height:0}.app-shell[data-v-05f63ca5] main{display:flex;flex-direction:column;overflow:hidden}.app-page[data-v-05f63ca5]{flex:1 1 auto;min-height:0;overflow-y:auto}.app-page-content[data-v-05f63ca5]{padding-bottom:var(--ui-space-6, 24px)}.file-browser[data-v-c689761c]{min-width:0;color:var(--ui-text)}.file-toolbar[data-v-c689761c]{display:flex;flex-wrap:wrap;gap:var(--ui-space-2, 8px);margin-block:var(--ui-space-3, 12px)}.file-location[data-v-c689761c]{flex-wrap:nowrap;align-items:center;gap:var(--ui-space-1, 4px)}.file-path[data-v-c689761c]{flex:1;min-width:0;margin-inline-start:var(--ui-space-2, 8px)}.file-columns[data-v-c689761c]{display:grid;grid-template-columns:minmax(140px,1fr) minmax(0,3fr);gap:var(--ui-space-3, 12px);height:clamp(240px,60vh,400px)}.file-tree[data-v-c689761c]{min-width:0;overflow:auto;border-inline-end:1px solid var(--ui-border)}.file-content[data-v-c689761c]{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:auto}.file-empty[data-v-c689761c]{display:flex;flex-direction:column;align-items:center;gap:var(--ui-space-2, 8px);padding-block:var(--ui-space-6, 24px);color:var(--ui-text-muted)}.file-name[data-v-c689761c]{display:inline-flex;align-items:center;gap:var(--ui-space-2, 8px);overflow-wrap:anywhere}.file-bytes[data-v-c689761c]{font-variant-numeric:tabular-nums}.file-selection[data-v-c689761c]{overflow-wrap:anywhere}@media(max-width:600px){.file-columns[data-v-c689761c]{grid-template-columns:minmax(0,1fr);height:auto}.file-content[data-v-c689761c] th:nth-child(2),.file-content[data-v-c689761c] td:nth-child(2),.file-content[data-v-c689761c] th:nth-child(3),.file-content[data-v-c689761c] td:nth-child(3){display:none}.file-tree[data-v-c689761c]{height:clamp(96px,25vh,180px);border-inline-end:0;border-bottom:1px solid var(--ui-border)}.file-content[data-v-c689761c]{height:clamp(200px,45vh,360px)}}.nas-picker-content[data-v-6ed8b8f6]{max-height:65vh;overflow:auto}.save-message[data-v-6ed8b8f6]{--ui-text-secondary:var(--ui-text)}.save-consent[data-v-6ed8b8f6]{color:var(--ui-text);margin-block:0 var(--ui-space-4,16px)}.files-actions[data-v-15f9eb8e]{display:flex;flex-wrap:wrap;gap:var(--ui-space-2, 8px)}.files-path[data-v-15f9eb8e]{display:flex;min-width:0}p[data-v-d803ac7c]{margin:0 0 var(--ui-space-3,12px);max-width:72ch}.portal-form[data-v-d803ac7c]{max-width:28rem}.identity-login[data-v-1c0358cd]{max-width:36rem;margin-inline:auto;padding:var(--ui-space-4, 16px);overflow:auto;max-height:100%;color:var(--ui-text);font-family:var(--ui-font-sans);font-size:13px;line-height:1.5}.identity-login p[data-v-1c0358cd]{margin-block:var(--ui-space-4, 16px);overflow-wrap:anywhere}.bg-cards[data-v-fa2b6101]{display:grid;gap:var(--ui-space-3,12px);min-width:0;overflow-wrap:anywhere}.bg-card-body[data-v-fa2b6101]{display:flex;flex-direction:column;align-items:flex-start;gap:var(--ui-space-2,8px);min-width:0}.bg-card-body>span[data-v-fa2b6101]{min-width:0;max-width:100%}.integration-actions[data-v-fa2b6101]{display:flex;flex-wrap:wrap;gap:var(--ui-space-2,8px)}.bg-schedules[data-v-fa2b6101]{margin-block-start:var(--ui-space-6,24px);min-width:0;overflow-wrap:anywhere}.bg-schedules p[data-v-fa2b6101]{min-width:0;max-width:100%}.bg-schedules h3[data-v-fa2b6101]{font-size:15px;font-weight:600}.bg-schedule-form[data-v-fa2b6101]{margin-block:var(--ui-space-4,16px)}.bg-path[data-v-fa2b6101]{display:flex;align-items:center;gap:var(--ui-space-2,8px);min-width:0;max-width:100%}.executor-help[data-v-868f9f3a]{margin:0 0 var(--ui-space-3,12px);max-width:72ch;color:var(--ui-text-secondary)}.executor-summary[data-v-868f9f3a]{margin-bottom:var(--ui-space-3,12px)}.executor-form[data-v-868f9f3a]{margin-top:var(--ui-space-4,16px);max-width:28rem}.executor-hint[data-v-868f9f3a]{font-size:12px;color:var(--ui-text-secondary)}.executor-alert[data-v-868f9f3a]{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--ui-space-2,8px)}.delegate-panel[data-v-c590f505]{min-width:0;overflow-wrap:anywhere}h3[data-v-c590f505]{font-size:var(--ui-font-size-base,14px);font-weight:600;margin:var(--ui-space-6,24px) 0 var(--ui-space-3,12px)}.records[data-v-c590f505]{list-style:none;padding:0;margin:0}.records li[data-v-c590f505]{display:flex;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;gap:var(--ui-space-3,12px);padding-block:var(--ui-space-4,16px);border-bottom:1px solid var(--ui-border)}.records li>div[data-v-c590f505],.records li>span[data-v-c590f505],.destination[data-v-c590f505]{min-width:0;max-width:100%;overflow-wrap:anywhere}.records p[data-v-c590f505]{margin:var(--ui-space-2,8px) 0 0}.destination[data-v-c590f505]{display:flex;align-items:center;gap:var(--ui-space-2,8px)}.background-save-panel[data-v-5ef5e256]{min-width:0;overflow-wrap:anywhere}.save-message[data-v-5ef5e256]{--ui-text-secondary:var(--ui-text)}.save-help[data-v-5ef5e256]{margin:0 0 var(--ui-space-3,12px);max-width:72ch;color:var(--ui-text-secondary)}.save-summary[data-v-5ef5e256]{margin-bottom:var(--ui-space-3,12px)}h3[data-v-5ef5e256]{font-size:var(--ui-font-size-base,14px);font-weight:600;margin:var(--ui-space-6,24px) 0 var(--ui-space-3,12px)}.save-actions[data-v-5ef5e256],.save-records li[data-v-5ef5e256],.save-danger[data-v-5ef5e256]{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--ui-space-2,8px)}.save-actions[data-v-5ef5e256]{justify-content:flex-start;margin-top:var(--ui-space-3,12px)}.save-records[data-v-5ef5e256]{list-style:none;margin:0;padding:0}.save-records li[data-v-5ef5e256]{padding-block:var(--ui-space-2,8px);border-bottom:1px solid var(--ui-border)}.save-record[data-v-5ef5e256]{display:flex;flex-direction:column;align-items:flex-start;min-width:0;flex:1}.save-name[data-v-5ef5e256]{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.save-note[data-v-5ef5e256]{margin:var(--ui-space-1,4px) 0 0;font-size:12px;color:var(--ui-text-secondary);white-space:normal}.save-alert[data-v-5ef5e256]{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--ui-space-2,8px)}.save-icon[data-v-5ef5e256]{width:16px;height:16px}[data-save-operations][data-v-5ef5e256] table{table-layout:fixed}[data-save-operations][data-v-5ef5e256] th,[data-save-operations][data-v-5ef5e256] td{overflow-wrap:normal;white-space:nowrap}[data-save-operations][data-v-5ef5e256] td{padding-block:var(--ui-space-2,8px)}.save-example[data-v-5ef5e256]{margin-top:var(--ui-space-5,20px)}.save-pages[data-v-5ef5e256]{margin-top:var(--ui-space-3,12px)}.save-danger[data-v-5ef5e256]{flex:1;margin-top:var(--ui-space-2,8px);padding-top:var(--ui-space-4,16px);border-top:1px solid var(--ui-border)}.save-danger p[data-v-5ef5e256]{margin:0;flex:1 1 20rem;max-width:72ch;color:var(--ui-text-secondary)}.authorization-view[data-v-e16b5f7d]{display:flex;flex-direction:column;gap:12px}.authorization-body[data-v-e16b5f7d]{margin:0;font-size:13px;line-height:1.6;color:var(--ui-text);text-wrap:pretty}.authorization-status[data-v-e16b5f7d],.authorization-failed[data-v-e16b5f7d]{color:var(--ui-text)}.authorization-content[data-v-e16b5f7d]{display:flex;flex-direction:column;height:100%;min-height:0}.authorization-content[data-v-e16b5f7d] .app-shell{flex:1;min-height:0;height:auto}.authorization-setting[data-v-f031e7c7]{display:flex;align-items:center;flex-wrap:wrap;gap:12px}.authorization-revoke-body[data-v-f031e7c7]{margin:0;font-size:13px;line-height:1.6;color:var(--ui-text)}.authorization-state[data-v-f031e7c7]{display:inline-flex;align-items:center;gap:6px;color:var(--ui-text)}
1
+ .app-shell[data-v-60e630d5]{height:100%;min-width:0;grid-template-rows:minmax(0,1fr)}.app-shell[data-v-60e630d5]>div{min-height:0}.app-shell[data-v-60e630d5] main{display:flex;flex-direction:column;overflow:hidden}.app-page[data-v-60e630d5]{flex:1 1 auto;min-height:0;overflow-y:auto}.app-denied[data-v-60e630d5]{height:100%;overflow-y:auto}.app-shell-fill[data-v-60e630d5]{max-height:100dvh}.app-page-fill[data-v-60e630d5]{display:flex;flex-direction:column;overflow:hidden}.app-page-fill[data-v-60e630d5]>[data-page-content-body]{display:flex;flex:1 1 auto;flex-direction:column;width:100%;min-height:0}.app-page-fill .app-notices[data-v-60e630d5]{flex:0 0 auto}.app-page-fill .app-page-content[data-v-60e630d5]{display:grid;flex:1 1 auto;grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr);min-height:0}.app-notices[data-v-60e630d5]{display:flex;flex-direction:column;gap:var(--ui-space-2, 8px);margin-bottom:var(--ui-space-4, 16px)}.app-page-content[data-v-60e630d5]{padding-bottom:var(--ui-space-6, 24px)}.file-browser[data-v-c689761c]{min-width:0;color:var(--ui-text)}.file-toolbar[data-v-c689761c]{display:flex;flex-wrap:wrap;gap:var(--ui-space-2, 8px);margin-block:var(--ui-space-3, 12px)}.file-location[data-v-c689761c]{flex-wrap:nowrap;align-items:center;gap:var(--ui-space-1, 4px)}.file-path[data-v-c689761c]{flex:1;min-width:0;margin-inline-start:var(--ui-space-2, 8px)}.file-columns[data-v-c689761c]{display:grid;grid-template-columns:minmax(140px,1fr) minmax(0,3fr);gap:var(--ui-space-3, 12px);height:clamp(240px,60vh,400px)}.file-tree[data-v-c689761c]{min-width:0;overflow:auto;border-inline-end:1px solid var(--ui-border)}.file-content[data-v-c689761c]{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:auto}.file-empty[data-v-c689761c]{display:flex;flex-direction:column;align-items:center;gap:var(--ui-space-2, 8px);padding-block:var(--ui-space-6, 24px);color:var(--ui-text-muted)}.file-name[data-v-c689761c]{display:inline-flex;align-items:center;gap:var(--ui-space-2, 8px);overflow-wrap:anywhere}.file-bytes[data-v-c689761c]{font-variant-numeric:tabular-nums}.file-selection[data-v-c689761c]{overflow-wrap:anywhere}@media(max-width:600px){.file-columns[data-v-c689761c]{grid-template-columns:minmax(0,1fr);height:auto}.file-content[data-v-c689761c] th:nth-child(2),.file-content[data-v-c689761c] td:nth-child(2),.file-content[data-v-c689761c] th:nth-child(3),.file-content[data-v-c689761c] td:nth-child(3){display:none}.file-tree[data-v-c689761c]{height:clamp(96px,25vh,180px);border-inline-end:0;border-bottom:1px solid var(--ui-border)}.file-content[data-v-c689761c]{height:clamp(200px,45vh,360px)}}.nas-picker-content[data-v-6ed8b8f6]{max-height:65vh;overflow:auto}.save-message[data-v-6ed8b8f6]{--ui-text-secondary:var(--ui-text)}.save-consent[data-v-6ed8b8f6]{color:var(--ui-text);margin-block:0 var(--ui-space-4,16px)}.files-actions[data-v-15f9eb8e]{display:flex;flex-wrap:wrap;gap:var(--ui-space-2, 8px)}.files-path[data-v-15f9eb8e]{display:flex;min-width:0}.identity-login[data-v-1c0358cd]{max-width:36rem;margin-inline:auto;padding:var(--ui-space-4, 16px);overflow:auto;max-height:100%;color:var(--ui-text);font-family:var(--ui-font-sans);font-size:13px;line-height:1.5}.identity-login p[data-v-1c0358cd]{margin-block:var(--ui-space-4, 16px);overflow-wrap:anywhere}.bg-cards[data-v-fa2b6101]{display:grid;gap:var(--ui-space-3,12px);min-width:0;overflow-wrap:anywhere}.bg-card-body[data-v-fa2b6101]{display:flex;flex-direction:column;align-items:flex-start;gap:var(--ui-space-2,8px);min-width:0}.bg-card-body>span[data-v-fa2b6101]{min-width:0;max-width:100%}.integration-actions[data-v-fa2b6101]{display:flex;flex-wrap:wrap;gap:var(--ui-space-2,8px)}.bg-schedules[data-v-fa2b6101]{margin-block-start:var(--ui-space-6,24px);min-width:0;overflow-wrap:anywhere}.bg-schedules p[data-v-fa2b6101]{min-width:0;max-width:100%}.bg-schedules h3[data-v-fa2b6101]{font-size:15px;font-weight:600}.bg-schedule-form[data-v-fa2b6101]{margin-block:var(--ui-space-4,16px)}.bg-path[data-v-fa2b6101]{display:flex;align-items:center;gap:var(--ui-space-2,8px);min-width:0;max-width:100%}.executor-help[data-v-868f9f3a]{margin:0 0 var(--ui-space-3,12px);max-width:72ch;color:var(--ui-text-secondary)}.executor-summary[data-v-868f9f3a]{margin-bottom:var(--ui-space-3,12px)}.executor-form[data-v-868f9f3a]{margin-top:var(--ui-space-4,16px);max-width:28rem}.executor-hint[data-v-868f9f3a]{font-size:12px;color:var(--ui-text-secondary)}.executor-alert[data-v-868f9f3a]{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--ui-space-2,8px)}.delegate-panel[data-v-c590f505]{min-width:0;overflow-wrap:anywhere}h3[data-v-c590f505]{font-size:var(--ui-font-size-base,14px);font-weight:600;margin:var(--ui-space-6,24px) 0 var(--ui-space-3,12px)}.records[data-v-c590f505]{list-style:none;padding:0;margin:0}.records li[data-v-c590f505]{display:flex;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;gap:var(--ui-space-3,12px);padding-block:var(--ui-space-4,16px);border-bottom:1px solid var(--ui-border)}.records li>div[data-v-c590f505],.records li>span[data-v-c590f505],.destination[data-v-c590f505]{min-width:0;max-width:100%;overflow-wrap:anywhere}.records p[data-v-c590f505]{margin:var(--ui-space-2,8px) 0 0}.destination[data-v-c590f505]{display:flex;align-items:center;gap:var(--ui-space-2,8px)}.background-save-panel[data-v-5ef5e256]{min-width:0;overflow-wrap:anywhere}.save-message[data-v-5ef5e256]{--ui-text-secondary:var(--ui-text)}.save-help[data-v-5ef5e256]{margin:0 0 var(--ui-space-3,12px);max-width:72ch;color:var(--ui-text-secondary)}.save-summary[data-v-5ef5e256]{margin-bottom:var(--ui-space-3,12px)}h3[data-v-5ef5e256]{font-size:var(--ui-font-size-base,14px);font-weight:600;margin:var(--ui-space-6,24px) 0 var(--ui-space-3,12px)}.save-actions[data-v-5ef5e256],.save-records li[data-v-5ef5e256],.save-danger[data-v-5ef5e256]{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--ui-space-2,8px)}.save-actions[data-v-5ef5e256]{justify-content:flex-start;margin-top:var(--ui-space-3,12px)}.save-records[data-v-5ef5e256]{list-style:none;margin:0;padding:0}.save-records li[data-v-5ef5e256]{padding-block:var(--ui-space-2,8px);border-bottom:1px solid var(--ui-border)}.save-record[data-v-5ef5e256]{display:flex;flex-direction:column;align-items:flex-start;min-width:0;flex:1}.save-name[data-v-5ef5e256]{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.save-note[data-v-5ef5e256]{margin:var(--ui-space-1,4px) 0 0;font-size:12px;color:var(--ui-text-secondary);white-space:normal}.save-alert[data-v-5ef5e256]{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--ui-space-2,8px)}.save-icon[data-v-5ef5e256]{width:16px;height:16px}[data-save-operations][data-v-5ef5e256] table{table-layout:fixed}[data-save-operations][data-v-5ef5e256] th,[data-save-operations][data-v-5ef5e256] td{overflow-wrap:normal;white-space:nowrap}[data-save-operations][data-v-5ef5e256] td{padding-block:var(--ui-space-2,8px)}.save-example[data-v-5ef5e256]{margin-top:var(--ui-space-5,20px)}.save-pages[data-v-5ef5e256]{margin-top:var(--ui-space-3,12px)}.save-danger[data-v-5ef5e256]{flex:1;margin-top:var(--ui-space-2,8px);padding-top:var(--ui-space-4,16px);border-top:1px solid var(--ui-border)}.save-danger p[data-v-5ef5e256]{margin:0;flex:1 1 20rem;max-width:72ch;color:var(--ui-text-secondary)}.authorization-view[data-v-e16b5f7d]{display:flex;flex-direction:column;gap:12px}.authorization-body[data-v-e16b5f7d]{margin:0;font-size:13px;line-height:1.6;color:var(--ui-text);text-wrap:pretty}.authorization-status[data-v-e16b5f7d],.authorization-failed[data-v-e16b5f7d]{color:var(--ui-text)}.authorization-content[data-v-e16b5f7d]{display:flex;flex-direction:column;height:100%;min-height:0}.authorization-content[data-v-e16b5f7d] .app-shell{flex:1;min-height:0;height:auto}.authorization-setting[data-v-f031e7c7]{display:flex;align-items:center;flex-wrap:wrap;gap:12px}.authorization-revoke-body[data-v-f031e7c7]{margin:0;font-size:13px;line-height:1.6;color:var(--ui-text)}.authorization-state[data-v-f031e7c7]{display:inline-flex;align-items:center;gap:6px;color:var(--ui-text)}