quilt-vanilla 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +6 -0
  2. package/dist/action-icons.d.ts +2 -2
  3. package/dist/action-icons.d.ts.map +1 -1
  4. package/dist/action-icons.js.map +1 -1
  5. package/dist/close.d.ts +4 -0
  6. package/dist/close.d.ts.map +1 -0
  7. package/dist/close.js +95 -0
  8. package/dist/close.js.map +1 -0
  9. package/dist/corners.d.ts +2 -2
  10. package/dist/corners.d.ts.map +1 -1
  11. package/dist/corners.js +15 -7
  12. package/dist/corners.js.map +1 -1
  13. package/dist/index.d.ts +7 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +4 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/lifetime.d.ts.map +1 -1
  18. package/dist/lifetime.js +1 -0
  19. package/dist/lifetime.js.map +1 -1
  20. package/dist/menu.d.ts +2 -2
  21. package/dist/menu.d.ts.map +1 -1
  22. package/dist/menu.js +35 -28
  23. package/dist/menu.js.map +1 -1
  24. package/dist/messages.d.ts +62 -0
  25. package/dist/messages.d.ts.map +1 -0
  26. package/dist/messages.js +62 -0
  27. package/dist/messages.js.map +1 -0
  28. package/dist/panes.d.ts +2 -2
  29. package/dist/panes.d.ts.map +1 -1
  30. package/dist/panes.js +20 -4
  31. package/dist/panes.js.map +1 -1
  32. package/dist/picker.d.ts +2 -2
  33. package/dist/picker.d.ts.map +1 -1
  34. package/dist/picker.js +9 -6
  35. package/dist/picker.js.map +1 -1
  36. package/dist/registry.d.ts +21 -0
  37. package/dist/registry.d.ts.map +1 -1
  38. package/dist/registry.js +61 -0
  39. package/dist/registry.js.map +1 -1
  40. package/dist/renderer.d.ts +1 -1
  41. package/dist/renderer.d.ts.map +1 -1
  42. package/dist/renderer.js +169 -25
  43. package/dist/renderer.js.map +1 -1
  44. package/dist/shortcuts.d.ts +5 -3
  45. package/dist/shortcuts.d.ts.map +1 -1
  46. package/dist/shortcuts.js +57 -41
  47. package/dist/shortcuts.js.map +1 -1
  48. package/dist/styles.css +9 -9
  49. package/dist/tab-bar.d.ts +2 -1
  50. package/dist/tab-bar.d.ts.map +1 -1
  51. package/dist/tab-bar.js +25 -4
  52. package/dist/tab-bar.js.map +1 -1
  53. package/dist/tab-tooltip.d.ts +2 -1
  54. package/dist/tab-tooltip.d.ts.map +1 -1
  55. package/dist/tab-tooltip.js +5 -2
  56. package/dist/tab-tooltip.js.map +1 -1
  57. package/dist/tabs.d.ts +3 -2
  58. package/dist/tabs.d.ts.map +1 -1
  59. package/dist/tabs.js +3 -2
  60. package/dist/tabs.js.map +1 -1
  61. package/dist/theme.d.ts +3 -0
  62. package/dist/theme.d.ts.map +1 -1
  63. package/dist/theme.js +31 -0
  64. package/dist/theme.js.map +1 -1
  65. package/dist/types.d.ts +58 -11
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/types.js.map +1 -1
  68. package/dist/windows.d.ts +8 -4
  69. package/dist/windows.d.ts.map +1 -1
  70. package/dist/windows.js +118 -23
  71. package/dist/windows.js.map +1 -1
  72. package/dist/workspace.d.ts +14 -0
  73. package/dist/workspace.d.ts.map +1 -0
  74. package/dist/workspace.js +39 -0
  75. package/dist/workspace.js.map +1 -0
  76. package/package.json +2 -2
  77. package/src/action-icons.ts +2 -2
  78. package/src/close.ts +120 -0
  79. package/src/corners.ts +28 -9
  80. package/src/index.ts +7 -0
  81. package/src/lifetime.ts +1 -0
  82. package/src/menu.ts +52 -34
  83. package/src/messages.ts +71 -0
  84. package/src/panes.ts +20 -6
  85. package/src/picker.ts +11 -8
  86. package/src/registry.ts +71 -0
  87. package/src/renderer.ts +205 -38
  88. package/src/shortcuts.ts +63 -54
  89. package/src/styles.css +9 -9
  90. package/src/tab-bar.ts +30 -5
  91. package/src/tab-tooltip.ts +6 -1
  92. package/src/tabs.ts +6 -4
  93. package/src/theme.ts +30 -0
  94. package/src/types.ts +57 -11
  95. package/src/windows.ts +127 -23
  96. package/src/workspace.ts +44 -0
package/README.md CHANGED
@@ -56,3 +56,9 @@ The host needs an explicit height. Supply a `TabRegistry` through `tabs` to offe
56
56
  Call `mounted.popout(id)` from a user action. Companions are same-origin and owned by the main session. Use the supplied document/window inside renderers and release every view-owned resource in `dispose`. Keep app data outside view lifetimes.
57
57
 
58
58
  See [API](https://github.com/niko-dellic/quilt/blob/main/docs/api.md), [lifecycle](https://github.com/niko-dellic/quilt/blob/main/docs/lifecycle.md), and [migration](https://github.com/niko-dellic/quilt/blob/main/docs/migration.md).
59
+
60
+ Use `mounted.exportWorkspace()` and `mounted.loadWorkspace(input)` to round-trip
61
+ layout and appearance as JSON. Popouts export docked copies without closing live
62
+ windows. `popout` returns `Promise<boolean>` and must be called from a user gesture.
63
+ See [web integration](https://github.com/niko-dellic/quilt/blob/main/docs/integration.md)
64
+ for unified registration, custom themes, close confirmation, and storage examples.
@@ -1,4 +1,4 @@
1
- import type { LayoutOptions } from './types.js';
1
+ import type { ResolvedLayoutOptions } from './types.js';
2
2
  declare const paths: {
3
3
  readonly 'add-tab': readonly ['M12 5v14M5 12h14'];
4
4
  readonly 'split-right': readonly ['M3 4h18v16H3z', 'M12 4v16'];
@@ -13,6 +13,6 @@ declare const paths: {
13
13
  };
14
14
  export type ActionIcon = keyof typeof paths;
15
15
  /** Small built-in chrome icons; applications may override the namespaced keys. */
16
- export declare function actionIcon(doc: Document, name: ActionIcon, options: Pick<LayoutOptions, 'renderIcon'>): Element;
16
+ export declare function actionIcon(doc: Document, name: ActionIcon, options: Pick<ResolvedLayoutOptions, 'renderIcon'>): Element;
17
17
  export {};
18
18
  //# sourceMappingURL=action-icons.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action-icons.d.ts","sourceRoot":"","sources":["../src/action-icons.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,QAAA,MAAM,KAAK;aACT,SAAS,YAAG,kBAAkB;aAC9B,aAAa,YAAG,eAAe,EAAE,UAAU;aAC3C,aAAa,YAAG,eAAe,EAAE,UAAU;aAC3C,iBAAiB,YAAG,eAAe,EAAE,SAAS,EAAE,SAAS;aACzD,IAAI,YAAG,eAAe,EAAE,2BAA2B;aACnD,QAAQ,YAAG,sCAAsC;aACjD,OAAO,YAAG,wCAAwC;aAClD,MAAM,YAAG,qBAAqB,EAAE,kBAAkB;aAClD,KAAK,YAAG,sBAAsB;aAC9B,MAAM,YAAG,sCAAsC;CACvC,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,KAAK,CAAC;AAC5C,kFAAkF;AAClF,wBAAgB,UAAU,CACxB,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,GACzC,OAAO,CAoBT"}
1
+ {"version":3,"file":"action-icons.d.ts","sourceRoot":"","sources":["../src/action-icons.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,QAAA,MAAM,KAAK;aACT,SAAS,YAAG,kBAAkB;aAC9B,aAAa,YAAG,eAAe,EAAE,UAAU;aAC3C,aAAa,YAAG,eAAe,EAAE,UAAU;aAC3C,iBAAiB,YAAG,eAAe,EAAE,SAAS,EAAE,SAAS;aACzD,IAAI,YAAG,eAAe,EAAE,2BAA2B;aACnD,QAAQ,YAAG,sCAAsC;aACjD,OAAO,YAAG,wCAAwC;aAClD,MAAM,YAAG,qBAAqB,EAAE,kBAAkB;aAClD,KAAK,YAAG,sBAAsB;aAC9B,MAAM,YAAG,sCAAsC;CACvC,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,KAAK,CAAC;AAC5C,kFAAkF;AAClF,wBAAgB,UAAU,CACxB,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,YAAY,CAAC,GACjD,OAAO,CAoBT"}
@@ -1 +1 @@
1
- {"version":3,"file":"action-icons.js","sourceRoot":"","sources":["../src/action-icons.ts"],"names":[],"mappings":"AACA,MAAM,KAAK,GAAG;IACZ,SAAS,EAAE,CAAC,kBAAkB,CAAC;IAC/B,aAAa,EAAE,CAAC,eAAe,EAAE,UAAU,CAAC;IAC5C,aAAa,EAAE,CAAC,eAAe,EAAE,UAAU,CAAC;IAC5C,iBAAiB,EAAE,CAAC,eAAe,EAAE,SAAS,EAAE,SAAS,CAAC;IAC1D,IAAI,EAAE,CAAC,eAAe,EAAE,2BAA2B,CAAC;IACpD,QAAQ,EAAE,CAAC,sCAAsC,CAAC;IAClD,OAAO,EAAE,CAAC,wCAAwC,CAAC;IACnD,MAAM,EAAE,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;IACnD,KAAK,EAAE,CAAC,sBAAsB,CAAC;IAC/B,MAAM,EAAE,CAAC,sCAAsC,CAAC;CACxC,CAAC;AAEX,kFAAkF;AAClF,MAAM,UAAU,UAAU,CACxB,GAAa,EACb,IAAgB,EAChB,OAA0C;IAE1C,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,WAAW,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;IAC5D,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;IACrE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC;QACxC,OAAO,EAAE,WAAW;QACpB,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,cAAc;QACtB,cAAc,EAAE,KAAK;QACrB,gBAAgB,EAAE,OAAO;QACzB,iBAAiB,EAAE,OAAO;QAC1B,SAAS,EAAE,OAAO;KACnB,CAAC;QACA,GAAG,CAAC,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAC3D,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC1B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["import type { LayoutOptions } from './types.js';\nconst paths = {\n 'add-tab': ['M12 5v14M5 12h14'],\n 'split-right': ['M3 4h18v16H3z', 'M12 4v16'],\n 'split-below': ['M3 4h18v16H3z', 'M3 12h18'],\n 'tab-orientation': ['M3 4h18v16H3z', 'M3 9h18', 'M8 9v11'],\n join: ['M3 4h18v16H3z', 'm7 9 3 3-3 3m10-6-3 3 3 3'],\n maximize: ['M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5'],\n restore: ['M3 8h5V3m13 5h-5V3M8 21v-5H3m13 5v-5h5'],\n popout: ['M14 3h7v7m0-7L11 13', 'M10 3H3v18h18v-7'],\n close: ['m6 6 12 12M6 18 18 6'],\n cancel: ['M9 5 2 12l7 7M2 12h14a5 5 0 0 0 0-10'],\n} as const;\nexport type ActionIcon = keyof typeof paths;\n/** Small built-in chrome icons; applications may override the namespaced keys. */\nexport function actionIcon(\n doc: Document,\n name: ActionIcon,\n options: Pick<LayoutOptions, 'renderIcon'>,\n): Element {\n const custom = options.renderIcon?.(`layouts:${name}`, doc);\n if (custom) return custom;\n const svg = doc.createElementNS('http://www.w3.org/2000/svg', 'svg');\n for (const [key, value] of Object.entries({\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': '1.7',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n focusable: 'false',\n }))\n svg.setAttribute(key, value);\n for (const d of paths[name]) {\n const path = doc.createElementNS(svg.namespaceURI, 'path');\n path.setAttribute('d', d);\n svg.append(path);\n }\n return svg;\n}\n"]}
1
+ {"version":3,"file":"action-icons.js","sourceRoot":"","sources":["../src/action-icons.ts"],"names":[],"mappings":"AACA,MAAM,KAAK,GAAG;IACZ,SAAS,EAAE,CAAC,kBAAkB,CAAC;IAC/B,aAAa,EAAE,CAAC,eAAe,EAAE,UAAU,CAAC;IAC5C,aAAa,EAAE,CAAC,eAAe,EAAE,UAAU,CAAC;IAC5C,iBAAiB,EAAE,CAAC,eAAe,EAAE,SAAS,EAAE,SAAS,CAAC;IAC1D,IAAI,EAAE,CAAC,eAAe,EAAE,2BAA2B,CAAC;IACpD,QAAQ,EAAE,CAAC,sCAAsC,CAAC;IAClD,OAAO,EAAE,CAAC,wCAAwC,CAAC;IACnD,MAAM,EAAE,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;IACnD,KAAK,EAAE,CAAC,sBAAsB,CAAC;IAC/B,MAAM,EAAE,CAAC,sCAAsC,CAAC;CACxC,CAAC;AAEX,kFAAkF;AAClF,MAAM,UAAU,UAAU,CACxB,GAAa,EACb,IAAgB,EAChB,OAAkD;IAElD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,WAAW,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;IAC5D,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;IACrE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC;QACxC,OAAO,EAAE,WAAW;QACpB,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,cAAc;QACtB,cAAc,EAAE,KAAK;QACrB,gBAAgB,EAAE,OAAO;QACzB,iBAAiB,EAAE,OAAO;QAC1B,SAAS,EAAE,OAAO;KACnB,CAAC;QACA,GAAG,CAAC,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAC3D,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC1B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["import type { ResolvedLayoutOptions } from './types.js';\nconst paths = {\n 'add-tab': ['M12 5v14M5 12h14'],\n 'split-right': ['M3 4h18v16H3z', 'M12 4v16'],\n 'split-below': ['M3 4h18v16H3z', 'M3 12h18'],\n 'tab-orientation': ['M3 4h18v16H3z', 'M3 9h18', 'M8 9v11'],\n join: ['M3 4h18v16H3z', 'm7 9 3 3-3 3m10-6-3 3 3 3'],\n maximize: ['M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5'],\n restore: ['M3 8h5V3m13 5h-5V3M8 21v-5H3m13 5v-5h5'],\n popout: ['M14 3h7v7m0-7L11 13', 'M10 3H3v18h18v-7'],\n close: ['m6 6 12 12M6 18 18 6'],\n cancel: ['M9 5 2 12l7 7M2 12h14a5 5 0 0 0 0-10'],\n} as const;\nexport type ActionIcon = keyof typeof paths;\n/** Small built-in chrome icons; applications may override the namespaced keys. */\nexport function actionIcon(\n doc: Document,\n name: ActionIcon,\n options: Pick<ResolvedLayoutOptions, 'renderIcon'>,\n): Element {\n const custom = options.renderIcon?.(`layouts:${name}`, doc);\n if (custom) return custom;\n const svg = doc.createElementNS('http://www.w3.org/2000/svg', 'svg');\n for (const [key, value] of Object.entries({\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': '1.7',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n focusable: 'false',\n }))\n svg.setAttribute(key, value);\n for (const d of paths[name]) {\n const path = doc.createElementNS(svg.namespaceURI, 'path');\n path.setAttribute('d', d);\n svg.append(path);\n }\n return svg;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { ResolvedLayoutOptions } from './types.js';
2
+ import { Scope } from './lifetime.js';
3
+ export declare function closeRequests(root: HTMLElement, options: ResolvedLayoutOptions, scope: Scope, report: (error: unknown) => void): (id: string, kind?: 'pane' | 'group') => Promise<boolean>;
4
+ //# sourceMappingURL=close.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"close.d.ts","sourceRoot":"","sources":["../src/close.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgB,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAM,KAAK,EAAE,MAAM,eAAe,CAAC;AAE1C,wBAAgB,aAAa,CAC3B,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,qBAAqB,EAC9B,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,QASd,MAAM,SAAQ,MAAM,GAAG,OAAO,KAAY,OAAO,CAAC,OAAO,CAAC,CAqG7E"}
package/dist/close.js ADDED
@@ -0,0 +1,95 @@
1
+ import { findNode } from 'quilt-core';
2
+ import { el, Scope } from './lifetime.js';
3
+ import { message } from './messages.js';
4
+ export function closeRequests(root, options, scope, report) {
5
+ let pending;
6
+ let disposed = false;
7
+ scope.add(() => {
8
+ disposed = true;
9
+ pending?.abort();
10
+ });
11
+ const signature = (panes) => JSON.stringify(panes);
12
+ return async (id, kind = 'pane') => {
13
+ if (pending || disposed)
14
+ return false;
15
+ const controller = new AbortController();
16
+ pending = controller;
17
+ const collect = () => {
18
+ const snapshot = options.store.getSnapshot();
19
+ const node = kind === 'group' ? findNode(snapshot.root, id) : undefined;
20
+ const ids = kind === 'group' ? (node?.kind === 'group' ? node.panes : []) : [id];
21
+ return ids.map((key) => snapshot.panes[key]).filter((pane) => !!pane);
22
+ };
23
+ const panes = collect();
24
+ const requiringConfirmation = panes.filter((pane) => pane.confirmClose ??
25
+ options.registry?.list().find((entry) => entry.type === pane.type)?.confirmClose ??
26
+ false);
27
+ const request = {
28
+ panes,
29
+ requiringConfirmation,
30
+ signal: controller.signal,
31
+ ...(kind === 'group' ? { groupId: id } : {}),
32
+ };
33
+ let cleanup = () => { };
34
+ try {
35
+ if (!panes.length || panes.some((pane) => !options.store.can(pane.id, 'close')))
36
+ return false;
37
+ if (requiringConfirmation.length) {
38
+ const decision = options.confirmClose
39
+ ? Promise.resolve(options.confirmClose(request))
40
+ : new Promise((resolve) => {
41
+ const dialog = el(root.ownerDocument, 'dialog', 'layouts-menu layouts-confirm');
42
+ dialog.setAttribute('aria-label', message(options, 'Close selected panes?'));
43
+ const title = el(root.ownerDocument, 'h2', '', message(options, 'Close selected panes?'));
44
+ const list = el(root.ownerDocument, 'p', '', requiringConfirmation.map((pane) => pane.title).join(', '));
45
+ const cancel = el(root.ownerDocument, 'button', 'layouts-button', message(options, 'Cancel'));
46
+ const close = el(root.ownerDocument, 'button', 'layouts-button', message(options, 'Close'));
47
+ cancel.type = close.type = 'button';
48
+ cancel.onclick = () => resolve(false);
49
+ close.onclick = () => resolve(true);
50
+ dialog.oncancel = (event) => {
51
+ event.preventDefault();
52
+ resolve(false);
53
+ };
54
+ dialog.append(title, list, cancel, close);
55
+ root.append(dialog);
56
+ cleanup = () => dialog.remove();
57
+ dialog.showModal();
58
+ cancel.focus();
59
+ });
60
+ const aborted = new Promise((resolve) => controller.signal.addEventListener('abort', () => resolve(false), { once: true }));
61
+ const unsubscribe = options.store.subscribe((event) => {
62
+ if (event.action === 'load' || signature(collect()) !== signature(panes))
63
+ controller.abort();
64
+ });
65
+ const previousCleanup = cleanup;
66
+ cleanup = () => {
67
+ unsubscribe();
68
+ previousCleanup();
69
+ };
70
+ if (!(await Promise.race([decision, aborted])))
71
+ return false;
72
+ }
73
+ if (controller.signal.aborted ||
74
+ signature(collect()) !== signature(panes) ||
75
+ panes.some((pane) => !options.store.can(pane.id, 'close')))
76
+ return false;
77
+ if (kind === 'group')
78
+ options.store.closeGroup(id, { source: 'user' });
79
+ else
80
+ options.store.close(id, { source: 'user' });
81
+ return true;
82
+ }
83
+ catch (error) {
84
+ report(error);
85
+ return false;
86
+ }
87
+ finally {
88
+ cleanup();
89
+ controller.abort();
90
+ if (pending === controller)
91
+ pending = undefined;
92
+ }
93
+ };
94
+ }
95
+ //# sourceMappingURL=close.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"close.js","sourceRoot":"","sources":["../src/close.ts"],"names":[],"mappings":"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","sourcesContent":["import { findNode } from 'quilt-core';\nimport type { Pane } from 'quilt-core';\nimport type { CloseRequest, ResolvedLayoutOptions } from './types.js';\nimport { el, Scope } from './lifetime.js';\nimport { message } from './messages.js';\nexport function closeRequests(\n root: HTMLElement,\n options: ResolvedLayoutOptions,\n scope: Scope,\n report: (error: unknown) => void,\n) {\n let pending: AbortController | undefined;\n let disposed = false;\n scope.add(() => {\n disposed = true;\n pending?.abort();\n });\n const signature = (panes: readonly Pane[]) => JSON.stringify(panes);\n return async (id: string, kind: 'pane' | 'group' = 'pane'): Promise<boolean> => {\n if (pending || disposed) return false;\n const controller = new AbortController();\n pending = controller;\n const collect = (): Pane[] => {\n const snapshot = options.store.getSnapshot();\n const node = kind === 'group' ? findNode(snapshot.root, id) : undefined;\n const ids = kind === 'group' ? (node?.kind === 'group' ? node.panes : []) : [id];\n return ids.map((key) => snapshot.panes[key]).filter((pane): pane is Pane => !!pane);\n };\n const panes = collect();\n const requiringConfirmation = panes.filter(\n (pane) =>\n pane.confirmClose ??\n options.registry?.list().find((entry) => entry.type === pane.type)?.confirmClose ??\n false,\n );\n const request: CloseRequest = {\n panes,\n requiringConfirmation,\n signal: controller.signal,\n ...(kind === 'group' ? { groupId: id } : {}),\n };\n let cleanup = () => {};\n try {\n if (!panes.length || panes.some((pane) => !options.store.can(pane.id, 'close'))) return false;\n if (requiringConfirmation.length) {\n const decision = options.confirmClose\n ? Promise.resolve(options.confirmClose(request))\n : new Promise<boolean>((resolve) => {\n const dialog = el(root.ownerDocument, 'dialog', 'layouts-menu layouts-confirm');\n dialog.setAttribute('aria-label', message(options, 'Close selected panes?'));\n const title = el(\n root.ownerDocument,\n 'h2',\n '',\n message(options, 'Close selected panes?'),\n );\n const list = el(\n root.ownerDocument,\n 'p',\n '',\n requiringConfirmation.map((pane) => pane.title).join(', '),\n );\n const cancel = el(\n root.ownerDocument,\n 'button',\n 'layouts-button',\n message(options, 'Cancel'),\n );\n const close = el(\n root.ownerDocument,\n 'button',\n 'layouts-button',\n message(options, 'Close'),\n );\n cancel.type = close.type = 'button';\n cancel.onclick = () => resolve(false);\n close.onclick = () => resolve(true);\n dialog.oncancel = (event) => {\n event.preventDefault();\n resolve(false);\n };\n dialog.append(title, list, cancel, close);\n root.append(dialog);\n cleanup = () => dialog.remove();\n dialog.showModal();\n cancel.focus();\n });\n const aborted = new Promise<boolean>((resolve) =>\n controller.signal.addEventListener('abort', () => resolve(false), { once: true }),\n );\n const unsubscribe = options.store.subscribe((event) => {\n if (event.action === 'load' || signature(collect()) !== signature(panes))\n controller.abort();\n });\n const previousCleanup = cleanup;\n cleanup = () => {\n unsubscribe();\n previousCleanup();\n };\n if (!(await Promise.race([decision, aborted]))) return false;\n }\n if (\n controller.signal.aborted ||\n signature(collect()) !== signature(panes) ||\n panes.some((pane) => !options.store.can(pane.id, 'close'))\n )\n return false;\n if (kind === 'group') options.store.closeGroup(id, { source: 'user' });\n else options.store.close(id, { source: 'user' });\n return true;\n } catch (error) {\n report(error);\n return false;\n } finally {\n cleanup();\n controller.abort();\n if (pending === controller) pending = undefined;\n }\n };\n}\n"]}
package/dist/corners.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import type { Group, Pane } from 'quilt-core';
2
- import type { LayoutOptions } from './types.js';
2
+ import type { ResolvedLayoutOptions } from './types.js';
3
3
  import { Scope } from './lifetime.js';
4
4
  type Direction = 'left' | 'right' | 'top' | 'bottom';
5
5
  /** Commit once on release; cancellation never changes the layout or pane ownership. */
6
- export declare function bindCorners(host: HTMLElement, id: string, options: LayoutOptions, scope: Scope, split: (pane: Pane | undefined, group: Group, direction: Direction, ratio: number) => void, report: (error: unknown) => void): void;
6
+ export declare function bindCorners(host: HTMLElement, id: string, options: ResolvedLayoutOptions, scope: Scope, split: (pane: Pane | undefined, group: Group, direction: Direction, ratio: number) => void, report: (error: unknown) => void): void;
7
7
  export {};
8
8
  //# sourceMappingURL=corners.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"corners.d.ts","sourceRoot":"","sources":["../src/corners.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAM,KAAK,EAAE,MAAM,eAAe,CAAC;AAC1C,KAAK,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;AACrD,uFAAuF;AACvF,wBAAgB,WAAW,CACzB,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,EAC1F,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,QAsPjC"}
1
+ {"version":3,"file":"corners.d.ts","sourceRoot":"","sources":["../src/corners.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAM,KAAK,EAAE,MAAM,eAAe,CAAC;AAC1C,KAAK,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;AACrD,uFAAuF;AACvF,wBAAgB,WAAW,CACzB,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,qBAAqB,EAC9B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,EAC1F,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,QAwQjC"}
package/dist/corners.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { message } from './messages.js';
1
2
  import { allocate, bounds, DIVIDER, findNode, joinRange } from 'quilt-core';
2
3
  import { el, Scope } from './lifetime.js';
3
4
  /** Commit once on release; cancellation never changes the layout or pane ownership. */
@@ -11,8 +12,8 @@ export function bindCorners(host, id, options, scope, split, report) {
11
12
  for (const corner of ['tl', 'tr', 'bl', 'br']) {
12
13
  const handle = el(doc, 'button', 'layouts-corner');
13
14
  handle.dataset.corner = corner;
14
- handle.title = 'Drag inward to split; drag across adjacent regions to join. Escape cancels.';
15
- handle.setAttribute('aria-label', 'Split or join region from ' + corner + ' corner');
15
+ handle.title = message(options, 'Drag inward to split; drag across adjacent regions to join. Escape cancels.');
16
+ handle.setAttribute('aria-label', message(options, 'Split or join region from {corner} corner', { corner }));
16
17
  handle.tabIndex = -1; // Equivalent keyboard operations live in the pane menu.
17
18
  host.append(handle);
18
19
  scope.add(() => handle.remove());
@@ -120,7 +121,7 @@ export function bindCorners(host, id, options, scope, split, report) {
120
121
  width: rect.width,
121
122
  height: before ? first : second,
122
123
  };
123
- mark(newRect, 'split', 'New pane');
124
+ mark(newRect, 'split', message(options, 'New pane'));
124
125
  }
125
126
  else {
126
127
  const layout = options.store.getSnapshot();
@@ -156,14 +157,21 @@ export function bindCorners(host, id, options, scope, split, report) {
156
157
  return;
157
158
  const extents = Object.fromEntries([...boxes].map(([nodeId, box]) => [nodeId, horizontal ? box.width : box.height]));
158
159
  target = { kind: 'join', id: receiverId, extents };
159
- const targetTitle = layout.panes[receiver.active ?? '']?.title ?? 'Empty region';
160
+ const targetTitle = layout.panes[receiver.active ?? '']?.title ?? message(options, 'Empty region');
160
161
  const sources = range.selected.filter((g) => g.id !== receiverId);
161
162
  const sourceLabel = sources.every((g) => g.panes.length === 0)
162
- ? `${sources.length} empty ${sources.length === 1 ? 'region' : 'regions'}`
163
- : sources.map((g) => layout.panes[g.active ?? '']?.title ?? 'Empty region').join(', ');
163
+ ? message(options, sources.length === 1 ? '{count} empty region' : '{count} empty regions', { count: sources.length })
164
+ : sources
165
+ .map((g) => layout.panes[g.active ?? '']?.title ?? message(options, 'Empty region'))
166
+ .join(', ');
164
167
  for (const group of range.selected) {
165
168
  const receiving = group.id === receiverId;
166
- mark(boxes.get(group.id), receiving ? 'join-target' : 'join-source', receiving ? `${targetTitle} absorbs ${sourceLabel}` : `Joins ${targetTitle}`);
169
+ mark(boxes.get(group.id), receiving ? 'join-target' : 'join-source', receiving
170
+ ? message(options, '{target} absorbs {source}', {
171
+ target: targetTitle,
172
+ source: sourceLabel,
173
+ })
174
+ : message(options, 'Joins {target}', { target: targetTitle }));
167
175
  }
168
176
  const left = Math.min(...selectedBoxes.map((box) => box.left));
169
177
  const top = Math.min(...selectedBoxes.map((box) => box.top));
@@ -1 +1 @@
1
- {"version":3,"file":"corners.js","sourceRoot":"","sources":["../src/corners.ts"],"names":[],"mappings":"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","sourcesContent":["import { allocate, bounds, DIVIDER, findNode, joinRange } from 'quilt-core';\nimport type { Group, Pane } from 'quilt-core';\nimport type { LayoutOptions } from './types.js';\nimport { el, Scope } from './lifetime.js';\ntype Direction = 'left' | 'right' | 'top' | 'bottom';\n/** Commit once on release; cancellation never changes the layout or pane ownership. */\nexport function bindCorners(\n host: HTMLElement,\n id: string,\n options: LayoutOptions,\n scope: Scope,\n split: (pane: Pane | undefined, group: Group, direction: Direction, ratio: number) => void,\n report: (error: unknown) => void,\n) {\n const doc = host.ownerDocument;\n let drag: Scope | undefined;\n scope.add(() => drag?.dispose());\n const initial = findNode(options.store.getSnapshot().root, id);\n if (initial?.kind !== 'group' || !initial.panes.every((p) => options.store.can(p, 'split')))\n return;\n for (const corner of ['tl', 'tr', 'bl', 'br']) {\n const handle = el(doc, 'button', 'layouts-corner');\n handle.dataset.corner = corner;\n handle.title = 'Drag inward to split; drag across adjacent regions to join. Escape cancels.';\n handle.setAttribute('aria-label', 'Split or join region from ' + corner + ' corner');\n handle.tabIndex = -1; // Equivalent keyboard operations live in the pane menu.\n host.append(handle);\n scope.add(() => handle.remove());\n scope.listen(handle, 'pointerdown', (event) => {\n const start = event as PointerEvent;\n if (start.button !== 0) return;\n const node = findNode(options.store.getSnapshot().root, id);\n if (node?.kind !== 'group' || !node.panes.every((p) => options.store.can(p, 'split'))) return;\n event.preventDefault();\n event.stopPropagation();\n drag?.dispose();\n const local = new Scope();\n drag = local;\n local.add(options.store.subscribe(() => local.dispose()));\n handle.setPointerCapture(start.pointerId);\n let target:\n | { kind: 'split'; direction: Direction; ratio: number }\n | { kind: 'join'; id: string; extents: Record<string, number> }\n | undefined;\n const overlay = el(doc, 'div', 'layouts-corner-overlay');\n overlay.setAttribute('aria-hidden', 'true');\n host.closest('.layouts')!.append(overlay);\n local.add(() => overlay.remove());\n const mark = (\n rect: { left: number; top: number; width: number; height: number },\n kind: string,\n label: string,\n ) => {\n const box = el(doc, 'div', 'layouts-corner-preview');\n box.dataset.cornerPreview = kind;\n Object.assign(box.style, {\n left: `${rect.left}px`,\n top: `${rect.top}px`,\n width: `${rect.width}px`,\n height: `${rect.height}px`,\n });\n box.append(el(doc, 'span', 'layouts-corner-label', label));\n overlay.append(box);\n };\n const clear = () => {\n overlay.replaceChildren();\n target = undefined;\n };\n local.add(() => {\n clear();\n try {\n handle.releasePointerCapture(start.pointerId);\n } catch {}\n });\n local.listen(handle, 'pointermove', (e) => {\n const move = e as PointerEvent;\n clear();\n if (Math.hypot(move.clientX - start.clientX, move.clientY - start.clientY) < 24) return;\n const rect = host.getBoundingClientRect();\n const inside =\n move.clientX > rect.left &&\n move.clientX < rect.right &&\n move.clientY > rect.top &&\n move.clientY < rect.bottom;\n if (inside) {\n const horizontal =\n Math.abs(move.clientX - start.clientX) > Math.abs(move.clientY - start.clientY);\n const direction: Direction = horizontal\n ? corner.endsWith('l')\n ? 'left'\n : 'right'\n : corner.startsWith('t')\n ? 'top'\n : 'bottom';\n target = {\n kind: 'split',\n direction,\n ratio: Math.max(\n 0.1,\n Math.min(\n 0.9,\n horizontal\n ? (move.clientX - rect.left) / rect.width\n : (move.clientY - rect.top) / rect.height,\n ),\n ),\n };\n const before = direction === 'left' || direction === 'top';\n const layout = options.store.getSnapshot();\n const current = findNode(layout.root, id);\n if (!current) return;\n const constraints = bounds(current, layout);\n const min = horizontal ? constraints.minWidth : constraints.minHeight;\n const max = horizontal ? constraints.maxWidth : constraints.maxHeight;\n const total = horizontal ? rect.width : rect.height;\n const disabled =\n current.kind === 'group' &&\n (!current.panes.every((p) => options.store.can(p, 'resize')) || min === max);\n const configuredGap = parseFloat(\n doc\n .defaultView!.getComputedStyle(host.closest('.layouts')!)\n .getPropertyValue(\n disabled\n ? '--layouts-disabled-resize-handle-width'\n : '--layouts-resize-handle-width',\n ),\n );\n const gap =\n Number.isFinite(configuredGap) && configuredGap >= 0\n ? configuredGap\n : disabled\n ? 0\n : DIVIDER;\n const [first, second] = allocate(\n total,\n target.ratio,\n before ? 0 : min,\n before ? Infinity : max,\n before ? min : 0,\n before ? max : Infinity,\n gap,\n );\n // Store uses content extents (excluding the divider), matching this preview exactly.\n const newRect = horizontal\n ? {\n left: rect.left + (before ? 0 : first + gap),\n top: rect.top,\n width: before ? first : second,\n height: rect.height,\n }\n : {\n left: rect.left,\n top: rect.top + (before ? 0 : first + gap),\n width: rect.width,\n height: before ? first : second,\n };\n mark(newRect, 'split', 'New pane');\n } else {\n const layout = options.store.getSnapshot();\n const elements = Array.from(\n host.closest('.layouts')!.querySelectorAll<HTMLElement>('[data-node-id]'),\n ).filter((element) => element.closest('.layouts') === host.closest('.layouts'));\n const boxes = new Map(\n elements.map((element) => [element.dataset.nodeId!, element.getBoundingClientRect()]),\n );\n const hovered = elements.find((element) => {\n const candidate = findNode(layout.root, element.dataset.nodeId!);\n const box = boxes.get(element.dataset.nodeId!)!;\n return (\n candidate?.kind === 'group' &&\n move.clientX >= box.left &&\n move.clientX <= box.right &&\n move.clientY >= box.top &&\n move.clientY <= box.bottom\n );\n });\n if (!hovered) return;\n const receiverId = hovered.dataset.nodeId!;\n const range = joinRange(layout.root, id, receiverId);\n if (\n !range ||\n !range.selected.every((g) => g.panes.every((p) => options.store.can(p, 'join')))\n )\n return;\n const receiver = findNode(layout.root, receiverId);\n if (receiver?.kind !== 'group') return;\n const selectedBoxes = range.selected.map((g) => boxes.get(g.id));\n if (selectedBoxes.some((box) => !box)) return;\n // Require full-edge alignment in the rendered layout too (constraints can leave slack).\n const horizontal = range.row.axis === 'horizontal';\n if (\n selectedBoxes.some((box) =>\n horizontal\n ? Math.abs(box!.top - rect.top) > 1 || Math.abs(box!.bottom - rect.bottom) > 1\n : Math.abs(box!.left - rect.left) > 1 || Math.abs(box!.right - rect.right) > 1,\n )\n )\n return;\n const extents = Object.fromEntries(\n [...boxes].map(([nodeId, box]) => [nodeId, horizontal ? box.width : box.height]),\n );\n target = { kind: 'join', id: receiverId, extents };\n const targetTitle = layout.panes[receiver.active ?? '']?.title ?? 'Empty region';\n const sources = range.selected.filter((g) => g.id !== receiverId);\n const sourceLabel = sources.every((g) => g.panes.length === 0)\n ? `${sources.length} empty ${sources.length === 1 ? 'region' : 'regions'}`\n : sources.map((g) => layout.panes[g.active ?? '']?.title ?? 'Empty region').join(', ');\n for (const group of range.selected) {\n const receiving = group.id === receiverId;\n mark(\n boxes.get(group.id)!,\n receiving ? 'join-target' : 'join-source',\n receiving ? `${targetTitle} absorbs ${sourceLabel}` : `Joins ${targetTitle}`,\n );\n }\n const left = Math.min(...selectedBoxes.map((box) => box!.left));\n const top = Math.min(...selectedBoxes.map((box) => box!.top));\n mark(\n {\n left,\n top,\n width: Math.max(...selectedBoxes.map((box) => box!.right)) - left,\n height: Math.max(...selectedBoxes.map((box) => box!.bottom)) - top,\n },\n 'join',\n '',\n );\n }\n });\n local.listen(handle, 'pointerup', () => {\n const action = target;\n local.dispose();\n if (!action) return;\n try {\n if (action.kind === 'join')\n options.store.joinRegions(action.id, id, { source: 'user', extents: action.extents });\n else {\n const layout = options.store.getSnapshot(),\n group = findNode(layout.root, id);\n if (group?.kind === 'group')\n split(\n group.active ? layout.panes[group.active] : undefined,\n group,\n action.direction,\n action.ratio,\n );\n }\n } catch (error) {\n report(error);\n }\n });\n local.listen(handle, 'pointercancel', () => local.dispose());\n local.listen(handle, 'lostpointercapture', () => local.dispose());\n local.listen(doc, 'keydown', (e) => {\n if ((e as KeyboardEvent).key === 'Escape') local.dispose();\n });\n });\n }\n}\n"]}
1
+ {"version":3,"file":"corners.js","sourceRoot":"","sources":["../src/corners.ts"],"names":[],"mappings":"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","sourcesContent":["import { message } from './messages.js';\nimport { allocate, bounds, DIVIDER, findNode, joinRange } from 'quilt-core';\nimport type { Group, Pane } from 'quilt-core';\nimport type { ResolvedLayoutOptions } from './types.js';\nimport { el, Scope } from './lifetime.js';\ntype Direction = 'left' | 'right' | 'top' | 'bottom';\n/** Commit once on release; cancellation never changes the layout or pane ownership. */\nexport function bindCorners(\n host: HTMLElement,\n id: string,\n options: ResolvedLayoutOptions,\n scope: Scope,\n split: (pane: Pane | undefined, group: Group, direction: Direction, ratio: number) => void,\n report: (error: unknown) => void,\n) {\n const doc = host.ownerDocument;\n let drag: Scope | undefined;\n scope.add(() => drag?.dispose());\n const initial = findNode(options.store.getSnapshot().root, id);\n if (initial?.kind !== 'group' || !initial.panes.every((p) => options.store.can(p, 'split')))\n return;\n for (const corner of ['tl', 'tr', 'bl', 'br']) {\n const handle = el(doc, 'button', 'layouts-corner');\n handle.dataset.corner = corner;\n handle.title = message(\n options,\n 'Drag inward to split; drag across adjacent regions to join. Escape cancels.',\n );\n handle.setAttribute(\n 'aria-label',\n message(options, 'Split or join region from {corner} corner', { corner }),\n );\n handle.tabIndex = -1; // Equivalent keyboard operations live in the pane menu.\n host.append(handle);\n scope.add(() => handle.remove());\n scope.listen(handle, 'pointerdown', (event) => {\n const start = event as PointerEvent;\n if (start.button !== 0) return;\n const node = findNode(options.store.getSnapshot().root, id);\n if (node?.kind !== 'group' || !node.panes.every((p) => options.store.can(p, 'split'))) return;\n event.preventDefault();\n event.stopPropagation();\n drag?.dispose();\n const local = new Scope();\n drag = local;\n local.add(options.store.subscribe(() => local.dispose()));\n handle.setPointerCapture(start.pointerId);\n let target:\n | { kind: 'split'; direction: Direction; ratio: number }\n | { kind: 'join'; id: string; extents: Record<string, number> }\n | undefined;\n const overlay = el(doc, 'div', 'layouts-corner-overlay');\n overlay.setAttribute('aria-hidden', 'true');\n host.closest('.layouts')!.append(overlay);\n local.add(() => overlay.remove());\n const mark = (\n rect: { left: number; top: number; width: number; height: number },\n kind: string,\n label: string,\n ) => {\n const box = el(doc, 'div', 'layouts-corner-preview');\n box.dataset.cornerPreview = kind;\n Object.assign(box.style, {\n left: `${rect.left}px`,\n top: `${rect.top}px`,\n width: `${rect.width}px`,\n height: `${rect.height}px`,\n });\n box.append(el(doc, 'span', 'layouts-corner-label', label));\n overlay.append(box);\n };\n const clear = () => {\n overlay.replaceChildren();\n target = undefined;\n };\n local.add(() => {\n clear();\n try {\n handle.releasePointerCapture(start.pointerId);\n } catch {}\n });\n local.listen(handle, 'pointermove', (e) => {\n const move = e as PointerEvent;\n clear();\n if (Math.hypot(move.clientX - start.clientX, move.clientY - start.clientY) < 24) return;\n const rect = host.getBoundingClientRect();\n const inside =\n move.clientX > rect.left &&\n move.clientX < rect.right &&\n move.clientY > rect.top &&\n move.clientY < rect.bottom;\n if (inside) {\n const horizontal =\n Math.abs(move.clientX - start.clientX) > Math.abs(move.clientY - start.clientY);\n const direction: Direction = horizontal\n ? corner.endsWith('l')\n ? 'left'\n : 'right'\n : corner.startsWith('t')\n ? 'top'\n : 'bottom';\n target = {\n kind: 'split',\n direction,\n ratio: Math.max(\n 0.1,\n Math.min(\n 0.9,\n horizontal\n ? (move.clientX - rect.left) / rect.width\n : (move.clientY - rect.top) / rect.height,\n ),\n ),\n };\n const before = direction === 'left' || direction === 'top';\n const layout = options.store.getSnapshot();\n const current = findNode(layout.root, id);\n if (!current) return;\n const constraints = bounds(current, layout);\n const min = horizontal ? constraints.minWidth : constraints.minHeight;\n const max = horizontal ? constraints.maxWidth : constraints.maxHeight;\n const total = horizontal ? rect.width : rect.height;\n const disabled =\n current.kind === 'group' &&\n (!current.panes.every((p) => options.store.can(p, 'resize')) || min === max);\n const configuredGap = parseFloat(\n doc\n .defaultView!.getComputedStyle(host.closest('.layouts')!)\n .getPropertyValue(\n disabled\n ? '--layouts-disabled-resize-handle-width'\n : '--layouts-resize-handle-width',\n ),\n );\n const gap =\n Number.isFinite(configuredGap) && configuredGap >= 0\n ? configuredGap\n : disabled\n ? 0\n : DIVIDER;\n const [first, second] = allocate(\n total,\n target.ratio,\n before ? 0 : min,\n before ? Infinity : max,\n before ? min : 0,\n before ? max : Infinity,\n gap,\n );\n // Store uses content extents (excluding the divider), matching this preview exactly.\n const newRect = horizontal\n ? {\n left: rect.left + (before ? 0 : first + gap),\n top: rect.top,\n width: before ? first : second,\n height: rect.height,\n }\n : {\n left: rect.left,\n top: rect.top + (before ? 0 : first + gap),\n width: rect.width,\n height: before ? first : second,\n };\n mark(newRect, 'split', message(options, 'New pane'));\n } else {\n const layout = options.store.getSnapshot();\n const elements = Array.from(\n host.closest('.layouts')!.querySelectorAll<HTMLElement>('[data-node-id]'),\n ).filter((element) => element.closest('.layouts') === host.closest('.layouts'));\n const boxes = new Map(\n elements.map((element) => [element.dataset.nodeId!, element.getBoundingClientRect()]),\n );\n const hovered = elements.find((element) => {\n const candidate = findNode(layout.root, element.dataset.nodeId!);\n const box = boxes.get(element.dataset.nodeId!)!;\n return (\n candidate?.kind === 'group' &&\n move.clientX >= box.left &&\n move.clientX <= box.right &&\n move.clientY >= box.top &&\n move.clientY <= box.bottom\n );\n });\n if (!hovered) return;\n const receiverId = hovered.dataset.nodeId!;\n const range = joinRange(layout.root, id, receiverId);\n if (\n !range ||\n !range.selected.every((g) => g.panes.every((p) => options.store.can(p, 'join')))\n )\n return;\n const receiver = findNode(layout.root, receiverId);\n if (receiver?.kind !== 'group') return;\n const selectedBoxes = range.selected.map((g) => boxes.get(g.id));\n if (selectedBoxes.some((box) => !box)) return;\n // Require full-edge alignment in the rendered layout too (constraints can leave slack).\n const horizontal = range.row.axis === 'horizontal';\n if (\n selectedBoxes.some((box) =>\n horizontal\n ? Math.abs(box!.top - rect.top) > 1 || Math.abs(box!.bottom - rect.bottom) > 1\n : Math.abs(box!.left - rect.left) > 1 || Math.abs(box!.right - rect.right) > 1,\n )\n )\n return;\n const extents = Object.fromEntries(\n [...boxes].map(([nodeId, box]) => [nodeId, horizontal ? box.width : box.height]),\n );\n target = { kind: 'join', id: receiverId, extents };\n const targetTitle =\n layout.panes[receiver.active ?? '']?.title ?? message(options, 'Empty region');\n const sources = range.selected.filter((g) => g.id !== receiverId);\n const sourceLabel = sources.every((g) => g.panes.length === 0)\n ? message(\n options,\n sources.length === 1 ? '{count} empty region' : '{count} empty regions',\n { count: sources.length },\n )\n : sources\n .map((g) => layout.panes[g.active ?? '']?.title ?? message(options, 'Empty region'))\n .join(', ');\n for (const group of range.selected) {\n const receiving = group.id === receiverId;\n mark(\n boxes.get(group.id)!,\n receiving ? 'join-target' : 'join-source',\n receiving\n ? message(options, '{target} absorbs {source}', {\n target: targetTitle,\n source: sourceLabel,\n })\n : message(options, 'Joins {target}', { target: targetTitle }),\n );\n }\n const left = Math.min(...selectedBoxes.map((box) => box!.left));\n const top = Math.min(...selectedBoxes.map((box) => box!.top));\n mark(\n {\n left,\n top,\n width: Math.max(...selectedBoxes.map((box) => box!.right)) - left,\n height: Math.max(...selectedBoxes.map((box) => box!.bottom)) - top,\n },\n 'join',\n '',\n );\n }\n });\n local.listen(handle, 'pointerup', () => {\n const action = target;\n local.dispose();\n if (!action) return;\n try {\n if (action.kind === 'join')\n options.store.joinRegions(action.id, id, { source: 'user', extents: action.extents });\n else {\n const layout = options.store.getSnapshot(),\n group = findNode(layout.root, id);\n if (group?.kind === 'group')\n split(\n group.active ? layout.panes[group.active] : undefined,\n group,\n action.direction,\n action.ratio,\n );\n }\n } catch (error) {\n report(error);\n }\n });\n local.listen(handle, 'pointercancel', () => local.dispose());\n local.listen(handle, 'lostpointercapture', () => local.dispose());\n local.listen(doc, 'keydown', (e) => {\n if ((e as KeyboardEvent).key === 'Escape') local.dispose();\n });\n });\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -6,4 +6,11 @@ export { themes, themeFamilies } from './theme.js';
6
6
  export type { LayoutTheme } from './theme.js';
7
7
  export { LayoutStore, LayoutError, createLayout, parseLayout, validate } from 'quilt-core';
8
8
  export type { AutoCollapse, LayoutStoreOptions, Json, Capability, Axis, Pane, Group, Split, Node, WindowPlacement, Popout, Layout as LayoutSnapshot, Issue, CommandOptions, Change, Bounds, JoinOptions, } from 'quilt-core';
9
+ export { PaneRegistry } from './registry.js';
10
+ export type { PaneRegistration } from './registry.js';
11
+ export { parseWorkspace, dockLayout } from './workspace.js';
12
+ export type { WorkspacePreset } from './workspace.js';
13
+ export { themeProperties } from './theme.js';
14
+ export { defaultMessages } from './messages.js';
15
+ export type { Messages } from './messages.js';
9
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,mBAAmB,YAAY,CAAC;AAEhC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3F,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,IAAI,EACJ,UAAU,EACV,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,KAAK,EACL,IAAI,EACJ,eAAe,EACf,MAAM,EACN,MAAM,IAAI,cAAc,EACxB,KAAK,EACL,cAAc,EACd,MAAM,EACN,MAAM,EACN,WAAW,GACZ,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,mBAAmB,YAAY,CAAC;AAEhC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3F,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,IAAI,EACJ,UAAU,EACV,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,KAAK,EACL,IAAI,EACJ,eAAe,EACf,MAAM,EACN,MAAM,IAAI,cAAc,EACxB,KAAK,EACL,cAAc,EACd,MAAM,EACN,MAAM,EACN,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5D,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -2,4 +2,8 @@ export { mountLayout } from './renderer.js';
2
2
  export { TabRegistry } from './registry.js';
3
3
  export { themes, themeFamilies } from './theme.js';
4
4
  export { LayoutStore, LayoutError, createLayout, parseLayout, validate } from 'quilt-core';
5
+ export { PaneRegistry } from './registry.js';
6
+ export { parseWorkspace, dockLayout } from './workspace.js';
7
+ export { themeProperties } from './theme.js';
8
+ export { defaultMessages } from './messages.js';
5
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC","sourcesContent":["export { mountLayout } from './renderer.js';\nexport type * from './types.js';\n\nexport { TabRegistry } from './registry.js';\nexport type { TabRegistration } from './registry.js';\nexport { themes, themeFamilies } from './theme.js';\nexport type { LayoutTheme } from './theme.js';\nexport { LayoutStore, LayoutError, createLayout, parseLayout, validate } from 'quilt-core';\nexport type {\n AutoCollapse,\n LayoutStoreOptions,\n Json,\n Capability,\n Axis,\n Pane,\n Group,\n Split,\n Node,\n WindowPlacement,\n Popout,\n Layout as LayoutSnapshot,\n Issue,\n CommandOptions,\n Change,\n Bounds,\n JoinOptions,\n} from 'quilt-core';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAoB3F,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC","sourcesContent":["export { mountLayout } from './renderer.js';\nexport type * from './types.js';\n\nexport { TabRegistry } from './registry.js';\nexport type { TabRegistration } from './registry.js';\nexport { themes, themeFamilies } from './theme.js';\nexport type { LayoutTheme } from './theme.js';\nexport { LayoutStore, LayoutError, createLayout, parseLayout, validate } from 'quilt-core';\nexport type {\n AutoCollapse,\n LayoutStoreOptions,\n Json,\n Capability,\n Axis,\n Pane,\n Group,\n Split,\n Node,\n WindowPlacement,\n Popout,\n Layout as LayoutSnapshot,\n Issue,\n CommandOptions,\n Change,\n Bounds,\n JoinOptions,\n} from 'quilt-core';\nexport { PaneRegistry } from './registry.js';\nexport type { PaneRegistration } from './registry.js';\nexport { parseWorkspace, dockLayout } from './workspace.js';\nexport type { WorkspacePreset } from './workspace.js';\nexport { themeProperties } from './theme.js';\nexport { defaultMessages } from './messages.js';\nexport type { Messages } from './messages.js';\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"lifetime.d.ts","sourceRoot":"","sources":["../src/lifetime.ts"],"names":[],"mappings":"AAAA,qBAAa,KAAK;IAChB,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,KAAK,CAAS;IACtB,GAAG,CAAC,OAAO,EAAE,MAAM,IAAI,QAGtB;IACD,MAAM,CACJ,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,uBAAuB,QAIlC;IACD,OAAO,SAUN;CACF;AACD,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,QAMxE;AACD,wBAAgB,EAAE,CAAC,CAAC,SAAS,MAAM,qBAAqB,EACtD,GAAG,EAAE,QAAQ,EACb,GAAG,EAAE,CAAC,EACN,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,GACZ,qBAAqB,CAAC,CAAC,CAAC,CAK1B"}
1
+ {"version":3,"file":"lifetime.d.ts","sourceRoot":"","sources":["../src/lifetime.ts"],"names":[],"mappings":"AAAA,qBAAa,KAAK;IAChB,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,KAAK,CAAS;IACtB,GAAG,CAAC,OAAO,EAAE,MAAM,IAAI,QAGtB;IACD,MAAM,CACJ,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,uBAAuB,QAIlC;IACD,OAAO,SAUN;CACF;AACD,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,QAMxE;AACD,wBAAgB,EAAE,CAAC,CAAC,SAAS,MAAM,qBAAqB,EACtD,GAAG,EAAE,QAAQ,EACb,GAAG,EAAE,CAAC,EACN,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,GACZ,qBAAqB,CAAC,CAAC,CAAC,CAM1B"}
package/dist/lifetime.js CHANGED
@@ -37,6 +37,7 @@ export function syncChildren(parent, children) {
37
37
  export function el(doc, tag, className, text) {
38
38
  const node = doc.createElement(tag);
39
39
  node.className = className;
40
+ node.dataset.layoutsChrome = '';
40
41
  if (text !== undefined)
41
42
  node.textContent = text;
42
43
  return node;
@@ -1 +1 @@
1
- {"version":3,"file":"lifetime.js","sourceRoot":"","sources":["../src/lifetime.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,KAAK;IACR,KAAK,GAAmB,EAAE,CAAC;IAC3B,KAAK,GAAG,KAAK,CAAC;IACtB,GAAG,CAAC,OAAmB;QACrB,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAC;;YACrB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,CACJ,MAAmB,EACnB,IAAY,EACZ,QAAuB,EACvB,OAAiC;QAEjC,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QACjD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,OAAO;QACL,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAClD,IAAI,CAAC;gBACH,IAAI,EAAE,CAAC;YACT,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;YAChE,CAAC;QACH,CAAC;IACH,CAAC;CACF;AACD,MAAM,UAAU,YAAY,CAAC,MAAmB,EAAE,QAAuB;IACvE,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC5B,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK;YAAE,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;IACH,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC7C,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAoB,CAAC;YAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AACjE,CAAC;AACD,MAAM,UAAU,EAAE,CAChB,GAAa,EACb,GAAM,EACN,SAAiB,EACjB,IAAa;IAEb,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC3B,IAAI,IAAI,KAAK,SAAS;QAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAChD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["export class Scope {\n private tasks: (() => void)[] = [];\n private ended = false;\n add(cleanup: () => void) {\n if (this.ended) cleanup();\n else this.tasks.push(cleanup);\n }\n listen(\n target: EventTarget,\n type: string,\n listener: EventListener,\n options?: AddEventListenerOptions,\n ) {\n target.addEventListener(type, listener, options);\n this.add(() => target.removeEventListener(type, listener, options));\n }\n dispose() {\n if (this.ended) return;\n this.ended = true;\n for (const task of this.tasks.splice(0).reverse()) {\n try {\n task();\n } catch {\n /* Complete the remaining cleanup even if a consumer fails. */\n }\n }\n }\n}\nexport function syncChildren(parent: HTMLElement, children: HTMLElement[]) {\n children.forEach((child, i) => {\n if (parent.children[i] !== child) parent.insertBefore(child, parent.children[i] ?? null);\n });\n for (const child of Array.from(parent.children))\n if (!children.includes(child as HTMLElement)) child.remove();\n}\nexport function el<K extends keyof HTMLElementTagNameMap>(\n doc: Document,\n tag: K,\n className: string,\n text?: string,\n): HTMLElementTagNameMap[K] {\n const node = doc.createElement(tag);\n node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n}\n"]}
1
+ {"version":3,"file":"lifetime.js","sourceRoot":"","sources":["../src/lifetime.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,KAAK;IACR,KAAK,GAAmB,EAAE,CAAC;IAC3B,KAAK,GAAG,KAAK,CAAC;IACtB,GAAG,CAAC,OAAmB;QACrB,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAC;;YACrB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,CACJ,MAAmB,EACnB,IAAY,EACZ,QAAuB,EACvB,OAAiC;QAEjC,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QACjD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,OAAO;QACL,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAClD,IAAI,CAAC;gBACH,IAAI,EAAE,CAAC;YACT,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;YAChE,CAAC;QACH,CAAC;IACH,CAAC;CACF;AACD,MAAM,UAAU,YAAY,CAAC,MAAmB,EAAE,QAAuB;IACvE,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC5B,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK;YAAE,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;IACH,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC7C,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAoB,CAAC;YAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AACjE,CAAC;AACD,MAAM,UAAU,EAAE,CAChB,GAAa,EACb,GAAM,EACN,SAAiB,EACjB,IAAa;IAEb,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC3B,IAAI,CAAC,OAAO,CAAC,aAAa,GAAG,EAAE,CAAC;IAChC,IAAI,IAAI,KAAK,SAAS;QAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAChD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["export class Scope {\n private tasks: (() => void)[] = [];\n private ended = false;\n add(cleanup: () => void) {\n if (this.ended) cleanup();\n else this.tasks.push(cleanup);\n }\n listen(\n target: EventTarget,\n type: string,\n listener: EventListener,\n options?: AddEventListenerOptions,\n ) {\n target.addEventListener(type, listener, options);\n this.add(() => target.removeEventListener(type, listener, options));\n }\n dispose() {\n if (this.ended) return;\n this.ended = true;\n for (const task of this.tasks.splice(0).reverse()) {\n try {\n task();\n } catch {\n /* Complete the remaining cleanup even if a consumer fails. */\n }\n }\n }\n}\nexport function syncChildren(parent: HTMLElement, children: HTMLElement[]) {\n children.forEach((child, i) => {\n if (parent.children[i] !== child) parent.insertBefore(child, parent.children[i] ?? null);\n });\n for (const child of Array.from(parent.children))\n if (!children.includes(child as HTMLElement)) child.remove();\n}\nexport function el<K extends keyof HTMLElementTagNameMap>(\n doc: Document,\n tag: K,\n className: string,\n text?: string,\n): HTMLElementTagNameMap[K] {\n const node = doc.createElement(tag);\n node.className = className;\n node.dataset.layoutsChrome = '';\n if (text !== undefined) node.textContent = text;\n return node;\n}\n"]}
package/dist/menu.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { Group, Pane } from 'quilt-core';
2
- import type { LayoutOptions } from './types.js';
2
+ import type { ResolvedLayoutOptions } from './types.js';
3
3
  import type { Windows } from './windows.js';
4
- export declare function createPaneMenu(root: HTMLElement, options: LayoutOptions, windows: Pick<Windows, 'pending' | 'open'>, refresh: () => void, report: (e: unknown) => void): {
4
+ export declare function createPaneMenu(root: HTMLElement, options: ResolvedLayoutOptions, windows: Pick<Windows, 'open'>, refresh: () => void, report: (e: unknown) => void, requestClose: (id: string, kind?: 'pane' | 'group') => Promise<boolean>): {
5
5
  open: (anchor: HTMLElement, pane: Pane | undefined, group: Group, direction?: 'left' | 'right' | 'top' | 'bottom', ratio?: number, groupActions?: boolean, addTab?: boolean) => void;
6
6
  addTab(anchor: HTMLElement, group: Group): void;
7
7
  openEmpty(anchor: HTMLElement, group: Group): void;
@@ -1 +1 @@
1
- {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../src/menu.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,wBAAgB,cAAc,CAC5B,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC,EAC1C,OAAO,EAAE,MAAM,IAAI,EACnB,MAAM,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI;mBAsBlB,WAAW,QACb,IAAI,GAAG,SAAS,SACf,KAAK,cACA,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ;mBAyahC,WAAW,SAAS,KAAK;sBAYtB,WAAW,SAAS,KAAK;;EAW9C"}
1
+ {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../src/menu.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,wBAAgB,cAAc,CAC5B,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAC9B,OAAO,EAAE,MAAM,IAAI,EACnB,MAAM,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,EAC5B,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC;mBAsB7D,WAAW,QACb,IAAI,GAAG,SAAS,SACf,KAAK,cACA,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ;mBAybhC,WAAW,SAAS,KAAK;sBAYtB,WAAW,SAAS,KAAK;;EAW9C"}
package/dist/menu.js CHANGED
@@ -1,8 +1,9 @@
1
+ import { message } from './messages.js';
1
2
  import { actionIcon } from './action-icons.js';
2
3
  import { fillTabPicker } from './picker.js';
3
4
  import { findNode, findParent, groups, paneIds } from 'quilt-core';
4
5
  import { el, Scope } from './lifetime.js';
5
- export function createPaneMenu(root, options, windows, refresh, report) {
6
+ export function createPaneMenu(root, options, windows, refresh, report, requestClose) {
6
7
  const doc = root.ownerDocument, win = doc.defaultView;
7
8
  let current;
8
9
  const persistentPickers = new Map();
@@ -33,7 +34,9 @@ export function createPaneMenu(root, options, windows, refresh, report) {
33
34
  const dialog = el(doc, 'dialog', 'layouts-menu');
34
35
  dialog.tabIndex = -1;
35
36
  dialog.autofocus = true;
36
- dialog.setAttribute('aria-label', pane && !groupActions ? `${pane.title} actions` : 'Empty pane actions');
37
+ dialog.setAttribute('aria-label', pane && !groupActions
38
+ ? message(options, '{title} actions', { title: pane.title })
39
+ : message(options, 'Empty pane actions'));
37
40
  local.add(() => dialog.remove());
38
41
  const add = (icon, label, enabled, fn) => {
39
42
  const b = button(label, label, () => {
@@ -148,7 +151,7 @@ export function createPaneMenu(root, options, windows, refresh, report) {
148
151
  create(direction === 'left' || direction === 'right' ? 'horizontal' : 'vertical', direction === 'left' || direction === 'top');
149
152
  return;
150
153
  }
151
- add('add-tab', '+ Add tab', available && allowed('move'), () => create());
154
+ add('add-tab', message(options, '+ Add tab'), available && allowed('move'), () => create());
152
155
  const canCreate = !group.panes.length || available;
153
156
  const flyouts = [];
154
157
  let positionMenus = () => { };
@@ -202,12 +205,12 @@ export function createPaneMenu(root, options, windows, refresh, report) {
202
205
  dialog.append(container);
203
206
  return flyout;
204
207
  }
205
- const flyout = submenu('Split', 'split-right', canCreate && allowed('split'));
208
+ const flyout = submenu(message(options, 'Split'), 'split-right', canCreate && allowed('split'));
206
209
  for (const [label, axis, before] of [
207
- ['Split left', 'horizontal', true],
208
- ['Split right', 'horizontal', false],
209
- ['Split up', 'vertical', true],
210
- ['Split down', 'vertical', false],
210
+ [message(options, 'Split left'), 'horizontal', true],
211
+ [message(options, 'Split right'), 'horizontal', false],
212
+ [message(options, 'Split up'), 'vertical', true],
213
+ [message(options, 'Split down'), 'vertical', false],
211
214
  ]) {
212
215
  const option = button(label, label, () => {
213
216
  local.dispose();
@@ -221,7 +224,7 @@ export function createPaneMenu(root, options, windows, refresh, report) {
221
224
  flyout.append(option);
222
225
  }
223
226
  const joinParent = findParent(options.store.getSnapshot().root, group.id);
224
- const join = add('join', 'Join sibling region', Boolean(joinParent) && paneIds(joinParent).every((id) => options.store.can(id, 'join')), () => options.store.join(group.id, { source: 'user' }));
227
+ const join = add('join', message(options, 'Join sibling region'), Boolean(joinParent) && paneIds(joinParent).every((id) => options.store.can(id, 'join')), () => options.store.join(group.id, { source: 'user' }));
225
228
  let preview;
226
229
  const clearPreview = () => {
227
230
  preview?.dispose();
@@ -240,7 +243,7 @@ export function createPaneMenu(root, options, windows, refresh, report) {
240
243
  overlay.setAttribute('aria-hidden', 'true');
241
244
  root.append(overlay);
242
245
  scope.add(() => overlay.remove());
243
- const targetTitle = layout.panes[group.active ?? '']?.title ?? 'This region';
246
+ const targetTitle = layout.panes[group.active ?? '']?.title ?? message(options, 'This region');
244
247
  const regions = groups(parent).map((region) => ({
245
248
  region,
246
249
  element: Array.from(root.querySelectorAll('[data-node-id]')).find((element) => element.dataset.nodeId === region.id && element.closest('.layouts') === root),
@@ -259,7 +262,9 @@ export function createPaneMenu(root, options, windows, refresh, report) {
259
262
  width: `${rect.width}px`,
260
263
  height: `${rect.height}px`,
261
264
  });
262
- box.append(el(doc, 'span', 'layouts-corner-label', region.id === group.id ? `${targetTitle} keeps all tabs` : `Joins ${targetTitle}`));
265
+ box.append(el(doc, 'span', 'layouts-corner-label', region.id === group.id
266
+ ? message(options, '{target} keeps all tabs', { target: targetTitle })
267
+ : message(options, 'Joins {target}', { target: targetTitle })));
263
268
  overlay.append(box);
264
269
  }
265
270
  };
@@ -277,18 +282,20 @@ export function createPaneMenu(root, options, windows, refresh, report) {
277
282
  local.listen(join, 'pointerleave', clearPreview);
278
283
  local.listen(join, 'focus', showPreview);
279
284
  local.listen(join, 'blur', clearPreview);
280
- add(options.store.getSnapshot().maximized === group.id ? 'restore' : 'maximize', options.store.getSnapshot().maximized === group.id ? 'Restore region' : 'Maximize region', true, () => options.store.maximize(options.store.getSnapshot().maximized === group.id ? null : group.id));
281
- if (group.panes.length && pane) {
282
- add('popout', windows.pending.has(pane.id) ? 'Reopen window' : 'Open in window', options.store.can(pane.id, 'popout'), () => {
283
- windows.open(pane.id, windows.pending.get(pane.id));
285
+ add(options.store.getSnapshot().maximized === group.id ? 'restore' : 'maximize', options.store.getSnapshot().maximized === group.id
286
+ ? message(options, 'Restore region')
287
+ : message(options, 'Maximize region'), true, () => options.store.maximize(options.store.getSnapshot().maximized === group.id ? null : group.id));
288
+ if (group.panes.length && pane && options.popouts !== false) {
289
+ add('popout', message(options, 'Open in window'), options.store.can(pane.id, 'popout'), () => {
290
+ void windows.open(pane.id);
284
291
  refresh();
285
292
  });
286
293
  }
287
- const orientation = submenu('Tab orientation', 'tab-orientation');
294
+ const orientation = submenu(message(options, 'Tab orientation'), 'tab-orientation');
288
295
  for (const [value, label] of [
289
- [undefined, 'Workspace default'],
290
- ['top', 'Horizontal'],
291
- ['left', 'Vertical'],
296
+ [undefined, message(options, 'Workspace default')],
297
+ ['top', message(options, 'Horizontal')],
298
+ ['left', message(options, 'Vertical')],
292
299
  ]) {
293
300
  const selected = group.tabPlacement === value;
294
301
  const option = button(label, label, () => {
@@ -302,11 +309,11 @@ export function createPaneMenu(root, options, windows, refresh, report) {
302
309
  option.prepend(mark);
303
310
  orientation.append(option);
304
311
  }
305
- const display = submenu('Tab display', 'tab-orientation');
312
+ const display = submenu(message(options, 'Tab display'), 'tab-orientation');
306
313
  for (const [value, label] of [
307
- [undefined, 'Workspace default'],
308
- ['automatic', 'Automatic'],
309
- ['compact', 'Compact'],
314
+ [undefined, message(options, 'Workspace default')],
315
+ ['automatic', message(options, 'Automatic')],
316
+ ['compact', message(options, 'Compact')],
310
317
  ]) {
311
318
  const selected = group.tabDisplay === value;
312
319
  const option = button(label, label, () => {
@@ -321,14 +328,14 @@ export function createPaneMenu(root, options, windows, refresh, report) {
321
328
  display.append(option);
322
329
  }
323
330
  if (group.panes.length && pane) {
324
- add('close', 'Close active tab', options.store.can(pane.id, 'close'), () => options.store.close(pane.id, { source: 'user' }));
325
- add('close', 'Close pane', group.panes.every((id) => options.store.can(id, 'close')), () => options.store.closeGroup(group.id, { source: 'user' }));
331
+ add('close', message(options, 'Close active tab'), options.store.can(pane.id, 'close'), () => void requestClose(pane.id));
332
+ add('close', message(options, 'Close pane'), group.panes.every((id) => options.store.can(id, 'close')), () => void requestClose(group.id, 'group'));
326
333
  }
327
334
  else {
328
- add('close', 'Close empty pane', options.store.getSnapshot().root.id !== group.id, () => options.store.removeEmptyGroup(group.id));
335
+ add('close', message(options, 'Close empty pane'), options.store.getSnapshot().root.id !== group.id, () => options.store.removeEmptyGroup(group.id));
329
336
  }
330
- add('restore', 'Restore closed tab', options.store.canRestoreClosedTab(), () => options.store.restoreClosedTab());
331
- add('cancel', 'Cancel', true, () => { });
337
+ add('restore', message(options, 'Restore closed tab'), options.store.canRestoreClosedTab(), () => options.store.restoreClosedTab());
338
+ add('cancel', message(options, 'Cancel'), true, () => { });
332
339
  root.append(dialog);
333
340
  // Measure after opening: theme density, labels, and available actions change the size.
334
341
  // Fixed flyouts escape the scrolling dialog while retaining its modal focus scope.