sh3-core 0.1.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/dist/Shell.svelte +185 -0
- package/dist/Shell.svelte.d.ts +4 -0
- package/dist/api.d.ts +22 -0
- package/dist/api.js +45 -0
- package/dist/apps/lifecycle.d.ts +37 -0
- package/dist/apps/lifecycle.js +153 -0
- package/dist/apps/registry.svelte.d.ts +37 -0
- package/dist/apps/registry.svelte.js +60 -0
- package/dist/apps/types.d.ts +61 -0
- package/dist/apps/types.js +10 -0
- package/dist/assets/icons.svg +1119 -0
- package/dist/auth/auth.svelte.d.ts +44 -0
- package/dist/auth/auth.svelte.js +119 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.js +1 -0
- package/dist/build.d.ts +29 -0
- package/dist/build.js +85 -0
- package/dist/contract.d.ts +20 -0
- package/dist/contract.js +28 -0
- package/dist/documents/backends.d.ts +17 -0
- package/dist/documents/backends.js +156 -0
- package/dist/documents/config.d.ts +7 -0
- package/dist/documents/config.js +27 -0
- package/dist/documents/handle.d.ts +6 -0
- package/dist/documents/handle.js +154 -0
- package/dist/documents/http-backend.d.ts +22 -0
- package/dist/documents/http-backend.js +78 -0
- package/dist/documents/index.d.ts +6 -0
- package/dist/documents/index.js +8 -0
- package/dist/documents/notifications.d.ts +9 -0
- package/dist/documents/notifications.js +39 -0
- package/dist/documents/types.d.ts +97 -0
- package/dist/documents/types.js +12 -0
- package/dist/host-entry.d.ts +9 -0
- package/dist/host-entry.js +15 -0
- package/dist/host.d.ts +13 -0
- package/dist/host.js +73 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +13 -0
- package/dist/layout/DragPreview.svelte +63 -0
- package/dist/layout/DragPreview.svelte.d.ts +3 -0
- package/dist/layout/LayoutRenderer.svelte +260 -0
- package/dist/layout/LayoutRenderer.svelte.d.ts +6 -0
- package/dist/layout/SlotContainer.svelte +140 -0
- package/dist/layout/SlotContainer.svelte.d.ts +8 -0
- package/dist/layout/SlotDropZone.svelte +122 -0
- package/dist/layout/SlotDropZone.svelte.d.ts +8 -0
- package/dist/layout/drag.svelte.d.ts +45 -0
- package/dist/layout/drag.svelte.js +191 -0
- package/dist/layout/inspection.d.ts +52 -0
- package/dist/layout/inspection.js +157 -0
- package/dist/layout/ops.d.ts +78 -0
- package/dist/layout/ops.js +281 -0
- package/dist/layout/slotHostPool.svelte.d.ts +36 -0
- package/dist/layout/slotHostPool.svelte.js +229 -0
- package/dist/layout/store.svelte.d.ts +39 -0
- package/dist/layout/store.svelte.js +150 -0
- package/dist/layout/tree-walk.d.ts +15 -0
- package/dist/layout/tree-walk.js +33 -0
- package/dist/layout/types.d.ts +108 -0
- package/dist/layout/types.js +25 -0
- package/dist/overlays/ModalFrame.svelte +87 -0
- package/dist/overlays/ModalFrame.svelte.d.ts +10 -0
- package/dist/overlays/PopupFrame.svelte +85 -0
- package/dist/overlays/PopupFrame.svelte.d.ts +10 -0
- package/dist/overlays/ToastItem.svelte +77 -0
- package/dist/overlays/ToastItem.svelte.d.ts +9 -0
- package/dist/overlays/focusTrap.d.ts +1 -0
- package/dist/overlays/focusTrap.js +64 -0
- package/dist/overlays/modal.d.ts +9 -0
- package/dist/overlays/modal.js +141 -0
- package/dist/overlays/popup.d.ts +9 -0
- package/dist/overlays/popup.js +108 -0
- package/dist/overlays/roots.d.ts +4 -0
- package/dist/overlays/roots.js +31 -0
- package/dist/overlays/toast.d.ts +6 -0
- package/dist/overlays/toast.js +93 -0
- package/dist/overlays/types.d.ts +31 -0
- package/dist/overlays/types.js +15 -0
- package/dist/primitives/.gitkeep +0 -0
- package/dist/primitives/ResizableSplitter.svelte +333 -0
- package/dist/primitives/ResizableSplitter.svelte.d.ts +35 -0
- package/dist/primitives/TabbedPanel.svelte +305 -0
- package/dist/primitives/TabbedPanel.svelte.d.ts +50 -0
- package/dist/registry/client.d.ts +74 -0
- package/dist/registry/client.js +118 -0
- package/dist/registry/index.d.ts +13 -0
- package/dist/registry/index.js +14 -0
- package/dist/registry/installer.d.ts +53 -0
- package/dist/registry/installer.js +170 -0
- package/dist/registry/integrity.d.ts +32 -0
- package/dist/registry/integrity.js +92 -0
- package/dist/registry/loader.d.ts +50 -0
- package/dist/registry/loader.js +145 -0
- package/dist/registry/schema.d.ts +47 -0
- package/dist/registry/schema.js +180 -0
- package/dist/registry/storage.d.ts +37 -0
- package/dist/registry/storage.js +101 -0
- package/dist/registry/types.d.ts +245 -0
- package/dist/registry/types.js +14 -0
- package/dist/registry-shard/RegistryView.svelte +561 -0
- package/dist/registry-shard/RegistryView.svelte.d.ts +3 -0
- package/dist/registry-shard/registryApp.d.ts +10 -0
- package/dist/registry-shard/registryApp.js +24 -0
- package/dist/registry-shard/registryShard.svelte.d.ts +45 -0
- package/dist/registry-shard/registryShard.svelte.js +125 -0
- package/dist/shards/activate.svelte.d.ts +45 -0
- package/dist/shards/activate.svelte.js +124 -0
- package/dist/shards/registry.d.ts +4 -0
- package/dist/shards/registry.js +28 -0
- package/dist/shards/types.d.ts +155 -0
- package/dist/shards/types.js +20 -0
- package/dist/shell-shard/ShellHome.svelte +285 -0
- package/dist/shell-shard/ShellHome.svelte.d.ts +3 -0
- package/dist/shell-shard/shellShard.svelte.d.ts +2 -0
- package/dist/shell-shard/shellShard.svelte.js +47 -0
- package/dist/shellRuntime.svelte.d.ts +27 -0
- package/dist/shellRuntime.svelte.js +27 -0
- package/dist/state/backends.d.ts +26 -0
- package/dist/state/backends.js +99 -0
- package/dist/state/types.d.ts +38 -0
- package/dist/state/types.js +15 -0
- package/dist/state/zones.svelte.d.ts +52 -0
- package/dist/state/zones.svelte.js +141 -0
- package/dist/store/InstalledView.svelte +201 -0
- package/dist/store/InstalledView.svelte.d.ts +3 -0
- package/dist/store/StoreView.svelte +470 -0
- package/dist/store/StoreView.svelte.d.ts +3 -0
- package/dist/store/storeApp.d.ts +11 -0
- package/dist/store/storeApp.js +26 -0
- package/dist/store/storeShard.svelte.d.ts +29 -0
- package/dist/store/storeShard.svelte.js +99 -0
- package/dist/tokens.css +79 -0
- package/package.json +50 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Document handle — the shard-facing API for the document zone.
|
|
3
|
+
*
|
|
4
|
+
* `createDocumentHandle` binds tenant, shard, backend, and options so
|
|
5
|
+
* shard code only deals in paths. Supports explicit save, autosave
|
|
6
|
+
* with debounce, and change notifications via the global emitter.
|
|
7
|
+
*/
|
|
8
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
9
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
10
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
11
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
12
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
13
|
+
};
|
|
14
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
15
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
16
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
17
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
18
|
+
};
|
|
19
|
+
var _AutosaveControllerImpl_instances, _AutosaveControllerImpl_handle, _AutosaveControllerImpl_path, _AutosaveControllerImpl_debounceMs, _AutosaveControllerImpl_pending, _AutosaveControllerImpl_timer, _AutosaveControllerImpl_dirty, _AutosaveControllerImpl_disposed, _AutosaveControllerImpl_scheduleFlush, _AutosaveControllerImpl_clearTimer;
|
|
20
|
+
import { documentChanges } from './notifications';
|
|
21
|
+
const DEFAULT_DEBOUNCE_MS = 1000;
|
|
22
|
+
/**
|
|
23
|
+
* Create a document handle scoped to a tenant, shard, and file filter.
|
|
24
|
+
* The framework calls this from `ShardContext.documents()`.
|
|
25
|
+
*/
|
|
26
|
+
export function createDocumentHandle(tenantId, shardId, backend, options) {
|
|
27
|
+
const controllers = new Set();
|
|
28
|
+
const unsubscribers = new Set();
|
|
29
|
+
function matchesExtensions(path) {
|
|
30
|
+
if (!options.extensions || options.extensions.length === 0)
|
|
31
|
+
return true;
|
|
32
|
+
return options.extensions.some((ext) => path.endsWith(ext));
|
|
33
|
+
}
|
|
34
|
+
function emitChange(type, path) {
|
|
35
|
+
documentChanges.emit({ type, path, tenantId, shardId });
|
|
36
|
+
}
|
|
37
|
+
const handle = {
|
|
38
|
+
async list() {
|
|
39
|
+
const all = await backend.list(tenantId, shardId);
|
|
40
|
+
if (!options.extensions || options.extensions.length === 0)
|
|
41
|
+
return all;
|
|
42
|
+
return all.filter((meta) => matchesExtensions(meta.path));
|
|
43
|
+
},
|
|
44
|
+
async read(path) {
|
|
45
|
+
const content = await backend.read(tenantId, shardId, path);
|
|
46
|
+
if (content === null)
|
|
47
|
+
return null;
|
|
48
|
+
// Phase 1: text format only. Binary returns as-is from the backend
|
|
49
|
+
// but the handle types it as string for text-format handles.
|
|
50
|
+
return typeof content === 'string' ? content : new TextDecoder().decode(content);
|
|
51
|
+
},
|
|
52
|
+
async write(path, content) {
|
|
53
|
+
const existed = await backend.exists(tenantId, shardId, path);
|
|
54
|
+
await backend.write(tenantId, shardId, path, content);
|
|
55
|
+
emitChange(existed ? 'update' : 'create', path);
|
|
56
|
+
},
|
|
57
|
+
async delete(path) {
|
|
58
|
+
await backend.delete(tenantId, shardId, path);
|
|
59
|
+
emitChange('delete', path);
|
|
60
|
+
},
|
|
61
|
+
async exists(path) {
|
|
62
|
+
return backend.exists(tenantId, shardId, path);
|
|
63
|
+
},
|
|
64
|
+
watch(callback) {
|
|
65
|
+
// Subscribe to global emitter, filtered to this handle's scope.
|
|
66
|
+
const unsub = documentChanges.subscribe((change) => {
|
|
67
|
+
if (change.tenantId !== tenantId)
|
|
68
|
+
return;
|
|
69
|
+
if (change.shardId !== shardId)
|
|
70
|
+
return;
|
|
71
|
+
if (!matchesExtensions(change.path))
|
|
72
|
+
return;
|
|
73
|
+
callback(change);
|
|
74
|
+
});
|
|
75
|
+
unsubscribers.add(unsub);
|
|
76
|
+
return () => {
|
|
77
|
+
unsub();
|
|
78
|
+
unsubscribers.delete(unsub);
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
autosave(path, opts) {
|
|
82
|
+
var _a;
|
|
83
|
+
const ctrl = new AutosaveControllerImpl(handle, path, (_a = opts === null || opts === void 0 ? void 0 : opts.debounceMs) !== null && _a !== void 0 ? _a : DEFAULT_DEBOUNCE_MS);
|
|
84
|
+
controllers.add(ctrl);
|
|
85
|
+
return ctrl;
|
|
86
|
+
},
|
|
87
|
+
async dispose() {
|
|
88
|
+
// Flush and dispose all active autosave controllers.
|
|
89
|
+
const flushes = [...controllers].map((c) => c.dispose());
|
|
90
|
+
await Promise.all(flushes);
|
|
91
|
+
controllers.clear();
|
|
92
|
+
// Unsubscribe all watchers.
|
|
93
|
+
for (const unsub of unsubscribers)
|
|
94
|
+
unsub();
|
|
95
|
+
unsubscribers.clear();
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
return handle;
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// AutosaveController implementation
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
class AutosaveControllerImpl {
|
|
104
|
+
constructor(handle, path, debounceMs) {
|
|
105
|
+
_AutosaveControllerImpl_instances.add(this);
|
|
106
|
+
_AutosaveControllerImpl_handle.set(this, void 0);
|
|
107
|
+
_AutosaveControllerImpl_path.set(this, void 0);
|
|
108
|
+
_AutosaveControllerImpl_debounceMs.set(this, void 0);
|
|
109
|
+
_AutosaveControllerImpl_pending.set(this, null);
|
|
110
|
+
_AutosaveControllerImpl_timer.set(this, null);
|
|
111
|
+
_AutosaveControllerImpl_dirty.set(this, false);
|
|
112
|
+
_AutosaveControllerImpl_disposed.set(this, false);
|
|
113
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_handle, handle, "f");
|
|
114
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_path, path, "f");
|
|
115
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_debounceMs, debounceMs, "f");
|
|
116
|
+
}
|
|
117
|
+
get dirty() {
|
|
118
|
+
return __classPrivateFieldGet(this, _AutosaveControllerImpl_dirty, "f");
|
|
119
|
+
}
|
|
120
|
+
update(content) {
|
|
121
|
+
if (__classPrivateFieldGet(this, _AutosaveControllerImpl_disposed, "f"))
|
|
122
|
+
return;
|
|
123
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_pending, content, "f");
|
|
124
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_dirty, true, "f");
|
|
125
|
+
__classPrivateFieldGet(this, _AutosaveControllerImpl_instances, "m", _AutosaveControllerImpl_scheduleFlush).call(this);
|
|
126
|
+
}
|
|
127
|
+
async flush() {
|
|
128
|
+
__classPrivateFieldGet(this, _AutosaveControllerImpl_instances, "m", _AutosaveControllerImpl_clearTimer).call(this);
|
|
129
|
+
if (__classPrivateFieldGet(this, _AutosaveControllerImpl_pending, "f") !== null) {
|
|
130
|
+
const content = __classPrivateFieldGet(this, _AutosaveControllerImpl_pending, "f");
|
|
131
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_pending, null, "f");
|
|
132
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_dirty, false, "f");
|
|
133
|
+
await __classPrivateFieldGet(this, _AutosaveControllerImpl_handle, "f").write(__classPrivateFieldGet(this, _AutosaveControllerImpl_path, "f"), content);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async dispose() {
|
|
137
|
+
if (__classPrivateFieldGet(this, _AutosaveControllerImpl_disposed, "f"))
|
|
138
|
+
return;
|
|
139
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_disposed, true, "f");
|
|
140
|
+
await this.flush();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
_AutosaveControllerImpl_handle = new WeakMap(), _AutosaveControllerImpl_path = new WeakMap(), _AutosaveControllerImpl_debounceMs = new WeakMap(), _AutosaveControllerImpl_pending = new WeakMap(), _AutosaveControllerImpl_timer = new WeakMap(), _AutosaveControllerImpl_dirty = new WeakMap(), _AutosaveControllerImpl_disposed = new WeakMap(), _AutosaveControllerImpl_instances = new WeakSet(), _AutosaveControllerImpl_scheduleFlush = function _AutosaveControllerImpl_scheduleFlush() {
|
|
144
|
+
__classPrivateFieldGet(this, _AutosaveControllerImpl_instances, "m", _AutosaveControllerImpl_clearTimer).call(this);
|
|
145
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_timer, setTimeout(() => {
|
|
146
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_timer, null, "f");
|
|
147
|
+
void this.flush();
|
|
148
|
+
}, __classPrivateFieldGet(this, _AutosaveControllerImpl_debounceMs, "f")), "f");
|
|
149
|
+
}, _AutosaveControllerImpl_clearTimer = function _AutosaveControllerImpl_clearTimer() {
|
|
150
|
+
if (__classPrivateFieldGet(this, _AutosaveControllerImpl_timer, "f") !== null) {
|
|
151
|
+
clearTimeout(__classPrivateFieldGet(this, _AutosaveControllerImpl_timer, "f"));
|
|
152
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_timer, null, "f");
|
|
153
|
+
}
|
|
154
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP document backend — connects to sh3-server's document API.
|
|
3
|
+
*
|
|
4
|
+
* Implements the DocumentBackend interface over HTTP. Read operations
|
|
5
|
+
* are unauthenticated; write operations send the API key as a Bearer
|
|
6
|
+
* token. This is the web-hosted equivalent of the Tauri filesystem
|
|
7
|
+
* backend.
|
|
8
|
+
*/
|
|
9
|
+
import type { DocumentBackend, DocumentMeta } from './types';
|
|
10
|
+
export declare class HttpDocumentBackend implements DocumentBackend {
|
|
11
|
+
#private;
|
|
12
|
+
/**
|
|
13
|
+
* @param baseUrl - The server origin (e.g. 'http://localhost:3000' or window.location.origin).
|
|
14
|
+
* @param apiKey - Optional API key for write operations.
|
|
15
|
+
*/
|
|
16
|
+
constructor(baseUrl: string, apiKey?: string);
|
|
17
|
+
read(tenantId: string, shardId: string, path: string): Promise<string | ArrayBuffer | null>;
|
|
18
|
+
write(tenantId: string, shardId: string, path: string, content: string | ArrayBuffer): Promise<void>;
|
|
19
|
+
delete(tenantId: string, shardId: string, path: string): Promise<void>;
|
|
20
|
+
list(tenantId: string, shardId: string): Promise<DocumentMeta[]>;
|
|
21
|
+
exists(tenantId: string, shardId: string, path: string): Promise<boolean>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP document backend — connects to sh3-server's document API.
|
|
3
|
+
*
|
|
4
|
+
* Implements the DocumentBackend interface over HTTP. Read operations
|
|
5
|
+
* are unauthenticated; write operations send the API key as a Bearer
|
|
6
|
+
* token. This is the web-hosted equivalent of the Tauri filesystem
|
|
7
|
+
* backend.
|
|
8
|
+
*/
|
|
9
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
10
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
11
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
12
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
13
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
14
|
+
};
|
|
15
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
16
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
17
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
18
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
19
|
+
};
|
|
20
|
+
var _HttpDocumentBackend_instances, _HttpDocumentBackend_baseUrl, _HttpDocumentBackend_apiKey, _HttpDocumentBackend_authHeaders;
|
|
21
|
+
export class HttpDocumentBackend {
|
|
22
|
+
/**
|
|
23
|
+
* @param baseUrl - The server origin (e.g. 'http://localhost:3000' or window.location.origin).
|
|
24
|
+
* @param apiKey - Optional API key for write operations.
|
|
25
|
+
*/
|
|
26
|
+
constructor(baseUrl, apiKey) {
|
|
27
|
+
_HttpDocumentBackend_instances.add(this);
|
|
28
|
+
_HttpDocumentBackend_baseUrl.set(this, void 0);
|
|
29
|
+
_HttpDocumentBackend_apiKey.set(this, void 0);
|
|
30
|
+
// Strip trailing slash
|
|
31
|
+
__classPrivateFieldSet(this, _HttpDocumentBackend_baseUrl, baseUrl.replace(/\/$/, ''), "f");
|
|
32
|
+
__classPrivateFieldSet(this, _HttpDocumentBackend_apiKey, apiKey, "f");
|
|
33
|
+
}
|
|
34
|
+
async read(tenantId, shardId, path) {
|
|
35
|
+
var _a;
|
|
36
|
+
const url = `${__classPrivateFieldGet(this, _HttpDocumentBackend_baseUrl, "f")}/api/docs/${tenantId}/${shardId}/${path}`;
|
|
37
|
+
const res = await fetch(url);
|
|
38
|
+
if (res.status === 404)
|
|
39
|
+
return null;
|
|
40
|
+
if (!res.ok)
|
|
41
|
+
throw new Error(`Document read failed: ${res.status}`);
|
|
42
|
+
const ct = (_a = res.headers.get('content-type')) !== null && _a !== void 0 ? _a : '';
|
|
43
|
+
if (ct.includes('application/octet-stream')) {
|
|
44
|
+
return res.arrayBuffer();
|
|
45
|
+
}
|
|
46
|
+
return res.text();
|
|
47
|
+
}
|
|
48
|
+
async write(tenantId, shardId, path, content) {
|
|
49
|
+
const url = `${__classPrivateFieldGet(this, _HttpDocumentBackend_baseUrl, "f")}/api/docs/${tenantId}/${shardId}/${path}`;
|
|
50
|
+
const headers = Object.assign(Object.assign({}, __classPrivateFieldGet(this, _HttpDocumentBackend_instances, "m", _HttpDocumentBackend_authHeaders).call(this)), { 'Content-Type': typeof content === 'string' ? 'text/plain' : 'application/octet-stream' });
|
|
51
|
+
const res = await fetch(url, { method: 'PUT', headers, body: content });
|
|
52
|
+
if (!res.ok)
|
|
53
|
+
throw new Error(`Document write failed: ${res.status}`);
|
|
54
|
+
}
|
|
55
|
+
async delete(tenantId, shardId, path) {
|
|
56
|
+
const url = `${__classPrivateFieldGet(this, _HttpDocumentBackend_baseUrl, "f")}/api/docs/${tenantId}/${shardId}/${path}`;
|
|
57
|
+
const res = await fetch(url, { method: 'DELETE', headers: __classPrivateFieldGet(this, _HttpDocumentBackend_instances, "m", _HttpDocumentBackend_authHeaders).call(this) });
|
|
58
|
+
if (!res.ok)
|
|
59
|
+
throw new Error(`Document delete failed: ${res.status}`);
|
|
60
|
+
}
|
|
61
|
+
async list(tenantId, shardId) {
|
|
62
|
+
const url = `${__classPrivateFieldGet(this, _HttpDocumentBackend_baseUrl, "f")}/api/docs/${tenantId}/${shardId}`;
|
|
63
|
+
const res = await fetch(url);
|
|
64
|
+
if (!res.ok)
|
|
65
|
+
throw new Error(`Document list failed: ${res.status}`);
|
|
66
|
+
return res.json();
|
|
67
|
+
}
|
|
68
|
+
async exists(tenantId, shardId, path) {
|
|
69
|
+
const url = `${__classPrivateFieldGet(this, _HttpDocumentBackend_baseUrl, "f")}/api/docs/${tenantId}/${shardId}/${path}`;
|
|
70
|
+
const res = await fetch(url, { method: 'HEAD' });
|
|
71
|
+
return res.ok;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
_HttpDocumentBackend_baseUrl = new WeakMap(), _HttpDocumentBackend_apiKey = new WeakMap(), _HttpDocumentBackend_instances = new WeakSet(), _HttpDocumentBackend_authHeaders = function _HttpDocumentBackend_authHeaders() {
|
|
75
|
+
if (!__classPrivateFieldGet(this, _HttpDocumentBackend_apiKey, "f"))
|
|
76
|
+
return {};
|
|
77
|
+
return { Authorization: `Bearer ${__classPrivateFieldGet(this, _HttpDocumentBackend_apiKey, "f")}` };
|
|
78
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { DocumentFormat, DocumentHandleOptions, DocumentMeta, DocumentChange, DocumentBackend, DocumentHandle, AutosaveController, } from './types';
|
|
2
|
+
export { MemoryDocumentBackend, IndexedDBDocumentBackend } from './backends';
|
|
3
|
+
export { HttpDocumentBackend } from './http-backend';
|
|
4
|
+
export { createDocumentHandle } from './handle';
|
|
5
|
+
export { documentChanges } from './notifications';
|
|
6
|
+
export { getTenantId, getDocumentBackend, __setTenantId, __setDocumentBackend, } from './config';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Document zone barrel — internal re-exports.
|
|
3
|
+
*/
|
|
4
|
+
export { MemoryDocumentBackend, IndexedDBDocumentBackend } from './backends';
|
|
5
|
+
export { HttpDocumentBackend } from './http-backend';
|
|
6
|
+
export { createDocumentHandle } from './handle';
|
|
7
|
+
export { documentChanges } from './notifications';
|
|
8
|
+
export { getTenantId, getDocumentBackend, __setTenantId, __setDocumentBackend, } from './config';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DocumentChange } from './types';
|
|
2
|
+
type Listener = (change: DocumentChange) => void;
|
|
3
|
+
declare class DocumentChangeEmitter {
|
|
4
|
+
#private;
|
|
5
|
+
subscribe(fn: Listener): () => void;
|
|
6
|
+
emit(change: DocumentChange): void;
|
|
7
|
+
}
|
|
8
|
+
export declare const documentChanges: DocumentChangeEmitter;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Document change notification emitter — in-process pub/sub.
|
|
3
|
+
*
|
|
4
|
+
* A single global emitter connects document handles so that a write
|
|
5
|
+
* from one handle (e.g. an editor shard saving a file) is visible to
|
|
6
|
+
* another handle watching the same scope (e.g. a preview shard).
|
|
7
|
+
*
|
|
8
|
+
* Cross-tab sync (BroadcastChannel) is deferred — this emitter is
|
|
9
|
+
* in-process only.
|
|
10
|
+
*/
|
|
11
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
12
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
13
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
14
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
15
|
+
};
|
|
16
|
+
var _DocumentChangeEmitter_listeners;
|
|
17
|
+
class DocumentChangeEmitter {
|
|
18
|
+
constructor() {
|
|
19
|
+
_DocumentChangeEmitter_listeners.set(this, new Set());
|
|
20
|
+
}
|
|
21
|
+
subscribe(fn) {
|
|
22
|
+
__classPrivateFieldGet(this, _DocumentChangeEmitter_listeners, "f").add(fn);
|
|
23
|
+
return () => {
|
|
24
|
+
__classPrivateFieldGet(this, _DocumentChangeEmitter_listeners, "f").delete(fn);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
emit(change) {
|
|
28
|
+
for (const fn of __classPrivateFieldGet(this, _DocumentChangeEmitter_listeners, "f")) {
|
|
29
|
+
try {
|
|
30
|
+
fn(change);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
console.error('SH3: document change listener threw', e);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
_DocumentChangeEmitter_listeners = new WeakMap();
|
|
39
|
+
export const documentChanges = new DocumentChangeEmitter();
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format hint for document content. Determines whether reads return a string
|
|
3
|
+
* (`text`) or an `ArrayBuffer` (`binary`).
|
|
4
|
+
*/
|
|
5
|
+
export type DocumentFormat = 'text' | 'binary';
|
|
6
|
+
/**
|
|
7
|
+
* Options passed to `ctx.documents()` to scope the handle. The handle
|
|
8
|
+
* only operates on files matching the declared extensions (if provided).
|
|
9
|
+
* Omitting `extensions` means "all files."
|
|
10
|
+
*/
|
|
11
|
+
export interface DocumentHandleOptions {
|
|
12
|
+
format: DocumentFormat;
|
|
13
|
+
/** File extensions this handle operates on, e.g. ['.guml', '.md']. */
|
|
14
|
+
extensions?: string[];
|
|
15
|
+
}
|
|
16
|
+
/** Metadata about a stored document. */
|
|
17
|
+
export interface DocumentMeta {
|
|
18
|
+
path: string;
|
|
19
|
+
size: number;
|
|
20
|
+
/** Last modified timestamp in epoch milliseconds. */
|
|
21
|
+
lastModified: number;
|
|
22
|
+
}
|
|
23
|
+
/** Change notification payload delivered to watch callbacks. */
|
|
24
|
+
export interface DocumentChange {
|
|
25
|
+
type: 'create' | 'update' | 'delete';
|
|
26
|
+
path: string;
|
|
27
|
+
tenantId: string;
|
|
28
|
+
shardId: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* File-oriented backend for the document zone.
|
|
32
|
+
*
|
|
33
|
+
* Every method is tenant- and shard-scoped. The backend is responsible
|
|
34
|
+
* for enforcing isolation: a read for tenant A must never return
|
|
35
|
+
* content belonging to tenant B.
|
|
36
|
+
*
|
|
37
|
+
* Logical storage namespace: /{tenantId}/{shardId}/{path}
|
|
38
|
+
*/
|
|
39
|
+
export interface DocumentBackend {
|
|
40
|
+
/**
|
|
41
|
+
* Read a document. Returns the content string (text format) or ArrayBuffer
|
|
42
|
+
* (binary format), or null if the document does not exist.
|
|
43
|
+
*/
|
|
44
|
+
read(tenantId: string, shardId: string, path: string): Promise<string | ArrayBuffer | null>;
|
|
45
|
+
/** Write (create or overwrite) a document with the given content. */
|
|
46
|
+
write(tenantId: string, shardId: string, path: string, content: string | ArrayBuffer): Promise<void>;
|
|
47
|
+
/** Delete a document. No-op if the document does not exist. */
|
|
48
|
+
delete(tenantId: string, shardId: string, path: string): Promise<void>;
|
|
49
|
+
/** List all documents stored for this tenant + shard combination. */
|
|
50
|
+
list(tenantId: string, shardId: string): Promise<DocumentMeta[]>;
|
|
51
|
+
/** Return true if the document at `path` exists. */
|
|
52
|
+
exists(tenantId: string, shardId: string, path: string): Promise<boolean>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Shard-facing document handle returned by `ctx.documents()`. Binds
|
|
56
|
+
* the tenant, shard, and backend so shard code only deals in paths.
|
|
57
|
+
*/
|
|
58
|
+
export interface DocumentHandle {
|
|
59
|
+
/** List documents matching the handle's extensions filter. */
|
|
60
|
+
list(): Promise<DocumentMeta[]>;
|
|
61
|
+
/** Read a document by path. Returns null if not found. */
|
|
62
|
+
read(path: string): Promise<string | null>;
|
|
63
|
+
/** Write (create or overwrite) a document. Explicit save. */
|
|
64
|
+
write(path: string, content: string): Promise<void>;
|
|
65
|
+
/** Delete a document. */
|
|
66
|
+
delete(path: string): Promise<void>;
|
|
67
|
+
/** Check existence without reading content. */
|
|
68
|
+
exists(path: string): Promise<boolean>;
|
|
69
|
+
/**
|
|
70
|
+
* Subscribe to change notifications within this handle's scope.
|
|
71
|
+
* Returns an unsubscribe function.
|
|
72
|
+
*/
|
|
73
|
+
watch(callback: (change: DocumentChange) => void): () => void;
|
|
74
|
+
/**
|
|
75
|
+
* Enable autosave for a document. Returns a controller that accepts
|
|
76
|
+
* content updates and debounces writes to the backend.
|
|
77
|
+
*/
|
|
78
|
+
autosave(path: string, options?: {
|
|
79
|
+
debounceMs?: number;
|
|
80
|
+
}): AutosaveController;
|
|
81
|
+
/**
|
|
82
|
+
* Flush all active autosave controllers and release resources.
|
|
83
|
+
* Called by the framework on shard deactivation.
|
|
84
|
+
*/
|
|
85
|
+
dispose(): Promise<void>;
|
|
86
|
+
}
|
|
87
|
+
/** Controller for autosaved documents. */
|
|
88
|
+
export interface AutosaveController {
|
|
89
|
+
/** Feed new content. A debounced write is scheduled automatically. */
|
|
90
|
+
update(content: string): void;
|
|
91
|
+
/** Force an immediate flush of pending content. */
|
|
92
|
+
flush(): Promise<void>;
|
|
93
|
+
/** Stop autosaving. Flushes pending content first. */
|
|
94
|
+
dispose(): Promise<void>;
|
|
95
|
+
/** Whether there are unflushed changes. */
|
|
96
|
+
readonly dirty: boolean;
|
|
97
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Document zone types — the file-oriented storage subsystem.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the KV state zones (workspace/user) which store opaque blobs
|
|
5
|
+
* per shard, the document zone deals in named files: read, write, list,
|
|
6
|
+
* watch. Every operation is tenant-scoped for multi-user isolation and
|
|
7
|
+
* async because file I/O is inherently async regardless of backend.
|
|
8
|
+
*
|
|
9
|
+
* The document zone is a parallel subsystem — it does not extend
|
|
10
|
+
* ZoneName or ZoneSchema. Shards access it via `ctx.documents()`.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { registerShard, registerApp, bootstrap, __setBackend } from './host';
|
|
2
|
+
export { __setTenantId, __setDocumentBackend } from './host';
|
|
3
|
+
export type { Backend } from './state/types';
|
|
4
|
+
export type { DocumentBackend } from './documents/types';
|
|
5
|
+
export { HttpDocumentBackend } from './documents/http-backend';
|
|
6
|
+
export { installPackage, uninstallPackage, listInstalledPackages, loadInstalledPackages, } from './registry/index';
|
|
7
|
+
export type { InstalledPackage, InstallResult, PackageMeta } from './registry/types';
|
|
8
|
+
export { initAuth, elevate, deescalate } from './auth/index';
|
|
9
|
+
export { adminAppIds } from './host';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Host-process entry for the `sh3-core` package.
|
|
3
|
+
*
|
|
4
|
+
* This file is what `import ... from 'sh3-core/host'` resolves to. Only code that
|
|
5
|
+
* boots an SH3 shell (a main.ts that mounts Shell and registers shards/apps)
|
|
6
|
+
* should touch this path. Shards and apps must not import from here.
|
|
7
|
+
*/
|
|
8
|
+
export { registerShard, registerApp, bootstrap, __setBackend } from './host';
|
|
9
|
+
export { __setTenantId, __setDocumentBackend } from './host';
|
|
10
|
+
export { HttpDocumentBackend } from './documents/http-backend';
|
|
11
|
+
// Install API (host-only).
|
|
12
|
+
export { installPackage, uninstallPackage, listInstalledPackages, loadInstalledPackages, } from './registry/index';
|
|
13
|
+
// Admin mode (host-only — elevate/deescalate drive the shell UI, initAuth runs at boot).
|
|
14
|
+
export { initAuth, elevate, deescalate } from './auth/index';
|
|
15
|
+
export { adminAppIds } from './host';
|
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { registerShard as registerShardInternal } from './shards/activate.svelte';
|
|
2
|
+
import { registerApp } from './apps/registry.svelte';
|
|
3
|
+
import { __setBackend } from './state/zones.svelte';
|
|
4
|
+
export { __setBackend };
|
|
5
|
+
export { __setTenantId, __setDocumentBackend } from './documents/config';
|
|
6
|
+
export declare function registerShard(shard: Parameters<typeof registerShardInternal>[0]): void;
|
|
7
|
+
export { registerApp };
|
|
8
|
+
export declare function bootstrap(): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Set of app IDs that require admin mode. Framework-internal — used by
|
|
11
|
+
* the shell home to gate visibility. Not part of the app contract.
|
|
12
|
+
*/
|
|
13
|
+
export declare const adminAppIds: ReadonlySet<string>;
|
package/dist/host.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Host surface — functions `main.ts` (and only main.ts) calls.
|
|
3
|
+
*
|
|
4
|
+
* Registration APIs (`registerShard`, `registerApp`) are data-only and
|
|
5
|
+
* safe to call at any time; a future runtime loader uses them identically
|
|
6
|
+
* to the boot-time glob loop.
|
|
7
|
+
*
|
|
8
|
+
* `bootstrap()` runs the post-registration boot sequence: it registers
|
|
9
|
+
* the framework-owned __shell__ pseudo-shard, walks the registered-shards
|
|
10
|
+
* map and activates every self-starting shard, then reads the last-app
|
|
11
|
+
* user-zone entry and either launches that app or leaves the shell on
|
|
12
|
+
* home.
|
|
13
|
+
*
|
|
14
|
+
* This file is intentionally NOT re-exported through `api.ts`. The
|
|
15
|
+
* import-hygiene rule is: shards and apps import from `api.ts`, the host
|
|
16
|
+
* imports from `host.ts`.
|
|
17
|
+
*/
|
|
18
|
+
import { registerShard as registerShardInternal, activateShard, registeredShards, } from './shards/activate.svelte';
|
|
19
|
+
import { registerApp, registeredApps } from './apps/registry.svelte';
|
|
20
|
+
import { launchApp, readLastApp } from './apps/lifecycle';
|
|
21
|
+
import { shellShard } from './shell-shard/shellShard.svelte';
|
|
22
|
+
import { storeShard } from './store/storeShard.svelte';
|
|
23
|
+
import { __setBackend } from './state/zones.svelte';
|
|
24
|
+
import { loadInstalledPackages } from './registry/installer';
|
|
25
|
+
import { initAuth } from './auth/index';
|
|
26
|
+
import { storeApp } from './store/storeApp';
|
|
27
|
+
import { registryShard } from './registry-shard/registryShard.svelte';
|
|
28
|
+
import { registryApp } from './registry-shard/registryApp';
|
|
29
|
+
export { __setBackend };
|
|
30
|
+
export { __setTenantId, __setDocumentBackend } from './documents/config';
|
|
31
|
+
export function registerShard(shard) {
|
|
32
|
+
registerShardInternal(shard);
|
|
33
|
+
}
|
|
34
|
+
export { registerApp };
|
|
35
|
+
export async function bootstrap() {
|
|
36
|
+
// 1. Framework-owned shards are registered first — before installed
|
|
37
|
+
// packages — so that a malicious installed package cannot claim a
|
|
38
|
+
// reserved id like __shell__ or sh3-store.
|
|
39
|
+
registerShardInternal(shellShard);
|
|
40
|
+
registerShardInternal(storeShard);
|
|
41
|
+
registerShardInternal(registryShard);
|
|
42
|
+
// 2. Framework-shipped admin apps.
|
|
43
|
+
registerApp(storeApp);
|
|
44
|
+
registerApp(registryApp);
|
|
45
|
+
// 3. Verify stored admin key (if any) against the server. Uses a
|
|
46
|
+
// 3-second timeout and fails open — the shell boots without admin
|
|
47
|
+
// access if the server is slow or offline.
|
|
48
|
+
await initAuth();
|
|
49
|
+
// 4. Load any packages that were hot-installed in a previous session
|
|
50
|
+
// from IndexedDB. Runs after framework shards but before activation
|
|
51
|
+
// so installed packages participate in the self-starting pass.
|
|
52
|
+
await loadInstalledPackages();
|
|
53
|
+
// 5. Activate every self-starting shard (any shard whose `autostart`
|
|
54
|
+
// field is defined). Iteration order is insertion order, which
|
|
55
|
+
// puts __shell__ first and glob-discovered shards after — exactly
|
|
56
|
+
// what we need.
|
|
57
|
+
for (const [id, shard] of registeredShards) {
|
|
58
|
+
if (shard.autostart) {
|
|
59
|
+
await activateShard(id);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// 6. Read the last-active app from the user zone. If it names a still-
|
|
63
|
+
// registered app, launch it; otherwise leave the shell on home.
|
|
64
|
+
const lastId = readLastApp();
|
|
65
|
+
if (lastId && registeredApps.has(lastId)) {
|
|
66
|
+
await launchApp(lastId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Set of app IDs that require admin mode. Framework-internal — used by
|
|
71
|
+
* the shell home to gate visibility. Not part of the app contract.
|
|
72
|
+
*/
|
|
73
|
+
export const adminAppIds = new Set(['sh3-store-app', 'sh3-registry-app']);
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Main public entry for the `sh3-core` package.
|
|
3
|
+
*
|
|
4
|
+
* This file is what `import ... from 'sh3-core'` resolves to. It re-exports the
|
|
5
|
+
* phase 8 public surface (api.ts) plus the Shell host component. Shard and
|
|
6
|
+
* app authors import from here.
|
|
7
|
+
*
|
|
8
|
+
* Host-process functions (registerShard, registerApp, bootstrap) are
|
|
9
|
+
* deliberately NOT exported here — they live at `sh3-core/host` because shards
|
|
10
|
+
* and apps must not register each other. See host-entry.ts.
|
|
11
|
+
*/
|
|
12
|
+
export * from './api';
|
|
13
|
+
export { default as Shell } from './Shell.svelte';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* DragPreview — the ghost tab rendered on overlay layer 2 during a
|
|
4
|
+
* drag-reorganize gesture.
|
|
5
|
+
*
|
|
6
|
+
* The composition root mounts this component once into the drag-
|
|
7
|
+
* preview layer root. It reads `dragState` and renders itself only
|
|
8
|
+
* while `phase === 'dragging'`. Position tracks `dragState.pointerX`
|
|
9
|
+
* / `pointerY` minus the drag source's initial grab offset, so the
|
|
10
|
+
* ghost appears "held" at the same point on the tab where the user
|
|
11
|
+
* started dragging.
|
|
12
|
+
*
|
|
13
|
+
* Pointer events are disabled on the ghost itself — it must not
|
|
14
|
+
* intercept elementsFromPoint checks used by drop zones.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { dragState } from './drag.svelte';
|
|
18
|
+
|
|
19
|
+
const visible = $derived(dragState.phase === 'dragging' && !!dragState.source);
|
|
20
|
+
const left = $derived(
|
|
21
|
+
dragState.source ? dragState.pointerX - dragState.source.offsetX : 0,
|
|
22
|
+
);
|
|
23
|
+
const top = $derived(
|
|
24
|
+
dragState.source ? dragState.pointerY - dragState.source.offsetY : 0,
|
|
25
|
+
);
|
|
26
|
+
const width = $derived(dragState.source?.startRect.width ?? 0);
|
|
27
|
+
const height = $derived(dragState.source?.startRect.height ?? 0);
|
|
28
|
+
const label = $derived(dragState.source?.entry.label ?? '');
|
|
29
|
+
const icon = $derived(dragState.source?.entry.icon);
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
{#if visible}
|
|
33
|
+
<div
|
|
34
|
+
class="drag-preview"
|
|
35
|
+
style="left: {left}px; top: {top}px; width: {width}px; height: {height}px;"
|
|
36
|
+
>
|
|
37
|
+
{#if icon}<span class="drag-preview-icon">{icon}</span>{/if}
|
|
38
|
+
<span class="drag-preview-label">{label}</span>
|
|
39
|
+
</div>
|
|
40
|
+
{/if}
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
.drag-preview {
|
|
44
|
+
position: absolute;
|
|
45
|
+
display: inline-flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
gap: var(--shell-pad-sm);
|
|
48
|
+
padding: var(--shell-pad-sm) var(--shell-pad-md);
|
|
49
|
+
background: var(--shell-bg-elevated);
|
|
50
|
+
color: var(--shell-fg);
|
|
51
|
+
border: 1px solid var(--shell-accent);
|
|
52
|
+
border-radius: 3px;
|
|
53
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
|
|
54
|
+
font-size: 12px;
|
|
55
|
+
font-family: var(--shell-font-ui);
|
|
56
|
+
pointer-events: none;
|
|
57
|
+
opacity: 0.9;
|
|
58
|
+
/* The layer root inherits its z-index from --shell-z-layer-2 via
|
|
59
|
+
Shell.svelte; we don't set z-index here. */
|
|
60
|
+
}
|
|
61
|
+
.drag-preview-icon { font-size: 11px; }
|
|
62
|
+
.drag-preview-label { white-space: nowrap; }
|
|
63
|
+
</style>
|