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/src/styles.css
CHANGED
|
@@ -162,10 +162,13 @@
|
|
|
162
162
|
.layouts-menu {
|
|
163
163
|
position: fixed;
|
|
164
164
|
margin: 0;
|
|
165
|
-
padding: 5px;
|
|
166
165
|
width: 240px;
|
|
167
166
|
max-height: min(520px, 70vh);
|
|
168
167
|
overflow: auto;
|
|
168
|
+
}
|
|
169
|
+
.layouts-menu,
|
|
170
|
+
.layouts-submenu-content {
|
|
171
|
+
padding: var(--layouts-spacing, 5px);
|
|
169
172
|
background: var(--layouts-header, #242a34);
|
|
170
173
|
color: var(--layouts-text, #e5e9f0);
|
|
171
174
|
border: 1px solid var(--layouts-line, #39414e);
|
|
@@ -374,9 +377,6 @@
|
|
|
374
377
|
.layouts-submenu-content {
|
|
375
378
|
display: flex;
|
|
376
379
|
flex-direction: column;
|
|
377
|
-
padding: 4px;
|
|
378
|
-
border: 1px solid var(--layouts-line);
|
|
379
|
-
background: var(--layouts-panel);
|
|
380
380
|
}
|
|
381
381
|
.layouts-corner {
|
|
382
382
|
position: absolute;
|
|
@@ -437,8 +437,6 @@
|
|
|
437
437
|
box-sizing: border-box;
|
|
438
438
|
overflow: auto;
|
|
439
439
|
width: 200px;
|
|
440
|
-
box-shadow: 0 8px 24px #0005;
|
|
441
|
-
border-radius: var(--layouts-radius, 5px);
|
|
442
440
|
}
|
|
443
441
|
|
|
444
442
|
.layouts-corner-overlay {
|
|
@@ -482,9 +480,6 @@
|
|
|
482
480
|
.layouts-picker button {
|
|
483
481
|
min-height: var(--layouts-control-height, 28px);
|
|
484
482
|
}
|
|
485
|
-
.layouts-menu {
|
|
486
|
-
padding: var(--layouts-spacing, 5px);
|
|
487
|
-
}
|
|
488
483
|
|
|
489
484
|
/* Tapered chrome overlays the pane; only its painted footprint intercepts input. */
|
|
490
485
|
.layouts-group[data-tab-bar='tapered'] > .layouts-header {
|
|
@@ -831,3 +826,73 @@
|
|
|
831
826
|
flex: 0 0 calc(var(--layouts-icon-size, 16px) + 16px);
|
|
832
827
|
min-width: calc(var(--layouts-icon-size, 16px) + 16px);
|
|
833
828
|
}
|
|
829
|
+
|
|
830
|
+
/* Chrome remains usable at the renderer's minimum region size, even with larger icons. */
|
|
831
|
+
.layouts-group[data-tab-placement='top'] > .layouts-header {
|
|
832
|
+
min-height: calc(var(--layouts-icon-size, 16px) + 9px);
|
|
833
|
+
}
|
|
834
|
+
.layouts-group[data-tab-placement='top'][data-tab-display='automatic'] .layouts-tab-item {
|
|
835
|
+
min-width: max(32px, calc(var(--layouts-icon-size, 16px) + 8px));
|
|
836
|
+
}
|
|
837
|
+
.layouts-group[data-tab-placement='top'][data-tab-display='automatic']
|
|
838
|
+
.layouts-tab-item[data-active='true']:has(> .layouts-tab-close) {
|
|
839
|
+
min-width: max(
|
|
840
|
+
56px,
|
|
841
|
+
calc(var(--layouts-icon-size, 16px) + 11px + max(22px, var(--layouts-icon-size, 16px)))
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
.layouts-group[data-tab-placement='left'] > .layouts-header {
|
|
845
|
+
min-width: max(32px, calc(var(--layouts-icon-size, 16px) + 10px));
|
|
846
|
+
}
|
|
847
|
+
.layouts-group[data-tab-placement='left'] .layouts-tab-item,
|
|
848
|
+
.layouts-group[data-tab-placement='left'] > .layouts-header > .layouts-button {
|
|
849
|
+
flex-basis: max(30px, calc(var(--layouts-icon-size, 16px) + 8px));
|
|
850
|
+
height: max(30px, calc(var(--layouts-icon-size, 16px) + 8px));
|
|
851
|
+
}
|
|
852
|
+
.layouts-group[data-tab-placement='left'][data-tab-display='automatic']
|
|
853
|
+
.layouts-tab-item[data-active='true']:has(> .layouts-tab-close) {
|
|
854
|
+
flex-basis: calc(
|
|
855
|
+
max(30px, var(--layouts-icon-size, 16px) + 8px) + max(22px, var(--layouts-icon-size, 16px))
|
|
856
|
+
);
|
|
857
|
+
height: calc(
|
|
858
|
+
max(30px, var(--layouts-icon-size, 16px) + 8px) + max(22px, var(--layouts-icon-size, 16px))
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
.layouts-group[data-tab-placement='left'][data-tab-display='automatic']
|
|
862
|
+
.layouts-tab-item[data-active='true']:has(> .layouts-tab-close)
|
|
863
|
+
> .layouts-tab {
|
|
864
|
+
flex-basis: max(30px, calc(var(--layouts-icon-size, 16px) + 8px));
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
.layouts-tab-item .layouts-tab-close {
|
|
868
|
+
flex-basis: max(22px, var(--layouts-icon-size, 16px));
|
|
869
|
+
width: max(22px, var(--layouts-icon-size, 16px));
|
|
870
|
+
max-height: max(24px, var(--layouts-icon-size, 16px));
|
|
871
|
+
}
|
|
872
|
+
.layouts-group[data-tab-placement='left'][data-tab-display='automatic']
|
|
873
|
+
.layouts-tab-item[data-active='true']
|
|
874
|
+
> .layouts-tab-close {
|
|
875
|
+
flex-basis: max(22px, var(--layouts-icon-size, 16px));
|
|
876
|
+
height: max(22px, var(--layouts-icon-size, 16px));
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
.layouts-shortcut {
|
|
880
|
+
display: inline-flex;
|
|
881
|
+
align-items: center;
|
|
882
|
+
justify-content: center;
|
|
883
|
+
margin-left: auto;
|
|
884
|
+
min-width: 1.8em;
|
|
885
|
+
min-height: 1.8em;
|
|
886
|
+
padding: 0.15em 0.45em;
|
|
887
|
+
flex-shrink: 0;
|
|
888
|
+
border: 1px solid color-mix(in srgb, var(--layouts-text, #e5e9f0) 22%, transparent);
|
|
889
|
+
border-radius: 0.5em;
|
|
890
|
+
background: color-mix(in srgb, var(--layouts-text, #e5e9f0) 4%, transparent);
|
|
891
|
+
font: inherit;
|
|
892
|
+
font-size: 0.9em;
|
|
893
|
+
font-weight: 500;
|
|
894
|
+
line-height: 1.2;
|
|
895
|
+
color: var(--layouts-text, #e5e9f0);
|
|
896
|
+
white-space: nowrap;
|
|
897
|
+
pointer-events: none;
|
|
898
|
+
}
|
package/src/tabs.ts
CHANGED
|
@@ -114,17 +114,15 @@ export function fillTabs(tabs: HTMLElement, group: Group, panes: Pane[], deps: T
|
|
|
114
114
|
x.dataset.focusId = `close-${pane.id}`;
|
|
115
115
|
x.onclick = close;
|
|
116
116
|
item.append(x);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
};
|
|
127
|
-
}
|
|
117
|
+
item.onmousedown = (e) => {
|
|
118
|
+
if (e.button === 1 && shortcutEnabled(options, 'middleClickClose')) e.preventDefault();
|
|
119
|
+
};
|
|
120
|
+
item.onauxclick = (e) => {
|
|
121
|
+
if (e.button === 1 && shortcutEnabled(options, 'middleClickClose')) {
|
|
122
|
+
e.preventDefault();
|
|
123
|
+
close();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
128
126
|
}
|
|
129
127
|
tabs.append(item);
|
|
130
128
|
});
|
package/src/types.ts
CHANGED
|
@@ -3,9 +3,13 @@ import type { WorkspacePreset } from './workspace.js';
|
|
|
3
3
|
import type { Messages } from './messages.js';
|
|
4
4
|
import type { TabRegistry } from './registry.js';
|
|
5
5
|
import type { LayoutTheme } from './theme.js';
|
|
6
|
-
import type { LayoutStore, Pane, WindowPlacement } from 'quilt-core';
|
|
6
|
+
import type { LayoutStore, Layout, Pane, WindowPlacement } from 'quilt-core';
|
|
7
7
|
export interface PaneContext<State = unknown> {
|
|
8
8
|
element: HTMLElement;
|
|
9
|
+
/** Aborted when this view is torn down, including a failed mount. */
|
|
10
|
+
signal: AbortSignal;
|
|
11
|
+
/** Register view-owned cleanup; callbacks run once in reverse order. */
|
|
12
|
+
onCleanup(callback: () => void): void;
|
|
9
13
|
reportError(error: unknown): void;
|
|
10
14
|
document: Document;
|
|
11
15
|
window: Window;
|
|
@@ -21,7 +25,7 @@ export interface PaneView {
|
|
|
21
25
|
resize?(width: number, height: number): void;
|
|
22
26
|
update?(pane: Pane): void;
|
|
23
27
|
}
|
|
24
|
-
export type PaneRenderer<State = unknown> = (context: PaneContext<State>) => PaneView;
|
|
28
|
+
export type PaneRenderer<State = unknown> = (context: PaneContext<State>) => PaneView | void;
|
|
25
29
|
export interface TabBarStyle {
|
|
26
30
|
/** Left placement uses an icon-only rail; mode and shape apply in either placement. */
|
|
27
31
|
placement?: 'top' | 'left';
|
|
@@ -40,13 +44,10 @@ export interface TabBarOptions extends TabBarStyle {
|
|
|
40
44
|
/** Partial overrides keyed by stable group ID; not serialized in Layout JSON. */
|
|
41
45
|
regions?: Record<string, TabBarStyle>;
|
|
42
46
|
}
|
|
43
|
-
|
|
47
|
+
/** Live appearance, interactions, and application integration callbacks. */
|
|
48
|
+
export interface WorkspaceSettings<State = unknown> {
|
|
44
49
|
tabBar?: TabBarOptions;
|
|
45
|
-
store: LayoutStore;
|
|
46
|
-
tabs?: TabRegistry;
|
|
47
50
|
theme?: LayoutTheme;
|
|
48
|
-
renderers?: Record<string, PaneRenderer<State>>;
|
|
49
|
-
registry?: PaneRegistry<PaneRenderer<State>>;
|
|
50
51
|
messages?: Messages;
|
|
51
52
|
popouts?: boolean;
|
|
52
53
|
confirmClose?: (request: CloseRequest) => boolean | Promise<boolean>;
|
|
@@ -55,12 +56,11 @@ export interface ResolvedLayoutOptions<State = unknown> {
|
|
|
55
56
|
/** Opt-in conveniences; omitted and false disable all. */
|
|
56
57
|
shortcuts?:
|
|
57
58
|
| boolean
|
|
58
|
-
| {
|
|
59
|
-
maximize?: boolean | KeyBinding | readonly KeyBinding[];
|
|
59
|
+
| (Partial<Record<WorkspaceAction, ShortcutRegistration>> & {
|
|
60
60
|
middleClickClose?: boolean;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
});
|
|
62
|
+
/** Customize presentation only; matching and accessibility encoding use the binding. */
|
|
63
|
+
formatShortcut?: (binding: Readonly<KeyBinding>) => string;
|
|
64
64
|
getPaneState?: (paneId: string) => State;
|
|
65
65
|
onError?: (error: unknown) => void;
|
|
66
66
|
/** Copy additional app styles/assets into a same-origin companion document. */
|
|
@@ -68,12 +68,25 @@ export interface ResolvedLayoutOptions<State = unknown> {
|
|
|
68
68
|
/** Override window creation for applications with a managed same-origin host. Must return synchronously. */
|
|
69
69
|
openWindow?: (pane: Pane, placement: WindowPlacement) => Window | null;
|
|
70
70
|
}
|
|
71
|
+
/** Internal renderer configuration; mounted workspaces create this model themselves. */
|
|
72
|
+
export interface ResolvedLayoutOptions<State = unknown> extends WorkspaceSettings<State> {
|
|
73
|
+
store: LayoutStore;
|
|
74
|
+
tabs?: TabRegistry;
|
|
75
|
+
renderers?: Record<string, PaneRenderer<State>>;
|
|
76
|
+
registry?: PaneRegistry<PaneRenderer<State>>;
|
|
77
|
+
}
|
|
78
|
+
/** Initial configuration is consumed once. Use workspace commands for later changes. */
|
|
79
|
+
export type InitialConfiguration =
|
|
80
|
+
| { initialLayout?: Layout; initialWorkspace?: never }
|
|
81
|
+
| { initialLayout?: never; initialWorkspace: WorkspacePreset };
|
|
82
|
+
/** Commands and subscriptions for a workspace-owned store. */
|
|
83
|
+
export type WorkspaceStore = Omit<LayoutStore, 'dispose'>;
|
|
71
84
|
/** Choose a unified registry OR the low-level renderer and creation maps. */
|
|
72
85
|
export type LayoutOptions<State = unknown> = Omit<
|
|
73
86
|
ResolvedLayoutOptions<State>,
|
|
74
|
-
'registry' | 'renderers' | 'tabs' | 'getPaneState'
|
|
87
|
+
'store' | 'registry' | 'renderers' | 'tabs' | 'getPaneState'
|
|
75
88
|
> &
|
|
76
|
-
(unknown extends State
|
|
89
|
+
InitialConfiguration & { store?: never } & (unknown extends State
|
|
77
90
|
? { getPaneState?: (paneId: string) => State }
|
|
78
91
|
: { getPaneState: (paneId: string) => State }) &
|
|
79
92
|
(
|
|
@@ -85,6 +98,42 @@ export type LayoutOptionUpdates<State = unknown> = {
|
|
|
85
98
|
[Key in keyof Omit<ResolvedLayoutOptions<State>, 'store'>]?:
|
|
86
99
|
ResolvedLayoutOptions<State>[Key] | undefined;
|
|
87
100
|
};
|
|
101
|
+
export type WorkspaceAction =
|
|
102
|
+
| 'maximize'
|
|
103
|
+
| 'addTab'
|
|
104
|
+
| 'restoreClosedTab'
|
|
105
|
+
| 'closeActiveTab'
|
|
106
|
+
| 'closePane'
|
|
107
|
+
| 'closeEmptyPane'
|
|
108
|
+
| 'popout'
|
|
109
|
+
| 'joinSiblingRegion'
|
|
110
|
+
| 'splitLeft'
|
|
111
|
+
| 'splitRight'
|
|
112
|
+
| 'splitUp'
|
|
113
|
+
| 'splitDown'
|
|
114
|
+
| 'tabOrientationDefault'
|
|
115
|
+
| 'tabOrientationHorizontal'
|
|
116
|
+
| 'tabOrientationVertical'
|
|
117
|
+
| 'tabDisplayDefault'
|
|
118
|
+
| 'tabDisplayAutomatic'
|
|
119
|
+
| 'tabDisplayCompact';
|
|
120
|
+
export interface ActionContext {
|
|
121
|
+
groupId?: string;
|
|
122
|
+
paneId?: string;
|
|
123
|
+
}
|
|
124
|
+
export type ShortcutRegistration =
|
|
125
|
+
| boolean
|
|
126
|
+
| KeyBinding
|
|
127
|
+
| readonly KeyBinding[]
|
|
128
|
+
| {
|
|
129
|
+
bindings: true | KeyBinding | readonly KeyBinding[];
|
|
130
|
+
handling?: 'quilt' | 'external';
|
|
131
|
+
};
|
|
132
|
+
export interface RegisteredShortcut {
|
|
133
|
+
readonly action: WorkspaceAction;
|
|
134
|
+
readonly bindings: readonly Readonly<KeyBinding>[];
|
|
135
|
+
readonly handling: 'quilt' | 'external';
|
|
136
|
+
}
|
|
88
137
|
export interface KeyBinding {
|
|
89
138
|
key: string;
|
|
90
139
|
ctrl?: boolean;
|
|
@@ -99,6 +148,10 @@ export interface CloseRequest {
|
|
|
99
148
|
signal: AbortSignal;
|
|
100
149
|
}
|
|
101
150
|
export interface MountedLayout<State = unknown> {
|
|
151
|
+
readonly store: WorkspaceStore;
|
|
152
|
+
canExecuteAction(action: WorkspaceAction, context?: ActionContext): boolean;
|
|
153
|
+
executeAction(action: WorkspaceAction, context?: ActionContext): Promise<boolean>;
|
|
154
|
+
getShortcuts(): readonly RegisteredShortcut[];
|
|
102
155
|
updateOptions(options: LayoutOptionUpdates<State>): void;
|
|
103
156
|
exportWorkspace(): WorkspacePreset;
|
|
104
157
|
loadWorkspace(input: unknown): void;
|