@columenlabs/lumen-ui 0.1.189 → 0.1.190
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/frame-script.d.ts +4 -1
- package/dist/frame-script.js +11 -2
- package/dist/index.d.ts +1 -1
- package/dist/workspaces.d.ts +15 -0
- package/dist/workspaces.js +47 -5
- package/package.json +2 -2
package/dist/frame-script.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import type { FrameRun } from '@columenlabs/lumen-ui-types/frames';
|
|
2
|
+
export declare function FrameScriptDialog({ api, frame, name, runs, open, onOpenChange, }: {
|
|
2
3
|
api: string;
|
|
3
4
|
frame: string;
|
|
4
5
|
name: string;
|
|
6
|
+
/** The last runs, newest first: when, how many rows, how long, who, and why one failed. */
|
|
7
|
+
runs?: FrameRun[];
|
|
5
8
|
open: boolean;
|
|
6
9
|
onOpenChange: (open: boolean) => void;
|
|
7
10
|
}): import("react").JSX.Element;
|
package/dist/frame-script.js
CHANGED
|
@@ -47,7 +47,7 @@ function parseScript(was, text) {
|
|
|
47
47
|
args: (o.args ?? {}),
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
-
export function FrameScriptDialog({ api, frame, name, open, onOpenChange, }) {
|
|
50
|
+
export function FrameScriptDialog({ api, frame, name, runs, open, onOpenChange, }) {
|
|
51
51
|
const qc = useQueryClient();
|
|
52
52
|
const assistant = useAssistantName();
|
|
53
53
|
const read = useQuery({
|
|
@@ -96,8 +96,17 @@ export function FrameScriptDialog({ api, frame, name, open, onOpenChange, }) {
|
|
|
96
96
|
setError(e.message);
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "flex h-[min(44rem,calc(100dvh-2rem))] flex-col sm:max-w-4xl", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Script \u00B7 ", name] }), _jsxs(DialogDescription, { children: ["What ", assistant, " runs to make these rows. Saved only if it runs."] })] }), read.isPending ? (_jsx(Skeleton, { className: "min-h-0 flex-1" })) : read.error ? (_jsxs(Alert, { variant: "destructive", children: [_jsx(AlertTitle, { children: "Couldn't read the script" }), _jsx(AlertDescription, { children: read.error.message })] })) : !script ? (_jsxs(Alert, { children: [_jsx(AlertTitle, { children: "No script" }), _jsxs(AlertDescription, { children: [assistant, " wrote these rows once: nothing runs to make them."] })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center justify-end gap-1", children: [lang && (_jsx(Badge, { variant: "secondary", className: "font-mono", children: lang })), _jsx(IconButton, { icon: WrapTextIcon, label: wrap ? 'Stop wrapping lines' : 'Wrap long lines', pressed: wrap, onClick: () => setWrap(!wrap) })] }), _jsx(LumenCodeEditor, { className: "flex-1", value: text, lang: lang, wrap: wrap, onChange: setText }), error && (_jsxs(Alert, { variant: "destructive", className: "max-h-32 overflow-y-auto", children: [_jsx(AlertTitle, { children: "Didn't save" }), _jsx(AlertDescription, { className: "break-words font-mono text-xs", children: error })] }))] })), _jsxs(DialogFooter, { children: [_jsx(IconButton, { icon: RotateCcwIcon, label: "Undo my changes", disabled: !changed || save.isPending, onClick: () => {
|
|
99
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "flex h-[min(44rem,calc(100dvh-2rem))] flex-col sm:max-w-4xl", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Script \u00B7 ", name] }), _jsxs(DialogDescription, { children: ["What ", assistant, " runs to make these rows. Saved only if it runs."] })] }), read.isPending ? (_jsx(Skeleton, { className: "min-h-0 flex-1" })) : read.error ? (_jsxs(Alert, { variant: "destructive", children: [_jsx(AlertTitle, { children: "Couldn't read the script" }), _jsx(AlertDescription, { children: read.error.message })] })) : !script ? (_jsxs(Alert, { children: [_jsx(AlertTitle, { children: "No script" }), _jsxs(AlertDescription, { children: [assistant, " wrote these rows once: nothing runs to make them."] })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center justify-end gap-1", children: [lang && (_jsx(Badge, { variant: "secondary", className: "font-mono", children: lang })), _jsx(IconButton, { icon: WrapTextIcon, label: wrap ? 'Stop wrapping lines' : 'Wrap long lines', pressed: wrap, onClick: () => setWrap(!wrap) })] }), _jsx(LumenCodeEditor, { className: "flex-1", value: text, lang: lang, wrap: wrap, onChange: setText }), runs && runs.length > 0 && _jsx(RunList, { runs: runs, assistant: assistant }), error && (_jsxs(Alert, { variant: "destructive", className: "max-h-32 overflow-y-auto", children: [_jsx(AlertTitle, { children: "Didn't save" }), _jsx(AlertDescription, { className: "break-words font-mono text-xs", children: error })] }))] })), _jsxs(DialogFooter, { children: [_jsx(IconButton, { icon: RotateCcwIcon, label: "Undo my changes", disabled: !changed || save.isPending, onClick: () => {
|
|
100
100
|
setText(original);
|
|
101
101
|
setError(null);
|
|
102
102
|
} }), _jsxs(Button, { disabled: !script || save.isPending, onClick: run, children: [save.isPending ? _jsx(Spinner, {}) : _jsx(PlayIcon, {}), " Run and save"] })] })] }) }));
|
|
103
103
|
}
|
|
104
|
+
/** The frame's last runs, one line each; a failure's reason in its hover. */
|
|
105
|
+
function RunList({ runs, assistant }) {
|
|
106
|
+
return (_jsx("ul", { className: "flex max-h-28 flex-col gap-0.5 overflow-y-auto text-xs text-muted-foreground", children: runs.map((r) => (_jsxs("li", { className: "flex min-w-0 items-center gap-2", title: r.error, children: [_jsx("span", { className: `size-1.5 shrink-0 rounded-full ${r.ok ? 'bg-[var(--meter-ok)]' : 'bg-destructive'}` }), _jsx("time", { className: "shrink-0", dateTime: r.at, children: new Date(r.at).toLocaleString('en-US', {
|
|
107
|
+
month: 'short',
|
|
108
|
+
day: 'numeric',
|
|
109
|
+
hour: 'numeric',
|
|
110
|
+
minute: '2-digit',
|
|
111
|
+
}) }), _jsx("span", { className: "num shrink-0", children: r.ok ? `${r.rows.toLocaleString('en-US')} rows` : 'failed' }), _jsxs("span", { className: "num shrink-0", children: [(r.ms / 1000).toFixed(1), " s"] }), _jsx("span", { className: "min-w-0 truncate", children: r.ok ? `by ${r.by === 'you' ? 'you' : assistant}` : r.error })] }, r.at))) }));
|
|
112
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -105,7 +105,7 @@ export { type AppViewState, useAppView } from './app-views.js';
|
|
|
105
105
|
export { type ScreenMap, type ScreenOutcome, type ScreenStatus, type WorkspaceOnScreen, type AssistantMove, type AssistantMoveHandlers, type AssistantPage, type AssistantPageType, AssistantScreenProvider, useAssistantMoves, useAssistantPages, useAssistantScreen, useAssistantWorkspace, } from './assistant-screen.js';
|
|
106
106
|
export { type AssistantActions, AssistantContext, type AssistantNote, type AssistantRef, type AssistantSlot, useAssistant, AssistantPendingContext, useAssistantName, useAssistantNote, useAssistantPending, } from './assistant-context.js';
|
|
107
107
|
export { WORKSPACES_KEY, frameKey, workspacesListQuery } from './workspace-keys.js';
|
|
108
|
-
export { type WorkspaceApp, type WorkspaceDraftProps, WorkspacePage, type WorkspacesConfig, WorkspacesProvider, WorkspacesSection, } from './workspaces.js';
|
|
108
|
+
export { type WorkspaceApp, type WorkspaceDraftProps, WorkspacePage, type WorkspaceRecipe, type WorkspacesConfig, WorkspacesProvider, WorkspacesSection, } from './workspaces.js';
|
|
109
109
|
export { CHART_INDICATORS, type ChartIndicator, type IndicatorValues, LINE_COLOR, computeIndicators, formulaIndicator, lineColor, resolveIndicator, runFormula, } from './market/indicators.js';
|
|
110
110
|
export { IndicatorPicker } from './market/indicator-picker.js';
|
|
111
111
|
export { type OptionChainField, type OptionChainRow, OptionChainTable, } from './market/option-chain.js';
|
package/dist/workspaces.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FrameScript } from '@columenlabs/lumen-ui-types/frames';
|
|
1
2
|
import { type CustomViewConfig } from '@columenlabs/lumen-ui-types/views';
|
|
2
3
|
import { type ComponentType, type ReactNode } from 'react';
|
|
3
4
|
import { type Crumb } from './page.js';
|
|
@@ -24,6 +25,20 @@ export interface WorkspacesConfig {
|
|
|
24
25
|
defaultConfig?: (domain: string) => CustomViewConfig | null;
|
|
25
26
|
/** The empty workspace's example ask, on an app: `put my holdings with their earnings dates in`. */
|
|
26
27
|
example?: (app: string) => string;
|
|
28
|
+
/** Recipes a person picks to start a tab by hand (+ in the tab bar); none hides the +. */
|
|
29
|
+
recipes?: WorkspaceRecipe[];
|
|
30
|
+
}
|
|
31
|
+
/** A saved data pull a person can start a tab from: one call to one of the product's read tools. */
|
|
32
|
+
export interface WorkspaceRecipe {
|
|
33
|
+
id: string;
|
|
34
|
+
/** The tab's name and the picker's row: `Jobs scoring 70+`. */
|
|
35
|
+
label: string;
|
|
36
|
+
/** When to pick it, under the label. */
|
|
37
|
+
description?: string;
|
|
38
|
+
/** The picker's section: the app the rows come from. */
|
|
39
|
+
group?: string;
|
|
40
|
+
icon?: Icon;
|
|
41
|
+
recipe: FrameScript;
|
|
27
42
|
}
|
|
28
43
|
/** The product's apps and domains for its workspaces. */
|
|
29
44
|
export declare const WorkspacesProvider: import("react").Provider<WorkspacesConfig>;
|
package/dist/workspaces.js
CHANGED
|
@@ -4,7 +4,7 @@ import { customViewConfigSchema, frameSpace, } from '@columenlabs/lumen-ui-types
|
|
|
4
4
|
import { workspacePath, } from '@columenlabs/lumen-ui-types/workspaces';
|
|
5
5
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
6
6
|
import { useNavigate, useParams, useSearch } from '@tanstack/react-router';
|
|
7
|
-
import { ArrowRightLeftIcon, CalendarIcon, ExternalLinkIcon, HashIcon, LayersIcon, PencilIcon, SparklesIcon, TableIcon, TextCursorInputIcon, Trash2Icon, TypeIcon, } from 'lucide-react';
|
|
7
|
+
import { ArrowRightLeftIcon, CalendarIcon, ExternalLinkIcon, HashIcon, LayersIcon, PencilIcon, PlusIcon, RefreshCwIcon, SparklesIcon, TableIcon, TextCursorInputIcon, Trash2Icon, TypeIcon, } from 'lucide-react';
|
|
8
8
|
import { createContext, useContext, useEffect, useMemo, useState, } from 'react';
|
|
9
9
|
import { apiJson } from './api-fetch.js';
|
|
10
10
|
import { useAssistant, useAssistantName, useAssistantPending } from './assistant-context.js';
|
|
@@ -206,9 +206,48 @@ function WorkspaceBody({ api, w, tab, draft, }) {
|
|
|
206
206
|
}).then(() => qc.invalidateQueries({ queryKey: WORKSPACES_KEY }));
|
|
207
207
|
}, { id: `ws-save:${tab.id}`, duration: 4000 });
|
|
208
208
|
};
|
|
209
|
-
|
|
209
|
+
// + in the tab bar: a tab from a recipe, run once now; ↻ in the header runs every tab again.
|
|
210
|
+
const addTab = useMutation({
|
|
211
|
+
mutationFn: (r) => json(`${api}/workspaces/${w.id}/tabs`, {
|
|
212
|
+
method: 'POST',
|
|
213
|
+
body: JSON.stringify({ name: r.label, recipe: r.recipe }),
|
|
214
|
+
}),
|
|
215
|
+
onSuccess: ({ workspace, tab: t }) => {
|
|
216
|
+
put(workspace);
|
|
217
|
+
void qc.invalidateQueries({ queryKey: [...WORKSPACES_KEY, api, 'list'] });
|
|
218
|
+
void navigate({ href: workspacePath(w.id, t), replace: true });
|
|
219
|
+
},
|
|
220
|
+
onError: (e) => lumenToast.error(`Couldn’t add the tab: ${e.message}`),
|
|
221
|
+
});
|
|
222
|
+
const runAll = useMutation({
|
|
223
|
+
mutationFn: () => json(`${api}/workspaces/${w.id}/refresh`, {
|
|
224
|
+
method: 'POST',
|
|
225
|
+
}),
|
|
226
|
+
onSuccess: (r) => {
|
|
227
|
+
void qc.invalidateQueries({ queryKey: WORKSPACES_KEY });
|
|
228
|
+
const ran = `Ran ${r.ran} ${r.ran === 1 ? 'tab' : 'tabs'}: ${r.rows.toLocaleString('en-US')} rows`;
|
|
229
|
+
if (r.failed.length)
|
|
230
|
+
lumenToast.warning(`${ran}. Failed: ${r.failed.join(', ')}`);
|
|
231
|
+
else
|
|
232
|
+
lumenToast.success(ran);
|
|
233
|
+
},
|
|
234
|
+
onError: (e) => lumenToast.error(`Couldn’t run the tabs: ${e.message}`),
|
|
235
|
+
});
|
|
236
|
+
const recipes = config.recipes ?? [];
|
|
237
|
+
const runnable = w.tabs.some((t) => 'frame' in t.source);
|
|
238
|
+
const intro = (_jsx(LumenTabs, { tabs: w.tabs.map((t) => ({ value: t.id, label: t.name, icon: tabIcon(t) })), value: tab?.id, onChange: (t) => void navigate({ href: workspacePath(w.id, t), replace: true }), actions: _jsxs(_Fragment, { children: [recipes.length > 0 && (_jsx(LumenSelector, { trigger: "icon", icon: PlusIcon, label: addTab.isPending ? 'Adding a tab…' : 'New tab from a recipe', align: "end", disabled: addTab.isPending, options: recipes.map((r) => ({
|
|
239
|
+
value: r.id,
|
|
240
|
+
label: r.label,
|
|
241
|
+
description: r.description,
|
|
242
|
+
group: r.group,
|
|
243
|
+
icon: r.icon,
|
|
244
|
+
})), value: null, onChange: (id) => {
|
|
245
|
+
const r = recipes.find((x) => x.id === id);
|
|
246
|
+
if (r)
|
|
247
|
+
addTab.mutate(r);
|
|
248
|
+
} })), tab && (_jsx(IconButton, { icon: PencilIcon, label: "Edit tab", onClick: () => setEditingTab(true) }))] }) }));
|
|
210
249
|
const crumbs = [{ label: 'Workspaces' }, { label: w.name }];
|
|
211
|
-
const actions = (_jsxs(_Fragment, { children: [_jsx(IconButton, { icon: TextCursorInputIcon, label: "Rename workspace", onClick: () => setNaming('workspace') }), moveTo.length > 0 && (_jsx(LumenSelector, { trigger: "icon", icon: ArrowRightLeftIcon, label: `Move to ${noun}`, align: "end", search: false, options: moveTo.map((a) => ({ value: a.id, label: a.label, icon: a.icon })), value: null, onChange: (a) => move.mutate(a) })), _jsx(IconButton, { icon: Trash2Icon, label: "Delete workspace", className: "text-muted-foreground hover:text-destructive", onClick: () => setRemoving('workspace') })] }));
|
|
250
|
+
const actions = (_jsxs(_Fragment, { children: [runnable && (_jsx(IconButton, { icon: RefreshCwIcon, label: runAll.isPending ? 'Running every tab…' : 'Run every tab again', disabled: runAll.isPending, className: runAll.isPending ? '[&_svg]:animate-spin' : undefined, onClick: () => runAll.mutate() })), _jsx(IconButton, { icon: TextCursorInputIcon, label: "Rename workspace", onClick: () => setNaming('workspace') }), moveTo.length > 0 && (_jsx(LumenSelector, { trigger: "icon", icon: ArrowRightLeftIcon, label: `Move to ${noun}`, align: "end", search: false, options: moveTo.map((a) => ({ value: a.id, label: a.label, icon: a.icon })), value: null, onChange: (a) => move.mutate(a) })), _jsx(IconButton, { icon: Trash2Icon, label: "Delete workspace", className: "text-muted-foreground hover:text-destructive", onClick: () => setRemoving('workspace') })] }));
|
|
212
251
|
const dialogs = (_jsxs(_Fragment, { children: [_jsx(NameDialog, { open: naming !== null, title: "Rename workspace", value: w.name, onClose: () => setNaming(null), onSave: (name) => rename.mutate(name) }), tab && (_jsx(LumenTabDrawer, { open: editingTab, onClose: () => setEditingTab(false), name: tab.name, icon: tab.config?.icon, fallback: tabIcon({ ...tab, config: null }), description: "Its filters and columns save as you change them.", maxName: 60, busy: editTab.isPending, error: editTab.error?.message, onSave: ({ name, icon }) => editTab.mutate(icon === (tab.config?.icon ?? null)
|
|
213
252
|
? { name }
|
|
214
253
|
: { name, config: { ...tabConfig(tab, w, config.defaultConfig), name, icon } }), remove: {
|
|
@@ -226,7 +265,9 @@ function WorkspaceBody({ api, w, tab, draft, }) {
|
|
|
226
265
|
title: 'No tabs yet',
|
|
227
266
|
icon: LayersIcon,
|
|
228
267
|
kind: 'widget',
|
|
229
|
-
children: (
|
|
268
|
+
children: (_jsx("p", { className: "text-sm text-muted-foreground", children: recipes.length > 0
|
|
269
|
+
? 'Pick a recipe from + above: its rows open here as a tab, and run again whenever you tap ↻.'
|
|
270
|
+
: `Ask ${assistant} for data and to open it here: “${config.example?.(here) ?? 'put this month’s numbers in'} ${w.name}”.` })),
|
|
230
271
|
},
|
|
231
272
|
] }));
|
|
232
273
|
}
|
|
@@ -290,6 +331,7 @@ assistant = 'Assistant') {
|
|
|
290
331
|
value: (r) => r.v[i] ?? null,
|
|
291
332
|
format: c.format,
|
|
292
333
|
amount: c.amount,
|
|
334
|
+
...(c.tags ? { tags: true } : {}),
|
|
293
335
|
filterable: true,
|
|
294
336
|
groupable: c.type !== 'number',
|
|
295
337
|
...(c.type === 'number' ? { aggregate: c.format === 'percent' ? 'mean' : 'sum' } : {}),
|
|
@@ -438,7 +480,7 @@ function FrameTab({ api, tab, meta, crumbs, actions, intro, onChange, }) {
|
|
|
438
480
|
day: 'numeric',
|
|
439
481
|
hour: 'numeric',
|
|
440
482
|
minute: '2-digit',
|
|
441
|
-
}) }), failed ? (_jsx(StatusItem, { label: "Refresh failed", className: "min-w-0", value: _jsx("span", { className: "inline-block max-w-72 truncate align-bottom", title: failed, children: failed }), ok: false })) : null] }) }, `frame:${tab.id}`), assistantMade && (_jsx(FrameScriptDialog, { api: api, frame: current.id, name: current.name, open: scriptOpen, onOpenChange: setScriptOpen }))] }));
|
|
483
|
+
}) }), failed ? (_jsx(StatusItem, { label: "Refresh failed", className: "min-w-0", value: _jsx("span", { className: "inline-block max-w-72 truncate align-bottom", title: failed, children: failed }), ok: false })) : null] }) }, `frame:${tab.id}`), assistantMade && (_jsx(FrameScriptDialog, { api: api, frame: current.id, name: current.name, runs: current.runs, open: scriptOpen, onOpenChange: setScriptOpen }))] }));
|
|
442
484
|
}
|
|
443
485
|
/** A tab's icon: the person's pick, else the assistant's mark on a frame, else a table. */
|
|
444
486
|
function tabIcon(t) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.190",
|
|
4
4
|
"description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@codemirror/lang-javascript": "6.2.5",
|
|
50
50
|
"@codemirror/lang-json": "6.0.2",
|
|
51
51
|
"@codemirror/lang-sql": "6.10.0",
|
|
52
|
-
"@columenlabs/lumen-ui-types": "0.1.
|
|
52
|
+
"@columenlabs/lumen-ui-types": "0.1.190",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|