sh3-core 0.11.4 → 0.11.6
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/dist/BrandSlot.svelte +80 -0
- package/dist/BrandSlot.svelte.d.ts +3 -0
- package/dist/BrandSlot.test.d.ts +1 -0
- package/dist/BrandSlot.test.js +71 -0
- package/dist/Shell.svelte +8 -10
- package/dist/actions/ActionPanel.svelte +105 -0
- package/dist/actions/ActionPanel.svelte.d.ts +13 -0
- package/dist/actions/ActionPanel.test.d.ts +1 -0
- package/dist/actions/ActionPanel.test.js +80 -0
- package/dist/actions/ContextMenu.svelte +17 -85
- package/dist/actions/MenuBar.svelte +57 -0
- package/dist/actions/MenuBar.svelte.d.ts +3 -0
- package/dist/actions/MenuBar.test.d.ts +1 -0
- package/dist/actions/MenuBar.test.js +109 -0
- package/dist/actions/MenuButton.svelte +104 -0
- package/dist/actions/MenuButton.svelte.d.ts +9 -0
- package/dist/actions/MenuButton.test.d.ts +1 -0
- package/dist/actions/MenuButton.test.js +88 -0
- package/dist/actions/defaultMenuContainers.d.ts +2 -0
- package/dist/actions/defaultMenuContainers.js +7 -0
- package/dist/actions/defaultMenuContainers.test.d.ts +1 -0
- package/dist/actions/defaultMenuContainers.test.js +23 -0
- package/dist/actions/menuBarModel.d.ts +28 -0
- package/dist/actions/menuBarModel.js +67 -0
- package/dist/actions/menuBarModel.test.d.ts +1 -0
- package/dist/actions/menuBarModel.test.js +84 -0
- package/dist/actions/types.d.ts +8 -0
- package/dist/apps/lifecycle.js +8 -1
- package/dist/apps/lifecycle.test.js +211 -1
- package/dist/apps/registry.svelte.d.ts +17 -1
- package/dist/apps/registry.svelte.js +20 -1
- package/dist/apps/types.d.ts +28 -0
- package/dist/layout/store.svelte.d.ts +27 -0
- package/dist/layout/store.svelte.js +63 -0
- package/dist/overlays/ConfirmDialog.svelte +138 -0
- package/dist/overlays/ConfirmDialog.svelte.d.ts +13 -0
- package/dist/overlays/ConfirmDialog.test.d.ts +1 -0
- package/dist/overlays/ConfirmDialog.test.js +123 -0
- package/dist/overlays/FloatFrame.svelte +2 -2
- package/dist/overlays/ToastItem.svelte +3 -3
- package/dist/primitives/base.css +5 -5
- package/dist/sh3core-shard/sh3coreShard.svelte.js +20 -0
- package/dist/shell-shard/shellShard.svelte.js +0 -4
- package/dist/tokens.css +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { tick } from 'svelte';
|
|
3
|
+
import { modalManager } from './modal';
|
|
4
|
+
import { registerLayerRoot, unregisterLayerRoot } from './roots';
|
|
5
|
+
import ConfirmDialog from './ConfirmDialog.svelte';
|
|
6
|
+
function makeLayerRoot() {
|
|
7
|
+
const el = document.createElement('div');
|
|
8
|
+
el.style.position = 'relative';
|
|
9
|
+
document.body.appendChild(el);
|
|
10
|
+
registerLayerRoot('modal', el);
|
|
11
|
+
return el;
|
|
12
|
+
}
|
|
13
|
+
function teardownLayerRoot(el) {
|
|
14
|
+
unregisterLayerRoot('modal');
|
|
15
|
+
el.remove();
|
|
16
|
+
}
|
|
17
|
+
describe('ConfirmDialog', () => {
|
|
18
|
+
let layerRoot;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
layerRoot = makeLayerRoot();
|
|
21
|
+
});
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
modalManager.closeAll();
|
|
24
|
+
teardownLayerRoot(layerRoot);
|
|
25
|
+
});
|
|
26
|
+
it('renders title and body', async () => {
|
|
27
|
+
modalManager.open(ConfirmDialog, {
|
|
28
|
+
title: 'Reset layout?',
|
|
29
|
+
body: 'This discards your customizations.',
|
|
30
|
+
onConfirm: () => { },
|
|
31
|
+
});
|
|
32
|
+
await tick();
|
|
33
|
+
const box = layerRoot.querySelector('.modal-box');
|
|
34
|
+
expect(box.textContent).toContain('Reset layout?');
|
|
35
|
+
expect(box.textContent).toContain('This discards your customizations.');
|
|
36
|
+
});
|
|
37
|
+
it('Cancel button calls onCancel and closes', async () => {
|
|
38
|
+
const onCancel = vi.fn();
|
|
39
|
+
const onConfirm = vi.fn();
|
|
40
|
+
modalManager.open(ConfirmDialog, {
|
|
41
|
+
title: 't',
|
|
42
|
+
body: 'b',
|
|
43
|
+
onCancel,
|
|
44
|
+
onConfirm,
|
|
45
|
+
});
|
|
46
|
+
await tick();
|
|
47
|
+
const cancelBtn = layerRoot.querySelector('[data-confirm-dialog-cancel]');
|
|
48
|
+
cancelBtn.click();
|
|
49
|
+
await tick();
|
|
50
|
+
expect(onCancel).toHaveBeenCalledOnce();
|
|
51
|
+
expect(onConfirm).not.toHaveBeenCalled();
|
|
52
|
+
expect(layerRoot.querySelector('.sh3-modal-host')).toBeNull();
|
|
53
|
+
});
|
|
54
|
+
it('Confirm button calls onConfirm and closes', async () => {
|
|
55
|
+
const onConfirm = vi.fn();
|
|
56
|
+
modalManager.open(ConfirmDialog, {
|
|
57
|
+
title: 't',
|
|
58
|
+
body: 'b',
|
|
59
|
+
onConfirm,
|
|
60
|
+
});
|
|
61
|
+
await tick();
|
|
62
|
+
const confirmBtn = layerRoot.querySelector('[data-confirm-dialog-confirm]');
|
|
63
|
+
confirmBtn.click();
|
|
64
|
+
await tick();
|
|
65
|
+
expect(onConfirm).toHaveBeenCalledOnce();
|
|
66
|
+
expect(layerRoot.querySelector('.sh3-modal-host')).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
it('awaits async onConfirm before closing', async () => {
|
|
69
|
+
let resolveFn = () => { };
|
|
70
|
+
const onConfirm = vi.fn(() => new Promise((resolve) => {
|
|
71
|
+
resolveFn = resolve;
|
|
72
|
+
}));
|
|
73
|
+
modalManager.open(ConfirmDialog, {
|
|
74
|
+
title: 't',
|
|
75
|
+
body: 'b',
|
|
76
|
+
onConfirm,
|
|
77
|
+
});
|
|
78
|
+
await tick();
|
|
79
|
+
const confirmBtn = layerRoot.querySelector('[data-confirm-dialog-confirm]');
|
|
80
|
+
confirmBtn.click();
|
|
81
|
+
await tick();
|
|
82
|
+
// Modal still open — onConfirm hasn't resolved yet.
|
|
83
|
+
expect(layerRoot.querySelector('.sh3-modal-host')).not.toBeNull();
|
|
84
|
+
resolveFn();
|
|
85
|
+
await tick();
|
|
86
|
+
await tick();
|
|
87
|
+
expect(layerRoot.querySelector('.sh3-modal-host')).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
it('confirmTone: "danger" applies the danger class to the confirm button', async () => {
|
|
90
|
+
modalManager.open(ConfirmDialog, {
|
|
91
|
+
title: 't',
|
|
92
|
+
body: 'b',
|
|
93
|
+
confirmTone: 'danger',
|
|
94
|
+
onConfirm: () => { },
|
|
95
|
+
});
|
|
96
|
+
await tick();
|
|
97
|
+
const confirmBtn = layerRoot.querySelector('[data-confirm-dialog-confirm]');
|
|
98
|
+
expect(confirmBtn.classList.contains('confirm-dialog-btn-danger')).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
it('uses provided confirmLabel and cancelLabel', async () => {
|
|
101
|
+
modalManager.open(ConfirmDialog, {
|
|
102
|
+
title: 't',
|
|
103
|
+
body: 'b',
|
|
104
|
+
confirmLabel: 'Wipe',
|
|
105
|
+
cancelLabel: 'Keep',
|
|
106
|
+
onConfirm: () => { },
|
|
107
|
+
});
|
|
108
|
+
await tick();
|
|
109
|
+
expect(layerRoot.querySelector('[data-confirm-dialog-confirm]').textContent).toContain('Wipe');
|
|
110
|
+
expect(layerRoot.querySelector('[data-confirm-dialog-cancel]').textContent).toContain('Keep');
|
|
111
|
+
});
|
|
112
|
+
it('default focus is the Cancel button', async () => {
|
|
113
|
+
modalManager.open(ConfirmDialog, {
|
|
114
|
+
title: 't',
|
|
115
|
+
body: 'b',
|
|
116
|
+
onConfirm: () => { },
|
|
117
|
+
});
|
|
118
|
+
await tick();
|
|
119
|
+
await tick(); // focus is set in $effect after mount
|
|
120
|
+
const cancelBtn = layerRoot.querySelector('[data-confirm-dialog-cancel]');
|
|
121
|
+
expect(document.activeElement).toBe(cancelBtn);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
position: absolute;
|
|
110
110
|
display: flex;
|
|
111
111
|
flex-direction: column;
|
|
112
|
-
background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated
|
|
112
|
+
background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
|
|
113
113
|
color: var(--shell-fg);
|
|
114
114
|
border: 1px solid var(--shell-border-strong);
|
|
115
115
|
border-radius: var(--shell-radius);
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
align-items: center;
|
|
122
122
|
justify-content: space-between;
|
|
123
123
|
padding: 4px 8px;
|
|
124
|
-
background: var(--shell-bg,
|
|
124
|
+
background: var(--shell-grad-bg-sunken, var(--shell-bg-sunken));
|
|
125
125
|
cursor: move;
|
|
126
126
|
user-select: none;
|
|
127
127
|
border-bottom: 1px solid var(--shell-border-strong);
|
|
@@ -66,9 +66,9 @@
|
|
|
66
66
|
.toast-message { flex: 1; }
|
|
67
67
|
|
|
68
68
|
.toast-info { border-left-color: var(--shell-accent); }
|
|
69
|
-
.toast-success { border-left-color:
|
|
70
|
-
.toast-warn { border-left-color:
|
|
71
|
-
.toast-error { border-left-color:
|
|
69
|
+
.toast-success { border-left-color: var(--shell-success); }
|
|
70
|
+
.toast-warn { border-left-color: var(--shell-warning); }
|
|
71
|
+
.toast-error { border-left-color: var(--shell-error); }
|
|
72
72
|
|
|
73
73
|
@keyframes toast-in {
|
|
74
74
|
from { opacity: 0; transform: translateY(8px); }
|
package/dist/primitives/base.css
CHANGED
|
@@ -15,8 +15,8 @@ input[type="submit"],
|
|
|
15
15
|
input[type="reset"],
|
|
16
16
|
.shell-base-button {
|
|
17
17
|
padding: 6px 14px;
|
|
18
|
-
background: var(--shell-accent
|
|
19
|
-
color: var(--shell-fg
|
|
18
|
+
background: var(--shell-accent);
|
|
19
|
+
color: var(--shell-fg-on-accent);
|
|
20
20
|
border: none;
|
|
21
21
|
border-radius: var(--shell-radius);
|
|
22
22
|
cursor: pointer;
|
|
@@ -113,7 +113,7 @@ input[type="radio"].shell-base-radio {
|
|
|
113
113
|
content: "";
|
|
114
114
|
width: 8px;
|
|
115
115
|
height: 8px;
|
|
116
|
-
background:
|
|
116
|
+
background: var(--shell-fg-on-accent);
|
|
117
117
|
clip-path: polygon(14% 44%, 0 60%, 40% 100%, 100% 20%, 85% 8%, 38% 70%);
|
|
118
118
|
}
|
|
119
119
|
|
|
@@ -122,7 +122,7 @@ input[type="radio"].shell-base-radio {
|
|
|
122
122
|
width: 6px;
|
|
123
123
|
height: 6px;
|
|
124
124
|
border-radius: 50%;
|
|
125
|
-
background:
|
|
125
|
+
background: var(--shell-fg-on-accent);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
.shell-base-check:focus-visible,
|
|
@@ -167,7 +167,7 @@ input[type="checkbox"].shell-base-switch {
|
|
|
167
167
|
.shell-base-switch:checked { background: var(--shell-accent); }
|
|
168
168
|
.shell-base-switch:checked::before {
|
|
169
169
|
transform: translateX(12px);
|
|
170
|
-
background:
|
|
170
|
+
background: var(--shell-fg-on-accent);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
.shell-base-switch:focus-visible {
|
|
@@ -24,10 +24,13 @@
|
|
|
24
24
|
import { mount, unmount } from 'svelte';
|
|
25
25
|
import ShellHome from './ShellHome.svelte';
|
|
26
26
|
import KeysAndPeers from '../shell/views/KeysAndPeers.svelte';
|
|
27
|
+
import ConfirmDialog from '../overlays/ConfirmDialog.svelte';
|
|
27
28
|
import { VERSION } from '../version';
|
|
28
29
|
import { __setBindingsZone } from '../actions/bindings-store';
|
|
29
30
|
import { registeredApps } from '../apps/registry.svelte';
|
|
30
31
|
import { launchApp } from '../apps/lifecycle';
|
|
32
|
+
import { resetActivePresetToDefault } from '../layout/store.svelte';
|
|
33
|
+
import { modalManager } from '../overlays/modal';
|
|
31
34
|
export const sh3coreShard = {
|
|
32
35
|
manifest: {
|
|
33
36
|
id: '__sh3core__',
|
|
@@ -57,6 +60,23 @@ export const sh3coreShard = {
|
|
|
57
60
|
import('../actions/listeners').then(({ openPalette }) => openPalette());
|
|
58
61
|
},
|
|
59
62
|
});
|
|
63
|
+
ctx.actions.register({
|
|
64
|
+
id: 'sh3.app.reset-layout',
|
|
65
|
+
label: 'Reset Current Layout',
|
|
66
|
+
scope: ['app'],
|
|
67
|
+
paletteItem: true,
|
|
68
|
+
contextItem: false,
|
|
69
|
+
run() {
|
|
70
|
+
modalManager.open(ConfirmDialog, {
|
|
71
|
+
title: 'Reset layout?',
|
|
72
|
+
body: 'This discards the current arrangement of the active preset and ' +
|
|
73
|
+
'rebuilds it from the app default. Floats will be removed.',
|
|
74
|
+
confirmLabel: 'Reset',
|
|
75
|
+
confirmTone: 'danger',
|
|
76
|
+
onConfirm: () => resetActivePresetToDefault(),
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
});
|
|
60
80
|
const factory = {
|
|
61
81
|
mount(container, _context) {
|
|
62
82
|
const instance = mount(ShellHome, { target: container });
|
|
@@ -176,10 +176,6 @@ export function makeShellApiForTest() {
|
|
|
176
176
|
export const shellShard = {
|
|
177
177
|
manifest,
|
|
178
178
|
activate(ctx) {
|
|
179
|
-
if (!ctx.isAdmin) {
|
|
180
|
-
// Non-admin: don't expose the view. Nothing to register.
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
179
|
registerV1Verbs(ctx);
|
|
184
180
|
const shell = makeShellApi(ctx);
|
|
185
181
|
// The AZERTY `²` key (top-left on FR keyboards, below Escape) opens the
|
package/dist/tokens.css
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
--shell-accent-muted: #3a5580;
|
|
34
34
|
|
|
35
35
|
/* Inputs */
|
|
36
|
-
--shell-input-bg:
|
|
36
|
+
--shell-input-bg: var(--shell-bg-sunken);
|
|
37
37
|
--shell-input-border-focus: var(--shell-accent);
|
|
38
38
|
--shell-focus-ring: 0 0 0 2px color-mix(in srgb, var(--shell-accent) 40%, transparent);
|
|
39
39
|
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
-
export declare const VERSION = "0.11.
|
|
2
|
+
export declare const VERSION = "0.11.6";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
-
export const VERSION = '0.11.
|
|
2
|
+
export const VERSION = '0.11.6';
|