@entifix/next-shell 0.1.0-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.
- package/LICENSE +21 -0
- package/README.md +7 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/back-office/account-menu.d.ts +29 -0
- package/dist/lib/back-office/account-menu.d.ts.map +1 -0
- package/dist/lib/back-office/account-menu.js +70 -0
- package/dist/lib/back-office/account-menu.js.map +1 -0
- package/dist/lib/back-office/back-office-shell.d.ts +50 -0
- package/dist/lib/back-office/back-office-shell.d.ts.map +1 -0
- package/dist/lib/back-office/back-office-shell.js +128 -0
- package/dist/lib/back-office/back-office-shell.js.map +1 -0
- package/dist/lib/back-office/breadcrumbs.d.ts +16 -0
- package/dist/lib/back-office/breadcrumbs.d.ts.map +1 -0
- package/dist/lib/back-office/breadcrumbs.js +53 -0
- package/dist/lib/back-office/breadcrumbs.js.map +1 -0
- package/dist/lib/back-office/group-by-screen-type.d.ts +21 -0
- package/dist/lib/back-office/group-by-screen-type.d.ts.map +1 -0
- package/dist/lib/back-office/group-by-screen-type.js +39 -0
- package/dist/lib/back-office/group-by-screen-type.js.map +1 -0
- package/dist/lib/back-office/index.d.ts +6 -0
- package/dist/lib/back-office/index.d.ts.map +1 -0
- package/dist/lib/back-office/index.js +7 -0
- package/dist/lib/back-office/index.js.map +1 -0
- package/dist/lib/back-office/nav.d.ts +39 -0
- package/dist/lib/back-office/nav.d.ts.map +1 -0
- package/dist/lib/back-office/nav.js +3 -0
- package/dist/lib/back-office/nav.js.map +1 -0
- package/dist/lib/back-office/sidebar-nav.d.ts +26 -0
- package/dist/lib/back-office/sidebar-nav.d.ts.map +1 -0
- package/dist/lib/back-office/sidebar-nav.js +181 -0
- package/dist/lib/back-office/sidebar-nav.js.map +1 -0
- package/dist/lib/command-palette/command-palette-host.d.ts +41 -0
- package/dist/lib/command-palette/command-palette-host.d.ts.map +1 -0
- package/dist/lib/command-palette/command-palette-host.js +196 -0
- package/dist/lib/command-palette/command-palette-host.js.map +1 -0
- package/dist/lib/command-palette/index.d.ts +9 -0
- package/dist/lib/command-palette/index.d.ts.map +1 -0
- package/dist/lib/command-palette/index.js +10 -0
- package/dist/lib/command-palette/index.js.map +1 -0
- package/dist/lib/command-palette/palette-command.d.ts +32 -0
- package/dist/lib/command-palette/palette-command.d.ts.map +1 -0
- package/dist/lib/command-palette/palette-command.js +19 -0
- package/dist/lib/command-palette/palette-command.js.map +1 -0
- package/dist/lib/command-palette/use-command-recency.d.ts +28 -0
- package/dist/lib/command-palette/use-command-recency.d.ts.map +1 -0
- package/dist/lib/command-palette/use-command-recency.js +67 -0
- package/dist/lib/command-palette/use-command-recency.js.map +1 -0
- package/dist/lib/command-palette/use-command-source.d.ts +17 -0
- package/dist/lib/command-palette/use-command-source.d.ts.map +1 -0
- package/dist/lib/command-palette/use-command-source.js +70 -0
- package/dist/lib/command-palette/use-command-source.js.map +1 -0
- package/dist/lib/command-palette/use-nav-source.d.ts +21 -0
- package/dist/lib/command-palette/use-nav-source.d.ts.map +1 -0
- package/dist/lib/command-palette/use-nav-source.js +78 -0
- package/dist/lib/command-palette/use-nav-source.js.map +1 -0
- package/dist/lib/command-palette/use-records-source.d.ts +23 -0
- package/dist/lib/command-palette/use-records-source.d.ts.map +1 -0
- package/dist/lib/command-palette/use-records-source.js +199 -0
- package/dist/lib/command-palette/use-records-source.js.map +1 -0
- package/dist/lib/command-palette/use-tabs-source.d.ts +20 -0
- package/dist/lib/command-palette/use-tabs-source.d.ts.map +1 -0
- package/dist/lib/command-palette/use-tabs-source.js +52 -0
- package/dist/lib/command-palette/use-tabs-source.js.map +1 -0
- package/dist/lib/command-palette/use-use-cases-source.d.ts +22 -0
- package/dist/lib/command-palette/use-use-cases-source.d.ts.map +1 -0
- package/dist/lib/command-palette/use-use-cases-source.js +93 -0
- package/dist/lib/command-palette/use-use-cases-source.js.map +1 -0
- package/dist/lib/config/config-route.d.ts +51 -0
- package/dist/lib/config/config-route.d.ts.map +1 -0
- package/dist/lib/config/config-route.js +65 -0
- package/dist/lib/config/config-route.js.map +1 -0
- package/dist/lib/config/service-proxy-route.d.ts +28 -0
- package/dist/lib/config/service-proxy-route.d.ts.map +1 -0
- package/dist/lib/config/service-proxy-route.js +125 -0
- package/dist/lib/config/service-proxy-route.js.map +1 -0
- package/dist/lib/config/service-token.d.ts +5 -0
- package/dist/lib/config/service-token.d.ts.map +1 -0
- package/dist/lib/config/service-token.js +18 -0
- package/dist/lib/config/service-token.js.map +1 -0
- package/dist/lib/crud/entity-crud-form.d.ts +49 -0
- package/dist/lib/crud/entity-crud-form.d.ts.map +1 -0
- package/dist/lib/crud/entity-crud-form.js +133 -0
- package/dist/lib/crud/entity-crud-form.js.map +1 -0
- package/dist/lib/crud/hand-off-write.d.ts +35 -0
- package/dist/lib/crud/hand-off-write.d.ts.map +1 -0
- package/dist/lib/crud/hand-off-write.js +27 -0
- package/dist/lib/crud/hand-off-write.js.map +1 -0
- package/dist/lib/crud/index.d.ts +9 -0
- package/dist/lib/crud/index.d.ts.map +1 -0
- package/dist/lib/crud/index.js +10 -0
- package/dist/lib/crud/index.js.map +1 -0
- package/dist/lib/crud/make-entity-crud.d.ts +28 -0
- package/dist/lib/crud/make-entity-crud.d.ts.map +1 -0
- package/dist/lib/crud/make-entity-crud.js +318 -0
- package/dist/lib/crud/make-entity-crud.js.map +1 -0
- package/dist/lib/crud/make-entity-crud.types.d.ts +237 -0
- package/dist/lib/crud/make-entity-crud.types.d.ts.map +1 -0
- package/dist/lib/crud/make-entity-crud.types.js +6 -0
- package/dist/lib/crud/make-entity-crud.types.js.map +1 -0
- package/dist/lib/crud/pending-notice.d.ts +31 -0
- package/dist/lib/crud/pending-notice.d.ts.map +1 -0
- package/dist/lib/crud/pending-notice.js +72 -0
- package/dist/lib/crud/pending-notice.js.map +1 -0
- package/dist/lib/crud/slug.d.ts +13 -0
- package/dist/lib/crud/slug.d.ts.map +1 -0
- package/dist/lib/crud/slug.js +13 -0
- package/dist/lib/crud/slug.js.map +1 -0
- package/dist/lib/crud/use-entity-affordances.d.ts +20 -0
- package/dist/lib/crud/use-entity-affordances.d.ts.map +1 -0
- package/dist/lib/crud/use-entity-affordances.js +22 -0
- package/dist/lib/crud/use-entity-affordances.js.map +1 -0
- package/dist/lib/crud/use-entity-bulk.d.ts +43 -0
- package/dist/lib/crud/use-entity-bulk.d.ts.map +1 -0
- package/dist/lib/crud/use-entity-bulk.js +106 -0
- package/dist/lib/crud/use-entity-bulk.js.map +1 -0
- package/dist/lib/crud/use-entity-link-sources.d.ts +25 -0
- package/dist/lib/crud/use-entity-link-sources.d.ts.map +1 -0
- package/dist/lib/crud/use-entity-link-sources.js +37 -0
- package/dist/lib/crud/use-entity-link-sources.js.map +1 -0
- package/dist/lib/health/health-routes.d.ts +37 -0
- package/dist/lib/health/health-routes.d.ts.map +1 -0
- package/dist/lib/health/health-routes.js +90 -0
- package/dist/lib/health/health-routes.js.map +1 -0
- package/dist/lib/i18n/catalog/en.d.ts +4 -0
- package/dist/lib/i18n/catalog/en.d.ts.map +1 -0
- package/dist/lib/i18n/catalog/en.js +347 -0
- package/dist/lib/i18n/catalog/en.js.map +1 -0
- package/dist/lib/i18n/catalog/es.d.ts +397 -0
- package/dist/lib/i18n/catalog/es.d.ts.map +1 -0
- package/dist/lib/i18n/catalog/es.js +407 -0
- package/dist/lib/i18n/catalog/es.js.map +1 -0
- package/dist/lib/i18n/catalog/index.d.ts +671 -0
- package/dist/lib/i18n/catalog/index.d.ts.map +1 -0
- package/dist/lib/i18n/catalog/index.js +28 -0
- package/dist/lib/i18n/catalog/index.js.map +1 -0
- package/dist/lib/i18n/index.d.ts +2 -0
- package/dist/lib/i18n/index.d.ts.map +1 -0
- package/dist/lib/i18n/index.js +3 -0
- package/dist/lib/i18n/index.js.map +1 -0
- package/dist/lib/i18n/locale-link.d.ts +23 -0
- package/dist/lib/i18n/locale-link.d.ts.map +1 -0
- package/dist/lib/i18n/locale-link.js +43 -0
- package/dist/lib/i18n/locale-link.js.map +1 -0
- package/dist/lib/i18n/spec-catalog.d.ts +2 -0
- package/dist/lib/i18n/spec-catalog.d.ts.map +1 -0
- package/dist/lib/i18n/spec-catalog.js +84 -0
- package/dist/lib/i18n/spec-catalog.js.map +1 -0
- package/dist/lib/search/record-search-route.d.ts +44 -0
- package/dist/lib/search/record-search-route.d.ts.map +1 -0
- package/dist/lib/search/record-search-route.js +158 -0
- package/dist/lib/search/record-search-route.js.map +1 -0
- package/dist/lib/search/record-search-source.d.ts +59 -0
- package/dist/lib/search/record-search-source.d.ts.map +1 -0
- package/dist/lib/search/record-search-source.js +118 -0
- package/dist/lib/search/record-search-source.js.map +1 -0
- package/dist/lib/search/record-search.types.d.ts +85 -0
- package/dist/lib/search/record-search.types.d.ts.map +1 -0
- package/dist/lib/search/record-search.types.js +29 -0
- package/dist/lib/search/record-search.types.js.map +1 -0
- package/dist/lib/search/search-records.d.ts +25 -0
- package/dist/lib/search/search-records.d.ts.map +1 -0
- package/dist/lib/search/search-records.js +36 -0
- package/dist/lib/search/search-records.js.map +1 -0
- package/dist/lib/session/account-links.d.ts +46 -0
- package/dist/lib/session/account-links.d.ts.map +1 -0
- package/dist/lib/session/account-links.js +41 -0
- package/dist/lib/session/account-links.js.map +1 -0
- package/dist/lib/session/bearer.d.ts +25 -0
- package/dist/lib/session/bearer.d.ts.map +1 -0
- package/dist/lib/session/bearer.js +31 -0
- package/dist/lib/session/bearer.js.map +1 -0
- package/dist/lib/session/cookies.d.ts +14 -0
- package/dist/lib/session/cookies.d.ts.map +1 -0
- package/dist/lib/session/cookies.js +33 -0
- package/dist/lib/session/cookies.js.map +1 -0
- package/dist/lib/session/device.d.ts +31 -0
- package/dist/lib/session/device.d.ts.map +1 -0
- package/dist/lib/session/device.js +80 -0
- package/dist/lib/session/device.js.map +1 -0
- package/dist/lib/session/index.d.ts +3 -0
- package/dist/lib/session/index.d.ts.map +1 -0
- package/dist/lib/session/index.js +12 -0
- package/dist/lib/session/index.js.map +1 -0
- package/dist/lib/session/refresh-route.d.ts +19 -0
- package/dist/lib/session/refresh-route.d.ts.map +1 -0
- package/dist/lib/session/refresh-route.js +85 -0
- package/dist/lib/session/refresh-route.js.map +1 -0
- package/dist/lib/session/session-keepalive.d.ts +52 -0
- package/dist/lib/session/session-keepalive.d.ts.map +1 -0
- package/dist/lib/session/session-keepalive.js +72 -0
- package/dist/lib/session/session-keepalive.js.map +1 -0
- package/dist/lib/session/use-session-refresh.d.ts +43 -0
- package/dist/lib/session/use-session-refresh.d.ts.map +1 -0
- package/dist/lib/session/use-session-refresh.js +120 -0
- package/dist/lib/session/use-session-refresh.js.map +1 -0
- package/dist/lib/wizard/index.d.ts +4 -0
- package/dist/lib/wizard/index.d.ts.map +1 -0
- package/dist/lib/wizard/index.js +5 -0
- package/dist/lib/wizard/index.js.map +1 -0
- package/dist/lib/wizard/use-wizard-draft.d.ts +22 -0
- package/dist/lib/wizard/use-wizard-draft.d.ts.map +1 -0
- package/dist/lib/wizard/use-wizard-draft.js +39 -0
- package/dist/lib/wizard/use-wizard-draft.js.map +1 -0
- package/dist/lib/wizard/use-wizard-step-param.d.ts +60 -0
- package/dist/lib/wizard/use-wizard-step-param.d.ts.map +1 -0
- package/dist/lib/wizard/use-wizard-step-param.js +94 -0
- package/dist/lib/wizard/use-wizard-step-param.js.map +1 -0
- package/dist/lib/wizard/wizard-tab-kind.d.ts +31 -0
- package/dist/lib/wizard/wizard-tab-kind.d.ts.map +1 -0
- package/dist/lib/wizard/wizard-tab-kind.js +33 -0
- package/dist/lib/wizard/wizard-tab-kind.js.map +1 -0
- package/dist/lib/workspace/drafts-state.d.ts +88 -0
- package/dist/lib/workspace/drafts-state.d.ts.map +1 -0
- package/dist/lib/workspace/drafts-state.js +128 -0
- package/dist/lib/workspace/drafts-state.js.map +1 -0
- package/dist/lib/workspace/entity-nav.d.ts +26 -0
- package/dist/lib/workspace/entity-nav.d.ts.map +1 -0
- package/dist/lib/workspace/entity-nav.js +56 -0
- package/dist/lib/workspace/entity-nav.js.map +1 -0
- package/dist/lib/workspace/entity-tab-kind.d.ts +51 -0
- package/dist/lib/workspace/entity-tab-kind.d.ts.map +1 -0
- package/dist/lib/workspace/entity-tab-kind.js +47 -0
- package/dist/lib/workspace/entity-tab-kind.js.map +1 -0
- package/dist/lib/workspace/idb-state-storage.d.ts +9 -0
- package/dist/lib/workspace/idb-state-storage.d.ts.map +1 -0
- package/dist/lib/workspace/idb-state-storage.js +32 -0
- package/dist/lib/workspace/idb-state-storage.js.map +1 -0
- package/dist/lib/workspace/index.d.ts +13 -0
- package/dist/lib/workspace/index.d.ts.map +1 -0
- package/dist/lib/workspace/index.js +14 -0
- package/dist/lib/workspace/index.js.map +1 -0
- package/dist/lib/workspace/pending-state.d.ts +84 -0
- package/dist/lib/workspace/pending-state.d.ts.map +1 -0
- package/dist/lib/workspace/pending-state.js +155 -0
- package/dist/lib/workspace/pending-state.js.map +1 -0
- package/dist/lib/workspace/pending-transactions.d.ts +44 -0
- package/dist/lib/workspace/pending-transactions.d.ts.map +1 -0
- package/dist/lib/workspace/pending-transactions.js +114 -0
- package/dist/lib/workspace/pending-transactions.js.map +1 -0
- package/dist/lib/workspace/tab-kind.d.ts +48 -0
- package/dist/lib/workspace/tab-kind.d.ts.map +1 -0
- package/dist/lib/workspace/tab-kind.js +47 -0
- package/dist/lib/workspace/tab-kind.js.map +1 -0
- package/dist/lib/workspace/tab-state.d.ts +36 -0
- package/dist/lib/workspace/tab-state.d.ts.map +1 -0
- package/dist/lib/workspace/tab-state.js +81 -0
- package/dist/lib/workspace/tab-state.js.map +1 -0
- package/dist/lib/workspace/tabs-state.d.ts +46 -0
- package/dist/lib/workspace/tabs-state.d.ts.map +1 -0
- package/dist/lib/workspace/tabs-state.js +56 -0
- package/dist/lib/workspace/tabs-state.js.map +1 -0
- package/dist/lib/workspace/use-entity-draft.d.ts +25 -0
- package/dist/lib/workspace/use-entity-draft.d.ts.map +1 -0
- package/dist/lib/workspace/use-entity-draft.js +38 -0
- package/dist/lib/workspace/use-entity-draft.js.map +1 -0
- package/dist/lib/workspace/workspace-scope.d.ts +51 -0
- package/dist/lib/workspace/workspace-scope.d.ts.map +1 -0
- package/dist/lib/workspace/workspace-scope.js +42 -0
- package/dist/lib/workspace/workspace-scope.js.map +1 -0
- package/dist/lib/workspace/workspace-shell.d.ts +33 -0
- package/dist/lib/workspace/workspace-shell.d.ts.map +1 -0
- package/dist/lib/workspace/workspace-shell.js +206 -0
- package/dist/lib/workspace/workspace-shell.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +41 -0
- package/dist/server.js.map +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { _ as _class_private_field_loose_base } from "@swc/helpers/_/_class_private_field_loose_base";
|
|
2
|
+
import { _ as _class_private_field_loose_key } from "@swc/helpers/_/_class_private_field_loose_key";
|
|
3
|
+
/** Split a `?tab=` value into its kind and payload halves. */ export function splitParam(param) {
|
|
4
|
+
const separator = param.indexOf(':');
|
|
5
|
+
return separator === -1 ? {
|
|
6
|
+
kind: param,
|
|
7
|
+
payload: ''
|
|
8
|
+
} : {
|
|
9
|
+
kind: param.slice(0, separator),
|
|
10
|
+
payload: param.slice(separator + 1)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
var _kinds = /*#__PURE__*/ _class_private_field_loose_key("_kinds");
|
|
14
|
+
/**
|
|
15
|
+
* The registry of tab kinds. `resolve` turns a `?tab=` value into a renderable
|
|
16
|
+
* tab, or `null` when the kind is unknown or the payload is invalid — the
|
|
17
|
+
* caller shows a fallback rather than crashing the workspace.
|
|
18
|
+
*/ export class TabRegistry {
|
|
19
|
+
register(kind) {
|
|
20
|
+
_class_private_field_loose_base(this, _kinds)[_kinds].set(kind.kind, kind);
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
has(kind) {
|
|
24
|
+
return _class_private_field_loose_base(this, _kinds)[_kinds].has(kind);
|
|
25
|
+
}
|
|
26
|
+
resolve(param, translate) {
|
|
27
|
+
const { kind: kindName, payload } = splitParam(param);
|
|
28
|
+
const kind = _class_private_field_loose_base(this, _kinds)[_kinds].get(kindName);
|
|
29
|
+
if (!kind) return null;
|
|
30
|
+
const addr = kind.match(payload);
|
|
31
|
+
if (addr === null) return null;
|
|
32
|
+
return {
|
|
33
|
+
param: `${kind.kind}:${kind.toParam(addr)}`,
|
|
34
|
+
title: kind.title(addr, translate),
|
|
35
|
+
render: ()=>kind.render(addr)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
constructor(){
|
|
39
|
+
Object.defineProperty(this, _kinds, {
|
|
40
|
+
writable: true,
|
|
41
|
+
value: void 0
|
|
42
|
+
});
|
|
43
|
+
_class_private_field_loose_base(this, _kinds)[_kinds] = new Map();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=tab-kind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/workspace/tab-kind.ts"],"sourcesContent":["import type { ReactNode } from 'react';\n\n/** Resolves a catalog key. The widened form: a tab title is data, not authored copy. */\nexport type TabTranslate = (\n key: string,\n params?: Record<string, unknown>,\n) => string;\n\n/**\n * One kind of tab (a catalog, an entity editor, later an operation or wizard).\n * A page is addressed by the `?tab=` value `\"<kind>:<payload>\"`; the kind owns\n * the payload half — parsing it to an address, serializing it back, and\n * rendering it. Registering a kind is the only change adding a new tab type\n * needs; the router and store never change.\n */\nexport interface TabKind<TAddr = unknown> {\n /** The leading segment of the `?tab=` value, e.g. `catalog`, `entity`. */\n kind: string;\n /** Parse the part after `<kind>:` into an address, or `null` if invalid. */\n match(payload: string): TAddr | null;\n /** Serialize an address back to the payload (without the `<kind>:` prefix). */\n toParam(addr: TAddr): string;\n /**\n * The tab caption. Handed a translate function rather than reaching for a\n * hook: kinds are registered at module scope, outside any React tree.\n */\n title(addr: TAddr, translate: TabTranslate): string;\n render(addr: TAddr): ReactNode;\n}\n\n/** A resolved tab: everything the strip and body need, addressed by `param`. */\nexport interface ResolvedTab {\n /** The canonical `?tab=` value — the tab's identity and deep link. */\n param: string;\n title: string;\n render: () => ReactNode;\n}\n\n/** Split a `?tab=` value into its kind and payload halves. */\nexport function splitParam(param: string): { kind: string; payload: string } {\n const separator = param.indexOf(':');\n return separator === -1\n ? { kind: param, payload: '' }\n : { kind: param.slice(0, separator), payload: param.slice(separator + 1) };\n}\n\n/**\n * The registry of tab kinds. `resolve` turns a `?tab=` value into a renderable\n * tab, or `null` when the kind is unknown or the payload is invalid — the\n * caller shows a fallback rather than crashing the workspace.\n */\nexport class TabRegistry {\n readonly #kinds = new Map<string, TabKind<unknown>>();\n\n register<TAddr>(kind: TabKind<TAddr>): this {\n this.#kinds.set(kind.kind, kind as TabKind<unknown>);\n return this;\n }\n\n has(kind: string): boolean {\n return this.#kinds.has(kind);\n }\n\n resolve(param: string, translate: TabTranslate): ResolvedTab | null {\n const { kind: kindName, payload } = splitParam(param);\n const kind = this.#kinds.get(kindName);\n if (!kind) return null;\n\n const addr = kind.match(payload);\n if (addr === null) return null;\n\n return {\n param: `${kind.kind}:${kind.toParam(addr)}`,\n title: kind.title(addr, translate),\n render: () => kind.render(addr),\n };\n }\n}\n"],"names":["splitParam","param","separator","indexOf","kind","payload","slice","TabRegistry","register","set","has","resolve","translate","kindName","get","addr","match","toParam","title","render","Map"],"mappings":";;AAsCA,4DAA4D,GAC5D,OAAO,SAASA,WAAWC,KAAa;IACtC,MAAMC,YAAYD,MAAME,OAAO,CAAC;IAChC,OAAOD,cAAc,CAAC,IAClB;QAAEE,MAAMH;QAAOI,SAAS;IAAG,IAC3B;QAAED,MAAMH,MAAMK,KAAK,CAAC,GAAGJ;QAAYG,SAASJ,MAAMK,KAAK,CAACJ,YAAY;IAAG;AAC7E;IAQW;AANX;;;;CAIC,GACD,OAAO,MAAMK;IAGXC,SAAgBJ,IAAoB,EAAQ;QAC1C,gCAAA,IAAI,EAAC,QAAA,QAAOK,GAAG,CAACL,KAAKA,IAAI,EAAEA;QAC3B,OAAO,IAAI;IACb;IAEAM,IAAIN,IAAY,EAAW;QACzB,OAAO,gCAAA,IAAI,EAAC,QAAA,QAAOM,GAAG,CAACN;IACzB;IAEAO,QAAQV,KAAa,EAAEW,SAAuB,EAAsB;QAClE,MAAM,EAAER,MAAMS,QAAQ,EAAER,OAAO,EAAE,GAAGL,WAAWC;QAC/C,MAAMG,OAAO,gCAAA,IAAI,EAAC,QAAA,QAAOU,GAAG,CAACD;QAC7B,IAAI,CAACT,MAAM,OAAO;QAElB,MAAMW,OAAOX,KAAKY,KAAK,CAACX;QACxB,IAAIU,SAAS,MAAM,OAAO;QAE1B,OAAO;YACLd,OAAO,GAAGG,KAAKA,IAAI,CAAC,CAAC,EAAEA,KAAKa,OAAO,CAACF,OAAO;YAC3CG,OAAOd,KAAKc,KAAK,CAACH,MAAMH;YACxBO,QAAQ,IAAMf,KAAKe,MAAM,CAACJ;QAC5B;IACF;;QAxBA,4BAAS;;mBAAT,KAAA;;8CAAS,QAAA,UAAS,IAAIK;;AAyBxB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single open tab. `param` is its `?tab=` value — both the deep-link address
|
|
3
|
+
* and the tab's identity, so opening the same address twice focuses the one
|
|
4
|
+
* that is already open rather than duplicating it.
|
|
5
|
+
*/
|
|
6
|
+
export interface TabRecord {
|
|
7
|
+
param: string;
|
|
8
|
+
title: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TabsSnapshot {
|
|
11
|
+
tabs: TabRecord[];
|
|
12
|
+
activeParam: string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare const emptyTabs: TabsSnapshot;
|
|
15
|
+
/** Upper bound on open tabs before the oldest inactive one is evicted. */
|
|
16
|
+
export declare const MAX_TABS = 12;
|
|
17
|
+
/**
|
|
18
|
+
* Trim the set to {@link MAX_TABS} by evicting the oldest tabs first, never the
|
|
19
|
+
* active one. Evicting a tab does not lose its work — any draft survives in
|
|
20
|
+
* IndexedDB and is restored when the tab is reopened.
|
|
21
|
+
*/
|
|
22
|
+
export declare function capTabs(state: TabsSnapshot): TabsSnapshot;
|
|
23
|
+
/**
|
|
24
|
+
* Open a tab, or focus and re-title it if its address is already open. The
|
|
25
|
+
* opened/focused tab becomes active; the set is capped at {@link MAX_TABS}.
|
|
26
|
+
*/
|
|
27
|
+
export declare function openOrFocus(state: TabsSnapshot, tab: TabRecord): TabsSnapshot;
|
|
28
|
+
/**
|
|
29
|
+
* Close a tab. If it was active, focus its neighbour (the tab that slides into
|
|
30
|
+
* its place, else the one before it, else nothing). Closing an unknown tab is a
|
|
31
|
+
* no-op.
|
|
32
|
+
*/
|
|
33
|
+
export declare function closeTab(state: TabsSnapshot, param: string): TabsSnapshot;
|
|
34
|
+
/** Focus an open tab; focusing an unknown one is a no-op. */
|
|
35
|
+
export declare function setActive(state: TabsSnapshot, param: string): TabsSnapshot;
|
|
36
|
+
//# sourceMappingURL=tab-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tab-state.d.ts","sourceRoot":"","sources":["../../../src/lib/workspace/tab-state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,eAAO,MAAM,SAAS,EAAE,YAA8C,CAAC;AAEvE,0EAA0E;AAC1E,eAAO,MAAM,QAAQ,KAAK,CAAC;AAE3B;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAYzD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,GAAG,YAAY,CAiB7E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAWzE;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAG1E"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single open tab. `param` is its `?tab=` value — both the deep-link address
|
|
3
|
+
* and the tab's identity, so opening the same address twice focuses the one
|
|
4
|
+
* that is already open rather than duplicating it.
|
|
5
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
6
|
+
export const emptyTabs = {
|
|
7
|
+
tabs: [],
|
|
8
|
+
activeParam: null
|
|
9
|
+
};
|
|
10
|
+
/** Upper bound on open tabs before the oldest inactive one is evicted. */ export const MAX_TABS = 12;
|
|
11
|
+
/**
|
|
12
|
+
* Trim the set to {@link MAX_TABS} by evicting the oldest tabs first, never the
|
|
13
|
+
* active one. Evicting a tab does not lose its work — any draft survives in
|
|
14
|
+
* IndexedDB and is restored when the tab is reopened.
|
|
15
|
+
*/ export function capTabs(state) {
|
|
16
|
+
let overflow = state.tabs.length - MAX_TABS;
|
|
17
|
+
if (overflow <= 0) return state;
|
|
18
|
+
const tabs = state.tabs.filter((open)=>{
|
|
19
|
+
if (overflow > 0 && open.param !== state.activeParam) {
|
|
20
|
+
overflow -= 1;
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
});
|
|
25
|
+
return _extends({}, state, {
|
|
26
|
+
tabs
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Open a tab, or focus and re-title it if its address is already open. The
|
|
31
|
+
* opened/focused tab becomes active; the set is capped at {@link MAX_TABS}.
|
|
32
|
+
*/ export function openOrFocus(state, tab) {
|
|
33
|
+
const existing = state.tabs.find((open)=>open.param === tab.param);
|
|
34
|
+
// Re-opening the tab that is already open and already active changes nothing;
|
|
35
|
+
// returning the same snapshot keeps subscribers from re-rendering on every
|
|
36
|
+
// URL⇄store sync, which is what turned a settled workspace into a churn of
|
|
37
|
+
// new tab arrays.
|
|
38
|
+
if ((existing == null ? void 0 : existing.title) === tab.title && state.activeParam === tab.param) {
|
|
39
|
+
return state;
|
|
40
|
+
}
|
|
41
|
+
const exists = existing !== undefined;
|
|
42
|
+
const tabs = exists ? state.tabs.map((open)=>open.param === tab.param ? _extends({}, open, {
|
|
43
|
+
title: tab.title
|
|
44
|
+
}) : open) : [
|
|
45
|
+
...state.tabs,
|
|
46
|
+
tab
|
|
47
|
+
];
|
|
48
|
+
return capTabs({
|
|
49
|
+
tabs,
|
|
50
|
+
activeParam: tab.param
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Close a tab. If it was active, focus its neighbour (the tab that slides into
|
|
55
|
+
* its place, else the one before it, else nothing). Closing an unknown tab is a
|
|
56
|
+
* no-op.
|
|
57
|
+
*/ export function closeTab(state, param) {
|
|
58
|
+
var _tabs_index, _ref;
|
|
59
|
+
const index = state.tabs.findIndex((open)=>open.param === param);
|
|
60
|
+
if (index === -1) return state;
|
|
61
|
+
const tabs = state.tabs.filter((open)=>open.param !== param);
|
|
62
|
+
if (state.activeParam !== param) {
|
|
63
|
+
return {
|
|
64
|
+
tabs,
|
|
65
|
+
activeParam: state.activeParam
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const neighbour = (_tabs_index = tabs[index]) != null ? _tabs_index : tabs[index - 1];
|
|
69
|
+
return {
|
|
70
|
+
tabs,
|
|
71
|
+
activeParam: (_ref = neighbour == null ? void 0 : neighbour.param) != null ? _ref : null
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** Focus an open tab; focusing an unknown one is a no-op. */ export function setActive(state, param) {
|
|
75
|
+
if (!state.tabs.some((open)=>open.param === param)) return state;
|
|
76
|
+
return _extends({}, state, {
|
|
77
|
+
activeParam: param
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=tab-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/workspace/tab-state.ts"],"sourcesContent":["/**\n * A single open tab. `param` is its `?tab=` value — both the deep-link address\n * and the tab's identity, so opening the same address twice focuses the one\n * that is already open rather than duplicating it.\n */\nexport interface TabRecord {\n param: string;\n title: string;\n}\n\nexport interface TabsSnapshot {\n tabs: TabRecord[];\n activeParam: string | null;\n}\n\nexport const emptyTabs: TabsSnapshot = { tabs: [], activeParam: null };\n\n/** Upper bound on open tabs before the oldest inactive one is evicted. */\nexport const MAX_TABS = 12;\n\n/**\n * Trim the set to {@link MAX_TABS} by evicting the oldest tabs first, never the\n * active one. Evicting a tab does not lose its work — any draft survives in\n * IndexedDB and is restored when the tab is reopened.\n */\nexport function capTabs(state: TabsSnapshot): TabsSnapshot {\n let overflow = state.tabs.length - MAX_TABS;\n if (overflow <= 0) return state;\n\n const tabs = state.tabs.filter(open => {\n if (overflow > 0 && open.param !== state.activeParam) {\n overflow -= 1;\n return false;\n }\n return true;\n });\n return { ...state, tabs };\n}\n\n/**\n * Open a tab, or focus and re-title it if its address is already open. The\n * opened/focused tab becomes active; the set is capped at {@link MAX_TABS}.\n */\nexport function openOrFocus(state: TabsSnapshot, tab: TabRecord): TabsSnapshot {\n const existing = state.tabs.find(open => open.param === tab.param);\n // Re-opening the tab that is already open and already active changes nothing;\n // returning the same snapshot keeps subscribers from re-rendering on every\n // URL⇄store sync, which is what turned a settled workspace into a churn of\n // new tab arrays.\n if (existing?.title === tab.title && state.activeParam === tab.param) {\n return state;\n }\n\n const exists = existing !== undefined;\n const tabs = exists\n ? state.tabs.map(open =>\n open.param === tab.param ? { ...open, title: tab.title } : open,\n )\n : [...state.tabs, tab];\n return capTabs({ tabs, activeParam: tab.param });\n}\n\n/**\n * Close a tab. If it was active, focus its neighbour (the tab that slides into\n * its place, else the one before it, else nothing). Closing an unknown tab is a\n * no-op.\n */\nexport function closeTab(state: TabsSnapshot, param: string): TabsSnapshot {\n const index = state.tabs.findIndex(open => open.param === param);\n if (index === -1) return state;\n\n const tabs = state.tabs.filter(open => open.param !== param);\n if (state.activeParam !== param) {\n return { tabs, activeParam: state.activeParam };\n }\n\n const neighbour = tabs[index] ?? tabs[index - 1];\n return { tabs, activeParam: neighbour?.param ?? null };\n}\n\n/** Focus an open tab; focusing an unknown one is a no-op. */\nexport function setActive(state: TabsSnapshot, param: string): TabsSnapshot {\n if (!state.tabs.some(open => open.param === param)) return state;\n return { ...state, activeParam: param };\n}\n"],"names":["emptyTabs","tabs","activeParam","MAX_TABS","capTabs","state","overflow","length","filter","open","param","openOrFocus","tab","existing","find","title","exists","undefined","map","closeTab","index","findIndex","neighbour","setActive","some"],"mappings":"AAAA;;;;CAIC;AAWD,OAAO,MAAMA,YAA0B;IAAEC,MAAM,EAAE;IAAEC,aAAa;AAAK,EAAE;AAEvE,wEAAwE,GACxE,OAAO,MAAMC,WAAW,GAAG;AAE3B;;;;CAIC,GACD,OAAO,SAASC,QAAQC,KAAmB;IACzC,IAAIC,WAAWD,MAAMJ,IAAI,CAACM,MAAM,GAAGJ;IACnC,IAAIG,YAAY,GAAG,OAAOD;IAE1B,MAAMJ,OAAOI,MAAMJ,IAAI,CAACO,MAAM,CAACC,CAAAA;QAC7B,IAAIH,WAAW,KAAKG,KAAKC,KAAK,KAAKL,MAAMH,WAAW,EAAE;YACpDI,YAAY;YACZ,OAAO;QACT;QACA,OAAO;IACT;IACA,OAAO,aAAKD;QAAOJ;;AACrB;AAEA;;;CAGC,GACD,OAAO,SAASU,YAAYN,KAAmB,EAAEO,GAAc;IAC7D,MAAMC,WAAWR,MAAMJ,IAAI,CAACa,IAAI,CAACL,CAAAA,OAAQA,KAAKC,KAAK,KAAKE,IAAIF,KAAK;IACjE,8EAA8E;IAC9E,2EAA2E;IAC3E,2EAA2E;IAC3E,kBAAkB;IAClB,IAAIG,CAAAA,4BAAAA,SAAUE,KAAK,MAAKH,IAAIG,KAAK,IAAIV,MAAMH,WAAW,KAAKU,IAAIF,KAAK,EAAE;QACpE,OAAOL;IACT;IAEA,MAAMW,SAASH,aAAaI;IAC5B,MAAMhB,OAAOe,SACTX,MAAMJ,IAAI,CAACiB,GAAG,CAACT,CAAAA,OACbA,KAAKC,KAAK,KAAKE,IAAIF,KAAK,GAAG,aAAKD;YAAMM,OAAOH,IAAIG,KAAK;aAAKN,QAE7D;WAAIJ,MAAMJ,IAAI;QAAEW;KAAI;IACxB,OAAOR,QAAQ;QAAEH;QAAMC,aAAaU,IAAIF,KAAK;IAAC;AAChD;AAEA;;;;CAIC,GACD,OAAO,SAASS,SAASd,KAAmB,EAAEK,KAAa;QASvCT;IARlB,MAAMmB,QAAQf,MAAMJ,IAAI,CAACoB,SAAS,CAACZ,CAAAA,OAAQA,KAAKC,KAAK,KAAKA;IAC1D,IAAIU,UAAU,CAAC,GAAG,OAAOf;IAEzB,MAAMJ,OAAOI,MAAMJ,IAAI,CAACO,MAAM,CAACC,CAAAA,OAAQA,KAAKC,KAAK,KAAKA;IACtD,IAAIL,MAAMH,WAAW,KAAKQ,OAAO;QAC/B,OAAO;YAAET;YAAMC,aAAaG,MAAMH,WAAW;QAAC;IAChD;IAEA,MAAMoB,aAAYrB,cAAAA,IAAI,CAACmB,MAAM,YAAXnB,cAAeA,IAAI,CAACmB,QAAQ,EAAE;IAChD,OAAO;QAAEnB;QAAMC,WAAW,UAAEoB,6BAAAA,UAAWZ,KAAK,mBAAI;IAAK;AACvD;AAEA,2DAA2D,GAC3D,OAAO,SAASa,UAAUlB,KAAmB,EAAEK,KAAa;IAC1D,IAAI,CAACL,MAAMJ,IAAI,CAACuB,IAAI,CAACf,CAAAA,OAAQA,KAAKC,KAAK,KAAKA,QAAQ,OAAOL;IAC3D,OAAO,aAAKA;QAAOH,aAAaQ;;AAClC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type TabRecord, type TabsSnapshot } from './tab-state.js';
|
|
2
|
+
export interface TabsState extends TabsSnapshot {
|
|
3
|
+
open(tab: TabRecord): void;
|
|
4
|
+
close(param: string): void;
|
|
5
|
+
activate(param: string): void;
|
|
6
|
+
}
|
|
7
|
+
/** Only the data is persisted — the action functions are re-created on load. */
|
|
8
|
+
export declare function persistedTabs(store: TabsState): TabsSnapshot;
|
|
9
|
+
export declare const WORKSPACE_DB = "entifix-workspace";
|
|
10
|
+
/**
|
|
11
|
+
* Bump when {@link TabsSnapshot}'s shape changes, and every persisted tab set is
|
|
12
|
+
* discarded on the next load.
|
|
13
|
+
*
|
|
14
|
+
* Restoring a snapshot this build cannot read is worse than opening empty: a tab
|
|
15
|
+
* whose `param` no longer resolves renders the workspace's dead-link fallback,
|
|
16
|
+
* so a drifted snapshot is a strip of broken tabs rather than an error anyone
|
|
17
|
+
* can act on.
|
|
18
|
+
*/
|
|
19
|
+
export declare const TABS_VERSION = 2;
|
|
20
|
+
/**
|
|
21
|
+
* Discard, never guess — see {@link migrateDrafts} for the same decision on the
|
|
22
|
+
* other store, and for why this is written out instead of left to zustand's
|
|
23
|
+
* default (which discards too, but logs an error while doing it).
|
|
24
|
+
*/
|
|
25
|
+
export declare function migrateTabs(): TabsSnapshot;
|
|
26
|
+
/**
|
|
27
|
+
* The open tab set, persisted to IndexedDB so a refresh restores the workspace.
|
|
28
|
+
* Hydration is skipped on creation (it would touch IndexedDB during SSR) and
|
|
29
|
+
* triggered from the client by `WorkspaceShell`, which also holds every URL
|
|
30
|
+
* effect back until the read has landed — otherwise the restored `activeParam`
|
|
31
|
+
* overwrites a `?tab=` deep link.
|
|
32
|
+
*/
|
|
33
|
+
export declare const useTabsState: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<TabsState>, "setState" | "persist"> & {
|
|
34
|
+
setState(partial: TabsState | Partial<TabsState> | ((state: TabsState) => TabsState | Partial<TabsState>), replace?: false | undefined): unknown;
|
|
35
|
+
setState(state: TabsState | ((state: TabsState) => TabsState), replace: true): unknown;
|
|
36
|
+
persist: {
|
|
37
|
+
setOptions: (options: Partial<import("zustand/middleware").PersistOptions<TabsState, unknown, unknown>>) => void;
|
|
38
|
+
clearStorage: () => void;
|
|
39
|
+
rehydrate: () => Promise<void> | void;
|
|
40
|
+
hasHydrated: () => boolean;
|
|
41
|
+
onHydrate: (fn: (state: TabsState) => void) => () => void;
|
|
42
|
+
onFinishHydration: (fn: (state: TabsState) => void) => () => void;
|
|
43
|
+
getOptions: () => Partial<import("zustand/middleware").PersistOptions<TabsState, unknown, unknown>>;
|
|
44
|
+
};
|
|
45
|
+
}>;
|
|
46
|
+
//# sourceMappingURL=tabs-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tabs-state.d.ts","sourceRoot":"","sources":["../../../src/lib/workspace/tabs-state.ts"],"names":[],"mappings":"AAMA,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,SAAU,SAAQ,YAAY;IAC7C,IAAI,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,CAE5D;AAED,eAAO,MAAM,YAAY,sBAAsB,CAAC;AAGhD;;;;;;;;GAQG;AAIH,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,YAAY,CAE1C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;EAsBxB,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
3
|
+
import { create } from 'zustand';
|
|
4
|
+
import { createJSONStorage, persist } from 'zustand/middleware';
|
|
5
|
+
import { makeIndexedDbStateStorage } from './idb-state-storage.js';
|
|
6
|
+
import { closeTab, emptyTabs, openOrFocus, setActive } from './tab-state.js';
|
|
7
|
+
/** Only the data is persisted — the action functions are re-created on load. */ export function persistedTabs(store) {
|
|
8
|
+
return {
|
|
9
|
+
tabs: store.tabs,
|
|
10
|
+
activeParam: store.activeParam
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export const WORKSPACE_DB = 'entifix-workspace';
|
|
14
|
+
const WORKSPACE_STORE = 'stores';
|
|
15
|
+
/**
|
|
16
|
+
* Bump when {@link TabsSnapshot}'s shape changes, and every persisted tab set is
|
|
17
|
+
* discarded on the next load.
|
|
18
|
+
*
|
|
19
|
+
* Restoring a snapshot this build cannot read is worse than opening empty: a tab
|
|
20
|
+
* whose `param` no longer resolves renders the workspace's dead-link fallback,
|
|
21
|
+
* so a drifted snapshot is a strip of broken tabs rather than an error anyone
|
|
22
|
+
* can act on.
|
|
23
|
+
*/ // 2: the tab address became `<screenType>:<key>[:<id>]` (ADR 0042), so every
|
|
24
|
+
// `catalog:` / `entity:` / `system:` param a previous build persisted addresses
|
|
25
|
+
// a kind that no longer exists.
|
|
26
|
+
export const TABS_VERSION = 2;
|
|
27
|
+
/**
|
|
28
|
+
* Discard, never guess — see {@link migrateDrafts} for the same decision on the
|
|
29
|
+
* other store, and for why this is written out instead of left to zustand's
|
|
30
|
+
* default (which discards too, but logs an error while doing it).
|
|
31
|
+
*/ export function migrateTabs() {
|
|
32
|
+
return emptyTabs;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The open tab set, persisted to IndexedDB so a refresh restores the workspace.
|
|
36
|
+
* Hydration is skipped on creation (it would touch IndexedDB during SSR) and
|
|
37
|
+
* triggered from the client by `WorkspaceShell`, which also holds every URL
|
|
38
|
+
* effect back until the read has landed — otherwise the restored `activeParam`
|
|
39
|
+
* overwrites a `?tab=` deep link.
|
|
40
|
+
*/ export const useTabsState = create()(persist((set)=>_extends({}, emptyTabs, {
|
|
41
|
+
open: (tab)=>set((state)=>openOrFocus(state, tab)),
|
|
42
|
+
close: (param)=>set((state)=>closeTab(state, param)),
|
|
43
|
+
activate: (param)=>set((state)=>setActive(state, param))
|
|
44
|
+
}), {
|
|
45
|
+
// Replaced with a scoped name by `WorkspaceShell` before it rehydrates —
|
|
46
|
+
// see `workspace-scope.ts`. Nothing rehydrates without passing through
|
|
47
|
+
// that call, so this literal is only what an unscoped store would write.
|
|
48
|
+
name: 'tabs',
|
|
49
|
+
version: TABS_VERSION,
|
|
50
|
+
migrate: migrateTabs,
|
|
51
|
+
storage: createJSONStorage(()=>makeIndexedDbStateStorage(WORKSPACE_DB, WORKSPACE_STORE)),
|
|
52
|
+
partialize: persistedTabs,
|
|
53
|
+
skipHydration: true
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=tabs-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/workspace/tabs-state.ts"],"sourcesContent":["'use client';\n\nimport { create } from 'zustand';\nimport { createJSONStorage, persist } from 'zustand/middleware';\n\nimport { makeIndexedDbStateStorage } from './idb-state-storage.js';\nimport {\n closeTab,\n emptyTabs,\n openOrFocus,\n setActive,\n type TabRecord,\n type TabsSnapshot,\n} from './tab-state.js';\n\nexport interface TabsState extends TabsSnapshot {\n open(tab: TabRecord): void;\n close(param: string): void;\n activate(param: string): void;\n}\n\n/** Only the data is persisted — the action functions are re-created on load. */\nexport function persistedTabs(store: TabsState): TabsSnapshot {\n return { tabs: store.tabs, activeParam: store.activeParam };\n}\n\nexport const WORKSPACE_DB = 'entifix-workspace';\nconst WORKSPACE_STORE = 'stores';\n\n/**\n * Bump when {@link TabsSnapshot}'s shape changes, and every persisted tab set is\n * discarded on the next load.\n *\n * Restoring a snapshot this build cannot read is worse than opening empty: a tab\n * whose `param` no longer resolves renders the workspace's dead-link fallback,\n * so a drifted snapshot is a strip of broken tabs rather than an error anyone\n * can act on.\n */\n// 2: the tab address became `<screenType>:<key>[:<id>]` (ADR 0042), so every\n// `catalog:` / `entity:` / `system:` param a previous build persisted addresses\n// a kind that no longer exists.\nexport const TABS_VERSION = 2;\n\n/**\n * Discard, never guess — see {@link migrateDrafts} for the same decision on the\n * other store, and for why this is written out instead of left to zustand's\n * default (which discards too, but logs an error while doing it).\n */\nexport function migrateTabs(): TabsSnapshot {\n return emptyTabs;\n}\n\n/**\n * The open tab set, persisted to IndexedDB so a refresh restores the workspace.\n * Hydration is skipped on creation (it would touch IndexedDB during SSR) and\n * triggered from the client by `WorkspaceShell`, which also holds every URL\n * effect back until the read has landed — otherwise the restored `activeParam`\n * overwrites a `?tab=` deep link.\n */\nexport const useTabsState = create<TabsState>()(\n persist(\n set => ({\n ...emptyTabs,\n open: tab => set(state => openOrFocus(state, tab)),\n close: param => set(state => closeTab(state, param)),\n activate: param => set(state => setActive(state, param)),\n }),\n {\n // Replaced with a scoped name by `WorkspaceShell` before it rehydrates —\n // see `workspace-scope.ts`. Nothing rehydrates without passing through\n // that call, so this literal is only what an unscoped store would write.\n name: 'tabs',\n version: TABS_VERSION,\n migrate: migrateTabs,\n storage: createJSONStorage(() =>\n makeIndexedDbStateStorage(WORKSPACE_DB, WORKSPACE_STORE),\n ),\n partialize: persistedTabs,\n skipHydration: true,\n },\n ),\n);\n"],"names":["create","createJSONStorage","persist","makeIndexedDbStateStorage","closeTab","emptyTabs","openOrFocus","setActive","persistedTabs","store","tabs","activeParam","WORKSPACE_DB","WORKSPACE_STORE","TABS_VERSION","migrateTabs","useTabsState","set","open","tab","state","close","param","activate","name","version","migrate","storage","partialize","skipHydration"],"mappings":"AAAA;;AAEA,SAASA,MAAM,QAAQ,UAAU;AACjC,SAASC,iBAAiB,EAAEC,OAAO,QAAQ,qBAAqB;AAEhE,SAASC,yBAAyB,QAAQ,yBAAyB;AACnE,SACEC,QAAQ,EACRC,SAAS,EACTC,WAAW,EACXC,SAAS,QAGJ,iBAAiB;AAQxB,8EAA8E,GAC9E,OAAO,SAASC,cAAcC,KAAgB;IAC5C,OAAO;QAAEC,MAAMD,MAAMC,IAAI;QAAEC,aAAaF,MAAME,WAAW;IAAC;AAC5D;AAEA,OAAO,MAAMC,eAAe,oBAAoB;AAChD,MAAMC,kBAAkB;AAExB;;;;;;;;CAQC,GACD,6EAA6E;AAC7E,gFAAgF;AAChF,gCAAgC;AAChC,OAAO,MAAMC,eAAe,EAAE;AAE9B;;;;CAIC,GACD,OAAO,SAASC;IACd,OAAOV;AACT;AAEA;;;;;;CAMC,GACD,OAAO,MAAMW,eAAehB,SAC1BE,QACEe,CAAAA,MAAQ,aACHZ;QACHa,MAAMC,CAAAA,MAAOF,IAAIG,CAAAA,QAASd,YAAYc,OAAOD;QAC7CE,OAAOC,CAAAA,QAASL,IAAIG,CAAAA,QAAShB,SAASgB,OAAOE;QAC7CC,UAAUD,CAAAA,QAASL,IAAIG,CAAAA,QAASb,UAAUa,OAAOE;QAEnD;IACE,yEAAyE;IACzE,uEAAuE;IACvE,yEAAyE;IACzEE,MAAM;IACNC,SAASX;IACTY,SAASX;IACTY,SAAS1B,kBAAkB,IACzBE,0BAA0BS,cAAcC;IAE1Ce,YAAYpB;IACZqB,eAAe;AACjB,IAEF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EntityDraftStore } from '@entifix/react-integration';
|
|
2
|
+
/**
|
|
3
|
+
* The workspace's implementation of `useEntityForm`'s draft port: binds a form
|
|
4
|
+
* to the address-keyed draft this workspace persists to IndexedDB.
|
|
5
|
+
*
|
|
6
|
+
* The address is the tab's own `?tab=` value, which is what makes a tab's dirty
|
|
7
|
+
* marker and its close confirmation fall out of the same fact the form is
|
|
8
|
+
* autosaving into — `WorkspaceShell` reads `drafts` directly and asks nothing
|
|
9
|
+
* else.
|
|
10
|
+
*
|
|
11
|
+
* Handing this to a form is the *whole* opt-in. A plain route hands nothing and
|
|
12
|
+
* stays ephemeral, by construction rather than by a flag: there is no value of
|
|
13
|
+
* a missing prop that accidentally starts persisting.
|
|
14
|
+
*
|
|
15
|
+
* The returned object is memoised because the port requires a stable `save`:
|
|
16
|
+
* the hook writes from an effect keyed on it, so a fresh identity each render
|
|
17
|
+
* would turn every render into an IndexedDB write.
|
|
18
|
+
*
|
|
19
|
+
* `'use client'` is load-bearing here, not decoration. This module is reached
|
|
20
|
+
* through the package's flat client entry, and per-file swc keeps each file's
|
|
21
|
+
* own directive — a module with hooks and no directive is what drags `useMemo`
|
|
22
|
+
* into a Server Component's build.
|
|
23
|
+
*/
|
|
24
|
+
export declare function useEntityDraft(address: string): EntityDraftStore;
|
|
25
|
+
//# sourceMappingURL=use-entity-draft.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-entity-draft.d.ts","sourceRoot":"","sources":["../../../src/lib/workspace/use-entity-draft.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAKnE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAOhE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { useDraft } from './drafts-state.js';
|
|
4
|
+
/**
|
|
5
|
+
* The workspace's implementation of `useEntityForm`'s draft port: binds a form
|
|
6
|
+
* to the address-keyed draft this workspace persists to IndexedDB.
|
|
7
|
+
*
|
|
8
|
+
* The address is the tab's own `?tab=` value, which is what makes a tab's dirty
|
|
9
|
+
* marker and its close confirmation fall out of the same fact the form is
|
|
10
|
+
* autosaving into — `WorkspaceShell` reads `drafts` directly and asks nothing
|
|
11
|
+
* else.
|
|
12
|
+
*
|
|
13
|
+
* Handing this to a form is the *whole* opt-in. A plain route hands nothing and
|
|
14
|
+
* stays ephemeral, by construction rather than by a flag: there is no value of
|
|
15
|
+
* a missing prop that accidentally starts persisting.
|
|
16
|
+
*
|
|
17
|
+
* The returned object is memoised because the port requires a stable `save`:
|
|
18
|
+
* the hook writes from an effect keyed on it, so a fresh identity each render
|
|
19
|
+
* would turn every render into an IndexedDB write.
|
|
20
|
+
*
|
|
21
|
+
* `'use client'` is load-bearing here, not decoration. This module is reached
|
|
22
|
+
* through the package's flat client entry, and per-file swc keeps each file's
|
|
23
|
+
* own directive — a module with hooks and no directive is what drags `useMemo`
|
|
24
|
+
* into a Server Component's build.
|
|
25
|
+
*/ export function useEntityDraft(address) {
|
|
26
|
+
const { draft, setDraft, clearDraft } = useDraft(address);
|
|
27
|
+
return useMemo(()=>({
|
|
28
|
+
draft,
|
|
29
|
+
save: setDraft,
|
|
30
|
+
clear: clearDraft
|
|
31
|
+
}), [
|
|
32
|
+
draft,
|
|
33
|
+
setDraft,
|
|
34
|
+
clearDraft
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=use-entity-draft.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/workspace/use-entity-draft.ts"],"sourcesContent":["'use client';\n\nimport type { EntityDraft } from '@entifix/core';\nimport type { EntityDraftStore } from '@entifix/react-integration';\nimport { useMemo } from 'react';\n\nimport { useDraft } from './drafts-state.js';\n\n/**\n * The workspace's implementation of `useEntityForm`'s draft port: binds a form\n * to the address-keyed draft this workspace persists to IndexedDB.\n *\n * The address is the tab's own `?tab=` value, which is what makes a tab's dirty\n * marker and its close confirmation fall out of the same fact the form is\n * autosaving into — `WorkspaceShell` reads `drafts` directly and asks nothing\n * else.\n *\n * Handing this to a form is the *whole* opt-in. A plain route hands nothing and\n * stays ephemeral, by construction rather than by a flag: there is no value of\n * a missing prop that accidentally starts persisting.\n *\n * The returned object is memoised because the port requires a stable `save`:\n * the hook writes from an effect keyed on it, so a fresh identity each render\n * would turn every render into an IndexedDB write.\n *\n * `'use client'` is load-bearing here, not decoration. This module is reached\n * through the package's flat client entry, and per-file swc keeps each file's\n * own directive — a module with hooks and no directive is what drags `useMemo`\n * into a Server Component's build.\n */\nexport function useEntityDraft(address: string): EntityDraftStore {\n const { draft, setDraft, clearDraft } = useDraft<EntityDraft>(address);\n\n return useMemo(\n () => ({ draft, save: setDraft, clear: clearDraft }),\n [draft, setDraft, clearDraft],\n );\n}\n"],"names":["useMemo","useDraft","useEntityDraft","address","draft","setDraft","clearDraft","save","clear"],"mappings":"AAAA;AAIA,SAASA,OAAO,QAAQ,QAAQ;AAEhC,SAASC,QAAQ,QAAQ,oBAAoB;AAE7C;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,OAAO,SAASC,eAAeC,OAAe;IAC5C,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,UAAU,EAAE,GAAGL,SAAsBE;IAE9D,OAAOH,QACL,IAAO,CAAA;YAAEI;YAAOG,MAAMF;YAAUG,OAAOF;QAAW,CAAA,GAClD;QAACF;QAAOC;QAAUC;KAAW;AAEjC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who a persisted workspace belongs to.
|
|
3
|
+
*
|
|
4
|
+
* Tabs and drafts live in IndexedDB, which is a property of the *browser
|
|
5
|
+
* profile*, not of the session. Without a scope two accounts signing in on one
|
|
6
|
+
* machine share both stores: the second sees the first's open tabs and restores
|
|
7
|
+
* their half-finished edits. A draft is keyed by a record's address
|
|
8
|
+
* (`entity:product-specification:<id>`) and a record id is tenant-scoped, so a
|
|
9
|
+
* draft written under one organization and restored under another would post
|
|
10
|
+
* into the wrong tenant.
|
|
11
|
+
*
|
|
12
|
+
* The organization is part of the key for that reason, not only the user: a
|
|
13
|
+
* party may hold several memberships and switching re-mints the token
|
|
14
|
+
* (`TokenClaims.activeOrganizationId`).
|
|
15
|
+
*/
|
|
16
|
+
export interface WorkspaceScopeParts {
|
|
17
|
+
/** The canonical user id — `TokenClaims.userId`. */
|
|
18
|
+
readonly userId: string;
|
|
19
|
+
/**
|
|
20
|
+
* The organization the session is acting for, when it has one. A buyer and an
|
|
21
|
+
* operator hold no tenant scope at all, which is a legitimate scope of its
|
|
22
|
+
* own rather than a missing value.
|
|
23
|
+
*/
|
|
24
|
+
readonly organizationId?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The storage key suffix for one signed-in principal.
|
|
28
|
+
*
|
|
29
|
+
* ⚠️ **This is a separation mechanism, not a confidentiality boundary.** Anyone
|
|
30
|
+
* holding the browser profile can read every key in the object store, and they
|
|
31
|
+
* hold the session cookie too — the key stops an accidental cross-account
|
|
32
|
+
* restore, nothing more. It follows that deriving it from unverified token
|
|
33
|
+
* claims is safe: forging one shows you your own drafts under a different name,
|
|
34
|
+
* and every request behind a restored draft is still authorized by the service
|
|
35
|
+
* that answers it. Same reasoning as the nav's `unverifiedClaims` read.
|
|
36
|
+
*
|
|
37
|
+
* A session with no organization is scoped explicitly rather than left blank, so
|
|
38
|
+
* `user-1` with no organization and `user-1` acting for organization `-` are not
|
|
39
|
+
* the same workspace by accident.
|
|
40
|
+
*/
|
|
41
|
+
export declare function workspaceScopeKey({ userId, organizationId, }: WorkspaceScopeParts): string;
|
|
42
|
+
/**
|
|
43
|
+
* The scope for a visitor whose principal could not be read — an expired cookie
|
|
44
|
+
* behind a page that still rendered, say.
|
|
45
|
+
*
|
|
46
|
+
* It is a real, separate scope rather than an empty string: falling back to the
|
|
47
|
+
* unscoped key would hand an unidentified visitor whatever the last signed-in
|
|
48
|
+
* account left behind, which is the exact failure the scope exists to prevent.
|
|
49
|
+
*/
|
|
50
|
+
export declare const ANONYMOUS_WORKSPACE_SCOPE = "anonymous";
|
|
51
|
+
//# sourceMappingURL=workspace-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-scope.d.ts","sourceRoot":"","sources":["../../../src/lib/workspace/workspace-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,mBAAmB;IAClC,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAKD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,cAAc,GACf,EAAE,mBAAmB,GAAG,MAAM,CAE9B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,cAAc,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who a persisted workspace belongs to.
|
|
3
|
+
*
|
|
4
|
+
* Tabs and drafts live in IndexedDB, which is a property of the *browser
|
|
5
|
+
* profile*, not of the session. Without a scope two accounts signing in on one
|
|
6
|
+
* machine share both stores: the second sees the first's open tabs and restores
|
|
7
|
+
* their half-finished edits. A draft is keyed by a record's address
|
|
8
|
+
* (`entity:product-specification:<id>`) and a record id is tenant-scoped, so a
|
|
9
|
+
* draft written under one organization and restored under another would post
|
|
10
|
+
* into the wrong tenant.
|
|
11
|
+
*
|
|
12
|
+
* The organization is part of the key for that reason, not only the user: a
|
|
13
|
+
* party may hold several memberships and switching re-mints the token
|
|
14
|
+
* (`TokenClaims.activeOrganizationId`).
|
|
15
|
+
*/ /** What an absent `activeOrganizationId` is stored as — see {@link workspaceScopeKey}. */ const NO_ORGANIZATION = '-';
|
|
16
|
+
/**
|
|
17
|
+
* The storage key suffix for one signed-in principal.
|
|
18
|
+
*
|
|
19
|
+
* ⚠️ **This is a separation mechanism, not a confidentiality boundary.** Anyone
|
|
20
|
+
* holding the browser profile can read every key in the object store, and they
|
|
21
|
+
* hold the session cookie too — the key stops an accidental cross-account
|
|
22
|
+
* restore, nothing more. It follows that deriving it from unverified token
|
|
23
|
+
* claims is safe: forging one shows you your own drafts under a different name,
|
|
24
|
+
* and every request behind a restored draft is still authorized by the service
|
|
25
|
+
* that answers it. Same reasoning as the nav's `unverifiedClaims` read.
|
|
26
|
+
*
|
|
27
|
+
* A session with no organization is scoped explicitly rather than left blank, so
|
|
28
|
+
* `user-1` with no organization and `user-1` acting for organization `-` are not
|
|
29
|
+
* the same workspace by accident.
|
|
30
|
+
*/ export function workspaceScopeKey({ userId, organizationId }) {
|
|
31
|
+
return `${userId}:${organizationId != null ? organizationId : NO_ORGANIZATION}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The scope for a visitor whose principal could not be read — an expired cookie
|
|
35
|
+
* behind a page that still rendered, say.
|
|
36
|
+
*
|
|
37
|
+
* It is a real, separate scope rather than an empty string: falling back to the
|
|
38
|
+
* unscoped key would hand an unidentified visitor whatever the last signed-in
|
|
39
|
+
* account left behind, which is the exact failure the scope exists to prevent.
|
|
40
|
+
*/ export const ANONYMOUS_WORKSPACE_SCOPE = 'anonymous';
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=workspace-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/workspace/workspace-scope.ts"],"sourcesContent":["/**\n * Who a persisted workspace belongs to.\n *\n * Tabs and drafts live in IndexedDB, which is a property of the *browser\n * profile*, not of the session. Without a scope two accounts signing in on one\n * machine share both stores: the second sees the first's open tabs and restores\n * their half-finished edits. A draft is keyed by a record's address\n * (`entity:product-specification:<id>`) and a record id is tenant-scoped, so a\n * draft written under one organization and restored under another would post\n * into the wrong tenant.\n *\n * The organization is part of the key for that reason, not only the user: a\n * party may hold several memberships and switching re-mints the token\n * (`TokenClaims.activeOrganizationId`).\n */\nexport interface WorkspaceScopeParts {\n /** The canonical user id — `TokenClaims.userId`. */\n readonly userId: string;\n /**\n * The organization the session is acting for, when it has one. A buyer and an\n * operator hold no tenant scope at all, which is a legitimate scope of its\n * own rather than a missing value.\n */\n readonly organizationId?: string;\n}\n\n/** What an absent `activeOrganizationId` is stored as — see {@link workspaceScopeKey}. */\nconst NO_ORGANIZATION = '-';\n\n/**\n * The storage key suffix for one signed-in principal.\n *\n * ⚠️ **This is a separation mechanism, not a confidentiality boundary.** Anyone\n * holding the browser profile can read every key in the object store, and they\n * hold the session cookie too — the key stops an accidental cross-account\n * restore, nothing more. It follows that deriving it from unverified token\n * claims is safe: forging one shows you your own drafts under a different name,\n * and every request behind a restored draft is still authorized by the service\n * that answers it. Same reasoning as the nav's `unverifiedClaims` read.\n *\n * A session with no organization is scoped explicitly rather than left blank, so\n * `user-1` with no organization and `user-1` acting for organization `-` are not\n * the same workspace by accident.\n */\nexport function workspaceScopeKey({\n userId,\n organizationId,\n}: WorkspaceScopeParts): string {\n return `${userId}:${organizationId ?? NO_ORGANIZATION}`;\n}\n\n/**\n * The scope for a visitor whose principal could not be read — an expired cookie\n * behind a page that still rendered, say.\n *\n * It is a real, separate scope rather than an empty string: falling back to the\n * unscoped key would hand an unidentified visitor whatever the last signed-in\n * account left behind, which is the exact failure the scope exists to prevent.\n */\nexport const ANONYMOUS_WORKSPACE_SCOPE = 'anonymous';\n"],"names":["NO_ORGANIZATION","workspaceScopeKey","userId","organizationId","ANONYMOUS_WORKSPACE_SCOPE"],"mappings":"AAAA;;;;;;;;;;;;;;CAcC,GAYD,wFAAwF,GACxF,MAAMA,kBAAkB;AAExB;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASC,kBAAkB,EAChCC,MAAM,EACNC,cAAc,EACM;IACpB,OAAO,GAAGD,OAAO,CAAC,EAAEC,yBAAAA,iBAAkBH,iBAAiB;AACzD;AAEA;;;;;;;CAOC,GACD,OAAO,MAAMI,4BAA4B,YAAY"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { TabRegistry } from './tab-kind.js';
|
|
3
|
+
export interface WorkspaceShellProps {
|
|
4
|
+
/**
|
|
5
|
+
* Who this workspace belongs to — `workspaceScopeKey({ userId,
|
|
6
|
+
* organizationId })`, resolved on the server from the session.
|
|
7
|
+
*
|
|
8
|
+
* Required, deliberately: IndexedDB is a property of the browser profile, so a
|
|
9
|
+
* host that forgets to pass one hands the next account to sign in on that
|
|
10
|
+
* machine the previous account's open tabs and half-finished edits. A default
|
|
11
|
+
* would make that the quiet outcome of doing nothing.
|
|
12
|
+
*/
|
|
13
|
+
scope: string;
|
|
14
|
+
/** Resolves `?tab=` values to renderable tabs. */
|
|
15
|
+
registry: TabRegistry;
|
|
16
|
+
/** Right-aligned actions above the tab strip (search, the user menu). */
|
|
17
|
+
actions?: ReactNode;
|
|
18
|
+
/** Body when `?tab=` names something the registry cannot resolve. */
|
|
19
|
+
fallback?: ReactNode;
|
|
20
|
+
/** Body when no tab is open. */
|
|
21
|
+
emptyState?: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The tab workspace: a strip of persisted tabs over the active tab's body — no
|
|
25
|
+
* sidebar or brand of its own. It is mounted inside a host shell (the
|
|
26
|
+
* back-office layout, say) that already supplies those, so nesting two
|
|
27
|
+
* sidebars never happens. The open set lives in {@link useTabsState}
|
|
28
|
+
* (IndexedDB); the URL's `?tab=` projects only the active tab, so a deep link
|
|
29
|
+
* opens or focuses exactly that tab and an unknown kind shows the fallback
|
|
30
|
+
* instead of crashing.
|
|
31
|
+
*/
|
|
32
|
+
export declare function WorkspaceShell({ scope, registry, actions, fallback, emptyState, }: WorkspaceShellProps): import("react").JSX.Element;
|
|
33
|
+
//# sourceMappingURL=workspace-shell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-shell.d.ts","sourceRoot":"","sources":["../../../src/lib/workspace/workspace-shell.tsx"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAC;AAG5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;OAQG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,QAAQ,EAAE,WAAW,CAAC;IACtB,yEAAyE;IACzE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,gCAAgC;IAChC,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,UAAU,GACX,EAAE,mBAAmB,+BA+LrB"}
|