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,141 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* createStateZones — turns a declarative zone schema into live reactive
|
|
3
|
+
* per-zone objects backed by the right store for each zone.
|
|
4
|
+
*
|
|
5
|
+
* Usage (from a shard, or the shell itself):
|
|
6
|
+
*
|
|
7
|
+
* const state = createStateZones('graphlive', {
|
|
8
|
+
* ephemeral: { hoveredId: null as string | null },
|
|
9
|
+
* session: { lastFocus: null as string | null },
|
|
10
|
+
* workspace: { collapsed: false, panelSizes: [0.3, 0.7] },
|
|
11
|
+
* user: { showIcons: true },
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* state.workspace.collapsed = true; // persists to localStorage
|
|
15
|
+
* state.ephemeral.hoveredId = 'foo'; // in-memory only
|
|
16
|
+
*
|
|
17
|
+
* Guarantees (phase 3 subset of docs/design/state-zones.md):
|
|
18
|
+
* - Hydration before return: persistent zones are populated from the
|
|
19
|
+
* backend before this function returns, so reads in the next line of
|
|
20
|
+
* user code see persisted values.
|
|
21
|
+
* - Debounced flush: writes within the same microtask coalesce to a
|
|
22
|
+
* single backend write per (zone, shardId).
|
|
23
|
+
* - Atomicity per write: each flush serializes a full zone snapshot.
|
|
24
|
+
* - Isolation: zone data is keyed by shardId; shards cannot collide.
|
|
25
|
+
*
|
|
26
|
+
* Lifetime management (tracking teardown on shard deactivation) arrives in
|
|
27
|
+
* phase 4 when the shard contract exists. For phase 3, roots live until the
|
|
28
|
+
* page unloads, which is fine for a single-session shell.
|
|
29
|
+
*
|
|
30
|
+
* This file must be `.svelte.ts` so it can use the Svelte 5 runes
|
|
31
|
+
* `$state`, `$effect.root`, `$effect`, and `$state.snapshot` at module level.
|
|
32
|
+
*/
|
|
33
|
+
import { MemoryBackend, LocalStorageBackend } from './backends';
|
|
34
|
+
import { PERSISTENT_ZONES } from './types';
|
|
35
|
+
const backends = {
|
|
36
|
+
ephemeral: new MemoryBackend(),
|
|
37
|
+
session: new MemoryBackend(),
|
|
38
|
+
workspace: new LocalStorageBackend('sh3:workspace:'),
|
|
39
|
+
user: new LocalStorageBackend('sh3:user:'),
|
|
40
|
+
};
|
|
41
|
+
const pending = new Map();
|
|
42
|
+
let flushScheduled = false;
|
|
43
|
+
function scheduleFlush(zone, shardId, value) {
|
|
44
|
+
pending.set(`${zone}:${shardId}`, { zone, shardId, value });
|
|
45
|
+
if (flushScheduled)
|
|
46
|
+
return;
|
|
47
|
+
flushScheduled = true;
|
|
48
|
+
queueMicrotask(() => {
|
|
49
|
+
flushScheduled = false;
|
|
50
|
+
for (const { zone, shardId, value } of pending.values()) {
|
|
51
|
+
backends[zone].write(shardId, value);
|
|
52
|
+
}
|
|
53
|
+
pending.clear();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create live reactive state zones for a shard (or the shell itself).
|
|
58
|
+
*
|
|
59
|
+
* Each zone declared in `schema` is backed by the appropriate store:
|
|
60
|
+
* `ephemeral` and `session` are in-memory; `workspace` and `user` are
|
|
61
|
+
* persisted to localStorage and hydrated before this call returns.
|
|
62
|
+
*
|
|
63
|
+
* Writes to persistent zones are debounced and coalesced per microtask.
|
|
64
|
+
* Zones not declared in `schema` are not created; the result only
|
|
65
|
+
* contains the keys the caller asked for.
|
|
66
|
+
*
|
|
67
|
+
* @param shardId - Unique shard identifier used as the storage namespace.
|
|
68
|
+
* @param schema - Record of zone names to their default values.
|
|
69
|
+
* @returns A reactive `StateZones<T>` object keyed to the declared zones.
|
|
70
|
+
*/
|
|
71
|
+
export function createStateZones(shardId, schema) {
|
|
72
|
+
const result = {};
|
|
73
|
+
for (const key of Object.keys(schema)) {
|
|
74
|
+
const defaults = schema[key];
|
|
75
|
+
const backend = backends[key];
|
|
76
|
+
// Hydrate: shallow-merge stored values over the schema defaults so new
|
|
77
|
+
// fields added to the schema in a later version get their defaults even
|
|
78
|
+
// for shards whose backend entries predate them.
|
|
79
|
+
const stored = backend.read(shardId);
|
|
80
|
+
const initial = Object.assign(Object.assign({}, defaults), (stored !== null && stored !== void 0 ? stored : {}));
|
|
81
|
+
// The reactive proxy. Deep reactivity via $state lets shards mutate
|
|
82
|
+
// nested fields (e.g. state.workspace.panelSizes[0] = 0.4) and still
|
|
83
|
+
// trigger the flush effect.
|
|
84
|
+
const proxy = $state(initial);
|
|
85
|
+
result[key] = proxy;
|
|
86
|
+
// Set up change tracking for persistent zones. Memory-only zones don't
|
|
87
|
+
// need it: nothing reads from them beyond the in-process proxy.
|
|
88
|
+
if (!PERSISTENT_ZONES.includes(key))
|
|
89
|
+
continue;
|
|
90
|
+
// $effect.root creates a standalone reactivity scope outside any
|
|
91
|
+
// component. The returned cleanup is intentionally not captured in
|
|
92
|
+
// phase 3 — see the "Lifetime management" note at the top of the file.
|
|
93
|
+
$effect.root(() => {
|
|
94
|
+
let first = true;
|
|
95
|
+
$effect(() => {
|
|
96
|
+
// $state.snapshot deeply reads every reactive field, which both
|
|
97
|
+
// establishes dependency tracking and produces a plain-object copy
|
|
98
|
+
// safe to hand to the backend.
|
|
99
|
+
const snap = $state.snapshot(proxy);
|
|
100
|
+
if (first) {
|
|
101
|
+
first = false;
|
|
102
|
+
return; // don't write back the just-hydrated value
|
|
103
|
+
}
|
|
104
|
+
scheduleFlush(key, shardId, snap);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
// ---------- test hooks ----------------------------------------------------
|
|
111
|
+
/**
|
|
112
|
+
* Swap a backend implementation. Intended for tests and for phase 4+ when
|
|
113
|
+
* Tauri or IndexedDB backends replace the defaults. Not part of the shard-
|
|
114
|
+
* facing API.
|
|
115
|
+
*/
|
|
116
|
+
export function __setBackend(zone, backend) {
|
|
117
|
+
backends[zone] = backend;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Read a raw persisted zone entry without creating a reactive proxy.
|
|
121
|
+
*
|
|
122
|
+
* Intended for framework-internal consumers (e.g. the shell's layout
|
|
123
|
+
* persistence) that need to inspect a stored value before deciding
|
|
124
|
+
* whether to pass it to `createStateZones`. Not part of the shard-facing
|
|
125
|
+
* API — shards always go through `createStateZones` so hydration is
|
|
126
|
+
* reactive and debounced-flush applies.
|
|
127
|
+
*
|
|
128
|
+
* Returns `undefined` for missing, corrupt, or memory-only-zone entries
|
|
129
|
+
* that have no stored value.
|
|
130
|
+
*/
|
|
131
|
+
export function peekZone(zone, shardId) {
|
|
132
|
+
return backends[zone].read(shardId);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Delete a persisted zone entry. Intended for framework-internal use
|
|
136
|
+
* when a stored value is known to be incompatible (version mismatch,
|
|
137
|
+
* corruption) and should be removed before `createStateZones` hydrates.
|
|
138
|
+
*/
|
|
139
|
+
export function clearZone(zone, shardId) {
|
|
140
|
+
backends[zone].delete(shardId);
|
|
141
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* InstalledView — lists installed packages with uninstall capability.
|
|
4
|
+
*
|
|
5
|
+
* Simpler than the browse view: a flat list of installed packages with
|
|
6
|
+
* metadata (type, version, contract version, source registry, install date)
|
|
7
|
+
* and an uninstall button per entry.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { storeContext } from './storeShard.svelte';
|
|
11
|
+
import { uninstallPackage } from '../registry/installer';
|
|
12
|
+
|
|
13
|
+
const ctx = storeContext;
|
|
14
|
+
|
|
15
|
+
let uninstallingIds = $state<Set<string>>(new Set());
|
|
16
|
+
|
|
17
|
+
async function handleUninstall(id: string) {
|
|
18
|
+
if (uninstallingIds.has(id)) return;
|
|
19
|
+
|
|
20
|
+
uninstallingIds = new Set([...uninstallingIds, id]);
|
|
21
|
+
try {
|
|
22
|
+
await uninstallPackage(id);
|
|
23
|
+
await ctx.refreshInstalled();
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.warn('[sh3-store] Uninstall failed:', err instanceof Error ? err.message : err);
|
|
26
|
+
} finally {
|
|
27
|
+
const next = new Set(uninstallingIds);
|
|
28
|
+
next.delete(id);
|
|
29
|
+
uninstallingIds = next;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function handleRefresh() {
|
|
34
|
+
ctx.refreshInstalled();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function formatDate(iso: string): string {
|
|
38
|
+
try {
|
|
39
|
+
return new Date(iso).toLocaleDateString(undefined, {
|
|
40
|
+
year: 'numeric',
|
|
41
|
+
month: 'short',
|
|
42
|
+
day: 'numeric',
|
|
43
|
+
});
|
|
44
|
+
} catch {
|
|
45
|
+
return iso;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<div class="installed-view">
|
|
51
|
+
<header class="installed-header">
|
|
52
|
+
<h2>Installed Packages</h2>
|
|
53
|
+
<button class="installed-refresh" onclick={handleRefresh}>Refresh</button>
|
|
54
|
+
</header>
|
|
55
|
+
|
|
56
|
+
{#if ctx.state.ephemeral.installed.length === 0}
|
|
57
|
+
<div class="installed-empty">No packages installed.</div>
|
|
58
|
+
{:else}
|
|
59
|
+
<ul class="installed-list">
|
|
60
|
+
{#each ctx.state.ephemeral.installed as pkg (pkg.id)}
|
|
61
|
+
{@const uninstalling = uninstallingIds.has(pkg.id)}
|
|
62
|
+
<li class="installed-item">
|
|
63
|
+
<div class="installed-item-main">
|
|
64
|
+
<span class="installed-item-id">{pkg.id}</span>
|
|
65
|
+
<span class="installed-item-badge" class:badge-shard={pkg.type === 'shard'} class:badge-app={pkg.type === 'app'}>
|
|
66
|
+
{pkg.type}
|
|
67
|
+
</span>
|
|
68
|
+
<span class="installed-item-version">{pkg.version}</span>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="installed-item-meta">
|
|
71
|
+
<span>Contract: v{pkg.contractVersion}</span>
|
|
72
|
+
<span>Source: {pkg.sourceRegistry}</span>
|
|
73
|
+
<span>Installed: {formatDate(pkg.installedAt)}</span>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="installed-item-actions">
|
|
76
|
+
<button
|
|
77
|
+
class="installed-uninstall-btn"
|
|
78
|
+
onclick={() => handleUninstall(pkg.id)}
|
|
79
|
+
disabled={uninstalling}
|
|
80
|
+
>
|
|
81
|
+
{uninstalling ? 'Removing...' : 'Uninstall'}
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
84
|
+
</li>
|
|
85
|
+
{/each}
|
|
86
|
+
</ul>
|
|
87
|
+
{/if}
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<style>
|
|
91
|
+
.installed-view {
|
|
92
|
+
font-family: var(--shell-font, system-ui, sans-serif);
|
|
93
|
+
color: var(--shell-fg, #e0e0e0);
|
|
94
|
+
background: var(--shell-bg, #1e1e1e);
|
|
95
|
+
padding: 16px;
|
|
96
|
+
height: 100%;
|
|
97
|
+
overflow-y: auto;
|
|
98
|
+
box-sizing: border-box;
|
|
99
|
+
}
|
|
100
|
+
.installed-header {
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: space-between;
|
|
104
|
+
margin-bottom: 16px;
|
|
105
|
+
}
|
|
106
|
+
.installed-header h2 {
|
|
107
|
+
margin: 0;
|
|
108
|
+
font-size: 1.25rem;
|
|
109
|
+
font-weight: 600;
|
|
110
|
+
}
|
|
111
|
+
.installed-refresh {
|
|
112
|
+
padding: 6px 14px;
|
|
113
|
+
background: var(--shell-accent, #007acc);
|
|
114
|
+
color: #fff;
|
|
115
|
+
border: none;
|
|
116
|
+
border-radius: 4px;
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
font-family: inherit;
|
|
119
|
+
font-size: 0.875rem;
|
|
120
|
+
}
|
|
121
|
+
.installed-empty {
|
|
122
|
+
text-align: center;
|
|
123
|
+
padding: 32px 16px;
|
|
124
|
+
color: var(--shell-fg-muted, #888);
|
|
125
|
+
font-size: 0.875rem;
|
|
126
|
+
}
|
|
127
|
+
.installed-list {
|
|
128
|
+
list-style: none;
|
|
129
|
+
margin: 0;
|
|
130
|
+
padding: 0;
|
|
131
|
+
display: flex;
|
|
132
|
+
flex-direction: column;
|
|
133
|
+
gap: 8px;
|
|
134
|
+
}
|
|
135
|
+
.installed-item {
|
|
136
|
+
background: var(--shell-input-bg, #2a2a2a);
|
|
137
|
+
border: 1px solid var(--shell-border, #444);
|
|
138
|
+
border-radius: 6px;
|
|
139
|
+
padding: 12px 14px;
|
|
140
|
+
display: flex;
|
|
141
|
+
flex-direction: column;
|
|
142
|
+
gap: 6px;
|
|
143
|
+
}
|
|
144
|
+
.installed-item-main {
|
|
145
|
+
display: flex;
|
|
146
|
+
align-items: center;
|
|
147
|
+
gap: 8px;
|
|
148
|
+
}
|
|
149
|
+
.installed-item-id {
|
|
150
|
+
font-weight: 600;
|
|
151
|
+
font-size: 0.9375rem;
|
|
152
|
+
}
|
|
153
|
+
.installed-item-badge {
|
|
154
|
+
font-size: 0.6875rem;
|
|
155
|
+
padding: 1px 6px;
|
|
156
|
+
border-radius: 3px;
|
|
157
|
+
text-transform: uppercase;
|
|
158
|
+
font-weight: 600;
|
|
159
|
+
letter-spacing: 0.04em;
|
|
160
|
+
}
|
|
161
|
+
.badge-shard {
|
|
162
|
+
background: color-mix(in srgb, var(--shell-accent, #007acc) 25%, transparent);
|
|
163
|
+
color: var(--shell-accent, #007acc);
|
|
164
|
+
}
|
|
165
|
+
.badge-app {
|
|
166
|
+
background: color-mix(in srgb, var(--shell-success, #4caf50) 25%, transparent);
|
|
167
|
+
color: var(--shell-success, #4caf50);
|
|
168
|
+
}
|
|
169
|
+
.installed-item-version {
|
|
170
|
+
font-size: 0.75rem;
|
|
171
|
+
color: var(--shell-fg-muted, #888);
|
|
172
|
+
}
|
|
173
|
+
.installed-item-meta {
|
|
174
|
+
display: flex;
|
|
175
|
+
gap: 16px;
|
|
176
|
+
flex-wrap: wrap;
|
|
177
|
+
font-size: 0.75rem;
|
|
178
|
+
color: var(--shell-fg-muted, #888);
|
|
179
|
+
}
|
|
180
|
+
.installed-item-actions {
|
|
181
|
+
display: flex;
|
|
182
|
+
justify-content: flex-end;
|
|
183
|
+
}
|
|
184
|
+
.installed-uninstall-btn {
|
|
185
|
+
padding: 4px 12px;
|
|
186
|
+
background: transparent;
|
|
187
|
+
color: var(--shell-error, #d32f2f);
|
|
188
|
+
border: 1px solid var(--shell-error, #d32f2f);
|
|
189
|
+
border-radius: 4px;
|
|
190
|
+
cursor: pointer;
|
|
191
|
+
font-family: inherit;
|
|
192
|
+
font-size: 0.8125rem;
|
|
193
|
+
}
|
|
194
|
+
.installed-uninstall-btn:hover:not(:disabled) {
|
|
195
|
+
background: color-mix(in srgb, var(--shell-error, #d32f2f) 15%, transparent);
|
|
196
|
+
}
|
|
197
|
+
.installed-uninstall-btn:disabled {
|
|
198
|
+
opacity: 0.6;
|
|
199
|
+
cursor: not-allowed;
|
|
200
|
+
}
|
|
201
|
+
</style>
|