quilt-vanilla 0.2.4 → 0.4.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/README.md +35 -39
- package/dist/actions.d.ts +14 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +115 -0
- package/dist/actions.js.map +1 -0
- package/dist/chrome-size.d.ts +7 -0
- package/dist/chrome-size.d.ts.map +1 -0
- package/dist/chrome-size.js +86 -0
- package/dist/chrome-size.js.map +1 -0
- package/dist/close.d.ts.map +1 -1
- package/dist/close.js +3 -1
- package/dist/close.js.map +1 -1
- package/dist/corners.d.ts +2 -1
- package/dist/corners.d.ts.map +1 -1
- package/dist/corners.js +2 -2
- package/dist/corners.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/menu.d.ts +9 -3
- package/dist/menu.d.ts.map +1 -1
- package/dist/menu.js +110 -37
- package/dist/menu.js.map +1 -1
- package/dist/panes.d.ts.map +1 -1
- package/dist/panes.js +84 -52
- package/dist/panes.js.map +1 -1
- package/dist/registry.d.ts +1 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +4 -3
- package/dist/registry.js.map +1 -1
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +128 -36
- package/dist/renderer.js.map +1 -1
- package/dist/resize.d.ts +2 -1
- package/dist/resize.d.ts.map +1 -1
- package/dist/resize.js +2 -2
- package/dist/resize.js.map +1 -1
- package/dist/shortcut-registration.d.ts +9 -0
- package/dist/shortcut-registration.d.ts.map +1 -0
- package/dist/shortcut-registration.js +95 -0
- package/dist/shortcut-registration.js.map +1 -0
- package/dist/shortcuts.d.ts +7 -6
- package/dist/shortcuts.d.ts.map +1 -1
- package/dist/shortcuts.js +28 -50
- package/dist/shortcuts.js.map +1 -1
- package/dist/styles.css +74 -9
- package/dist/tabs.d.ts.map +1 -1
- package/dist/tabs.js +10 -12
- package/dist/tabs.js.map +1 -1
- package/dist/types.d.ts +50 -13
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/workspace-api.d.ts +186 -0
- package/dist/workspace-api.d.ts.map +1 -0
- package/dist/workspace-api.js +409 -0
- package/dist/workspace-api.js.map +1 -0
- package/package.json +2 -2
- package/src/actions.ts +125 -0
- package/src/chrome-size.ts +99 -0
- package/src/close.ts +6 -1
- package/src/corners.ts +2 -1
- package/src/index.ts +31 -4
- package/src/menu.ts +123 -43
- package/src/panes.ts +82 -55
- package/src/registry.ts +6 -4
- package/src/renderer.ts +181 -54
- package/src/resize.ts +2 -1
- package/src/shortcut-registration.ts +106 -0
- package/src/shortcuts.ts +38 -63
- package/src/styles.css +74 -9
- package/src/tabs.ts +9 -11
- package/src/types.ts +67 -14
- package/src/workspace-api.ts +583 -0
package/dist/panes.js
CHANGED
|
@@ -9,66 +9,98 @@ export function mountPane(doc, pane, location, options) {
|
|
|
9
9
|
const renderer = Object.hasOwn(options.renderers ?? {}, pane.type)
|
|
10
10
|
? options.renderers?.[pane.type]
|
|
11
11
|
: undefined;
|
|
12
|
-
let view;
|
|
12
|
+
let view = { dispose() { } };
|
|
13
|
+
let observer;
|
|
14
|
+
let disposed = false;
|
|
15
|
+
const controller = new win.AbortController();
|
|
16
|
+
const cleanups = [];
|
|
17
|
+
const reportCleanup = (error) => {
|
|
18
|
+
try {
|
|
19
|
+
options.onError?.(error);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
/* Finish teardown even if reporting fails. */
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const run = (callback) => {
|
|
26
|
+
try {
|
|
27
|
+
callback();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
reportCleanup(error);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const dispose = () => {
|
|
34
|
+
if (disposed)
|
|
35
|
+
return;
|
|
36
|
+
disposed = true;
|
|
37
|
+
run(() => controller.abort());
|
|
38
|
+
run(() => observer?.disconnect());
|
|
39
|
+
run(() => view.dispose());
|
|
40
|
+
for (const cleanup of cleanups.splice(0).reverse())
|
|
41
|
+
run(cleanup);
|
|
42
|
+
element.remove();
|
|
43
|
+
};
|
|
13
44
|
let result;
|
|
14
45
|
const reportError = (error) => {
|
|
15
46
|
if (result)
|
|
16
47
|
result.failed = true;
|
|
17
48
|
options.onError?.(error);
|
|
18
49
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
options.onError?.(error);
|
|
43
|
-
}
|
|
50
|
+
try {
|
|
51
|
+
if (renderer)
|
|
52
|
+
view = renderer({
|
|
53
|
+
signal: controller.signal,
|
|
54
|
+
onCleanup(callback) {
|
|
55
|
+
if (disposed)
|
|
56
|
+
run(callback);
|
|
57
|
+
else
|
|
58
|
+
cleanups.push(callback);
|
|
59
|
+
},
|
|
60
|
+
element,
|
|
61
|
+
reportError,
|
|
62
|
+
document: doc,
|
|
63
|
+
window: win,
|
|
64
|
+
pane,
|
|
65
|
+
state: options.getPaneState?.(pane.id),
|
|
66
|
+
location,
|
|
67
|
+
}) ?? { dispose() { } };
|
|
68
|
+
else {
|
|
69
|
+
element.append(el(doc, 'div', 'layouts-placeholder', message(options, 'Unknown pane type: {type}. Register a renderer to display this pane.', {
|
|
70
|
+
type: pane.type,
|
|
71
|
+
})));
|
|
72
|
+
view = { dispose() { } };
|
|
44
73
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
dispose() {
|
|
55
|
-
if (disposed)
|
|
56
|
-
return;
|
|
57
|
-
disposed = true;
|
|
58
|
-
observer.disconnect();
|
|
59
|
-
try {
|
|
60
|
-
view.dispose();
|
|
61
|
-
}
|
|
62
|
-
finally {
|
|
63
|
-
element.remove();
|
|
74
|
+
observer = new ResizeObserver((entries) => {
|
|
75
|
+
const rect = entries[0]?.contentRect;
|
|
76
|
+
if (rect) {
|
|
77
|
+
try {
|
|
78
|
+
view.resize?.(rect.width, rect.height);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
options.onError?.(error);
|
|
82
|
+
}
|
|
64
83
|
}
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
if (location === 'main')
|
|
68
|
-
void view.ready?.catch((error) => {
|
|
69
|
-
if (!disposed)
|
|
70
|
-
reportError(error);
|
|
71
84
|
});
|
|
72
|
-
|
|
85
|
+
observer.observe(element);
|
|
86
|
+
result = {
|
|
87
|
+
element,
|
|
88
|
+
view,
|
|
89
|
+
pane,
|
|
90
|
+
signature: JSON.stringify(pane),
|
|
91
|
+
renderer,
|
|
92
|
+
dispose,
|
|
93
|
+
};
|
|
94
|
+
if (location === 'main')
|
|
95
|
+
void view.ready?.catch((error) => {
|
|
96
|
+
if (!disposed)
|
|
97
|
+
reportError(error);
|
|
98
|
+
});
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
dispose();
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
73
105
|
}
|
|
74
106
|
//# sourceMappingURL=panes.js.map
|
package/dist/panes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panes.js","sourceRoot":"","sources":["../src/panes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxC,OAAO,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAUnC,MAAM,UAAU,SAAS,CACvB,GAAa,EACb,IAAU,EACV,QAA2B,EAC3B,OAA8B;IAE9B,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC;IAC5B,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;IAC/C,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC;QAChE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;QAChC,CAAC,CAAC,SAAS,CAAC;IACd,IAAI,
|
|
1
|
+
{"version":3,"file":"panes.js","sourceRoot":"","sources":["../src/panes.ts"],"names":[],"mappings":"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","sourcesContent":["import { message } from './messages.js';\nimport type { Pane } from 'quilt-core';\nimport type { ResolvedLayoutOptions, PaneView, PaneRenderer } from './types.js';\nimport { el } from './lifetime.js';\nexport interface MountedPane {\n element: HTMLElement;\n view: PaneView;\n pane: Pane;\n signature: string;\n renderer: PaneRenderer | undefined;\n failed?: boolean;\n dispose(): void;\n}\nexport function mountPane(\n doc: Document,\n pane: Pane,\n location: 'main' | 'popout',\n options: ResolvedLayoutOptions,\n): MountedPane {\n const win = doc.defaultView;\n if (!win) throw new Error('Pane needs a live document');\n const element = el(doc, 'div', 'layouts-pane');\n element.dataset.paneId = pane.id;\n const renderer = Object.hasOwn(options.renderers ?? {}, pane.type)\n ? options.renderers?.[pane.type]\n : undefined;\n let view: PaneView = { dispose() {} };\n let observer: ResizeObserver | undefined;\n let disposed = false;\n const controller = new (win as Window & typeof globalThis).AbortController();\n const cleanups: (() => void)[] = [];\n const reportCleanup = (error: unknown) => {\n try {\n options.onError?.(error);\n } catch {\n /* Finish teardown even if reporting fails. */\n }\n };\n const run = (callback: () => void) => {\n try {\n callback();\n } catch (error) {\n reportCleanup(error);\n }\n };\n const dispose = () => {\n if (disposed) return;\n disposed = true;\n run(() => controller.abort());\n run(() => observer?.disconnect());\n run(() => view.dispose());\n for (const cleanup of cleanups.splice(0).reverse()) run(cleanup);\n element.remove();\n };\n let result: MountedPane | undefined;\n const reportError = (error: unknown) => {\n if (result) result.failed = true;\n options.onError?.(error);\n };\n try {\n if (renderer)\n view = renderer({\n signal: controller.signal,\n onCleanup(callback) {\n if (disposed) run(callback);\n else cleanups.push(callback);\n },\n element,\n reportError,\n document: doc,\n window: win,\n pane,\n state: options.getPaneState?.(pane.id),\n location,\n }) ?? { dispose() {} };\n else {\n element.append(\n el(\n doc,\n 'div',\n 'layouts-placeholder',\n message(options, 'Unknown pane type: {type}. Register a renderer to display this pane.', {\n type: pane.type,\n }),\n ),\n );\n view = { dispose() {} };\n }\n observer = new ResizeObserver((entries) => {\n const rect = entries[0]?.contentRect;\n if (rect) {\n try {\n view.resize?.(rect.width, rect.height);\n } catch (error) {\n options.onError?.(error);\n }\n }\n });\n observer.observe(element);\n result = {\n element,\n view,\n pane,\n signature: JSON.stringify(pane),\n renderer,\n dispose,\n };\n if (location === 'main')\n void view.ready?.catch((error) => {\n if (!disposed) reportError(error);\n });\n return result;\n } catch (error) {\n dispose();\n throw error;\n }\n}\n"]}
|
package/dist/registry.d.ts
CHANGED
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B;2EACuE;IACvE,MAAM,EAAE,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,IAAI,GAAG,SAAS,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,SAAS,CAAC;CACtF;AACD,mFAAmF;AACnF,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,SAAS,CAAyB;IAC1C,YAAY,OAAO,GAAE,SAAS,eAAe,EAAO,EAEnD;IACD,IAAI,IAAI,SAAS,eAAe,EAAE,CAEjC;IACD,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,IAAI,CAc3C;IACD,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAK1C;IACD,OAAO,CAAC,IAAI;CAGb;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B;2EACuE;IACvE,MAAM,EAAE,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,IAAI,GAAG,SAAS,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,SAAS,CAAC;CACtF;AACD,mFAAmF;AACnF,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,SAAS,CAAyB;IAC1C,YAAY,OAAO,GAAE,SAAS,eAAe,EAAO,EAEnD;IACD,IAAI,IAAI,SAAS,eAAe,EAAE,CAEjC;IACD,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,IAAI,CAc3C;IACD,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAK1C;IACD,OAAO,CAAC,IAAI;CAGb;AAGD,wBAAgB,YAAY,IAAI,MAAM,CAIrC;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,CAAC,CAAC;IACR,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;CAC3F;AACD,2EAA2E;AAC3E,qBAAa,YAAY,CAAC,CAAC;IACzB,OAAO,CAAC,OAAO,CAAoD;IACnE,OAAO,CAAC,SAAS,CAAyB;IAC1C,QAAQ,CAAC,IAAI,cAAqB;IAClC,YAAY,OAAO,GAAE,SAAS,gBAAgB,CAAC,CAAC,CAAC,EAAO,EAEvD;IACD,IAAI,IAAI,SAAS,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,CAE/C;IACD,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAiC/C;IACD,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAK1C;IACD,OAAO,CAAC,IAAI;CAGb"}
|
package/dist/registry.js
CHANGED
|
@@ -37,6 +37,9 @@ export class TabRegistry {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
let instanceSequence = 0;
|
|
40
|
+
export function createPaneId() {
|
|
41
|
+
return (globalThis.crypto?.randomUUID?.() ?? `quilt-${Date.now().toString(36)}-${++instanceSequence}`);
|
|
42
|
+
}
|
|
40
43
|
/** Framework-neutral registrations; the adapter supplies the view type. */
|
|
41
44
|
export class PaneRegistry {
|
|
42
45
|
entries = new Map();
|
|
@@ -71,9 +74,7 @@ export class PaneRegistry {
|
|
|
71
74
|
title: stored.title,
|
|
72
75
|
...(stored.icon !== undefined ? { icon: stored.icon } : {}),
|
|
73
76
|
...supplied,
|
|
74
|
-
id: supplied.id ??
|
|
75
|
-
globalThis.crypto?.randomUUID?.() ??
|
|
76
|
-
`quilt-${Date.now().toString(36)}-${++instanceSequence}`,
|
|
77
|
+
id: supplied.id ?? createPaneId(),
|
|
77
78
|
};
|
|
78
79
|
},
|
|
79
80
|
});
|
package/dist/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAWA,mFAAmF;AACnF,MAAM,OAAO,WAAW;IACd,OAAO,GAAG,IAAI,GAAG,EAA2B,CAAC;IAC7C,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;IAC1C,YAAY,OAAO,GAA+B,EAAE;QAClD,KAAK,MAAM,KAAK,IAAI,OAAO;YAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC;IACD,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,QAAQ,CAAC,KAAsB;QAC7B,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YAC3B,GAAG,KAAK;YACR,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5E,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,GAAG,EAAE;YACV,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,MAAM;gBAAE,OAAO;YAClD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,CAAC,CAAC;IACJ,CAAC;IACD,SAAS,CAAC,QAAoB;QAC5B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC,CAAC;IACJ,CAAC;IACO,IAAI;QACV,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS;YAAE,QAAQ,EAAE,CAAC;IACpD,CAAC;CACF;AAED,IAAI,gBAAgB,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Pane } from 'quilt-core';\nexport interface TabRegistration {\n id: string;\n title: string;\n description?: string;\n icon?: string;\n keywords?: readonly string[];\n /** Source is undefined when creating content in a completely empty workspace.\n * Return a fresh pane with a unique id; undefined cancels creation. */\n create: (context: { source: Pane | undefined; groupId: string }) => Pane | undefined;\n}\n/** Available content types, independent of open pane instances and layout JSON. */\nexport class TabRegistry {\n private entries = new Map<string, TabRegistration>();\n private listeners = new Set<() => void>();\n constructor(entries: readonly TabRegistration[] = []) {\n for (const entry of entries) this.register(entry);\n }\n list(): readonly TabRegistration[] {\n return [...this.entries.values()];\n }\n register(entry: TabRegistration): () => void {\n if (!entry.id || this.entries.has(entry.id))\n throw new Error(`Duplicate or empty tab registration: ${entry.id}`);\n const stored = Object.freeze({\n ...entry,\n ...(entry.keywords ? { keywords: Object.freeze([...entry.keywords]) } : {}),\n });\n this.entries.set(entry.id, stored);\n this.emit();\n return () => {\n if (this.entries.get(entry.id) !== stored) return;\n this.entries.delete(entry.id);\n this.emit();\n };\n }\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n private emit() {\n for (const listener of this.listeners) listener();\n }\n}\n\nlet instanceSequence = 0;\nexport function createPaneId(): string {\n return (\n globalThis.crypto?.randomUUID?.() ?? `quilt-${Date.now().toString(36)}-${++instanceSequence}`\n );\n}\n\nexport interface PaneRegistration<T> {\n type: string;\n title: string;\n view: T;\n description?: string;\n icon?: string;\n keywords?: readonly string[];\n confirmClose?: boolean;\n create?: (context: Parameters<TabRegistration['create']>[0]) => Partial<Pane> | undefined;\n}\n/** Framework-neutral registrations; the adapter supplies the view type. */\nexport class PaneRegistry<T> {\n private entries = new Map<string, Readonly<PaneRegistration<T>>>();\n private listeners = new Set<() => void>();\n readonly tabs = new TabRegistry();\n constructor(entries: readonly PaneRegistration<T>[] = []) {\n entries.forEach((entry) => this.register(entry));\n }\n list(): readonly Readonly<PaneRegistration<T>>[] {\n return [...this.entries.values()];\n }\n register(entry: PaneRegistration<T>): () => void {\n if (!entry.type || this.entries.has(entry.type))\n throw new Error(`Duplicate or empty pane type: ${entry.type}`);\n const stored = Object.freeze({\n ...entry,\n ...(entry.keywords ? { keywords: Object.freeze([...entry.keywords]) } : {}),\n });\n this.entries.set(entry.type, stored);\n const removeTab = this.tabs.register({\n id: entry.type,\n title: entry.title,\n ...(entry.description !== undefined ? { description: entry.description } : {}),\n ...(entry.icon !== undefined ? { icon: entry.icon } : {}),\n ...(entry.keywords !== undefined ? { keywords: entry.keywords } : {}),\n create: (context) => {\n const supplied = stored.create ? stored.create(context) : {};\n if (supplied === undefined) return undefined;\n return {\n type: stored.type,\n title: stored.title,\n ...(stored.icon !== undefined ? { icon: stored.icon } : {}),\n ...supplied,\n id: supplied.id ?? createPaneId(),\n };\n },\n });\n this.emit();\n return () => {\n if (this.entries.get(entry.type) !== stored) return;\n this.entries.delete(entry.type);\n removeTab();\n this.emit();\n };\n }\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n private emit() {\n for (const listener of [...this.listeners]) listener();\n }\n}\n"]}
|
package/dist/renderer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,aAAa,EAAyB,aAAa,EAAe,MAAM,YAAY,CAAC;AAkBnG,wBAAgB,WAAW,CAAC,KAAK,GAAG,OAAO,EACzC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,GAC1B,aAAa,CAAC,KAAK,CAAC,CAgCtB"}
|
package/dist/renderer.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { chromeMinimum } from './chrome-size.js';
|
|
1
2
|
import { closeRequests } from './close.js';
|
|
2
3
|
import { dockLayout, parseWorkspace } from './workspace.js';
|
|
3
4
|
import { validateTheme, themePixels } from './theme.js';
|
|
@@ -9,17 +10,41 @@ import { bindCorners } from './corners.js';
|
|
|
9
10
|
import { applyTheme } from './theme.js';
|
|
10
11
|
import { fillTabs } from './tabs.js';
|
|
11
12
|
import { bindShortcuts } from './shortcuts.js';
|
|
12
|
-
import {
|
|
13
|
+
import { createActions } from './actions.js';
|
|
14
|
+
import { registeredShortcuts } from './shortcut-registration.js';
|
|
15
|
+
import { LayoutStore, createLayout, allocate, bounds as modelBounds, DIVIDER, findNode, groups, paneIds, } from 'quilt-core';
|
|
13
16
|
import { Scope, el, syncChildren } from './lifetime.js';
|
|
14
17
|
import { mountPane } from './panes.js';
|
|
15
18
|
import { Windows } from './windows.js';
|
|
16
19
|
import { createPaneMenu } from './menu.js';
|
|
17
20
|
let nextMount = 0;
|
|
18
21
|
export function mountLayout(host, input) {
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
if ('store' in input)
|
|
23
|
+
throw new Error('Quilt owns its store; pass initialLayout or initialWorkspace');
|
|
24
|
+
if (input.initialLayout !== undefined && input.initialWorkspace !== undefined)
|
|
25
|
+
throw new Error('initialLayout and initialWorkspace are mutually exclusive');
|
|
26
|
+
if (input.registry && (input.renderers || input.tabs))
|
|
27
|
+
throw new Error('registry cannot be combined with renderers or tabs');
|
|
28
|
+
const preset = input.initialWorkspace === undefined ? undefined : parseWorkspace(input.initialWorkspace);
|
|
29
|
+
const theme = preset?.theme ?? input.theme ?? {};
|
|
30
|
+
const tabBar = preset?.tabBar ?? input.tabBar ?? {};
|
|
31
|
+
validateTheme(theme);
|
|
32
|
+
validateTabBar(tabBar);
|
|
33
|
+
const store = new LayoutStore(dockLayout(preset?.layout ?? input.initialLayout ?? createLayout()), {
|
|
34
|
+
autoCollapse: preset?.autoCollapse ?? 'disabled',
|
|
35
|
+
});
|
|
36
|
+
// The initialization scope rolls back even if setup fails before returning a handle.
|
|
37
|
+
const scope = new Scope();
|
|
38
|
+
scope.add(() => store.dispose());
|
|
39
|
+
try {
|
|
40
|
+
return mountLayoutInternal(host, { ...input, store, theme, tabBar }, scope);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
scope.dispose();
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
21
46
|
}
|
|
22
|
-
function mountLayoutInternal(host, input) {
|
|
47
|
+
function mountLayoutInternal(host, input, scope) {
|
|
23
48
|
const options = { ...input };
|
|
24
49
|
const configure = () => {
|
|
25
50
|
if (options.registry) {
|
|
@@ -37,7 +62,7 @@ function mountLayoutInternal(host, input) {
|
|
|
37
62
|
const doc = host.ownerDocument, win = doc.defaultView;
|
|
38
63
|
if (!win)
|
|
39
64
|
throw new Error('Layout host must belong to a live document');
|
|
40
|
-
const
|
|
65
|
+
const regions = new Map(), panes = new Map();
|
|
41
66
|
const root = el(doc, 'div', 'layouts');
|
|
42
67
|
applyTheme(root, options.theme ?? {});
|
|
43
68
|
root.setAttribute('aria-label', message(options, 'Pane workspace'));
|
|
@@ -46,6 +71,7 @@ function mountLayoutInternal(host, input) {
|
|
|
46
71
|
status.setAttribute('role', 'status');
|
|
47
72
|
root.append(stage, status);
|
|
48
73
|
host.append(root);
|
|
74
|
+
scope.add(() => root.remove());
|
|
49
75
|
let disposed = false, rendering = false, again = false, dragScope;
|
|
50
76
|
let dragId;
|
|
51
77
|
const error = (e) => {
|
|
@@ -65,14 +91,49 @@ function mountLayoutInternal(host, input) {
|
|
|
65
91
|
error(e);
|
|
66
92
|
}
|
|
67
93
|
};
|
|
68
|
-
const
|
|
94
|
+
const closeScope = new Scope();
|
|
95
|
+
scope.add(() => closeScope.dispose());
|
|
96
|
+
const requestClose = closeRequests(root, options, closeScope, error);
|
|
69
97
|
const windows = new Windows(doc, options, error, root);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
98
|
+
scope.add(() => windows.dispose());
|
|
99
|
+
scope.add(() => {
|
|
100
|
+
for (const pane of panes.values())
|
|
101
|
+
pane.dispose();
|
|
102
|
+
panes.clear();
|
|
103
|
+
for (const region of regions.values())
|
|
104
|
+
region.scope.dispose();
|
|
105
|
+
regions.clear();
|
|
75
106
|
});
|
|
107
|
+
const actions = createActions(options, {
|
|
108
|
+
context: () => shortcutContext(),
|
|
109
|
+
disposed: () => disposed,
|
|
110
|
+
close: requestClose,
|
|
111
|
+
popout: (id) => windows.open(id),
|
|
112
|
+
report: error,
|
|
113
|
+
create(action, group, pane) {
|
|
114
|
+
const region = regions.get(group.id);
|
|
115
|
+
if (!region)
|
|
116
|
+
throw new Error('Region is not mounted');
|
|
117
|
+
const anchor = region.header ?? region.element;
|
|
118
|
+
if (action === 'addTab')
|
|
119
|
+
menu.addTab(anchor, group);
|
|
120
|
+
else {
|
|
121
|
+
const direction = action === 'splitLeft'
|
|
122
|
+
? 'left'
|
|
123
|
+
: action === 'splitRight'
|
|
124
|
+
? 'right'
|
|
125
|
+
: action === 'splitUp'
|
|
126
|
+
? 'top'
|
|
127
|
+
: 'bottom';
|
|
128
|
+
menu.open(anchor, pane, group, direction);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
const menu = createPaneMenu(root, options, render, error, actions);
|
|
133
|
+
scope.add(() => menu.dispose());
|
|
134
|
+
scope.add(() => dragScope?.dispose());
|
|
135
|
+
const shortcutContext = bindShortcuts(root, options, scope, actions.canExecuteAction, actions.executeAction, menu.getShortcutTarget);
|
|
136
|
+
registeredShortcuts(options);
|
|
76
137
|
const renderOptions = { ...options, onError: error };
|
|
77
138
|
function button(text, title, action) {
|
|
78
139
|
const b = el(doc, 'button', 'layouts-button', text);
|
|
@@ -101,7 +162,7 @@ function mountLayoutInternal(host, input) {
|
|
|
101
162
|
r.header = el(doc, 'header', 'layouts-header');
|
|
102
163
|
r.body = el(doc, 'div', 'layouts-body');
|
|
103
164
|
r.element.append(r.header, r.body);
|
|
104
|
-
r.
|
|
165
|
+
r.tabStyle = () => {
|
|
105
166
|
const current = findNode(options.store.getSnapshot().root, node.id);
|
|
106
167
|
return {
|
|
107
168
|
...tabBar,
|
|
@@ -113,8 +174,9 @@ function mountLayoutInternal(host, input) {
|
|
|
113
174
|
? { placement: current.tabPlacement }
|
|
114
175
|
: {}),
|
|
115
176
|
};
|
|
116
|
-
}
|
|
117
|
-
|
|
177
|
+
};
|
|
178
|
+
r.updateTabBar = bindTabBar(r.element, r.header, root, r.scope, r.tabStyle, () => options.messages ?? {});
|
|
179
|
+
bindCorners(r.element, node.id, options, r.scope, (pane, group, direction, ratio) => menu.open(r.header, pane, group, direction, ratio), error, bounds);
|
|
118
180
|
r.element.setAttribute('aria-label', message(options, 'Pane region'));
|
|
119
181
|
r.scope.listen(r.element, 'dragover', (event) => {
|
|
120
182
|
if (!dragId)
|
|
@@ -169,7 +231,7 @@ function mountLayoutInternal(host, input) {
|
|
|
169
231
|
const resize = isolatedResize(geometryLayout(), node.id, (child, axis) => {
|
|
170
232
|
const rect = regions.get(child.id).element.getBoundingClientRect();
|
|
171
233
|
return axis === 'horizontal' ? rect.width : rect.height;
|
|
172
|
-
});
|
|
234
|
+
}, bounds);
|
|
173
235
|
r.divider.setPointerCapture(e.pointerId);
|
|
174
236
|
root.classList.add('layouts-resizing');
|
|
175
237
|
drag.add(() => {
|
|
@@ -212,7 +274,7 @@ function mountLayoutInternal(host, input) {
|
|
|
212
274
|
const resize = isolatedResize(geometryLayout(), node.id, (child, axis) => {
|
|
213
275
|
const rect = regions.get(child.id).element.getBoundingClientRect();
|
|
214
276
|
return axis === 'horizontal' ? rect.width : rect.height;
|
|
215
|
-
});
|
|
277
|
+
}, bounds);
|
|
216
278
|
act(() => options.store.resizeMany(resize.ratios(Math.max(0.001, Math.min(0.999, n.ratio + (e.key === keys[0] ? -1 : 1) * (e.shiftKey ? 0.1 : 0.02)))), { source: 'user' }));
|
|
217
279
|
}
|
|
218
280
|
});
|
|
@@ -445,9 +507,30 @@ function mountLayoutInternal(host, input) {
|
|
|
445
507
|
return node.axis === 'horizontal' ? b.minWidth < b.maxWidth : b.minHeight < b.maxHeight;
|
|
446
508
|
});
|
|
447
509
|
}
|
|
510
|
+
const chromeSizes = new Map();
|
|
511
|
+
function bounds(node, layout) {
|
|
512
|
+
return modelBounds(node, layout, (group, constraints) => {
|
|
513
|
+
const chrome = chromeSizes.get(group.id);
|
|
514
|
+
if (!chrome)
|
|
515
|
+
return constraints;
|
|
516
|
+
const minWidth = Math.max(constraints.minWidth, chrome.minWidth);
|
|
517
|
+
const minHeight = Math.max(constraints.minHeight, chrome.minHeight);
|
|
518
|
+
return {
|
|
519
|
+
minWidth,
|
|
520
|
+
minHeight,
|
|
521
|
+
maxWidth: Math.max(constraints.maxWidth, minWidth),
|
|
522
|
+
maxHeight: Math.max(constraints.maxHeight, minHeight),
|
|
523
|
+
};
|
|
524
|
+
});
|
|
525
|
+
}
|
|
448
526
|
// Renderer-only gaps participate in bounds/allocation without modifying Layout JSON.
|
|
449
527
|
function geometryLayout() {
|
|
450
528
|
const layout = options.store.getSnapshot();
|
|
529
|
+
chromeSizes.clear();
|
|
530
|
+
for (const [id, region] of regions) {
|
|
531
|
+
if (region.header && region.tabStyle && region.element.isConnected)
|
|
532
|
+
chromeSizes.set(id, chromeMinimum(region.element, region.header, region.tabStyle()));
|
|
533
|
+
}
|
|
451
534
|
const pixels = (property, fallback) => themePixels(root, property, fallback);
|
|
452
535
|
const width = pixels('--layouts-resize-handle-width', DIVIDER);
|
|
453
536
|
const disabledWidth = pixels('--layouts-disabled-resize-handle-width', 0);
|
|
@@ -537,9 +620,23 @@ function mountLayoutInternal(host, input) {
|
|
|
537
620
|
}
|
|
538
621
|
render();
|
|
539
622
|
}));
|
|
540
|
-
|
|
623
|
+
// Geometry can change the stage's scrollbar gutter. Defer those writes out
|
|
624
|
+
// of ResizeObserver delivery so WebKit does not detect a nested resize loop.
|
|
625
|
+
let measureFrame = 0;
|
|
626
|
+
const observer = new ResizeObserver(() => {
|
|
627
|
+
if (!measureFrame && !disposed)
|
|
628
|
+
measureFrame = doc.defaultView.requestAnimationFrame(() => {
|
|
629
|
+
measureFrame = 0;
|
|
630
|
+
if (!disposed)
|
|
631
|
+
measure();
|
|
632
|
+
});
|
|
633
|
+
});
|
|
541
634
|
observer.observe(stage);
|
|
542
|
-
scope.add(() =>
|
|
635
|
+
scope.add(() => {
|
|
636
|
+
observer.disconnect();
|
|
637
|
+
if (measureFrame)
|
|
638
|
+
doc.defaultView.cancelAnimationFrame(measureFrame);
|
|
639
|
+
});
|
|
543
640
|
scope.listen(doc, 'dragend', () => {
|
|
544
641
|
dragId = undefined;
|
|
545
642
|
for (const r of regions.values())
|
|
@@ -587,16 +684,27 @@ function mountLayoutInternal(host, input) {
|
|
|
587
684
|
themeObserver.observe(ancestor, { attributes: true });
|
|
588
685
|
themeObserver.observe(doc.head, { childList: true, subtree: true, characterData: true });
|
|
589
686
|
scope.add(() => themeObserver.disconnect());
|
|
687
|
+
// Cancel asynchronous actions before tearing down views and the owned store.
|
|
688
|
+
scope.add(() => {
|
|
689
|
+
closeScope.dispose();
|
|
690
|
+
windows.dispose();
|
|
691
|
+
});
|
|
590
692
|
render();
|
|
591
693
|
return {
|
|
694
|
+
store: options.store,
|
|
695
|
+
...actions,
|
|
696
|
+
getShortcuts: () => registeredShortcuts(options),
|
|
592
697
|
requestClose,
|
|
593
698
|
refreshTheme,
|
|
594
699
|
updateOptions(next) {
|
|
595
700
|
if (disposed)
|
|
596
701
|
return;
|
|
702
|
+
if ('store' in next || 'initialLayout' in next || 'initialWorkspace' in next)
|
|
703
|
+
throw new Error('Initial configuration cannot be updated; use loadWorkspace or store commands');
|
|
597
704
|
const candidate = { ...options, ...next };
|
|
598
705
|
if (candidate.registry && (next.renderers || next.tabs))
|
|
599
706
|
throw new Error('registry cannot be combined with renderers or tabs');
|
|
707
|
+
registeredShortcuts(candidate);
|
|
600
708
|
validateTheme(candidate.theme ?? {});
|
|
601
709
|
validateTabBar(candidate.tabBar ?? {});
|
|
602
710
|
const sameRegistrations = candidate.registry === options.registry ||
|
|
@@ -618,8 +726,7 @@ function mountLayoutInternal(host, input) {
|
|
|
618
726
|
(!candidate.registry && candidate.tabs !== options.tabs) ||
|
|
619
727
|
candidate.renderIcon !== options.renderIcon ||
|
|
620
728
|
candidate.popouts !== options.popouts ||
|
|
621
|
-
JSON.stringify(candidate.messages) !== JSON.stringify(options.messages)
|
|
622
|
-
JSON.stringify(candidate.shortcuts) !== JSON.stringify(options.shortcuts);
|
|
729
|
+
JSON.stringify(candidate.messages) !== JSON.stringify(options.messages);
|
|
623
730
|
// Clear maps derived from the old registry when returning to low-level registration.
|
|
624
731
|
if ('registry' in next && !next.registry && options.registry) {
|
|
625
732
|
Object.assign(options, { renderers: undefined, tabs: undefined });
|
|
@@ -631,6 +738,7 @@ function mountLayoutInternal(host, input) {
|
|
|
631
738
|
tabBar = structuredClone(options.tabBar ?? {});
|
|
632
739
|
bindRegistry();
|
|
633
740
|
refreshOptions(repaintChrome);
|
|
741
|
+
menu.refreshShortcuts();
|
|
634
742
|
refreshTheme();
|
|
635
743
|
},
|
|
636
744
|
exportWorkspace() {
|
|
@@ -691,23 +799,7 @@ function mountLayoutInternal(host, input) {
|
|
|
691
799
|
if (disposed)
|
|
692
800
|
return;
|
|
693
801
|
disposed = true;
|
|
694
|
-
menu.dispose();
|
|
695
|
-
dragScope?.dispose();
|
|
696
802
|
scope.dispose();
|
|
697
|
-
windows.dispose();
|
|
698
|
-
for (const p of panes.values()) {
|
|
699
|
-
try {
|
|
700
|
-
p.dispose();
|
|
701
|
-
}
|
|
702
|
-
catch (e) {
|
|
703
|
-
error(e);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
for (const r of regions.values())
|
|
707
|
-
r.scope.dispose();
|
|
708
|
-
panes.clear();
|
|
709
|
-
regions.clear();
|
|
710
|
-
root.remove();
|
|
711
803
|
},
|
|
712
804
|
};
|
|
713
805
|
}
|