@columenlabs/lumen-ui 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/NOTICE +12 -0
- package/README.md +4 -2
- package/dist/add-column.d.ts +23 -0
- package/dist/add-column.js +62 -0
- package/dist/api-fetch.d.ts +9 -0
- package/dist/api-fetch.js +60 -0
- package/dist/app-switcher.d.ts +21 -0
- package/dist/app-switcher.js +12 -0
- package/dist/app-views.d.ts +18 -0
- package/dist/app-views.js +41 -0
- package/dist/assistant-context.d.ts +72 -0
- package/dist/assistant-context.js +30 -0
- package/dist/assistant-panel.d.ts +58 -0
- package/dist/assistant-panel.js +244 -0
- package/dist/assistant-screen.d.ts +124 -0
- package/dist/assistant-screen.js +252 -0
- package/dist/card-switch.d.ts +16 -0
- package/dist/card-switch.js +16 -0
- package/dist/color-dot.d.ts +14 -0
- package/dist/color-dot.js +19 -0
- package/dist/confirm.d.ts +18 -0
- package/dist/confirm.js +5 -0
- package/dist/connections.d.ts +45 -0
- package/dist/connections.js +275 -0
- package/dist/coverage-calendar-map.d.ts +27 -0
- package/dist/coverage-calendar-map.js +84 -0
- package/dist/coverage-cells.d.ts +55 -0
- package/dist/coverage-cells.js +85 -0
- package/dist/coverage-map.d.ts +112 -0
- package/dist/coverage-map.js +428 -0
- package/dist/details-table.d.ts +20 -0
- package/dist/details-table.js +13 -0
- package/dist/drawer-section.d.ts +27 -0
- package/dist/drawer-section.js +23 -0
- package/dist/floating-head.d.ts +2 -0
- package/dist/floating-head.js +57 -0
- package/dist/fold.d.ts +16 -0
- package/dist/fold.js +70 -0
- package/dist/form-issues.d.ts +32 -0
- package/dist/form-issues.js +60 -0
- package/dist/formula-input.d.ts +17 -0
- package/dist/formula-input.js +104 -0
- package/dist/frame-script.d.ts +7 -0
- package/dist/frame-script.js +103 -0
- package/dist/freshness.d.ts +24 -0
- package/dist/freshness.js +99 -0
- package/dist/group-button.d.ts +33 -0
- package/dist/group-button.js +67 -0
- package/dist/growth-chart.d.ts +77 -0
- package/dist/growth-chart.js +195 -0
- package/dist/hooks/use-keyboard-fit.d.ts +15 -0
- package/dist/hooks/use-keyboard-fit.js +29 -0
- package/dist/hooks/use-mobile.d.ts +1 -0
- package/dist/hooks/use-mobile.js +15 -0
- package/dist/icon-button.d.ts +21 -0
- package/dist/icon-button.js +22 -0
- package/dist/icon-picker.d.ts +14 -0
- package/dist/icon-picker.js +36 -0
- package/dist/index.d.ts +83 -0
- package/dist/index.js +90 -0
- package/dist/integrations.d.ts +57 -0
- package/dist/integrations.js +73 -0
- package/dist/joins.d.ts +128 -0
- package/dist/joins.js +767 -0
- package/dist/kit-config.d.ts +13 -0
- package/dist/kit-config.js +15 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/lumen-boundary.d.ts +7 -0
- package/dist/lumen-boundary.js +10 -0
- package/dist/lumen-code-editor-impl.d.ts +2 -0
- package/dist/lumen-code-editor-impl.js +16 -0
- package/dist/lumen-code-editor.d.ts +11 -0
- package/dist/lumen-code-editor.js +11 -0
- package/dist/lumen-code.d.ts +22 -0
- package/dist/lumen-code.js +75 -0
- package/dist/lumen-drawer.d.ts +30 -0
- package/dist/lumen-drawer.js +27 -0
- package/dist/lumen-echart.d.ts +58 -0
- package/dist/lumen-echart.js +129 -0
- package/dist/lumen-filter.d.ts +75 -0
- package/dist/lumen-filter.js +546 -0
- package/dist/lumen-finder.d.ts +62 -0
- package/dist/lumen-finder.js +275 -0
- package/dist/lumen-form.d.ts +34 -0
- package/dist/lumen-form.js +81 -0
- package/dist/lumen-geo-map.d.ts +9 -0
- package/dist/lumen-geo-map.js +135 -0
- package/dist/lumen-grid.d.ts +49 -0
- package/dist/lumen-grid.js +203 -0
- package/dist/lumen-layout.d.ts +150 -0
- package/dist/lumen-layout.js +658 -0
- package/dist/lumen-links.d.ts +96 -0
- package/dist/lumen-links.js +213 -0
- package/dist/lumen-page-state.d.ts +20 -0
- package/dist/lumen-page-state.js +15 -0
- package/dist/lumen-selector.d.ts +139 -0
- package/dist/lumen-selector.js +308 -0
- package/dist/lumen-source.d.ts +137 -0
- package/dist/lumen-source.js +327 -0
- package/dist/lumen-tab-drawer.d.ts +27 -0
- package/dist/lumen-tab-drawer.js +26 -0
- package/dist/lumen-tabs.d.ts +21 -0
- package/dist/lumen-tabs.js +6 -0
- package/dist/lumen-toast.d.ts +11 -0
- package/dist/lumen-toast.js +16 -0
- package/dist/lumen-trend.d.ts +18 -0
- package/dist/lumen-trend.js +157 -0
- package/dist/lumen-view-card.d.ts +35 -0
- package/dist/lumen-view-card.js +50 -0
- package/dist/lumen-view.d.ts +172 -0
- package/dist/lumen-view.js +645 -0
- package/dist/lumen-widget-charts.d.ts +57 -0
- package/dist/lumen-widget-charts.js +69 -0
- package/dist/lumen-widget.d.ts +614 -0
- package/dist/lumen-widget.js +594 -0
- package/dist/market/compare-chart.d.ts +34 -0
- package/dist/market/compare-chart.js +292 -0
- package/dist/market/indicator-picker.d.ts +29 -0
- package/dist/market/indicator-picker.js +125 -0
- package/dist/market/indicators.d.ts +63 -0
- package/dist/market/indicators.js +314 -0
- package/dist/market/live.d.ts +120 -0
- package/dist/market/live.js +444 -0
- package/dist/market/lumen-chart.d.ts +88 -0
- package/dist/market/lumen-chart.js +645 -0
- package/dist/market/market-widget.d.ts +45 -0
- package/dist/market/market-widget.js +346 -0
- package/dist/market/option-chain.d.ts +19 -0
- package/dist/market/option-chain.js +53 -0
- package/dist/market/price-chart.d.ts +128 -0
- package/dist/market/price-chart.js +619 -0
- package/dist/mcp-settings.d.ts +20 -0
- package/dist/mcp-settings.js +65 -0
- package/dist/nav.d.ts +77 -0
- package/dist/nav.js +73 -0
- package/dist/note-line.d.ts +13 -0
- package/dist/note-line.js +5 -0
- package/dist/notifications.d.ts +9 -0
- package/dist/notifications.js +166 -0
- package/dist/origin-legend.d.ts +14 -0
- package/dist/origin-legend.js +38 -0
- package/dist/overview.d.ts +84 -0
- package/dist/overview.js +105 -0
- package/dist/page-inputs.d.ts +14 -0
- package/dist/page-inputs.js +16 -0
- package/dist/page.d.ts +32 -0
- package/dist/page.js +45 -0
- package/dist/pager.d.ts +10 -0
- package/dist/pager.js +14 -0
- package/dist/prefs.d.ts +71 -0
- package/dist/prefs.js +126 -0
- package/dist/query-client.d.ts +7 -0
- package/dist/query-client.js +19 -0
- package/dist/refresh.d.ts +4 -0
- package/dist/refresh.js +19 -0
- package/dist/rename-field.d.ts +11 -0
- package/dist/rename-field.js +26 -0
- package/dist/reset-button.d.ts +9 -0
- package/dist/reset-button.js +10 -0
- package/dist/route-error.d.ts +42 -0
- package/dist/route-error.js +229 -0
- package/dist/schedules.d.ts +88 -0
- package/dist/schedules.js +221 -0
- package/dist/settings.d.ts +51 -0
- package/dist/settings.js +181 -0
- package/dist/shell.d.ts +99 -0
- package/dist/shell.js +137 -0
- package/dist/source-health.d.ts +12 -0
- package/dist/source-health.js +58 -0
- package/dist/spotlight.d.ts +23 -0
- package/dist/spotlight.js +138 -0
- package/dist/status-health.d.ts +16 -0
- package/dist/status-health.js +41 -0
- package/dist/status.d.ts +9 -0
- package/dist/status.js +12 -0
- package/dist/summary-cards.d.ts +56 -0
- package/dist/summary-cards.js +270 -0
- package/dist/symbol-sheet-context.d.ts +4 -0
- package/dist/symbol-sheet-context.js +5 -0
- package/dist/symbol-sheet.d.ts +9 -0
- package/dist/symbol-sheet.js +24 -0
- package/dist/tasks.d.ts +29 -0
- package/dist/tasks.js +278 -0
- package/dist/theme.css +606 -0
- package/dist/tile.d.ts +37 -0
- package/dist/tile.js +23 -0
- package/dist/ui/alert-dialog.d.ts +18 -0
- package/dist/ui/alert-dialog.js +41 -0
- package/dist/ui/alert.d.ts +10 -0
- package/dist/ui/alert.js +27 -0
- package/dist/ui/avatar.d.ts +11 -0
- package/dist/ui/avatar.js +22 -0
- package/dist/ui/badge.d.ts +9 -0
- package/dist/ui/badge.js +27 -0
- package/dist/ui/breadcrumb.d.ts +11 -0
- package/dist/ui/breadcrumb.js +27 -0
- package/dist/ui/button-group.d.ts +11 -0
- package/dist/ui/button-group.js +27 -0
- package/dist/ui/button.d.ts +10 -0
- package/dist/ui/button.js +38 -0
- package/dist/ui/card.d.ts +11 -0
- package/dist/ui/card.js +24 -0
- package/dist/ui/chart.d.ts +44 -0
- package/dist/ui/chart.js +117 -0
- package/dist/ui/checkbox.d.ts +4 -0
- package/dist/ui/checkbox.js +8 -0
- package/dist/ui/collapsible.d.ts +5 -0
- package/dist/ui/collapsible.js +12 -0
- package/dist/ui/combobox.d.ts +24 -0
- package/dist/ui/combobox.js +60 -0
- package/dist/ui/command.d.ts +18 -0
- package/dist/ui/command.js +34 -0
- package/dist/ui/dialog.d.ts +23 -0
- package/dist/ui/dialog.js +50 -0
- package/dist/ui/dropdown-menu.d.ts +29 -0
- package/dist/ui/dropdown-menu.js +50 -0
- package/dist/ui/emoji-picker.d.ts +6 -0
- package/dist/ui/emoji-picker.js +28 -0
- package/dist/ui/empty.d.ts +11 -0
- package/dist/ui/empty.js +33 -0
- package/dist/ui/field.d.ts +24 -0
- package/dist/ui/field.js +65 -0
- package/dist/ui/hover-card.d.ts +6 -0
- package/dist/ui/hover-card.js +13 -0
- package/dist/ui/input-group.d.ts +16 -0
- package/dist/ui/input-group.js +56 -0
- package/dist/ui/input.d.ts +3 -0
- package/dist/ui/input.js +6 -0
- package/dist/ui/item.d.ts +23 -0
- package/dist/ui/item.js +67 -0
- package/dist/ui/kbd.d.ts +3 -0
- package/dist/ui/kbd.js +9 -0
- package/dist/ui/label.d.ts +4 -0
- package/dist/ui/label.js +7 -0
- package/dist/ui/popover.d.ts +10 -0
- package/dist/ui/popover.js +25 -0
- package/dist/ui/progress.d.ts +4 -0
- package/dist/ui/progress.js +9 -0
- package/dist/ui/resizable.d.ts +7 -0
- package/dist/ui/resizable.js +13 -0
- package/dist/ui/scroll-area.d.ts +5 -0
- package/dist/ui/scroll-area.js +10 -0
- package/dist/ui/select.d.ts +15 -0
- package/dist/ui/select.js +36 -0
- package/dist/ui/separator.d.ts +4 -0
- package/dist/ui/separator.js +7 -0
- package/dist/ui/sheet.d.ts +14 -0
- package/dist/ui/sheet.js +54 -0
- package/dist/ui/sidebar.d.ts +69 -0
- package/dist/ui/sidebar.js +206 -0
- package/dist/ui/skeleton.d.ts +2 -0
- package/dist/ui/skeleton.js +6 -0
- package/dist/ui/slider.d.ts +4 -0
- package/dist/ui/slider.js +9 -0
- package/dist/ui/sonner.d.ts +3 -0
- package/dist/ui/sonner.js +24 -0
- package/dist/ui/spinner.d.ts +2 -0
- package/dist/ui/spinner.js +7 -0
- package/dist/ui/switch.d.ts +6 -0
- package/dist/ui/switch.js +7 -0
- package/dist/ui/table.d.ts +10 -0
- package/dist/ui/table.js +31 -0
- package/dist/ui/tabs.d.ts +7 -0
- package/dist/ui/tabs.js +22 -0
- package/dist/ui/textarea.d.ts +3 -0
- package/dist/ui/textarea.js +6 -0
- package/dist/ui/toggle-group.d.ts +10 -0
- package/dist/ui/toggle-group.js +22 -0
- package/dist/ui/toggle.d.ts +9 -0
- package/dist/ui/toggle.js +25 -0
- package/dist/ui/tooltip.d.ts +7 -0
- package/dist/ui/tooltip.js +16 -0
- package/dist/view-edit-cell.d.ts +49 -0
- package/dist/view-edit-cell.js +198 -0
- package/dist/view-editor.d.ts +52 -0
- package/dist/view-editor.js +940 -0
- package/dist/view-formulas.d.ts +61 -0
- package/dist/view-formulas.js +247 -0
- package/dist/view-icons.d.ts +20 -0
- package/dist/view-icons.js +70 -0
- package/dist/view-model.d.ts +390 -0
- package/dist/view-model.js +316 -0
- package/dist/whats-new.d.ts +3 -0
- package/dist/whats-new.js +53 -0
- package/dist/widget-controls.d.ts +9 -0
- package/dist/widget-controls.js +26 -0
- package/dist/widget-settings.d.ts +436 -0
- package/dist/widget-settings.js +290 -0
- package/dist/widget-thumbs.d.ts +3 -0
- package/dist/widget-thumbs.js +29 -0
- package/dist/workspace-keys.d.ts +12 -0
- package/dist/workspace-keys.js +13 -0
- package/dist/workspaces.d.ts +57 -0
- package/dist/workspaces.js +473 -0
- package/package.json +82 -4
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { frameConfig, frameKeyAt, frameSymbolAt, liveQuoteColumns, } from '@columenlabs/lumen-ui-types/frames';
|
|
3
|
+
import { customViewConfigSchema, frameSpace, } from '@columenlabs/lumen-ui-types/views';
|
|
4
|
+
import { workspacePath, } from '@columenlabs/lumen-ui-types/workspaces';
|
|
5
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
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';
|
|
8
|
+
import { createContext, useContext, useEffect, useMemo, useState, } from 'react';
|
|
9
|
+
import { apiJson } from './api-fetch.js';
|
|
10
|
+
import { useAssistant, useAssistantName, useAssistantPending } from './assistant-context.js';
|
|
11
|
+
import { useAssistantWorkspace } from './assistant-screen.js';
|
|
12
|
+
import { LumenConfirm } from './confirm.js';
|
|
13
|
+
import { FrameScriptDialog } from './frame-script.js';
|
|
14
|
+
import { editedCells, editedKey, frameColor, useFrameEdits } from './joins.js';
|
|
15
|
+
import { LumenLayout } from './lumen-layout.js';
|
|
16
|
+
import { LumenPageState } from './lumen-page-state.js';
|
|
17
|
+
import { LumenSelector } from './lumen-selector.js';
|
|
18
|
+
import { useLumenLoad } from './lumen-source.js';
|
|
19
|
+
import { LumenTabDrawer } from './lumen-tab-drawer.js';
|
|
20
|
+
import { LumenTabs } from './lumen-tabs.js';
|
|
21
|
+
import { lumenToast } from './lumen-toast.js';
|
|
22
|
+
import { useHasMarket } from './market/live.js';
|
|
23
|
+
import { SidebarSection } from './nav.js';
|
|
24
|
+
// LumenWorkspaces (in Lumen): the assistant produces data, the shell renders
|
|
25
|
+
// it. A workspace is a page of tabs, one list per person, each belonging to one app of the product;
|
|
26
|
+
// each tab is a LumenLayout over a frame the assistant made (rows plus the script that made them) or
|
|
27
|
+
// a card over one of the app's own data domains, which the app draws live (an app's holdings,
|
|
28
|
+
// lots, ...). The person filters, groups, pivots and edits a tab like any view, the assistant patches
|
|
29
|
+
// it, and its layout is kept as they leave it; when the assistant remakes a frame of the same name,
|
|
30
|
+
// the tab's rows change and its layout stays (new columns join it after its own). A frame is a data
|
|
31
|
+
// domain like any other: one with a symbol column carries the symbol key, so the registry's Symbol
|
|
32
|
+
// join attaches to it through the shell's joins. Workspaces live under the assistant's API; the
|
|
33
|
+
// product says its apps and domains with `WorkspacesProvider`.
|
|
34
|
+
import { IconButton } from './page.js';
|
|
35
|
+
import { StatusItem, useShell } from './shell.js';
|
|
36
|
+
import { Button } from './ui/button.js';
|
|
37
|
+
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from './ui/dialog.js';
|
|
38
|
+
import { Input } from './ui/input.js';
|
|
39
|
+
import { iconOf } from './view-icons.js';
|
|
40
|
+
import { WORKSPACES_KEY, frameKey, workspacesListQuery } from './workspace-keys.js';
|
|
41
|
+
const ConfigCtx = createContext({});
|
|
42
|
+
/** The product's apps and domains for its workspaces. */
|
|
43
|
+
export const WorkspacesProvider = ConfigCtx.Provider;
|
|
44
|
+
const json = (url, init) => apiJson(url, init, 'workspaces');
|
|
45
|
+
const workspaceQuery = (api, id) => ({
|
|
46
|
+
queryKey: [...WORKSPACES_KEY, api, 'ws', id],
|
|
47
|
+
queryFn: () => json(`${api}/workspaces/${id}`),
|
|
48
|
+
});
|
|
49
|
+
/** The api the assistant answers on, where the app has one. */
|
|
50
|
+
function useApi() {
|
|
51
|
+
return useAssistant()?.api ?? null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Workspaces in the sidebar: one item per workspace, newest first; `+` starts an empty one, the
|
|
55
|
+
* trash icon deletes one (its frames stay until no tab shows them). Hidden where the app has no
|
|
56
|
+
* assistant.
|
|
57
|
+
*/
|
|
58
|
+
export function WorkspacesSection() {
|
|
59
|
+
const api = useApi();
|
|
60
|
+
const qc = useQueryClient();
|
|
61
|
+
const navigate = useNavigate();
|
|
62
|
+
const list = useQuery({ ...workspacesListQuery(api ?? ''), enabled: !!api });
|
|
63
|
+
const assistant = useAssistantName();
|
|
64
|
+
const create = useMutation({
|
|
65
|
+
mutationFn: () => json(`${api}/workspaces`, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
body: JSON.stringify({ name: 'New workspace' }),
|
|
68
|
+
}),
|
|
69
|
+
onSuccess: (w) => {
|
|
70
|
+
void qc.invalidateQueries({ queryKey: WORKSPACES_KEY });
|
|
71
|
+
void navigate({ href: workspacePath(w.id) });
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
const remove = useMutation({
|
|
75
|
+
mutationFn: (id) => json(`${api}/workspaces/${id}`, { method: 'DELETE' }),
|
|
76
|
+
onSuccess: () => void qc.invalidateQueries({ queryKey: WORKSPACES_KEY }),
|
|
77
|
+
});
|
|
78
|
+
if (!api || list.isError)
|
|
79
|
+
return null;
|
|
80
|
+
return (_jsx(SidebarSection, { label: "Workspaces", loading: list.isPending, onAdd: () => create.mutate(), addLabel: "New workspace", onRemove: (item) => remove.mutate(item.id), removeLabel: (item) => `Delete workspace ${item.title}`, empty: `Ask ${assistant} for data: it opens here`, items: (list.data?.workspaces ?? []).map((w) => ({
|
|
81
|
+
id: w.id,
|
|
82
|
+
title: w.name,
|
|
83
|
+
to: workspacePath(w.id),
|
|
84
|
+
icon: LayersIcon,
|
|
85
|
+
badge: w.tabs.length || undefined,
|
|
86
|
+
})) }));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* `/workspaces/$id` on every app with an assistant. `draft` draws a tab over one of the app's own
|
|
90
|
+
* domains (an app passes its portfolio view); a tab over another app's domain links there.
|
|
91
|
+
*/
|
|
92
|
+
export function WorkspacePage({ draft }) {
|
|
93
|
+
const api = useApi();
|
|
94
|
+
const { id = '' } = useParams({ strict: false });
|
|
95
|
+
const search = useSearch({ strict: false });
|
|
96
|
+
const q = useQuery({ ...workspaceQuery(api ?? '', id), enabled: !!api && !!id });
|
|
97
|
+
const pending = useAssistantPending();
|
|
98
|
+
const assistant = useAssistantName();
|
|
99
|
+
const w = q.data;
|
|
100
|
+
if (!api || !w)
|
|
101
|
+
return (_jsx(LumenPageState, { crumbs: [{ label: 'Workspaces' }], title: "Workspace", icon: LayersIcon, tabs: true, query: api ? q : undefined, waiting: pending, error: !api && !pending ? new Error(`Workspaces need ${assistant} here.`) : null, notFound: "No workspace with this id. It may have been deleted." }));
|
|
102
|
+
const tab = w.tabs.find((t) => t.id === search.tab) ?? w.tabs[0] ?? null;
|
|
103
|
+
return _jsx(WorkspaceBody, { api: api, w: w, tab: tab, draft: draft });
|
|
104
|
+
}
|
|
105
|
+
function WorkspaceBody({ api, w, tab, draft, }) {
|
|
106
|
+
const qc = useQueryClient();
|
|
107
|
+
const navigate = useNavigate();
|
|
108
|
+
// The assistant sees this workspace and its tabs by id, so it adds here and survives renames.
|
|
109
|
+
useAssistantWorkspace({
|
|
110
|
+
id: w.id,
|
|
111
|
+
name: w.name.slice(0, 80),
|
|
112
|
+
tabs: w.tabs.slice(0, 20).map((t) => ({
|
|
113
|
+
id: t.id,
|
|
114
|
+
name: t.name.slice(0, 80),
|
|
115
|
+
source: ('frame' in t.source
|
|
116
|
+
? `frame ${w.frames[t.source.frame]?.name ?? t.source.frame}`
|
|
117
|
+
: t.source.domain).slice(0, 120),
|
|
118
|
+
})),
|
|
119
|
+
open: tab?.id ?? null,
|
|
120
|
+
});
|
|
121
|
+
const [naming, setNaming] = useState(null);
|
|
122
|
+
const [editingTab, setEditingTab] = useState(false);
|
|
123
|
+
const [removing, setRemoving] = useState(null);
|
|
124
|
+
const key = workspaceQuery(api, w.id).queryKey;
|
|
125
|
+
const put = (next) => qc.setQueryData(key, next);
|
|
126
|
+
const rename = useMutation({
|
|
127
|
+
mutationFn: async (name) => json(`${api}/workspaces/${w.id}`, {
|
|
128
|
+
method: 'PATCH',
|
|
129
|
+
body: JSON.stringify({ name }),
|
|
130
|
+
}),
|
|
131
|
+
onSuccess: (next) => {
|
|
132
|
+
put({ ...w, ...next });
|
|
133
|
+
void qc.invalidateQueries({ queryKey: [...WORKSPACES_KEY, api, 'list'] });
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
// The Tab drawer's Save: its name, and its icon kept in the tab's config.
|
|
137
|
+
const editTab = useMutation({
|
|
138
|
+
mutationFn: async (next) => json(`${api}/workspaces/${w.id}/tabs/${tab?.id}`, {
|
|
139
|
+
method: 'PUT',
|
|
140
|
+
body: JSON.stringify(next),
|
|
141
|
+
}),
|
|
142
|
+
onSuccess: (next) => {
|
|
143
|
+
put({ ...w, ...next });
|
|
144
|
+
setEditingTab(false);
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
// Move to app: the workspace leaves this app's sidebar for another's.
|
|
148
|
+
const here = useShell().app;
|
|
149
|
+
const config = useContext(ConfigCtx);
|
|
150
|
+
const assistant = useAssistantName();
|
|
151
|
+
const noun = config.appNoun ?? 'app';
|
|
152
|
+
const appOf = (id) => config.apps?.find((a) => a.id === id);
|
|
153
|
+
const moveTo = (config.apps ?? []).filter((a) => a.id !== here);
|
|
154
|
+
const move = useMutation({
|
|
155
|
+
mutationFn: (app) => json(`${api}/workspaces/${w.id}`, {
|
|
156
|
+
method: 'PATCH',
|
|
157
|
+
body: JSON.stringify({ app }),
|
|
158
|
+
}),
|
|
159
|
+
onSuccess: (next) => {
|
|
160
|
+
const to = appOf(next.app) ?? { label: next.app, href: '/' };
|
|
161
|
+
void qc.invalidateQueries({ queryKey: WORKSPACES_KEY });
|
|
162
|
+
lumenToast(`Moved ${w.name} to ${to.label}`, {
|
|
163
|
+
action: {
|
|
164
|
+
label: 'Open',
|
|
165
|
+
onClick: () => window.open(`${to.href.replace(/\/$/, '')}${workspacePath(w.id)}`, '_blank'),
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
void navigate({ href: '/' });
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
const remove = useMutation({
|
|
172
|
+
mutationFn: (what) => json(what === 'workspace'
|
|
173
|
+
? `${api}/workspaces/${w.id}`
|
|
174
|
+
: `${api}/workspaces/${w.id}/tabs/${tab?.id}`, { method: 'DELETE' }),
|
|
175
|
+
onSuccess: (_, what) => {
|
|
176
|
+
void qc.invalidateQueries({ queryKey: WORKSPACES_KEY });
|
|
177
|
+
if (what === 'workspace')
|
|
178
|
+
void navigate({ href: '/' });
|
|
179
|
+
else
|
|
180
|
+
void navigate({ href: workspacePath(w.id), replace: true });
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
// A layout edit saves at once; the page keeps what is on screen meanwhile.
|
|
184
|
+
const saveConfig = useMutation({
|
|
185
|
+
mutationFn: ({ tab: t, config }) => json(`${api}/workspaces/${w.id}/tabs/${t}`, {
|
|
186
|
+
method: 'PUT',
|
|
187
|
+
body: JSON.stringify({ config }),
|
|
188
|
+
}),
|
|
189
|
+
});
|
|
190
|
+
// A tab is a sheet (rule C): every change saves at once, with Undo in a
|
|
191
|
+
// toast that puts the tab's config back as it was before the change.
|
|
192
|
+
const onChange = (config) => {
|
|
193
|
+
if (!tab)
|
|
194
|
+
return;
|
|
195
|
+
const before = tab.config;
|
|
196
|
+
put({ ...w, tabs: w.tabs.map((t) => (t.id === tab.id ? { ...t, config } : t)) });
|
|
197
|
+
saveConfig.mutate({ tab: tab.id, config });
|
|
198
|
+
lumenToast.undo('Saved to this tab', () => {
|
|
199
|
+
put({ ...w, tabs: w.tabs.map((t) => (t.id === tab.id ? { ...t, config: before } : t)) });
|
|
200
|
+
if (before)
|
|
201
|
+
saveConfig.mutate({ tab: tab.id, config: before });
|
|
202
|
+
else
|
|
203
|
+
void json(`${api}/workspaces/${w.id}/tabs/${tab.id}`, {
|
|
204
|
+
method: 'PUT',
|
|
205
|
+
body: JSON.stringify({ config: null }),
|
|
206
|
+
}).then(() => qc.invalidateQueries({ queryKey: WORKSPACES_KEY }));
|
|
207
|
+
}, { id: `ws-save:${tab.id}`, duration: 4000 });
|
|
208
|
+
};
|
|
209
|
+
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: tab && _jsx(IconButton, { icon: PencilIcon, label: "Edit tab", onClick: () => setEditingTab(true) }) }));
|
|
210
|
+
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') })] }));
|
|
212
|
+
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
|
+
? { name }
|
|
214
|
+
: { name, config: { ...tabConfig(tab, w, config.defaultConfig), name, icon } }), remove: {
|
|
215
|
+
description: 'Its layout goes with it.',
|
|
216
|
+
onRemove: () => {
|
|
217
|
+
setEditingTab(false);
|
|
218
|
+
remove.mutate('tab');
|
|
219
|
+
},
|
|
220
|
+
} })), _jsx(LumenConfirm, { open: removing !== null, onOpenChange: (o) => !o && setRemoving(null), title: `Delete ${w.name}?`, description: `Its tabs go with it. ${assistant} can make them again from its chats.`, onConfirm: () => remove.mutate('workspace') })] }));
|
|
221
|
+
let body;
|
|
222
|
+
if (!tab) {
|
|
223
|
+
body = (_jsx(LumenLayout, { crumbs: crumbs, actions: actions, intro: intro, cards: [
|
|
224
|
+
{
|
|
225
|
+
id: 'empty',
|
|
226
|
+
title: 'No tabs yet',
|
|
227
|
+
icon: LayersIcon,
|
|
228
|
+
kind: 'widget',
|
|
229
|
+
children: (_jsxs("p", { className: "text-sm text-muted-foreground", children: ["Ask ", assistant, " for data and to open it here: \u201C", config.example?.(here) ?? 'put this month’s numbers in', " ", w.name, "\u201D."] })),
|
|
230
|
+
},
|
|
231
|
+
] }));
|
|
232
|
+
}
|
|
233
|
+
else if ('frame' in tab.source) {
|
|
234
|
+
const meta = w.frames[tab.source.frame];
|
|
235
|
+
body = meta ? (_jsx(FrameTab, { api: api, tab: tab, meta: meta, crumbs: crumbs, actions: actions, intro: intro, onChange: onChange }, tab.id)) : (_jsx(LumenLayout, { crumbs: crumbs, actions: actions, intro: intro, cards: [
|
|
236
|
+
{
|
|
237
|
+
id: 'gone',
|
|
238
|
+
title: tab.name,
|
|
239
|
+
icon: TableIcon,
|
|
240
|
+
kind: 'widget',
|
|
241
|
+
children: (_jsxs("p", { className: "text-sm text-muted-foreground", children: ["This tab\u2019s frame is gone. Ask ", assistant, " to make it again."] })),
|
|
242
|
+
},
|
|
243
|
+
] }));
|
|
244
|
+
}
|
|
245
|
+
else if (draft && (config.domainApp?.(tab.source.domain) ?? here) === here) {
|
|
246
|
+
body = draft({
|
|
247
|
+
domain: tab.source.domain,
|
|
248
|
+
config: tabConfig(tab, w, config.defaultConfig),
|
|
249
|
+
onChange,
|
|
250
|
+
crumbs,
|
|
251
|
+
intro,
|
|
252
|
+
actions,
|
|
253
|
+
tabKey: tab.id,
|
|
254
|
+
save: 'sheet',
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// A tab over another app's data opens on that app (an app's portfolio, an app's bank).
|
|
259
|
+
const ownerId = config.domainApp?.(tab.source.domain) ?? here;
|
|
260
|
+
const owner = appOf(ownerId) ?? { label: ownerId, href: '/' };
|
|
261
|
+
const href = `${owner.href.replace(/\/$/, '')}${workspacePath(w.id, tab.id)}`;
|
|
262
|
+
body = (_jsx(LumenLayout, { crumbs: crumbs, actions: actions, intro: intro, cards: [
|
|
263
|
+
{
|
|
264
|
+
id: 'elsewhere',
|
|
265
|
+
title: tab.name,
|
|
266
|
+
icon: TableIcon,
|
|
267
|
+
kind: 'widget',
|
|
268
|
+
children: (_jsxs("div", { className: "flex flex-col items-start gap-2 text-sm text-muted-foreground", children: [_jsxs("p", { children: ["This tab shows your ", owner.label, " data, so it opens there."] }), _jsx(IconButton, { icon: ExternalLinkIcon, label: `Open in ${owner.label}`, variant: "outline", href: href })] })),
|
|
269
|
+
},
|
|
270
|
+
] }));
|
|
271
|
+
}
|
|
272
|
+
return (_jsxs(_Fragment, { children: [body, dialogs] }));
|
|
273
|
+
}
|
|
274
|
+
function frameDomain(meta, symbolAt, editing,
|
|
275
|
+
/** Columns left out: a frame's own copy of a live quote the app's Symbol join draws live. */
|
|
276
|
+
drop = new Set(),
|
|
277
|
+
/** The assistant's name, on the frame's field group. */
|
|
278
|
+
assistant = 'Assistant') {
|
|
279
|
+
const accountAt = frameKeyAt(meta, 'account');
|
|
280
|
+
const keyAt = meta.kind === 'linked' && meta.key ? frameKeyAt(meta, meta.key) : -1;
|
|
281
|
+
const rowKey = (r) => (keyAt >= 0 ? String(r.v[keyAt] ?? '') : r.i);
|
|
282
|
+
const fields = meta.columns.flatMap((c, i) => drop.has(c.key)
|
|
283
|
+
? []
|
|
284
|
+
: [
|
|
285
|
+
{
|
|
286
|
+
key: c.key,
|
|
287
|
+
name: `${frameSpace(meta.name)}.${c.key}`,
|
|
288
|
+
label: c.label,
|
|
289
|
+
icon: c.type === 'number' ? HashIcon : c.type === 'date' ? CalendarIcon : TypeIcon,
|
|
290
|
+
value: (r) => r.v[i] ?? null,
|
|
291
|
+
format: c.format,
|
|
292
|
+
amount: c.amount,
|
|
293
|
+
filterable: true,
|
|
294
|
+
groupable: c.type !== 'number',
|
|
295
|
+
...(c.type === 'number' ? { aggregate: c.format === 'percent' ? 'mean' : 'sum' } : {}),
|
|
296
|
+
...(i === symbolAt ? { chart: (r) => (r.v[i] ? String(r.v[i]) : null) } : {}),
|
|
297
|
+
// Every cell but the key's edits in place; the frame keeps the value through every refresh.
|
|
298
|
+
...(editing && i !== keyAt && c.type !== 'date'
|
|
299
|
+
? {
|
|
300
|
+
edit: {
|
|
301
|
+
input: c.type === 'number' ? 'number' : 'text',
|
|
302
|
+
edited: (r) => editing.edited.has(editedKey(c.key, rowKey(r))),
|
|
303
|
+
save: (r, value) => {
|
|
304
|
+
const k = rowKey(r);
|
|
305
|
+
return editing.save({
|
|
306
|
+
...(typeof k === 'number' ? { row: k } : { key: k }),
|
|
307
|
+
column: c.key,
|
|
308
|
+
value,
|
|
309
|
+
});
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
}
|
|
313
|
+
: {}),
|
|
314
|
+
},
|
|
315
|
+
]);
|
|
316
|
+
return {
|
|
317
|
+
id: 'frame',
|
|
318
|
+
rowKey: (r) => String(r.i),
|
|
319
|
+
text: (r) => r.v.filter((x) => typeof x === 'string'),
|
|
320
|
+
groups: [
|
|
321
|
+
{
|
|
322
|
+
id: 'frame',
|
|
323
|
+
label: `${meta.origin === 'yours' ? 'Yours' : assistant} · ${meta.name}`,
|
|
324
|
+
color: frameColor(meta),
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
// A the assistant frame's columns are as of its last read (◷); the person's own are fixed.
|
|
328
|
+
fields: fields.map((f) => ({
|
|
329
|
+
group: 'frame',
|
|
330
|
+
...(meta.origin === 'yours' ? {} : { updates: 'asof' }),
|
|
331
|
+
...f,
|
|
332
|
+
})),
|
|
333
|
+
keys: {
|
|
334
|
+
...(symbolAt >= 0
|
|
335
|
+
? { symbol: (r) => (r.v[symbolAt] ? String(r.v[symbolAt]) : null) }
|
|
336
|
+
: {}),
|
|
337
|
+
...(accountAt >= 0
|
|
338
|
+
? { account: (r) => (r.v[accountAt] ? String(r.v[accountAt]) : null) }
|
|
339
|
+
: {}),
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function FrameTab({ api, tab, meta, crumbs, actions, intro, onChange, }) {
|
|
344
|
+
const qc = useQueryClient();
|
|
345
|
+
const assistant = useAssistantName();
|
|
346
|
+
const rows = useQuery({
|
|
347
|
+
queryKey: frameKey(api, meta.id),
|
|
348
|
+
queryFn: () => json(`${api}/frames/${meta.id}`),
|
|
349
|
+
});
|
|
350
|
+
const refresh = useMutation({
|
|
351
|
+
mutationFn: () => json(`${api}/frames/${meta.id}/refresh`, { method: 'POST' }),
|
|
352
|
+
onSuccess: (data) => {
|
|
353
|
+
qc.setQueryData(frameKey(api, meta.id), data);
|
|
354
|
+
void qc.invalidateQueries({ queryKey: [...WORKSPACES_KEY, api, 'ws'] });
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
// A frame refreshes only when the person taps ↻: opening a tab shows the
|
|
358
|
+
// stored rows.
|
|
359
|
+
const current = rows.data?.meta ?? meta;
|
|
360
|
+
const reload = useLumenLoad(rows);
|
|
361
|
+
const data = useMemo(() => (rows.data?.rows ?? []).map((v, i) => ({ i, v })), [rows.data]);
|
|
362
|
+
const symbolAt = frameSymbolAt(current);
|
|
363
|
+
const edits = useFrameEdits(api);
|
|
364
|
+
const edited = useMemo(() => editedCells(rows.data), [rows.data]);
|
|
365
|
+
// Where the app draws live quotes, a frame's own Price or Day % beside a symbol column is a
|
|
366
|
+
// frozen copy that would hide the live one (frames saved before `frame.live_column`).
|
|
367
|
+
const hasMarket = useHasMarket();
|
|
368
|
+
const drop = useMemo(() => new Set(hasMarket ? liveQuoteColumns(current.columns).map((c) => c.key) : []), [hasMarket, current.columns]);
|
|
369
|
+
const domain = useMemo(() => frameDomain(current, symbolAt, {
|
|
370
|
+
edited,
|
|
371
|
+
save: (edit) => edits.mutateAsync({ id: current.id, edits: [edit] }),
|
|
372
|
+
}, drop, assistant), [current, symbolAt, edited, edits.mutateAsync, drop, assistant]);
|
|
373
|
+
const config = useMemo(() => tab.config ?? frameConfig(current), [tab.config, current]);
|
|
374
|
+
// The cause once, even from a frame stored before the server unwrapped it.
|
|
375
|
+
const failed = (refresh.error?.message ?? current.error)?.replace(/^(?:(?:Code execution failed|Refresh failed):\s*)+/, '');
|
|
376
|
+
// The card's own ↻ runs the assistant's script again and its footer says when they last ran (per card, not the page header); rows the assistant wrote itself only reload.
|
|
377
|
+
const runs = current.mode !== 'static' && current.origin !== 'yours';
|
|
378
|
+
// The assistant's script as code to read and edit; a Your data column has none.
|
|
379
|
+
const [scriptOpen, setScriptOpen] = useState(false);
|
|
380
|
+
const assistantMade = current.origin !== 'yours';
|
|
381
|
+
const load = useMemo(() => ({
|
|
382
|
+
...reload,
|
|
383
|
+
updated: {
|
|
384
|
+
at: Date.parse(current.at),
|
|
385
|
+
error: failed ?? null,
|
|
386
|
+
kind: runs
|
|
387
|
+
? current.mode === 'live'
|
|
388
|
+
? 'live'
|
|
389
|
+
: 'asof'
|
|
390
|
+
: 'fixed',
|
|
391
|
+
about: current.origin === 'yours'
|
|
392
|
+
? 'Your own data: you change it by typing.'
|
|
393
|
+
: !runs
|
|
394
|
+
? `${assistant} wrote these rows once: nothing to run again.`
|
|
395
|
+
: current.mode === 'asof'
|
|
396
|
+
? `${assistant}’s script reads the web, so it runs only when you ask.`
|
|
397
|
+
: `${assistant}’s script reads live data.`,
|
|
398
|
+
},
|
|
399
|
+
...(assistantMade ? { editScript: () => setScriptOpen(true) } : {}),
|
|
400
|
+
...(runs
|
|
401
|
+
? {
|
|
402
|
+
fetching: reload.fetching || refresh.isPending,
|
|
403
|
+
refreshing: reload.refreshing || refresh.isPending,
|
|
404
|
+
refresh: async () => {
|
|
405
|
+
await refresh.mutateAsync().catch(() => undefined);
|
|
406
|
+
},
|
|
407
|
+
refreshLabel: current.mode === 'asof'
|
|
408
|
+
? `Run ${assistant}’s script again (it reads the web, so only when you ask)`
|
|
409
|
+
: `Run ${assistant}’s script again`,
|
|
410
|
+
}
|
|
411
|
+
: {}),
|
|
412
|
+
}), [
|
|
413
|
+
reload,
|
|
414
|
+
current.at,
|
|
415
|
+
current.mode,
|
|
416
|
+
current.origin,
|
|
417
|
+
failed,
|
|
418
|
+
runs,
|
|
419
|
+
assistantMade,
|
|
420
|
+
assistant,
|
|
421
|
+
refresh.isPending,
|
|
422
|
+
refresh.mutateAsync,
|
|
423
|
+
]);
|
|
424
|
+
return (_jsxs(_Fragment, { children: [_jsx(LumenLayout, { crumbs: crumbs, intro: intro, actions: actions, domain: domain, config: config, rows: data, onConfigChange: onChange, save: "sheet", rowTitle: (r) => String(r.v[symbolAt >= 0 ? symbolAt : 0] ?? `Row ${r.i + 1}`), load: load, status: _jsxs(_Fragment, { children: [_jsx(StatusItem, { label: "Frame", value: current.name }), _jsx(StatusItem, { label: "Rows", value: current.rows.toLocaleString('en-US') }), _jsx(StatusItem, { label: "Kind", value: current.origin === 'yours'
|
|
425
|
+
? `Your data, by ${current.key}`
|
|
426
|
+
: current.kind === 'linked'
|
|
427
|
+
? `Linked by ${current.key}`
|
|
428
|
+
: 'Snapshot' }), current.edited ? (_jsx(StatusItem, { label: "Edited by hand", value: current.edited.toLocaleString('en-US') })) : null, _jsx(StatusItem, { label: current.kind === 'linked'
|
|
429
|
+
? 'As of'
|
|
430
|
+
: current.mode === 'live'
|
|
431
|
+
? 'Live, read'
|
|
432
|
+
: current.mode === 'asof'
|
|
433
|
+
? 'As of'
|
|
434
|
+
: current.origin === 'yours'
|
|
435
|
+
? 'Yours, changed'
|
|
436
|
+
: `Made by ${assistant}`, value: new Date(current.at).toLocaleString('en-US', {
|
|
437
|
+
month: 'short',
|
|
438
|
+
day: 'numeric',
|
|
439
|
+
hour: 'numeric',
|
|
440
|
+
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 }))] }));
|
|
442
|
+
}
|
|
443
|
+
/** A tab's icon: the person's pick, else the assistant's mark on a frame, else a table. */
|
|
444
|
+
function tabIcon(t) {
|
|
445
|
+
return iconOf(t.config?.icon, 'frame' in t.source ? SparklesIcon : TableIcon);
|
|
446
|
+
}
|
|
447
|
+
/** A tab's layout as it shows: its own config, else its source's default. */
|
|
448
|
+
function tabConfig(t, w, defaultConfig) {
|
|
449
|
+
if (t.config)
|
|
450
|
+
return t.config;
|
|
451
|
+
if ('frame' in t.source) {
|
|
452
|
+
const meta = w.frames[t.source.frame];
|
|
453
|
+
return { ...(meta ? frameConfig(meta) : customViewConfigSchema.parse({})), name: t.name };
|
|
454
|
+
}
|
|
455
|
+
const start = defaultConfig?.(t.source.domain);
|
|
456
|
+
return { ...customViewConfigSchema.parse(start ?? {}), name: t.name };
|
|
457
|
+
}
|
|
458
|
+
/** A name for a workspace. */
|
|
459
|
+
function NameDialog({ open, title, value, onClose, onSave, }) {
|
|
460
|
+
const [name, setName] = useState(value);
|
|
461
|
+
useEffect(() => {
|
|
462
|
+
if (open)
|
|
463
|
+
setName(value);
|
|
464
|
+
}, [open, value]);
|
|
465
|
+
const ok = name.trim().length > 0 && name.trim().length <= 60;
|
|
466
|
+
return (_jsx(Dialog, { open: open, onOpenChange: (o) => !o && onClose(), children: _jsxs(DialogContent, { className: "sm:max-w-sm", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: "Up to 60 characters." })] }), _jsxs("form", { onSubmit: (e) => {
|
|
467
|
+
e.preventDefault();
|
|
468
|
+
if (!ok)
|
|
469
|
+
return;
|
|
470
|
+
onSave(name.trim());
|
|
471
|
+
onClose();
|
|
472
|
+
}, className: "flex flex-col gap-4", children: [_jsx(Input, { autoFocus: true, "aria-label": "Name", value: name, maxLength: 60, onChange: (e) => setName(e.target.value) }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "submit", disabled: !ok, children: "Save" })] })] })] }) }));
|
|
473
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,84 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./ui/*": {
|
|
15
|
+
"types": "./dist/ui/*.d.ts",
|
|
16
|
+
"default": "./dist/ui/*.js"
|
|
17
|
+
},
|
|
18
|
+
"./theme.css": "./dist/theme.css"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"NOTICE"
|
|
24
|
+
],
|
|
25
|
+
"sideEffects": [
|
|
26
|
+
"**/*.css"
|
|
27
|
+
],
|
|
28
|
+
"homepage": "https://github.com/columenlabs/lumen-ui",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/columenlabs/lumen-ui.git",
|
|
32
|
+
"directory": "packages/lumen"
|
|
33
|
+
},
|
|
34
|
+
"bugs": "https://github.com/columenlabs/lumen-ui/issues",
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public",
|
|
37
|
+
"registry": "https://registry.npmjs.org/"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@base-ui/react": "^1.8.0",
|
|
41
|
+
"@codemirror/lang-javascript": "6.2.5",
|
|
42
|
+
"@codemirror/lang-json": "6.0.2",
|
|
43
|
+
"@codemirror/lang-sql": "6.10.0",
|
|
44
|
+
"@columenlabs/lumen-ui-types": "0.2.0",
|
|
45
|
+
"@dnd-kit/core": "6.3.1",
|
|
46
|
+
"@dnd-kit/sortable": "10.0.0",
|
|
47
|
+
"@dnd-kit/utilities": "3.2.2",
|
|
48
|
+
"@fontsource-variable/ibm-plex-sans": "^5.3.0",
|
|
49
|
+
"@fontsource-variable/inter": "^5.3.0",
|
|
50
|
+
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
|
51
|
+
"@fontsource-variable/manrope": "^5.3.0",
|
|
52
|
+
"@tanstack/react-table": "^9.2.4",
|
|
53
|
+
"@uiw/codemirror-theme-github": "4.25.12",
|
|
54
|
+
"@uiw/react-codemirror": "4.25.12",
|
|
55
|
+
"class-variance-authority": "^0.7.1",
|
|
56
|
+
"clsx": "^2.1.1",
|
|
57
|
+
"cmdk": "^1.1.1",
|
|
58
|
+
"echarts": "6.0.0",
|
|
59
|
+
"frimousse": "^0.4.0",
|
|
60
|
+
"i18n-iso-countries": "7.14.0",
|
|
61
|
+
"indicatorts": "^2.2.2",
|
|
62
|
+
"lightweight-charts": "^5.2.1",
|
|
63
|
+
"lucide-react": "^1.48.0",
|
|
64
|
+
"papaparse": "5.5.3",
|
|
65
|
+
"radix-ui": "^1.6.7",
|
|
66
|
+
"react-error-boundary": "^6.1.6",
|
|
67
|
+
"react-grid-layout": "2.2.4",
|
|
68
|
+
"react-intersection-observer": "9.16.0",
|
|
69
|
+
"react-markdown": "10.1.0",
|
|
70
|
+
"react-resizable-panels": "^4.14.3",
|
|
71
|
+
"recharts": "^3.10.1",
|
|
72
|
+
"shiki": "4.5.0",
|
|
73
|
+
"sonner": "^2.0.8",
|
|
74
|
+
"tailwind-merge": "^3.7.0",
|
|
75
|
+
"topojson-client": "3.1.0",
|
|
76
|
+
"world-atlas": "2.0.2"
|
|
77
|
+
},
|
|
78
|
+
"peerDependencies": {
|
|
79
|
+
"@tanstack/react-query": "^5.103.2",
|
|
80
|
+
"@tanstack/react-router": "^1.170.39",
|
|
81
|
+
"react": "^19.0.0",
|
|
82
|
+
"react-dom": "^19.0.0"
|
|
83
|
+
}
|
|
84
|
+
}
|