@tangle-network/ui 11.9.0 → 11.11.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/CHANGELOG.md +30 -0
- package/README.md +14 -0
- package/dist/active-sessions-store-Gt1R0rFG.js +262 -0
- package/dist/{active-sessions-store-CeOmXgv5.d.ts → active-sessions-store-nl70ZnMC.d.ts} +40 -41
- package/dist/artifact-pane-BL_DFWNl.d.ts +32 -0
- package/dist/artifact-pane-DMXT9a8U.js +70 -0
- package/dist/auth-DixfCMCk.js +154 -0
- package/dist/auth.d.ts +2 -74
- package/dist/auth.js +2 -15
- package/dist/branding-26d5ARIr.d.ts +14 -0
- package/dist/button-uhNnwdsD.js +78 -0
- package/dist/button-yoIv77e2.d.ts +16 -0
- package/dist/chat-CPLL0Fd9.js +667 -0
- package/dist/chat.d.ts +2 -201
- package/dist/chat.js +2 -30
- package/dist/code-block-B9miUPht.d.ts +23 -0
- package/dist/code-block-CngSn37j.js +165 -0
- package/dist/diff-preview-cySuqowW.js +533 -0
- package/dist/document-editor-pane-BcoABlhn.d.ts +131 -0
- package/dist/document-editor-pane-Cz6oEF1o.js +1287 -0
- package/dist/dropdown-menu-BpcLa5vF.js +101 -0
- package/dist/editor.d.ts +76 -78
- package/dist/editor.js +2 -33
- package/dist/file-format-BgXbr-Ib.js +160 -0
- package/dist/files-CYlOo0mE.js +743 -0
- package/dist/files.d.ts +2 -243
- package/dist/files.js +3 -40
- package/dist/focus-Cw02pNSr.js +56 -0
- package/dist/format-DfcLWqMa.js +54 -0
- package/dist/hooks.d.ts +30 -37
- package/dist/hooks.js +111 -45
- package/dist/index-2UntyjBb.d.ts +52 -0
- package/dist/index-COgqsFAQ.d.ts +42 -0
- package/dist/index-CPDET_s1.d.ts +549 -0
- package/dist/index-CUhfHT_V.d.ts +204 -0
- package/dist/index-Cg6gYAqh.d.ts +73 -0
- package/dist/index-Cq-EJzqW.d.ts +16 -0
- package/dist/index-DhYmSaLF.d.ts +245 -0
- package/dist/index-DjKxO357.d.ts +128 -0
- package/dist/index-b56ew9aF.d.ts +107 -0
- package/dist/index.d.ts +26 -43
- package/dist/index.js +35 -460
- package/dist/markdown-BprL96Gl.js +45 -0
- package/dist/markdown.d.ts +3 -18
- package/dist/markdown.js +3 -14
- package/dist/message-vhUI0c5G.d.ts +16 -0
- package/dist/nav.d.ts +6 -8
- package/dist/nav.js +29 -11
- package/dist/openui-artifact-renderer-D3E5bIAI.d.ts +114 -0
- package/dist/openui-artifact-renderer-DT4xbvWt.js +266 -0
- package/dist/openui.d.ts +2 -115
- package/dist/openui.js +2 -12
- package/dist/parts-5mhy9yJ3.d.ts +37 -0
- package/dist/primitives-DE-blUz-.js +1239 -0
- package/dist/primitives.d.ts +5 -523
- package/dist/primitives.js +9 -211
- package/dist/redaction-CGGB2ENG.js +74 -0
- package/dist/redaction.d.ts +2 -42
- package/dist/redaction.js +2 -7
- package/dist/run-OyWmIDqu.d.ts +41 -0
- package/dist/run-ZsIKPA6J.js +17 -0
- package/dist/run-group-D1InSdQu.js +410 -0
- package/dist/run.d.ts +3 -126
- package/dist/run.js +5 -39
- package/dist/sdk-hooks.d.ts +2 -285
- package/dist/sdk-hooks.js +3 -35
- package/dist/stores.d.ts +13 -14
- package/dist/stores.js +34 -76
- package/dist/table-DHyqDPbU.js +146 -0
- package/dist/tabs-ujuqAdtb.js +40 -0
- package/dist/text-shimmer-BUUo6I-g.js +64 -0
- package/dist/tool-call-feed-BeB1_4pX.d.ts +47 -0
- package/dist/tool-call-feed-C1W1N54c.js +109 -0
- package/dist/tool-call-step-Bi3_YJRV.js +434 -0
- package/dist/tool-display-BAuAVa43.js +158 -0
- package/dist/{tool-display-z4JcDmMQ.d.ts → tool-display-DgmL5rIU.d.ts} +16 -16
- package/dist/tool-previews.d.ts +2 -48
- package/dist/tool-previews.js +2 -22
- package/dist/types.d.ts +6 -19
- package/dist/types.js +0 -1
- package/dist/use-auto-scroll-C5Pjeqon.js +153 -0
- package/dist/use-sse-stream-BjVBDbxK.js +884 -0
- package/dist/use-sse-stream-Dm6R5YWz.d.ts +282 -0
- package/dist/utils-Bl_CZ0Qp.js +37 -0
- package/dist/{chunk-RQHJBTEU.js → utils-C_uf36nf.js} +4 -6
- package/dist/utils.d.ts +2 -51
- package/dist/utils.js +6 -32
- package/package.json +39 -41
- package/src/auth/auth.tsx +5 -1
- package/src/chat/agent-timeline.tsx +6 -1
- package/src/chat/chat-container.tsx +2 -1
- package/src/editor/document-editor-pane.test.tsx +3 -1
- package/src/editor/document-editor-pane.tsx +5 -1
- package/src/editor/markdown-document-editor.test.tsx +9 -4
- package/src/files/file-artifact-pane.tsx +3 -2
- package/src/files/file-preview.tsx +3 -1
- package/src/files/file-tabs.tsx +9 -2
- package/src/lib/focus.ts +63 -0
- package/src/markdown/code-block.tsx +5 -1
- package/src/primitives/badge.tsx +2 -1
- package/src/primitives/button.tsx +2 -1
- package/src/primitives/card.tsx +1 -2
- package/src/primitives/dialog.tsx +7 -1
- package/src/primitives/input.tsx +15 -14
- package/src/primitives/segmented-control.tsx +5 -3
- package/src/primitives/select.tsx +3 -1
- package/src/primitives/switch.tsx +3 -1
- package/src/primitives/tabs.tsx +6 -4
- package/src/primitives/terminal-display.tsx +6 -8
- package/src/primitives/toast.tsx +5 -1
- package/src/tool-previews/command-preview.tsx +5 -1
- package/src/tool-previews/web-search-preview.tsx +2 -1
- package/src/utils/index.ts +7 -0
- package/dist/artifact-pane-DpuWcAiF.d.ts +0 -33
- package/dist/button-CMQuQEW_.d.ts +0 -17
- package/dist/chunk-34A66VBG.js +0 -214
- package/dist/chunk-3OI2QKFD.js +0 -0
- package/dist/chunk-3TI5DEHV.js +0 -299
- package/dist/chunk-4CLN43XT.js +0 -45
- package/dist/chunk-565EATZZ.js +0 -156
- package/dist/chunk-5Z5ZYMOJ.js +0 -0
- package/dist/chunk-6BGQA4BQ.js +0 -0
- package/dist/chunk-7YOCS6IF.js +0 -728
- package/dist/chunk-EOAX2ENE.js +0 -60
- package/dist/chunk-GYPQXTJU.js +0 -230
- package/dist/chunk-HFL6R6IF.js +0 -37
- package/dist/chunk-HGML45QH.js +0 -104
- package/dist/chunk-KNYSKNZX.js +0 -550
- package/dist/chunk-L57WRIND.js +0 -59
- package/dist/chunk-LDLOJKPC.js +0 -1585
- package/dist/chunk-LTEKI7YJ.js +0 -59
- package/dist/chunk-MKTSMWVD.js +0 -109
- package/dist/chunk-ND3ZFXQK.js +0 -179
- package/dist/chunk-NKDZ7GZE.js +0 -192
- package/dist/chunk-NWECTSOH.js +0 -589
- package/dist/chunk-OEX7NZE3.js +0 -321
- package/dist/chunk-Q56BYXQF.js +0 -61
- package/dist/chunk-REJESC5U.js +0 -117
- package/dist/chunk-RQGKSCEZ.js +0 -0
- package/dist/chunk-TN4FUEWY.js +0 -437
- package/dist/chunk-VPWY4I5O.js +0 -1048
- package/dist/chunk-WUQDUBJG.js +0 -168
- package/dist/chunk-XGKULLYE.js +0 -40
- package/dist/chunk-XU3HB5QL.js +0 -127
- package/dist/chunk-XYAIX4SU.js +0 -133
- package/dist/chunk-YJ6RE5WV.js +0 -875
- package/dist/chunk-YTO5V6QS.js +0 -19
- package/dist/chunk-ZKCEAIIZ.js +0 -1395
- package/dist/code-block-0kSpWMnf.d.ts +0 -25
- package/dist/document-editor-pane-EX25CEYW.js +0 -12
- package/dist/document-editor-pane-IAEQn_Dl.d.ts +0 -128
- package/dist/message-BHWbxBtT.d.ts +0 -15
- package/dist/parts-dj7AcUg0.d.ts +0 -36
- package/dist/run-PfLmDAox.d.ts +0 -41
- package/dist/tool-call-feed-D9iofJgW.d.ts +0 -46
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @tangle-network/ui
|
|
2
2
|
|
|
3
|
+
## 11.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- dc84d1c: Update every dependency to its latest release and build with tsdown on TypeScript 7.
|
|
8
|
+
`@tangle-network/ui` now accepts `@nanostores/react` 2 as a peer, moves to lucide-react 1 and marked 18, and drops the unused `react-pdf` and `@radix-ui/react-toast` dependencies.
|
|
9
|
+
Export paths are unchanged; internal chunk file names differ.
|
|
10
|
+
|
|
11
|
+
## 11.10.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- 4649d22: Replace the hard 2px indigo focus ring with one soft focus treatment for every control.
|
|
16
|
+
|
|
17
|
+
brand adds `--focus-border`, `--focus-halo`, `--focus-border-danger`, `--focus-halo-danger` and `--border-strong`. Each derives from the ring, danger-ink, border and foreground tokens, so every theme, named theme and light island resolves its own values. The focus border clears 3:1 against the field in every theme, which `focus.test.ts` enforces.
|
|
18
|
+
|
|
19
|
+
brand's `globals.css` also gives any control that styles no focus of its own (a hand-rolled button, link, radio card or tab) the same keyboard focus line and halo in the base layer, in place of the browser's blue outline. Component utilities still override it.
|
|
20
|
+
|
|
21
|
+
ui exports `focusField`, `focusFieldWithin`, `focusFieldInvalid`, `focusRing` and `focusRingInset` from `utils`, and the primitives use them:
|
|
22
|
+
|
|
23
|
+
- `Input`, `Textarea`, `SelectTrigger` and `TerminalInput` rest on the neutral `border-border` hairline instead of the muted-text `border-input`, darken slightly on hover, and on focus shift the border to `--focus-border` with a 3px `--focus-halo` ring. The `default` and `sandbox` variants now render the same field.
|
|
24
|
+
- `Button`, `Tabs`, `Switch`, `Badge`, `SegmentedControl`, the dialog and toast close buttons, the auth menu trigger, and the chat and file controls show a 1px focus line with the same halo on `:focus-visible` only. A mouse click no longer draws a ring.
|
|
25
|
+
- Interactive `Card`, tool-preview and code-block hovers darken the border instead of tinting it indigo.
|
|
26
|
+
|
|
27
|
+
A consumer that removed the old ring with `focus-visible:ring-0` on a field should pass `focus:ring-0` instead, because the field ring now keys off `:focus`.
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- 53add99: `focusFieldWithin` lights the container only while a field inside it has focus: a text input, textarea, select or contenteditable editor. It used `:focus-within`, so clicking a composer's send button drew the field ring around the whole composer, and tabbing to that button showed two indicators. A focused button, checkbox or button-type input inside the container now shows only its own ring. Because the rule keys off `:has()`, a consumer's `focus-within:*` class does not override it; compose your own classes for a different container focus.
|
|
32
|
+
|
|
3
33
|
## 11.9.0
|
|
4
34
|
|
|
5
35
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -69,6 +69,20 @@ One consequence worth knowing. This is a scan, not a manifest, so a consumer emi
|
|
|
69
69
|
|
|
70
70
|
See [`@tangle-network/brand`'s README](../brand/README.md) for the token-level breakdown, the finer-grained imports, and the fonts, which brand deliberately does not bundle.
|
|
71
71
|
|
|
72
|
+
### Focus treatment
|
|
73
|
+
|
|
74
|
+
Every control shares one focus treatment, exported from `utils` as class strings.
|
|
75
|
+
Compose them with `cn` so tailwind-merge resolves any override.
|
|
76
|
+
|
|
77
|
+
- `focusField` — a text field: neutral hairline, a slightly darker border on hover, and on `:focus` a `--focus-border` border with a 3px `--focus-halo` ring.
|
|
78
|
+
- `focusFieldWithin` — the same, for a container that wraps a borderless field, such as a composer around a textarea. It lights while a text input, textarea, native `<select>` or contenteditable editor inside it has focus, not while a button inside it does. It keys off `:has()`, so a `focus-within:*` class does not override it.
|
|
79
|
+
- `focusFieldInvalid` — the error state of a field. Compose it after `focusField`.
|
|
80
|
+
- `focusRing` — keyboard focus on a button, tab, switch or link: a 1px `--focus-border` outline with the halo beyond it, on `:focus-visible` only.
|
|
81
|
+
- `focusRingInset` — `focusRing` drawn inside the box, for a control in a scrolling strip.
|
|
82
|
+
|
|
83
|
+
The colors come from the `--focus-*` tokens in `@tangle-network/brand`, so they need brand 1.6 or later.
|
|
84
|
+
To remove the ring from a field nested in a focusable container, pass `focus:ring-0 border-0` and put `focusFieldWithin` on the container.
|
|
85
|
+
|
|
72
86
|
## Subpaths
|
|
73
87
|
|
|
74
88
|
Sixteen named exports: `primitives`, `chat`, `run`, `openui`, `files`, `editor`, `markdown`, `auth`, `hooks`, `sdk-hooks`, `stores`, `types`, `utils`, `tool-previews`, `nav`, `redaction`.
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useStore } from "@nanostores/react";
|
|
3
|
+
import { atom } from "nanostores";
|
|
4
|
+
//#region src/stores/active-sessions-store.ts
|
|
5
|
+
const INITIAL_STATE = {
|
|
6
|
+
sessions: {},
|
|
7
|
+
lastUpdatedAt: 0
|
|
8
|
+
};
|
|
9
|
+
const STATUS_PRIORITY = {
|
|
10
|
+
running: 0,
|
|
11
|
+
error: 1,
|
|
12
|
+
"attention-needed": 2,
|
|
13
|
+
idle: 3
|
|
14
|
+
};
|
|
15
|
+
const DEFAULT_RECORD = {
|
|
16
|
+
status: "idle",
|
|
17
|
+
isRunning: false,
|
|
18
|
+
isForeground: false,
|
|
19
|
+
needsAttention: false,
|
|
20
|
+
connectionState: "disconnected",
|
|
21
|
+
reconnectState: "idle",
|
|
22
|
+
transportMode: null,
|
|
23
|
+
lastError: null,
|
|
24
|
+
lastEventAt: null
|
|
25
|
+
};
|
|
26
|
+
const activeSessionsAtom = atom(INITIAL_STATE);
|
|
27
|
+
let foregroundSessionId = null;
|
|
28
|
+
function createRecord(options, now) {
|
|
29
|
+
return {
|
|
30
|
+
sessionId: options.sessionId,
|
|
31
|
+
projectId: options.projectId ?? null,
|
|
32
|
+
projectLabel: options.projectLabel,
|
|
33
|
+
title: options.title,
|
|
34
|
+
href: options.href,
|
|
35
|
+
registeredAt: now,
|
|
36
|
+
lastActivityAt: now,
|
|
37
|
+
metadata: options.metadata,
|
|
38
|
+
...DEFAULT_RECORD,
|
|
39
|
+
isForeground: options.sessionId === foregroundSessionId
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function resolveStatus(session) {
|
|
43
|
+
if (session.isRunning) return "running";
|
|
44
|
+
if (session.lastError || session.reconnectState === "failed") return "error";
|
|
45
|
+
if (session.needsAttention) return "attention-needed";
|
|
46
|
+
return "idle";
|
|
47
|
+
}
|
|
48
|
+
function normalizeSession(session) {
|
|
49
|
+
return {
|
|
50
|
+
...session,
|
|
51
|
+
status: resolveStatus(session)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function updateSession(sessionId, updater) {
|
|
55
|
+
const current = activeSessionsAtom.get();
|
|
56
|
+
const existing = current.sessions[sessionId];
|
|
57
|
+
if (!existing) return;
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
activeSessionsAtom.set({
|
|
60
|
+
sessions: {
|
|
61
|
+
...current.sessions,
|
|
62
|
+
[sessionId]: normalizeSession(updater(existing, now))
|
|
63
|
+
},
|
|
64
|
+
lastUpdatedAt: now
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function registerActiveSession(options) {
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
const current = activeSessionsAtom.get();
|
|
70
|
+
const existing = current.sessions[options.sessionId];
|
|
71
|
+
activeSessionsAtom.set({
|
|
72
|
+
sessions: {
|
|
73
|
+
...current.sessions,
|
|
74
|
+
[options.sessionId]: normalizeSession(existing ? {
|
|
75
|
+
...existing,
|
|
76
|
+
projectId: existing.projectId ?? options.projectId ?? null,
|
|
77
|
+
projectLabel: options.projectLabel ?? existing.projectLabel,
|
|
78
|
+
title: options.title ?? existing.title,
|
|
79
|
+
href: options.href ?? existing.href,
|
|
80
|
+
metadata: options.metadata ?? existing.metadata,
|
|
81
|
+
lastActivityAt: now,
|
|
82
|
+
isForeground: options.sessionId === foregroundSessionId || existing.isForeground
|
|
83
|
+
} : createRecord(options, now))
|
|
84
|
+
},
|
|
85
|
+
lastUpdatedAt: now
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function unregisterActiveSession(sessionId) {
|
|
89
|
+
const current = activeSessionsAtom.get();
|
|
90
|
+
if (!current.sessions[sessionId]) return;
|
|
91
|
+
const { [sessionId]: _removed, ...remaining } = current.sessions;
|
|
92
|
+
if (foregroundSessionId === sessionId) foregroundSessionId = null;
|
|
93
|
+
activeSessionsAtom.set({
|
|
94
|
+
sessions: remaining,
|
|
95
|
+
lastUpdatedAt: Date.now()
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function setForegroundActiveSession(sessionId) {
|
|
99
|
+
foregroundSessionId = sessionId;
|
|
100
|
+
const current = activeSessionsAtom.get();
|
|
101
|
+
const now = Date.now();
|
|
102
|
+
let changed = false;
|
|
103
|
+
const sessions = Object.fromEntries(Object.entries(current.sessions).map(([id, session]) => {
|
|
104
|
+
const isForeground = id === sessionId;
|
|
105
|
+
if (session.isForeground !== isForeground) changed = true;
|
|
106
|
+
return [id, {
|
|
107
|
+
...session,
|
|
108
|
+
isForeground,
|
|
109
|
+
lastActivityAt: isForeground ? now : session.lastActivityAt
|
|
110
|
+
}];
|
|
111
|
+
}));
|
|
112
|
+
if (!changed) return;
|
|
113
|
+
activeSessionsAtom.set({
|
|
114
|
+
sessions,
|
|
115
|
+
lastUpdatedAt: now
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function updateActiveSessionMeta(sessionId, meta) {
|
|
119
|
+
updateSession(sessionId, (session, now) => ({
|
|
120
|
+
...session,
|
|
121
|
+
...meta,
|
|
122
|
+
lastActivityAt: now
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
function setActiveSessionConnection(sessionId, options) {
|
|
126
|
+
updateSession(sessionId, (session, now) => ({
|
|
127
|
+
...session,
|
|
128
|
+
connectionState: options.connectionState,
|
|
129
|
+
reconnectState: options.reconnectState ?? session.reconnectState,
|
|
130
|
+
transportMode: options.transportMode ?? session.transportMode,
|
|
131
|
+
lastError: options.lastError === void 0 ? session.lastError : options.lastError,
|
|
132
|
+
lastEventAt: options.lastEventAt ?? session.lastEventAt,
|
|
133
|
+
lastActivityAt: now
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
function setActiveSessionRunning(sessionId, isRunning, options) {
|
|
137
|
+
updateSession(sessionId, (session, now) => ({
|
|
138
|
+
...session,
|
|
139
|
+
isRunning,
|
|
140
|
+
needsAttention: isRunning ? false : session.needsAttention,
|
|
141
|
+
lastError: isRunning ? null : session.lastError,
|
|
142
|
+
lastEventAt: options?.lastEventAt ?? session.lastEventAt,
|
|
143
|
+
lastActivityAt: now
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
function setActiveSessionAttention(sessionId, needsAttention, options) {
|
|
147
|
+
updateSession(sessionId, (session, now) => ({
|
|
148
|
+
...session,
|
|
149
|
+
needsAttention,
|
|
150
|
+
isRunning: needsAttention ? false : session.isRunning,
|
|
151
|
+
lastEventAt: options?.lastEventAt ?? session.lastEventAt,
|
|
152
|
+
lastActivityAt: now
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
function setActiveSessionError(sessionId, error) {
|
|
156
|
+
updateSession(sessionId, (session, now) => ({
|
|
157
|
+
...session,
|
|
158
|
+
isRunning: false,
|
|
159
|
+
needsAttention: false,
|
|
160
|
+
lastError: error,
|
|
161
|
+
reconnectState: error ? "failed" : "idle",
|
|
162
|
+
connectionState: error ? "error" : session.connectionState,
|
|
163
|
+
lastEventAt: now,
|
|
164
|
+
lastActivityAt: now
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
function bumpActiveSessionActivity(sessionId, options) {
|
|
168
|
+
updateSession(sessionId, (session, now) => ({
|
|
169
|
+
...session,
|
|
170
|
+
lastEventAt: options?.lastEventAt ?? session.lastEventAt ?? now,
|
|
171
|
+
lastActivityAt: now
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
function resetActiveSessions() {
|
|
175
|
+
foregroundSessionId = null;
|
|
176
|
+
activeSessionsAtom.set(INITIAL_STATE);
|
|
177
|
+
}
|
|
178
|
+
function getAllActiveSessions(state) {
|
|
179
|
+
return Object.values(state.sessions);
|
|
180
|
+
}
|
|
181
|
+
function getActiveSession(state, sessionId) {
|
|
182
|
+
return state.sessions[sessionId] ?? null;
|
|
183
|
+
}
|
|
184
|
+
function getSessionsForProject(state, projectId) {
|
|
185
|
+
return Object.values(state.sessions).filter((session) => session.projectId === projectId);
|
|
186
|
+
}
|
|
187
|
+
function getSessionsForNavbar(state, projectId) {
|
|
188
|
+
return [...projectId == null ? Object.values(state.sessions) : getSessionsForProject(state, projectId)].sort((left, right) => {
|
|
189
|
+
if (left.isForeground !== right.isForeground) return Number(right.isForeground) - Number(left.isForeground);
|
|
190
|
+
const statusDiff = STATUS_PRIORITY[left.status] - STATUS_PRIORITY[right.status];
|
|
191
|
+
if (statusDiff !== 0) return statusDiff;
|
|
192
|
+
return right.lastActivityAt - left.lastActivityAt;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
function getSessionsByActivity(state) {
|
|
196
|
+
return [...Object.values(state.sessions)].sort((left, right) => right.lastActivityAt - left.lastActivityAt);
|
|
197
|
+
}
|
|
198
|
+
function getTotalRunningSessionCount(state) {
|
|
199
|
+
return Object.values(state.sessions).filter((session) => session.isRunning).length;
|
|
200
|
+
}
|
|
201
|
+
function hasBackgroundRunningSessions(state) {
|
|
202
|
+
return Object.values(state.sessions).some((session) => session.isRunning && !session.isForeground);
|
|
203
|
+
}
|
|
204
|
+
function getAllProjectActivity(state) {
|
|
205
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
206
|
+
for (const session of Object.values(state.sessions)) {
|
|
207
|
+
if (session.projectId == null) continue;
|
|
208
|
+
const existing = grouped.get(session.projectId);
|
|
209
|
+
if (existing) {
|
|
210
|
+
existing.activeSessionCount += 1;
|
|
211
|
+
existing.lastActivityAt = Math.max(existing.lastActivityAt, session.lastActivityAt);
|
|
212
|
+
if (session.isRunning) existing.runningSessionIds.push(session.sessionId);
|
|
213
|
+
if (!existing.projectLabel && session.projectLabel) existing.projectLabel = session.projectLabel;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
grouped.set(session.projectId, {
|
|
217
|
+
projectId: session.projectId,
|
|
218
|
+
projectLabel: session.projectLabel,
|
|
219
|
+
activeSessionCount: 1,
|
|
220
|
+
runningSessionIds: session.isRunning ? [session.sessionId] : [],
|
|
221
|
+
lastActivityAt: session.lastActivityAt
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return [...grouped.values()].sort((left, right) => right.lastActivityAt - left.lastActivityAt);
|
|
225
|
+
}
|
|
226
|
+
function useActiveSessionsState() {
|
|
227
|
+
return useStore(activeSessionsAtom);
|
|
228
|
+
}
|
|
229
|
+
function useActiveSessions() {
|
|
230
|
+
const state = useStore(activeSessionsAtom);
|
|
231
|
+
return useMemo(() => getAllActiveSessions(state), [state]);
|
|
232
|
+
}
|
|
233
|
+
function useActiveSession(sessionId) {
|
|
234
|
+
const state = useStore(activeSessionsAtom);
|
|
235
|
+
return useMemo(() => sessionId ? getActiveSession(state, sessionId) : null, [sessionId, state]);
|
|
236
|
+
}
|
|
237
|
+
function useProjectSessions(projectId) {
|
|
238
|
+
const state = useStore(activeSessionsAtom);
|
|
239
|
+
return useMemo(() => projectId == null ? [] : getSessionsForProject(state, projectId), [projectId, state]);
|
|
240
|
+
}
|
|
241
|
+
function useNavbarSessions(projectId) {
|
|
242
|
+
const state = useStore(activeSessionsAtom);
|
|
243
|
+
return useMemo(() => getSessionsForNavbar(state, projectId), [projectId, state]);
|
|
244
|
+
}
|
|
245
|
+
function useSessionsByActivity() {
|
|
246
|
+
const state = useStore(activeSessionsAtom);
|
|
247
|
+
return useMemo(() => getSessionsByActivity(state), [state]);
|
|
248
|
+
}
|
|
249
|
+
function useProjectActivity() {
|
|
250
|
+
const state = useStore(activeSessionsAtom);
|
|
251
|
+
return useMemo(() => getAllProjectActivity(state), [state]);
|
|
252
|
+
}
|
|
253
|
+
function useTotalRunningSessions() {
|
|
254
|
+
const state = useStore(activeSessionsAtom);
|
|
255
|
+
return useMemo(() => getTotalRunningSessionCount(state), [state]);
|
|
256
|
+
}
|
|
257
|
+
function useHasBackgroundRunningSessions() {
|
|
258
|
+
const state = useStore(activeSessionsAtom);
|
|
259
|
+
return useMemo(() => hasBackgroundRunningSessions(state), [state]);
|
|
260
|
+
}
|
|
261
|
+
//#endregion
|
|
262
|
+
export { useHasBackgroundRunningSessions as C, useSessionsByActivity as D, useProjectSessions as E, useTotalRunningSessions as O, useActiveSessionsState as S, useProjectActivity as T, setForegroundActiveSession as _, getAllProjectActivity as a, useActiveSession as b, getSessionsForProject as c, registerActiveSession as d, resetActiveSessions as f, setActiveSessionRunning as g, setActiveSessionError as h, getAllActiveSessions as i, getTotalRunningSessionCount as l, setActiveSessionConnection as m, bumpActiveSessionActivity as n, getSessionsByActivity as o, setActiveSessionAttention as p, getActiveSession as r, getSessionsForNavbar as s, activeSessionsAtom as t, hasBackgroundRunningSessions as u, unregisterActiveSession as v, useNavbarSessions as w, useActiveSessions as x, updateActiveSessionMeta as y };
|
|
@@ -1,59 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/stores/active-sessions-store.d.ts
|
|
3
2
|
type SessionProjectKey = string | number;
|
|
4
3
|
type ActiveSessionStatus = "idle" | "running" | "attention-needed" | "error";
|
|
5
4
|
type ActiveSessionReconnectState = "idle" | "reconnecting" | "failed";
|
|
6
5
|
type ActiveSessionConnectionState = "disconnected" | "connecting" | "connected" | "reconnecting" | "error";
|
|
7
6
|
type ActiveSessionTransportMode = "websocket" | "sse" | "polling" | "custom";
|
|
8
7
|
interface ActiveSessionRecord {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
8
|
+
sessionId: string;
|
|
9
|
+
projectId: SessionProjectKey | null;
|
|
10
|
+
projectLabel?: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
href?: string;
|
|
13
|
+
registeredAt: number;
|
|
14
|
+
lastActivityAt: number;
|
|
15
|
+
lastEventAt: number | null;
|
|
16
|
+
status: ActiveSessionStatus;
|
|
17
|
+
isRunning: boolean;
|
|
18
|
+
isForeground: boolean;
|
|
19
|
+
needsAttention: boolean;
|
|
20
|
+
connectionState: ActiveSessionConnectionState;
|
|
21
|
+
reconnectState: ActiveSessionReconnectState;
|
|
22
|
+
transportMode: ActiveSessionTransportMode | null;
|
|
23
|
+
lastError: string | null;
|
|
24
|
+
metadata?: Record<string, unknown>;
|
|
26
25
|
}
|
|
27
26
|
interface ActiveSessionsState {
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
sessions: Record<string, ActiveSessionRecord>;
|
|
28
|
+
lastUpdatedAt: number;
|
|
30
29
|
}
|
|
31
30
|
interface RegisterActiveSessionOptions {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
sessionId: string;
|
|
32
|
+
projectId?: SessionProjectKey | null;
|
|
33
|
+
projectLabel?: string;
|
|
34
|
+
title?: string;
|
|
35
|
+
href?: string;
|
|
36
|
+
metadata?: Record<string, unknown>;
|
|
38
37
|
}
|
|
39
38
|
interface ActiveSessionConnectionOptions {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
connectionState: ActiveSessionConnectionState;
|
|
40
|
+
reconnectState?: ActiveSessionReconnectState;
|
|
41
|
+
transportMode?: ActiveSessionTransportMode | null;
|
|
42
|
+
lastError?: string | null;
|
|
43
|
+
lastEventAt?: number | null;
|
|
45
44
|
}
|
|
46
45
|
interface ActiveSessionActivityOptions {
|
|
47
|
-
|
|
46
|
+
lastEventAt?: number | null;
|
|
48
47
|
}
|
|
49
48
|
interface ActiveProjectActivity {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
projectId: SessionProjectKey;
|
|
50
|
+
projectLabel?: string;
|
|
51
|
+
activeSessionCount: number;
|
|
52
|
+
runningSessionIds: string[];
|
|
53
|
+
lastActivityAt: number;
|
|
55
54
|
}
|
|
56
|
-
declare const activeSessionsAtom: nanostores.PreinitializedWritableAtom<ActiveSessionsState> & object;
|
|
55
|
+
declare const activeSessionsAtom: import("nanostores").PreinitializedWritableAtom<ActiveSessionsState> & object;
|
|
57
56
|
declare function registerActiveSession(options: RegisterActiveSessionOptions): void;
|
|
58
57
|
declare function unregisterActiveSession(sessionId: string): void;
|
|
59
58
|
declare function setForegroundActiveSession(sessionId: string | null): void;
|
|
@@ -81,5 +80,5 @@ declare function useSessionsByActivity(): ActiveSessionRecord[];
|
|
|
81
80
|
declare function useProjectActivity(): ActiveProjectActivity[];
|
|
82
81
|
declare function useTotalRunningSessions(): number;
|
|
83
82
|
declare function useHasBackgroundRunningSessions(): boolean;
|
|
84
|
-
|
|
85
|
-
export {
|
|
83
|
+
//#endregion
|
|
84
|
+
export { updateActiveSessionMeta as A, resetActiveSessions as C, setActiveSessionRunning as D, setActiveSessionError as E, useNavbarSessions as F, useProjectActivity as I, useProjectSessions as L, useActiveSessions as M, useActiveSessionsState as N, setForegroundActiveSession as O, useHasBackgroundRunningSessions as P, useSessionsByActivity as R, registerActiveSession as S, setActiveSessionConnection as T, getSessionsByActivity as _, ActiveSessionReconnectState as a, getTotalRunningSessionCount as b, ActiveSessionTransportMode as c, SessionProjectKey as d, activeSessionsAtom as f, getAllProjectActivity as g, getAllActiveSessions as h, ActiveSessionConnectionState as i, useActiveSession as j, unregisterActiveSession as k, ActiveSessionsState as l, getActiveSession as m, ActiveSessionActivityOptions as n, ActiveSessionRecord as o, bumpActiveSessionActivity as p, ActiveSessionConnectionOptions as r, ActiveSessionStatus as s, ActiveProjectActivity as t, RegisterActiveSessionOptions as u, getSessionsForNavbar as v, setActiveSessionAttention as w, hasBackgroundRunningSessions as x, getSessionsForProject as y, useTotalRunningSessions as z };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/primitives/artifact-pane.d.ts
|
|
3
|
+
interface ArtifactPaneProps {
|
|
4
|
+
eyebrow?: ReactNode;
|
|
5
|
+
title: ReactNode;
|
|
6
|
+
subtitle?: ReactNode;
|
|
7
|
+
meta?: ReactNode;
|
|
8
|
+
headerActions?: ReactNode;
|
|
9
|
+
tabs?: ReactNode;
|
|
10
|
+
toolbar?: ReactNode;
|
|
11
|
+
footer?: ReactNode;
|
|
12
|
+
emptyState?: ReactNode;
|
|
13
|
+
children?: ReactNode;
|
|
14
|
+
className?: string;
|
|
15
|
+
contentClassName?: string;
|
|
16
|
+
/** Extra classes for the header band (e.g. to align its surface with the host chrome). */
|
|
17
|
+
headerClassName?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Suppress the eyebrow/title/subtitle/meta block. Use when the host already
|
|
20
|
+
* shows the artifact's identity elsewhere (e.g. a surrounding path row), so the
|
|
21
|
+
* pane keeps only its tabs, toolbar, and header actions. The whole header row
|
|
22
|
+
* collapses when nothing else would render in it.
|
|
23
|
+
*/
|
|
24
|
+
hideTitleBlock?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* ArtifactPane — shared frame for files, previews, documents, inspectors, and
|
|
28
|
+
* other artifact-like surfaces inside sandbox applications.
|
|
29
|
+
*/
|
|
30
|
+
declare function ArtifactPane({ eyebrow, title, subtitle, meta, headerActions, tabs, toolbar, footer, emptyState, children, className, contentClassName, headerClassName, hideTitleBlock }: ArtifactPaneProps): import("react").JSX.Element;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { ArtifactPaneProps as n, ArtifactPane as t };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { t as cn } from "./utils-C_uf36nf.js";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/primitives/artifact-pane.tsx
|
|
4
|
+
/**
|
|
5
|
+
* ArtifactPane — shared frame for files, previews, documents, inspectors, and
|
|
6
|
+
* other artifact-like surfaces inside sandbox applications.
|
|
7
|
+
*/
|
|
8
|
+
function ArtifactPane({ eyebrow, title, subtitle, meta, headerActions, tabs, toolbar, footer, emptyState, children, className, contentClassName, headerClassName, hideTitleBlock = false }) {
|
|
9
|
+
const hasContent = children !== void 0 && children !== null;
|
|
10
|
+
const showTitleRow = !hideTitleBlock || Boolean(headerActions);
|
|
11
|
+
const hasHeader = showTitleRow || Boolean(tabs) || Boolean(toolbar);
|
|
12
|
+
const toolbarNeedsDivider = showTitleRow || Boolean(tabs);
|
|
13
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
14
|
+
className: cn("flex h-full min-h-0 flex-col overflow-hidden bg-background text-foreground", className),
|
|
15
|
+
children: [
|
|
16
|
+
hasHeader && /* @__PURE__ */ jsxs("header", {
|
|
17
|
+
className: cn("border-b border-border bg-muted/10", headerClassName),
|
|
18
|
+
children: [
|
|
19
|
+
showTitleRow && /* @__PURE__ */ jsxs("div", {
|
|
20
|
+
className: "flex items-start justify-between gap-3 px-3 py-2",
|
|
21
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
22
|
+
className: "min-w-0 flex-1",
|
|
23
|
+
children: !hideTitleBlock && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
24
|
+
eyebrow && /* @__PURE__ */ jsx("div", {
|
|
25
|
+
className: "mb-1 inline-flex max-w-full items-center px-1 py-0.5 text-[10px] font-bold uppercase tracking-wider text-muted-foreground",
|
|
26
|
+
children: eyebrow
|
|
27
|
+
}),
|
|
28
|
+
/* @__PURE__ */ jsx("div", {
|
|
29
|
+
className: "min-w-0 text-[13px] font-medium text-foreground",
|
|
30
|
+
children: title
|
|
31
|
+
}),
|
|
32
|
+
(subtitle || meta) && /* @__PURE__ */ jsxs("div", {
|
|
33
|
+
className: "mt-1 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-xs leading-relaxed text-muted-foreground",
|
|
34
|
+
children: [subtitle && /* @__PURE__ */ jsx("span", {
|
|
35
|
+
className: "truncate",
|
|
36
|
+
children: subtitle
|
|
37
|
+
}), meta && /* @__PURE__ */ jsx("span", {
|
|
38
|
+
className: "flex items-center gap-2",
|
|
39
|
+
children: meta
|
|
40
|
+
})]
|
|
41
|
+
})
|
|
42
|
+
] })
|
|
43
|
+
}), headerActions && /* @__PURE__ */ jsx("div", {
|
|
44
|
+
className: "flex shrink-0 items-center gap-1.5",
|
|
45
|
+
children: headerActions
|
|
46
|
+
})]
|
|
47
|
+
}),
|
|
48
|
+
tabs,
|
|
49
|
+
toolbar && /* @__PURE__ */ jsx("div", {
|
|
50
|
+
className: cn("px-3 py-2", toolbarNeedsDivider && "border-t border-border"),
|
|
51
|
+
children: toolbar
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
}),
|
|
55
|
+
/* @__PURE__ */ jsx("div", {
|
|
56
|
+
className: cn("min-h-0 flex-1 overflow-auto", contentClassName),
|
|
57
|
+
children: hasContent ? children : emptyState ? /* @__PURE__ */ jsx("div", {
|
|
58
|
+
className: "flex h-full items-center justify-center p-6",
|
|
59
|
+
children: emptyState
|
|
60
|
+
}) : null
|
|
61
|
+
}),
|
|
62
|
+
footer && /* @__PURE__ */ jsx("footer", {
|
|
63
|
+
className: "shrink-0 border-t border-border bg-card px-3 py-2",
|
|
64
|
+
children: footer
|
|
65
|
+
})
|
|
66
|
+
]
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { ArtifactPane as t };
|