diff --git a/apps/desktop/src/renderer/index.html b/apps/desktop/src/renderer/index.html index bfb11df2..2eaed6f9 100644 --- a/apps/desktop/src/renderer/index.html +++ b/apps/desktop/src/renderer/index.html @@ -3,6 +3,16 @@ DevTools Studio +
diff --git a/apps/desktop/tsconfig.lib.json b/apps/desktop/tsconfig.lib.json index 51c6d3e9..8b7a6860 100644 --- a/apps/desktop/tsconfig.lib.json +++ b/apps/desktop/tsconfig.lib.json @@ -11,6 +11,9 @@ { "path": "../../packages/worker-js" }, + { + "path": "../../packages/ui/tsconfig.lib.json" + }, { "path": "../../packages/client/tsconfig.lib.json" }, diff --git a/packages/client/src/app/styles.css b/packages/client/src/app/styles.css index 18a3effd..dbf8da30 100644 --- a/packages/client/src/app/styles.css +++ b/packages/client/src/app/styles.css @@ -12,4 +12,10 @@ body, height: 100%; overflow: hidden; user-select: none; + background-color: var(--color-surface); + color: var(--color-fg); +} + +.dark .react-flow { + --xy-background-color: var(--color-surface); } diff --git a/packages/client/src/features/delta/index.tsx b/packages/client/src/features/delta/index.tsx index ce5eeebb..ab805b1f 100644 --- a/packages/client/src/features/delta/index.tsx +++ b/packages/client/src/features/delta/index.tsx @@ -187,7 +187,7 @@ export const DeltaResetButton = - Reset delta + Reset delta ); }; diff --git a/packages/client/src/features/expression/code-mirror/extensions.tsx b/packages/client/src/features/expression/code-mirror/extensions.tsx index 9817b105..b908fbf4 100644 --- a/packages/client/src/features/expression/code-mirror/extensions.tsx +++ b/packages/client/src/features/expression/code-mirror/extensions.tsx @@ -89,7 +89,7 @@ const CompletionInfo = ({ completion, context, path }: CompletionInfoProps) => { }} variant='ghost' > - + diff --git a/packages/client/src/features/expression/reference.tsx b/packages/client/src/features/expression/reference.tsx index 2f2f5de3..9fa14746 100644 --- a/packages/client/src/features/expression/reference.tsx +++ b/packages/client/src/features/expression/reference.tsx @@ -17,6 +17,7 @@ import { ReferenceService, ReferenceTreeItem, } from '@the-dev-tools/spec/buf/api/reference/v1/reference_pb'; +import { useTheme } from '@the-dev-tools/ui'; import { tw } from '@the-dev-tools/ui/tailwind-literal'; import { TreeItem } from '@the-dev-tools/ui/tree'; import { useConnectSuspenseQuery } from '~/shared/api'; @@ -134,27 +135,27 @@ export const ReferenceTreeItemView = ({ id, parentKeys, reference }: ReferenceTr textValue={keyText ?? kindIndexTag ?? ''} > {key.kind === ReferenceKeyKind.GROUP && ( - {key.group} + {key.group} )} {key.kind === ReferenceKeyKind.KEY && ( - {key.key} + {key.key} )} {tags.map((tag, index) => ( {tag} ))} - {quantity && {quantity}} + {quantity && {quantity}} {reference.kind === ReferenceKind.VALUE && ( <> - : + : {reference.value} )} @@ -163,7 +164,7 @@ export const ReferenceTreeItemView = ({ id, parentKeys, reference }: ReferenceTr }; const fieldStyles = tv({ - base: tw`min-w-0 rounded-md border border-slate-200 px-3 py-0.5 text-md text-slate-800`, + base: tw`min-w-0 rounded-md border border-border px-3 py-0.5 text-md text-foreground`, variants: { variant: { 'table-cell': tw`w-full rounded-none border-transparent px-5 py-0.5 -outline-offset-4`, @@ -210,6 +211,8 @@ export const ReferenceField = ({ const reactRender = useReactRender(); + const { resolvedTheme } = useTheme(); + return ( ); diff --git a/packages/client/src/features/file-system/index.tsx b/packages/client/src/features/file-system/index.tsx index 0c37915c..cff2ba67 100644 --- a/packages/client/src/features/file-system/index.tsx +++ b/packages/client/src/features/file-system/index.tsx @@ -39,6 +39,7 @@ import { import { FileCollectionSchema, FolderCollectionSchema } from '@the-dev-tools/spec/tanstack-db/v1/api/file_system'; import { FlowCollectionSchema } from '@the-dev-tools/spec/tanstack-db/v1/api/flow'; import { HttpCollectionSchema, HttpDeltaCollectionSchema } from '@the-dev-tools/spec/tanstack-db/v1/api/http'; +import { useTheme } from '@the-dev-tools/ui'; import { Button } from '@the-dev-tools/ui/button'; import { FlowsIcon, FolderOpenedIcon } from '@the-dev-tools/ui/icons'; import { Menu, MenuItem, useContextMenuState } from '@the-dev-tools/ui/menu'; @@ -386,11 +387,11 @@ const FolderFile = ({ id }: FileItemProps) => { {({ isExpanded }) => ( <> {name === 'Credentials' ? ( - + ) : isExpanded ? ( - + ) : ( - + )} @@ -410,7 +411,7 @@ const FolderFile = ({ id }: FileItemProps) => { {showControls && ( @@ -441,6 +442,7 @@ const HttpFile = ({ id }: FileItemProps) => { const matchRoute = useMatchRoute(); const router = useRouter(); const navigate = useNavigate(); + const { resolvedTheme } = useTheme(); const { workspaceId } = routes.dashboard.workspace.route.useLoaderData(); @@ -517,7 +519,7 @@ const HttpFile = ({ id }: FileItemProps) => { {showControls && ( @@ -573,18 +575,24 @@ const HttpFile = ({ id }: FileItemProps) => { <>
cURL export
- + )} , @@ -610,7 +618,7 @@ const HttpFile = ({ id }: FileItemProps) => { const props = { children: content, - className: toNavigate && matchRoute(route) !== false ? tw`bg-slate-200` : '', + className: toNavigate && matchRoute(route) !== false ? tw`bg-accent` : '', id, item: (_) => , items: files, @@ -624,6 +632,7 @@ const HttpFile = ({ id }: FileItemProps) => { const HttpDeltaFile = ({ id }: FileItemProps) => { const router = useRouter(); const matchRoute = useMatchRoute(); + const { resolvedTheme } = useTheme(); const { workspaceId } = routes.dashboard.workspace.route.useLoaderData(); @@ -704,7 +713,7 @@ const HttpDeltaFile = ({ id }: FileItemProps) => { {showControls && ( @@ -733,18 +742,24 @@ const HttpDeltaFile = ({ id }: FileItemProps) => { <>
cURL export
- + )} , @@ -767,7 +782,7 @@ const HttpDeltaFile = ({ id }: FileItemProps) => { const props = { children: content, - className: toNavigate && matchRoute(route) !== false ? tw`bg-slate-200` : '', + className: toNavigate && matchRoute(route) !== false ? tw`bg-accent` : '', id, onContextMenu, textValue: name ?? '', @@ -820,7 +835,7 @@ const FlowFile = ({ id }: FileItemProps) => { const content = ( <> - + {name} @@ -839,7 +854,7 @@ const FlowFile = ({ id }: FileItemProps) => { {showControls && ( @@ -870,7 +885,7 @@ const FlowFile = ({ id }: FileItemProps) => { const props = { children: content, - className: toNavigate && matchRoute(route) !== false ? tw`bg-slate-200` : '', + className: toNavigate && matchRoute(route) !== false ? tw`bg-accent` : '', id, onContextMenu, textValue: name, @@ -920,10 +935,10 @@ const CredentialFile = ({ id }: FileItemProps) => { <> {pipe( Match.value(kind), - Match.when(CredentialKind.OPEN_AI, () => ), - Match.when(CredentialKind.ANTHROPIC, () => ), - Match.when(CredentialKind.GEMINI, () => ), - Match.orElse(() => ), + Match.when(CredentialKind.OPEN_AI, () => ), + Match.when(CredentialKind.ANTHROPIC, () => ), + Match.when(CredentialKind.GEMINI, () => ), + Match.orElse(() => ), )} @@ -943,7 +958,7 @@ const CredentialFile = ({ id }: FileItemProps) => { {showControls && ( @@ -963,7 +978,7 @@ const CredentialFile = ({ id }: FileItemProps) => { const props = { children: content, - className: toNavigate && matchRoute(route) !== false ? tw`bg-slate-200` : '', + className: toNavigate && matchRoute(route) !== false ? tw`bg-accent` : '', id, onContextMenu, textValue: name, diff --git a/packages/client/src/features/form-table/index.tsx b/packages/client/src/features/form-table/index.tsx index 91e33243..0028ca53 100644 --- a/packages/client/src/features/form-table/index.tsx +++ b/packages/client/src/features/form-table/index.tsx @@ -64,7 +64,7 @@ export const useFormTableAddRow = ({ }} variant='ghost' > - + {createLabel} ), @@ -170,16 +170,16 @@ interface ColumnActionDeleteProps { export const ColumnActionDelete = ({ onDelete }: ColumnActionDeleteProps) => ( - - Delete + Delete ); export const ColumnActionDrag = () => ( ); diff --git a/packages/client/src/pages/credential/tab.tsx b/packages/client/src/pages/credential/tab.tsx index 1884a129..b95725f8 100644 --- a/packages/client/src/pages/credential/tab.tsx +++ b/packages/client/src/pages/credential/tab.tsx @@ -42,10 +42,10 @@ export const CredentialTab = ({ credentialId }: CredentialTabProps) => { <> {pipe( Match.value(credential?.kind), - Match.when(CredentialKind.OPEN_AI, () => ), - Match.when(CredentialKind.ANTHROPIC, () => ), - Match.when(CredentialKind.GEMINI, () => ), - Match.orElse(() => ), + Match.when(CredentialKind.OPEN_AI, () => ), + Match.when(CredentialKind.ANTHROPIC, () => ), + Match.when(CredentialKind.GEMINI, () => ), + Match.orElse(() => ), )} {credential?.name} diff --git a/packages/client/src/pages/dashboard/routes/index.tsx b/packages/client/src/pages/dashboard/routes/index.tsx index ab72e462..5a1a87a4 100644 --- a/packages/client/src/pages/dashboard/routes/index.tsx +++ b/packages/client/src/pages/dashboard/routes/index.tsx @@ -68,15 +68,15 @@ export const WorkspaceListPage = () => { return (
- + {pipe(DateTime.unsafeNow(), DateTime.formatLocal({ dateStyle: 'full' }))} -

Welcome to DevTools 👋

+

Welcome to DevTools 👋

-
+
- Your Workspaces + Your Workspaces @@ -226,16 +225,13 @@ const Item = ({ containerRef, id }: ItemProps) => { void deleteModal.onOpenChange( true, - + Delete workspace? -
- Are you sure you want to delete “{name}”? This action - cannot be undone. +
+ Are you sure you want to delete “{name}”? This action cannot + be undone.
diff --git a/packages/client/src/pages/flow/add-node.tsx b/packages/client/src/pages/flow/add-node.tsx index 6fdf6f09..cc3697c6 100644 --- a/packages/client/src/pages/flow/add-node.tsx +++ b/packages/client/src/pages/flow/add-node.tsx @@ -75,17 +75,17 @@ export const SidebarHeader = ({ previous, title }: SidebarHeaderProps) => { const { setSidebar } = use(FlowContext); return ( -
+
{previous && ( )} -
{title}
+
{title}
); @@ -100,14 +100,14 @@ interface SidebarItemProps { export const SidebarItem = ({ description, icon, onAction, title }: SidebarItemProps) => ( -
{icon}
+
{icon}
-
{title}
- {description &&
{description}
} +
{title}
+ {description &&
{description}
}
- +
); diff --git a/packages/client/src/pages/flow/edge.tsx b/packages/client/src/pages/flow/edge.tsx index 7e6b819b..b1286773 100644 --- a/packages/client/src/pages/flow/edge.tsx +++ b/packages/client/src/pages/flow/edge.tsx @@ -137,7 +137,7 @@ const DefaultEdge = ({ id, sourcePosition, sourceX, sourceY, targetPosition, tar style={{ transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)` }} >
@@ -153,11 +153,11 @@ const connectionLineStyles = tv({ base: tw`fill-none stroke-1 transition-colors`, variants: { state: { - [FlowItemState.CANCELED]: tw`stroke-slate-400`, - [FlowItemState.FAILURE]: tw`stroke-red-600`, - [FlowItemState.RUNNING]: tw`stroke-violet-600`, - [FlowItemState.SUCCESS]: tw`stroke-green-600`, - [FlowItemState.UNSPECIFIED]: tw`stroke-slate-800`, + [FlowItemState.CANCELED]: tw`stroke-muted-foreground`, + [FlowItemState.FAILURE]: tw`stroke-red-600 dark:stroke-red-500`, + [FlowItemState.RUNNING]: tw`stroke-violet-600 dark:stroke-violet-400`, + [FlowItemState.SUCCESS]: tw`stroke-green-600 dark:stroke-green-400`, + [FlowItemState.UNSPECIFIED]: tw`stroke-muted-foreground`, } satisfies Record, }, }); diff --git a/packages/client/src/pages/flow/edit.tsx b/packages/client/src/pages/flow/edit.tsx index 28a4a196..82bb8a38 100644 --- a/packages/client/src/pages/flow/edit.tsx +++ b/packages/client/src/pages/flow/edit.tsx @@ -27,6 +27,7 @@ import { NodeCollectionSchema, NodeHttpCollectionSchema, } from '@the-dev-tools/spec/tanstack-db/v1/api/flow'; +import { useTheme } from '@the-dev-tools/ui'; import { Button, ButtonAsRouteLink } from '@the-dev-tools/ui/button'; import { DataTable, useReactTable } from '@the-dev-tools/ui/data-table'; import { PlayCircleIcon } from '@the-dev-tools/ui/icons'; @@ -104,7 +105,7 @@ export const FlowEditPage = () => { {sidebar && ( - + {sidebar} )} @@ -127,6 +128,7 @@ export const Flow = ({ children }: PropsWithChildren) => { const { getNodes, screenToFlowPosition } = XF.useReactFlow(); const { flowId, isReadOnly = false, setSidebar } = use(FlowContext); + const { resolvedTheme } = useTheme(); const { duration } = useLiveQuery( @@ -236,7 +238,7 @@ export const Flow = ({ children }: PropsWithChildren) => { <> {statusBarEndSlot && createPortal( -
+
{duration &&
Time: {pipe(duration, Duration.millis, Duration.format)}
}
, @@ -247,7 +249,7 @@ export const Flow = ({ children }: PropsWithChildren) => { { viewport={viewport} > { }); return ( -
+
{isEditing ? ( ) : ( void edit()} > @@ -350,9 +352,9 @@ export const TopBar = ({ children }: TopBarProps) => { { } variant='ghost' > - Flows History + Flows History - @@ -396,10 +398,10 @@ export const TopBarWithControls = () => { onPress={() => void zoomOut({ duration: 100 })} variant='ghost' > - + -
+
{Math.floor(zoom * 100)}%
@@ -409,10 +411,10 @@ export const TopBarWithControls = () => { onPress={() => void zoomIn({ duration: 100 })} variant='ghost' > - + -
+
); }; @@ -435,11 +437,11 @@ const ActionBar = () => { return ( @@ -567,13 +569,13 @@ const FlowSettings = () => { return ( <> -
-
Flow variables
+
+
Flow variables
diff --git a/packages/client/src/pages/flow/handle.tsx b/packages/client/src/pages/flow/handle.tsx index cbfc9f6f..8575369f 100644 --- a/packages/client/src/pages/flow/handle.tsx +++ b/packages/client/src/pages/flow/handle.tsx @@ -94,21 +94,21 @@ export const Handle = ({ >
-
+
)} @@ -154,7 +154,8 @@ export const Handle = ({ >
{label} @@ -165,7 +166,7 @@ export const Handle = ({
( -
+
); diff --git a/packages/client/src/pages/flow/history.tsx b/packages/client/src/pages/flow/history.tsx index 1b64b5f5..cb9efaee 100644 --- a/packages/client/src/pages/flow/history.tsx +++ b/packages/client/src/pages/flow/history.tsx @@ -68,15 +68,15 @@ export const FlowHistoryPage = () => {
-
Flow History
-
History of your flow responses
+
Flow History
+
History of your flow responses
@@ -84,22 +84,22 @@ export const FlowHistoryPage = () => {
-
+
Current Version
-
-
-
+
+
+
-
+
{versions.length} previous responses
-
+
{[...state.collection].map((item) => ( @@ -128,9 +128,10 @@ const Tab = ({ item, state }: TabProps) => { className={twJoin( tabProps.className, tw` - flex cursor-pointer items-center gap-1.5 rounded-md px-3 py-1.5 text-md leading-5 font-semibold text-slate-800 + flex cursor-pointer items-center gap-1.5 rounded-md px-3 py-1.5 text-md leading-5 font-semibold + text-foreground `, - isSelected && tw`bg-slate-200`, + isSelected && tw`bg-accent`, )} ref={ref} > diff --git a/packages/client/src/pages/flow/node.tsx b/packages/client/src/pages/flow/node.tsx index e013c4e5..6b142e57 100644 --- a/packages/client/src/pages/flow/node.tsx +++ b/packages/client/src/pages/flow/node.tsx @@ -148,16 +148,17 @@ export const useNodesState = () => { const nodeBodyStyles = tv({ base: tw` - relative size-16 overflow-clip rounded-xl border-2 border-white bg-white outline outline-slate-800 transition-colors + relative size-16 overflow-clip rounded-xl border-2 border-background bg-background outline outline-input + transition-colors `, variants: { - selected: { true: tw`bg-slate-200` }, + selected: { true: tw`bg-muted` }, state: { - [FlowItemState.CANCELED]: tw`outline-slate-300`, - [FlowItemState.FAILURE]: tw`outline-red-600`, - [FlowItemState.RUNNING]: tw`outline-violet-600`, - [FlowItemState.SUCCESS]: tw`outline-green-600`, - [FlowItemState.UNSPECIFIED]: tw`outline-slate-800`, + [FlowItemState.CANCELED]: tw`outline-muted-foreground`, + [FlowItemState.FAILURE]: tw`outline-red-600 dark:outline-red-500`, + [FlowItemState.RUNNING]: tw`outline-violet-600 dark:outline-violet-400`, + [FlowItemState.SUCCESS]: tw`outline-green-600 dark:outline-green-400`, + [FlowItemState.UNSPECIFIED]: tw`outline-input`, } satisfies Record, }, }); @@ -185,7 +186,13 @@ export const NodeBody = ({ children, className, icon, nodeId, selected }: NodeBo return (
-
+
{icon}
@@ -221,11 +228,11 @@ export const NodeStateIndicator = ({ children, nodeId }: NodeStateIndicatorProps let indicator = pipe( Match.value(state), Match.when(FlowItemState.RUNNING, () => ( - + )), - Match.when(FlowItemState.SUCCESS, () => ), - Match.when(FlowItemState.CANCELED, () => ), - Match.when(FlowItemState.FAILURE, () => ), + Match.when(FlowItemState.SUCCESS, () => ), + Match.when(FlowItemState.CANCELED, () => ), + Match.when(FlowItemState.FAILURE, () => ), Match.orElse(() => children), ); @@ -233,7 +240,7 @@ export const NodeStateIndicator = ({ children, nodeId }: NodeStateIndicatorProps indicator = ( {indicator} - {info} + {info} ); @@ -247,10 +254,7 @@ interface NodeTitleProps { export const NodeTitle = ({ children, className }: NodeTitleProps) => (
{children}
@@ -283,7 +287,7 @@ export const NodeName = ({ className, nodeId }: NodeNameProps) => {
{ {isEditing && ( )} @@ -362,10 +366,10 @@ export const NodeSettingsContainer = ({ return (
-
+
-
{name}
-
{title}
+
{name}
+
{title}
@@ -377,7 +381,7 @@ export const NodeSettingsContainer = ({
@@ -468,19 +472,17 @@ export const NodeSettingsBody = ({ children, input, nodeId, output, settingsHead > -
+
Input
{!selectedExecutionId ? (
-
- No input data yet -
-
+
No input data yet
+
The executed result from previous nodes will appear here
@@ -497,8 +499,8 @@ export const NodeSettingsBody = ({ children, input, nodeId, output, settingsHead
Settings @@ -511,9 +513,9 @@ export const NodeSettingsBody = ({ children, input, nodeId, output, settingsHead -
+
Output
@@ -521,10 +523,10 @@ export const NodeSettingsBody = ({ children, input, nodeId, output, settingsHead {!selectedExecutionId ? (
-
+
No output data yet
-
+
The executed result from this node will appear here
diff --git a/packages/client/src/pages/flow/nodes/ai.tsx b/packages/client/src/pages/flow/nodes/ai.tsx index dbdfb90c..af7a5685 100644 --- a/packages/client/src/pages/flow/nodes/ai.tsx +++ b/packages/client/src/pages/flow/nodes/ai.tsx @@ -344,7 +344,7 @@ export const AiProviderSettings = ({ nodeId }: NodeSettingsProps) => { params={{ credentialIdCan: Ulid.construct(data.credentialId).toCanonical() }} to={router.routesById[routes.dashboard.workspace.credential.id].fullPath} > - + Open )} @@ -412,7 +412,7 @@ export const AiProviderSettings = ({ nodeId }: NodeSettingsProps) => { }} variant='ghost' > - + New {credentialKindTitle} credential diff --git a/packages/client/src/pages/flow/nodes/http.tsx b/packages/client/src/pages/flow/nodes/http.tsx index 0bf40da4..e051ea8d 100644 --- a/packages/client/src/pages/flow/nodes/http.tsx +++ b/packages/client/src/pages/flow/nodes/http.tsx @@ -51,7 +51,7 @@ export const HttpNode = ({ id, selected }: XF.NodeProps) => { return ( @@ -66,7 +66,7 @@ export const HttpNode = ({ id, selected }: XF.NodeProps) => {
-
{name}
+
{name}
); @@ -117,7 +117,7 @@ export const HttpSettings = ({ nodeId }: NodeSettingsProps) => { to: router.routesById[routes.dashboard.workspace.http.route.id].fullPath, })} > - + Open API } diff --git a/packages/client/src/pages/flow/nodes/javascript.tsx b/packages/client/src/pages/flow/nodes/javascript.tsx index cde30f89..0504da4a 100644 --- a/packages/client/src/pages/flow/nodes/javascript.tsx +++ b/packages/client/src/pages/flow/nodes/javascript.tsx @@ -7,6 +7,7 @@ import { use } from 'react'; import { FiTerminal } from 'react-icons/fi'; import { NodeJsSchema } from '@the-dev-tools/spec/buf/api/flow/v1/flow_pb'; import { NodeJsCollectionSchema } from '@the-dev-tools/spec/tanstack-db/v1/api/flow'; +import { useTheme } from '@the-dev-tools/ui'; import { tw } from '@the-dev-tools/ui/tailwind-literal'; import { useCodeMirrorLanguageExtensions } from '~/features/expression'; import { useApiCollection } from '~/shared/api'; @@ -51,6 +52,7 @@ export const JavaScriptSettings = ({ nodeId }: NodeSettingsProps) => { const { isReadOnly = false } = use(FlowContext); const extensions = useCodeMirrorLanguageExtensions('javascript'); + const { resolvedTheme } = useTheme(); return ( @@ -59,6 +61,7 @@ export const JavaScriptSettings = ({ nodeId }: NodeSettingsProps) => { height='100%' onChange={(_) => collection.utils.updatePaced({ code: _, nodeId })} readOnly={isReadOnly} + theme={resolvedTheme} value={code} /> diff --git a/packages/client/src/pages/flow/nodes/manual-start.tsx b/packages/client/src/pages/flow/nodes/manual-start.tsx index 670990f1..0f5d15dc 100644 --- a/packages/client/src/pages/flow/nodes/manual-start.tsx +++ b/packages/client/src/pages/flow/nodes/manual-start.tsx @@ -15,7 +15,7 @@ export const ManualStartNode = ({ id, selected }: XF.NodeProps) => { handles={ <>
- +
diff --git a/packages/client/src/pages/flow/tab.tsx b/packages/client/src/pages/flow/tab.tsx index 7b16a949..492866b1 100644 --- a/packages/client/src/pages/flow/tab.tsx +++ b/packages/client/src/pages/flow/tab.tsx @@ -37,7 +37,7 @@ export const FlowTab = ({ flowId }: FlowTabProps) => { return ( <> - + {flow?.name} ); diff --git a/packages/client/src/pages/http/history.tsx b/packages/client/src/pages/http/history.tsx index 336a9503..b30c3dd7 100644 --- a/packages/client/src/pages/http/history.tsx +++ b/packages/client/src/pages/http/history.tsx @@ -37,10 +37,10 @@ export const HistoryModal = ({ deltaHttpId, httpId }: HistoryModalProps) => { -
+
-
Response History
-
History of your API response
+
Response History
+
History of your API response
@@ -48,7 +48,7 @@ export const HistoryModal = ({ deltaHttpId, httpId }: HistoryModalProps) => {
-
+
@@ -56,16 +56,16 @@ export const HistoryModal = ({ deltaHttpId, httpId }: HistoryModalProps) => {
-
-
-
+
+
+
-
+
{versions.length} previous responses
-
+
{(_) => ( @@ -74,9 +74,9 @@ export const HistoryModal = ({ deltaHttpId, httpId }: HistoryModalProps) => { twJoin( tw` flex cursor-pointer items-center gap-1.5 rounded-md px-3 py-1.5 text-md leading-5 - font-semibold text-slate-800 + font-semibold text-foreground `, - isSelected && tw`bg-slate-200`, + isSelected && tw`bg-accent`, ) } id={collection.utils.getKey(_)} diff --git a/packages/client/src/pages/http/request/body/raw.tsx b/packages/client/src/pages/http/request/body/raw.tsx index 860e6499..26cdb80f 100644 --- a/packages/client/src/pages/http/request/body/raw.tsx +++ b/packages/client/src/pages/http/request/body/raw.tsx @@ -6,6 +6,7 @@ import { HttpBodyRawCollectionSchema, HttpBodyRawDeltaCollectionSchema, } from '@the-dev-tools/spec/tanstack-db/v1/api/http'; +import { useTheme } from '@the-dev-tools/ui'; import { Button } from '@the-dev-tools/ui/button'; import { Select, SelectItem } from '@the-dev-tools/ui/select'; import { tw } from '@the-dev-tools/ui/tailwind-literal'; @@ -52,6 +53,8 @@ export const RawForm = ({ deltaHttpId, httpId, isReadOnly = false }: RawFormProp const client = createClient(ReferenceService, transport); const reactRender = useReactRender(); + const { resolvedTheme } = useTheme(); + // TODO: use pre-composed extensions instead of duplicating code here // Combine language extensions with reference extensions const combinedExtensions = [...languageExtensions, ...baseCodeMirrorExtensions({ client, context, reactRender })]; @@ -94,6 +97,7 @@ export const RawForm = ({ deltaHttpId, httpId, isReadOnly = false }: RawFormProp height='100%' onChange={(_) => void setValue(_)} readOnly={isReadOnly} + theme={resolvedTheme} value={value ?? ''} /> diff --git a/packages/client/src/pages/http/request/panel.tsx b/packages/client/src/pages/http/request/panel.tsx index bb7972cf..eaec3a25 100644 --- a/packages/client/src/pages/http/request/panel.tsx +++ b/packages/client/src/pages/http/request/panel.tsx @@ -107,15 +107,15 @@ export const HttpRequestPanel = ({ return ( - + twMerge( tw` -mb-px cursor-pointer border-b-2 border-transparent py-1.5 text-md leading-5 font-medium tracking-tight - text-slate-500 transition-colors + text-muted-foreground transition-colors `, - isSelected && tw`border-b-violet-700 text-slate-800`, + isSelected && tw`border-b-primary text-foreground`, ) } id='params' @@ -129,9 +129,9 @@ export const HttpRequestPanel = ({ twMerge( tw` -mb-px cursor-pointer border-b-2 border-transparent py-1.5 text-md leading-5 font-medium tracking-tight - text-slate-500 transition-colors + text-muted-foreground transition-colors `, - isSelected && tw`border-b-violet-700 text-slate-800`, + isSelected && tw`border-b-primary text-foreground`, ) } id='headers' @@ -145,9 +145,9 @@ export const HttpRequestPanel = ({ twMerge( tw` -mb-px cursor-pointer border-b-2 border-transparent py-1.5 text-md leading-5 font-medium tracking-tight - text-slate-500 transition-colors + text-muted-foreground transition-colors `, - isSelected && tw`border-b-violet-700 text-slate-800`, + isSelected && tw`border-b-primary text-foreground`, ) } id='body' @@ -166,9 +166,9 @@ export const HttpRequestPanel = ({ twMerge( tw` -mb-px cursor-pointer border-b-2 border-transparent py-1.5 text-md leading-5 font-medium tracking-tight - text-slate-500 transition-colors + text-muted-foreground transition-colors `, - isSelected && tw`border-b-violet-700 text-slate-800`, + isSelected && tw`border-b-primary text-foreground`, ) } id='assertions' diff --git a/packages/client/src/pages/http/request/top-bar.tsx b/packages/client/src/pages/http/request/top-bar.tsx index 0c3679f9..51f3e1d0 100644 --- a/packages/client/src/pages/http/request/top-bar.tsx +++ b/packages/client/src/pages/http/request/top-bar.tsx @@ -55,10 +55,10 @@ export const HttpTopBar = ({ deltaHttpId, httpId }: HttpTopBarProps) => { return ( <> -
+
{/* {example.breadcrumbs.map((_, index) => { @@ -76,12 +76,12 @@ export const HttpTopBar = ({ deltaHttpId, httpId }: HttpTopBarProps) => { {isEditing ? ( ) : ( void edit()} > @@ -93,8 +93,8 @@ export const HttpTopBar = ({ deltaHttpId, httpId }: HttpTopBarProps) => {
- @@ -102,7 +102,7 @@ export const HttpTopBar = ({ deltaHttpId, httpId }: HttpTopBarProps) => { diff --git a/packages/client/src/pages/http/request/url.tsx b/packages/client/src/pages/http/request/url.tsx index 4b0ee762..ed09d0ed 100644 --- a/packages/client/src/pages/http/request/url.tsx +++ b/packages/client/src/pages/http/request/url.tsx @@ -142,7 +142,7 @@ export const HttpUrl = ({ deltaHttpId, httpId, isReadOnly = false }: HttpUrlProp }; return ( -
+
{
{item.isGlobal ? : item.name[0]}
- + {item.isGlobal ? 'Global Environment' : item.name}
@@ -122,9 +124,9 @@ export const EnvironmentsWidget = () => { - + Manage Variables & Environments @@ -176,10 +178,10 @@ const EnvironmentModal = () => { {({ close }) => (
-
+
-
Variable Settings
-
Manage variables & environment
+
Variable Settings
+
Manage variables & environment
{globalKey && ( @@ -187,7 +189,7 @@ const EnvironmentModal = () => { className={({ isSelected }) => twJoin( tw`-mx-2 flex cursor-pointer items-center gap-1.5 rounded-md px-3 py-1.5 text-sm`, - isSelected && tw`bg-slate-200`, + isSelected && tw`bg-accent`, ) } isSelected={selectedKey === globalKey} @@ -195,17 +197,17 @@ const EnvironmentModal = () => { if (isSelected && globalKey) setSelectedKey(globalKey); }} > - + Global Variables )}
- Environments + Environments - + Add New Environment @@ -246,7 +248,7 @@ const EnvironmentModal = () => { className={({ isSelected }) => twJoin( tw`-mx-2 flex cursor-pointer items-center gap-1.5 rounded-md px-3 py-1.5 text-sm`, - isSelected && tw`bg-slate-200`, + isSelected && tw`bg-accent`, ) } id={environmentCollection.utils.getKey(_)} @@ -254,7 +256,8 @@ const EnvironmentModal = () => { >
{_.name[0]} @@ -268,7 +271,7 @@ const EnvironmentModal = () => {
{selectedKey && }
-
+
@@ -317,11 +320,11 @@ const EnvironmentPanel = ({ id }: EnvironmentPanelProps) => {
{isGlobal ? ( - + ) : (
{name[0]} @@ -331,12 +334,12 @@ const EnvironmentPanel = ({ id }: EnvironmentPanelProps) => { {isEditing ? ( ) : ( void edit()} @@ -350,7 +353,7 @@ const EnvironmentPanel = ({ id }: EnvironmentPanelProps) => { {!isGlobal && ( diff --git a/packages/client/src/widgets/export/index.tsx b/packages/client/src/widgets/export/index.tsx index a1b49b65..82810aec 100644 --- a/packages/client/src/widgets/export/index.tsx +++ b/packages/client/src/widgets/export/index.tsx @@ -33,23 +33,23 @@ export const ExportDialog = () => { - Export Files + Export Files
-
Export Files
+
Export Files
-
+
Please select the files that you would like to export.
@@ -65,7 +65,7 @@ export const ExportDialog = () => {
-
+
- + Import Collections and Flows @@ -74,19 +74,19 @@ const InnerDialog = ({ action, children }: InnerDialogProps) => ( <>
- + Import Collections and Flows
{children}
-
+
{action} @@ -146,8 +146,8 @@ const InitialDialog = ({ setModal, successAction }: InitialDialogProps) => { >
@@ -222,7 +222,7 @@ const DomainDialog = ({ domains, input, successAction }: DomainDialogProps) => { } > -
+
Please deselect the domain names to be excluded in the flow. There might be requests that you may not want to import.
diff --git a/packages/client/src/widgets/tabs/index.tsx b/packages/client/src/widgets/tabs/index.tsx index 95cd3725..29c06f9b 100644 --- a/packages/client/src/widgets/tabs/index.tsx +++ b/packages/client/src/widgets/tabs/index.tsx @@ -121,15 +121,15 @@ const TabItem = ({ id }: TabItemProps) => { aria-label='Tab' className={tw` relative -ml-px flex h-11 max-w-60 cursor-pointer items-center justify-between gap-3 border p-2.5 text-xs - leading-4 font-medium tracking-tight text-slate-800 + leading-4 font-medium tracking-tight text-foreground - not-route-active:border-b not-route-active:border-transparent not-route-active:border-b-gray-200 + not-route-active:border-b not-route-active:border-transparent not-route-active:border-b-border not-route-active:opacity-60 - before:absolute before:-left-px before:h-6 before:w-px before:bg-gray-200 + before:absolute before:-left-px before:h-6 before:w-px before:bg-border - route-active:rounded-t-md route-active:border route-active:border-gray-200 route-active:border-b-transparent - route-active:bg-white + route-active:rounded-t-md route-active:border route-active:border-border route-active:border-b-transparent + route-active:bg-background `} id={id} onAuxClick={(event) => { @@ -147,7 +147,7 @@ const TabItem = ({ id }: TabItemProps) => { }} variant='ghost' > - + ); @@ -244,7 +244,7 @@ export const RouteTabList = () => { className={tw` relative flex h-11 w-full overflow-x-auto overflow-y-hidden - before:absolute before:bottom-0 before:w-full before:border-b before:border-gray-200 + before:absolute before:bottom-0 before:w-full before:border-b before:border-border `} dragAndDropHooks={dragAndDropHooks} items={tabs} diff --git a/packages/ui/.storybook/preview.tsx b/packages/ui/.storybook/preview.tsx index eb72db9b..629b053f 100644 --- a/packages/ui/.storybook/preview.tsx +++ b/packages/ui/.storybook/preview.tsx @@ -1,13 +1,20 @@ import { Preview } from '@storybook/react-vite'; import { createRootRoute, createRouter, RouterProvider } from '@tanstack/react-router'; -import { StrictMode } from 'react'; +import { StrictMode, useEffect } from 'react'; import { UiProvider } from '../src/provider'; import '../src/styles.css'; const preview: Preview = { decorators: [ - (Story) => { + (Story, context) => { + const theme = context.globals['theme'] as string; + + useEffect(() => { + document.documentElement.classList.toggle('dark', theme === 'dark'); + document.body.style.backgroundColor = theme === 'dark' ? '#0f172a' : '#ffffff'; + }, [theme]); + const rootRoute = createRootRoute({ component: Story }); const router = createRouter({ routeTree: rootRoute }); @@ -17,6 +24,22 @@ const preview: Preview = { return _; }, ], + globalTypes: { + theme: { + description: 'Toggle dark mode', + toolbar: { + dynamicTitle: true, + icon: 'mirror', + items: [ + { title: 'Light', value: 'light' }, + { title: 'Dark', value: 'dark' }, + ], + }, + }, + }, + initialGlobals: { + theme: 'light', + }, parameters: { layout: 'centered', }, diff --git a/packages/ui/src/add-button.tsx b/packages/ui/src/add-button.tsx index f4a67150..67275907 100644 --- a/packages/ui/src/add-button.tsx +++ b/packages/ui/src/add-button.tsx @@ -11,11 +11,11 @@ export const addButtonStyles = tv({ variants: { variant: { dark: tw` - border-slate-300 text-slate-500 + border-input text-muted-foreground - hover:border-slate-500 hover:text-slate-600 + hover:border-muted-foreground hover:text-foreground - pressed:border-slate-800 pressed:text-slate-900 + pressed:border-foreground pressed:text-foreground `, light: tw`border-white/20 text-white hover:border-white/40 pressed:border-white`, }, @@ -41,7 +41,7 @@ export const AddButton = ({ tooltipText, ...props }: AddButtonProps) => { button = ( {button} - {tooltipText} + {tooltipText} ); diff --git a/packages/ui/src/avatar.tsx b/packages/ui/src/avatar.tsx index ffe784d5..39be8f89 100644 --- a/packages/ui/src/avatar.tsx +++ b/packages/ui/src/avatar.tsx @@ -29,13 +29,21 @@ export const avatarStyles = tv({ sm: tw`size-5 text-[0.625rem]`, }, variant: { - amber: tw`border-amber-500 bg-amber-100 text-amber-600`, - blue: tw`border-blue-400 bg-blue-100 text-blue-600`, - lime: tw`border-lime-500 bg-lime-200 text-lime-600`, - neutral: tw`border-slate-200 bg-white text-slate-800`, - pink: tw`border-pink-400 bg-pink-100 text-pink-600`, - teal: tw`border-teal-400 bg-teal-100 text-teal-600`, - violet: tw`border-violet-400 bg-violet-200 text-violet-600`, + amber: tw` + border-amber-500 bg-amber-100 text-amber-600 + + dark:border-amber-700 dark:bg-amber-900/40 dark:text-amber-400 + `, + blue: tw`border-blue-400 bg-blue-100 text-blue-600 dark:border-blue-700 dark:bg-blue-900/40 dark:text-blue-400`, + lime: tw`border-lime-500 bg-lime-200 text-lime-600 dark:border-lime-700 dark:bg-lime-900/40 dark:text-lime-400`, + neutral: tw`border-border bg-background text-foreground`, + pink: tw`border-pink-400 bg-pink-100 text-pink-600 dark:border-pink-700 dark:bg-pink-900/40 dark:text-pink-400`, + teal: tw`border-teal-400 bg-teal-100 text-teal-600 dark:border-teal-700 dark:bg-teal-900/40 dark:text-teal-400`, + violet: tw` + border-violet-400 bg-violet-200 text-violet-600 + + dark:border-violet-700 dark:bg-violet-900/40 dark:text-violet-400 + `, }, }, defaultVariants: { diff --git a/packages/ui/src/button.tsx b/packages/ui/src/button.tsx index ce540cd0..4af986b6 100644 --- a/packages/ui/src/button.tsx +++ b/packages/ui/src/button.tsx @@ -19,32 +19,32 @@ export const buttonStyles = tv({ variants: { variant: { primary: tw` - border-violet-700 bg-violet-600 text-white + border-primary bg-primary text-primary-foreground - hover:border-violet-800 hover:bg-violet-700 + hover:border-primary/80 hover:bg-primary/90 - disabled:border-violet-400 disabled:bg-violet-400 + disabled:border-primary/50 disabled:bg-primary/50 - pressed:border-violet-900 pressed:bg-violet-800 + pressed:border-primary/70 pressed:bg-primary/80 `, secondary: tw` - border-slate-200 bg-white text-slate-800 + border-border bg-background text-foreground - hover:border-slate-200 hover:bg-slate-100 + hover:border-border hover:bg-secondary - pressed:border-slate-300 pressed:bg-white + pressed:border-input pressed:bg-background `, danger: tw` - border-red-700 bg-red-600 text-white + border-destructive bg-destructive text-primary-foreground - hover:border-red-800 hover:bg-red-700 + hover:border-destructive/80 hover:bg-destructive/90 - pressed:border-red-900 pressed:bg-red-800 + pressed:border-destructive/80 pressed:bg-destructive/80 `, - ghost: tw`text-slate-800 hover:bg-slate-100 pressed:bg-slate-200`, + ghost: tw`text-foreground hover:bg-secondary pressed:bg-accent`, 'ghost dark': tw`text-white hover:bg-slate-600 pressed:bg-slate-700`, }, diff --git a/packages/ui/src/checkbox.tsx b/packages/ui/src/checkbox.tsx index 1bb44cd9..1ba64c77 100644 --- a/packages/ui/src/checkbox.tsx +++ b/packages/ui/src/checkbox.tsx @@ -12,10 +12,10 @@ const checkboxStyles = tv({ box: [ focusVisibleRingStyles(), tw` - flex size-4 flex-none cursor-pointer items-center justify-center rounded-sm border border-slate-200 bg-white - p-0.5 text-white + flex size-4 flex-none cursor-pointer items-center justify-center rounded-sm border border-border bg-background + p-0.5 text-primary-foreground - group-selected/checkbox:border-violet-600 group-selected/checkbox:bg-violet-600 + group-selected/checkbox:border-primary group-selected/checkbox:bg-primary `, ], }, diff --git a/packages/ui/src/data-table.tsx b/packages/ui/src/data-table.tsx index 2265376d..9cc7a1d8 100644 --- a/packages/ui/src/data-table.tsx +++ b/packages/ui/src/data-table.tsx @@ -17,15 +17,15 @@ declare module '@tanstack/react-table' { export const tableStyles = tv({ slots: { - base: tw`grid w-full border-inherit text-md leading-5 text-slate-800`, + base: tw`grid w-full border-inherit text-md leading-5 text-foreground`, body: tw`col-span-full grid grid-cols-subgrid divide-y border-inherit`, cell: tw`flex h-full min-w-0 items-center border-inherit align-middle break-all select-text`, - container: tw`block overflow-auto rounded-lg border border-slate-200`, + container: tw`block overflow-auto rounded-lg border border-border`, footer: tw` col-span-full block min-w-0 items-center divide-x border-t border-inherit align-middle break-all select-text `, header: tw` - col-span-full grid grid-cols-subgrid divide-x border-b border-inherit bg-slate-50 font-medium tracking-tight + col-span-full grid grid-cols-subgrid divide-x border-b border-inherit bg-muted font-medium tracking-tight *:contents `, diff --git a/packages/ui/src/field.tsx b/packages/ui/src/field.tsx index 4c31e727..3db4336f 100644 --- a/packages/ui/src/field.tsx +++ b/packages/ui/src/field.tsx @@ -10,7 +10,7 @@ export interface FieldLabelProps extends RAC.LabelProps {} export const FieldLabel = ({ className, ...props }: FieldLabelProps) => ( ); @@ -19,5 +19,5 @@ export const FieldLabel = ({ className, ...props }: FieldLabelProps) => ( export interface FieldErrorProps extends RAC.FieldErrorProps {} export const FieldError = ({ className, ...props }: FieldErrorProps) => ( - + ); diff --git a/packages/ui/src/file-drop-zone.tsx b/packages/ui/src/file-drop-zone.tsx index f7be2c97..fbb8649a 100644 --- a/packages/ui/src/file-drop-zone.tsx +++ b/packages/ui/src/file-drop-zone.tsx @@ -48,10 +48,10 @@ export const FileDropZone = ({ className, focusVisibleRingStyles(), tw` - flex min-h-40 flex-col items-center justify-center gap-2 rounded-md border border-dashed border-slate-300 - bg-white p-4 + flex min-h-40 flex-col items-center justify-center gap-2 rounded-md border border-dashed border-input + bg-background p-4 - drop-target:bg-violet-100 drop-target:outline-4 drop-target:outline-violet-200 + drop-target:bg-primary/10 drop-target:outline-4 drop-target:outline-ring `, )} isDisabled={isDisabled || (hasFiles && !allowsMultiple)} @@ -74,9 +74,9 @@ export const FileDropZone = ({
) : ( <> - + - + Drag and drop your files or @@ -96,19 +96,19 @@ interface FilePreviewProps { const FilePreview = ({ file, onRemove }: FilePreviewProps) => (
-
- +
+
-
+
{file.name}
-
{formatSize(file.size)}
+
{formatSize(file.size)}
{onRemove && ( )}
diff --git a/packages/ui/src/focus-ring.tsx b/packages/ui/src/focus-ring.tsx index 90231a0f..f35bdb5b 100644 --- a/packages/ui/src/focus-ring.tsx +++ b/packages/ui/src/focus-ring.tsx @@ -3,19 +3,19 @@ import { tw } from './tailwind-literal'; const baseStyles = tv({ base: tw`relative outline-0 outline-transparent transition-colors` }); -export const focusRingStyles = tv({ extend: baseStyles, base: tw`focus:outline-4 focus:outline-violet-200` }); +export const focusRingStyles = tv({ extend: baseStyles, base: tw`focus:outline-4 focus:outline-ring` }); export const focusVisibleRingStyles = tv({ extend: baseStyles, - base: tw`focus-visible:outline-4 focus-visible:outline-violet-200`, + base: tw`focus-visible:outline-4 focus-visible:outline-ring`, }); export const focusWithinRingStyles = tv({ extend: baseStyles, - base: tw`focus-within:outline-4 focus-within:outline-violet-200`, + base: tw`focus-within:outline-4 focus-within:outline-ring`, }); export const focusVisibleWithinRingStyles = tv({ extend: focusVisibleRingStyles, - base: tw`has-focus-visible:outline-4 has-focus-visible:outline-violet-200`, + base: tw`has-focus-visible:outline-4 has-focus-visible:outline-ring`, }); diff --git a/packages/ui/src/index.tsx b/packages/ui/src/index.tsx index a435460b..8260864f 100644 --- a/packages/ui/src/index.tsx +++ b/packages/ui/src/index.tsx @@ -1 +1,2 @@ export * as Primitive from './primitives'; +export { type Theme, ThemeProvider, useTheme } from './theme'; diff --git a/packages/ui/src/json-tree.tsx b/packages/ui/src/json-tree.tsx index 91f7308f..1c187df4 100644 --- a/packages/ui/src/json-tree.tsx +++ b/packages/ui/src/json-tree.tsx @@ -79,19 +79,19 @@ export const JsonTreeItem = ({ id = 'root', jsonIndex, jsonKey, jsonValue }: Jso textValue={valueText ?? jsonKey ?? indexText ?? ''} {...(itemProps as TreeItemProps)} > - {jsonKey && {jsonKey}} + {jsonKey && {jsonKey}} {indexText && ( - + {indexText} )} - {quantity && {quantity}} + {quantity && {quantity}} {valueText && ( <> - : + : {valueText} )} diff --git a/packages/ui/src/list-box.tsx b/packages/ui/src/list-box.tsx index 86662800..47712244 100644 --- a/packages/ui/src/list-box.tsx +++ b/packages/ui/src/list-box.tsx @@ -12,7 +12,7 @@ import { createLinkGeneric } from './utils/link'; export const listBoxStyles = tv({ base: tw` - pointer-events-auto overflow-auto rounded-lg border border-slate-200 bg-white py-0.5 shadow-md outline-hidden + pointer-events-auto overflow-auto rounded-lg border border-border bg-background py-0.5 shadow-md outline-hidden `, }); @@ -33,9 +33,9 @@ export const listBoxItemStyles = tv({ `, variants: { variant: { - accent: tw`text-violet-600 hover:bg-violet-100 pressed:bg-violet-200 selected:bg-violet-200`, - danger: tw`text-rose-700 hover:bg-rose-100 pressed:bg-rose-200 selected:bg-rose-200`, - default: tw`text-slate-800 hover:bg-slate-100 pressed:bg-slate-200 selected:bg-slate-200`, + accent: tw`text-primary hover:bg-primary/10 pressed:bg-primary/20 selected:bg-primary/20`, + danger: tw`text-destructive hover:bg-destructive/10 pressed:bg-destructive/20 selected:bg-destructive/20`, + default: tw`text-foreground hover:bg-secondary pressed:bg-accent selected:bg-accent`, }, }, defaultVariants: { @@ -63,7 +63,7 @@ export const ListBoxHeader = ({ className, ...props }: ListBoxHeaderProps) => ( diff --git a/packages/ui/src/menu.stories.tsx b/packages/ui/src/menu.stories.tsx index dc3c6a5e..12daa206 100644 --- a/packages/ui/src/menu.stories.tsx +++ b/packages/ui/src/menu.stories.tsx @@ -30,7 +30,7 @@ export const Basic: Story = { return ( @@ -68,7 +68,7 @@ export const WithAvatars: Story = { return ( diff --git a/packages/ui/src/menu.tsx b/packages/ui/src/menu.tsx index 7ae16a65..afa53a6c 100644 --- a/packages/ui/src/menu.tsx +++ b/packages/ui/src/menu.tsx @@ -81,7 +81,7 @@ export const MenuItem = ({ children, ...props }: MenuItemProps <> {children}
- {hasSubmenu && } + {hasSubmenu && } ))} diff --git a/packages/ui/src/modal.stories.tsx b/packages/ui/src/modal.stories.tsx index 5a6e9150..9b7662d6 100644 --- a/packages/ui/src/modal.stories.tsx +++ b/packages/ui/src/modal.stories.tsx @@ -25,8 +25,8 @@ export const Default: Story = { -

Delete workspace?

- +

Delete workspace?

+ This action will remove the workspace permanently diff --git a/packages/ui/src/modal.tsx b/packages/ui/src/modal.tsx index 3ad27501..8486d916 100644 --- a/packages/ui/src/modal.tsx +++ b/packages/ui/src/modal.tsx @@ -6,10 +6,10 @@ import { composeStyleRenderProps } from './utils'; export const modalStyles = tv({ slots: { - base: tw`size-full overflow-auto rounded-lg bg-white`, + base: tw`size-full overflow-auto rounded-lg bg-background`, overlay: tw` - fixed inset-0 z-20 flex h-(--visual-viewport-height) items-center justify-center bg-slate-800/50 + fixed inset-0 z-20 flex h-(--visual-viewport-height) items-center justify-center bg-overlay entering:animate-in entering:duration-200 entering:ease-out entering:fade-in diff --git a/packages/ui/src/number-field.tsx b/packages/ui/src/number-field.tsx index f6d5d6fd..84b4cfc4 100644 --- a/packages/ui/src/number-field.tsx +++ b/packages/ui/src/number-field.tsx @@ -21,16 +21,16 @@ export const NumberField = ({ className = '', groupClassName, label, ...props }: className={composeTailwindRenderProps( groupClassName, focusVisibleRingStyles(), - tw`flex min-w-0 rounded-md border border-slate-200 text-md leading-5 text-slate-800`, + tw`flex min-w-0 rounded-md border border-border text-md leading-5 text-foreground`, )} > - + - + diff --git a/packages/ui/src/progress-bar.tsx b/packages/ui/src/progress-bar.tsx index dad21a67..40fc1658 100644 --- a/packages/ui/src/progress-bar.tsx +++ b/packages/ui/src/progress-bar.tsx @@ -16,16 +16,16 @@ export const ProgressBar = ({ className, label, ...props }: ProgressBarProps) => <>
{label} - {valueText} + {valueText}
-
+
)} diff --git a/packages/ui/src/provider.tsx b/packages/ui/src/provider.tsx index 9906b942..d83bde87 100644 --- a/packages/ui/src/provider.tsx +++ b/packages/ui/src/provider.tsx @@ -1,6 +1,7 @@ import { Option } from 'effect'; import { ReactNode } from 'react'; import * as RAC from 'react-aria-components'; +import { ThemeProvider } from './theme'; import { ToastQueue, ToastQueueContext } from './toast'; export interface UiProviderProps { @@ -11,5 +12,6 @@ export interface UiProviderProps { export const UiProvider = ({ children, toastQueue }: UiProviderProps) => { let _ = undefined}>{children}; _ = {_}; + _ = {_}; return _; }; diff --git a/packages/ui/src/radio-group.tsx b/packages/ui/src/radio-group.tsx index 711099f4..c65cadc9 100644 --- a/packages/ui/src/radio-group.tsx +++ b/packages/ui/src/radio-group.tsx @@ -49,7 +49,7 @@ export const RadioGroup = ({ children, className, error, label, ...props }: Radi export const radioStyles = tv({ slots: { base: tw` - group/radio flex cursor-pointer items-center gap-1.5 text-md leading-5 font-medium tracking-tight text-slate-800 + group/radio flex cursor-pointer items-center gap-1.5 text-md leading-5 font-medium tracking-tight text-foreground disabled:text-gray-300 `, @@ -57,21 +57,21 @@ export const radioStyles = tv({ indicator: [ focusVisibleRingStyles(), tw` - size-4 rounded-full border border-slate-200 bg-white + size-4 rounded-full border border-border bg-background - group-invalid/radio:border-red-700 group-invalid/radio:bg-red-700 + group-invalid/radio:border-destructive group-invalid/radio:bg-destructive - group-disabled/radio:border-slate-200 group-disabled/radio:bg-slate-200 + group-disabled/radio:border-border group-disabled/radio:bg-border - group-pressed/radio:not-selected:border-slate-400 + group-pressed/radio:not-selected:border-muted-foreground - group-selected/radio:border-violet-600 group-selected/radio:bg-violet-600 + group-selected/radio:border-primary group-selected/radio:bg-primary - group-invalid/radio:pressed:border-red-800 + group-invalid/radio:pressed:border-destructive/80 `, ], - dot: tw`size-full rounded-full border-2 border-white`, + dot: tw`size-full rounded-full border-2 border-background`, }, }); diff --git a/packages/ui/src/reorder.tsx b/packages/ui/src/reorder.tsx index ec01c07b..efa21ede 100644 --- a/packages/ui/src/reorder.tsx +++ b/packages/ui/src/reorder.tsx @@ -6,9 +6,9 @@ interface DropIndicator { } export const DropIndicatorHorizontal = ({ as: Component = 'div' }: DropIndicator) => ( - + ); export const DropIndicatorVertical = ({ as: Component = 'div' }: DropIndicator) => ( - + ); diff --git a/packages/ui/src/resizable-panel.tsx b/packages/ui/src/resizable-panel.tsx index 656a1128..6d22fc55 100644 --- a/packages/ui/src/resizable-panel.tsx +++ b/packages/ui/src/resizable-panel.tsx @@ -5,7 +5,7 @@ import { tw } from './tailwind-literal'; export const panelResizeHandleStyles = tv({ extend: focusVisibleRingStyles, - base: tw`bg-slate-200`, + base: tw`bg-border`, variants: { direction: { horizontal: tw`h-full w-px cursor-col-resize`, diff --git a/packages/ui/src/select.tsx b/packages/ui/src/select.tsx index 41635d46..6604179f 100644 --- a/packages/ui/src/select.tsx +++ b/packages/ui/src/select.tsx @@ -37,7 +37,7 @@ export const Select = ({ {label && {label}} {error && {error}} @@ -56,7 +56,7 @@ export const SelectItem = (props: ListBoxItemProps) => ( <> {children}
- + ))} diff --git a/packages/ui/src/separator.tsx b/packages/ui/src/separator.tsx index 1223289f..940dd287 100644 --- a/packages/ui/src/separator.tsx +++ b/packages/ui/src/separator.tsx @@ -3,7 +3,7 @@ import { tv, VariantProps } from 'tailwind-variants'; import { tw } from './tailwind-literal'; export const separatorStyles = tv({ - base: tw`bg-slate-200`, + base: tw`bg-border`, variants: { orientation: { horizontal: tw`h-px w-full`, diff --git a/packages/ui/src/spinner.tsx b/packages/ui/src/spinner.tsx index 99cc2d9d..177cb0ec 100644 --- a/packages/ui/src/spinner.tsx +++ b/packages/ui/src/spinner.tsx @@ -33,7 +33,10 @@ export const Spinner = (props: SpinnerProps) => ( -
+
diff --git a/packages/ui/src/styles.css b/packages/ui/src/styles.css index e8e60d46..1d684d04 100644 --- a/packages/ui/src/styles.css +++ b/packages/ui/src/styles.css @@ -14,6 +14,65 @@ --font-mono: 'DM Mono', ui-monospace, monospace; --text-md: 0.8125rem; + + /* === Core backgrounds === */ + --color-background: #ffffff; + --color-muted: #f8fafc; + --color-secondary: #f1f5f9; + --color-accent: #e2e8f0; + --color-surface-invert: #020617; + + /* === Foreground (text) === */ + --color-foreground: #1e293b; + --color-muted-foreground: #64748b; + --color-primary-foreground: #ffffff; + + /* === Borders === */ + --color-border: #e2e8f0; + --color-input: #cbd5e1; + + /* === Primary (violet) === */ + --color-primary: #7c3aed; + + /* === Destructive (red) === */ + --color-destructive: #dc2626; + --color-destructive-foreground: #ffffff; + + /* === Focus ring === */ + --color-ring: #ddd6fe; + + /* === Utility === */ + --color-overlay: oklch(0.279 0.041 260.031 / 0.5); + --color-popover: #1e293b; + --color-popover-foreground: #ffffff; + --color-success: #16a34a; +} + +:root.dark { + --color-background: #0f172a; + --color-muted: #1e293b; + --color-secondary: #334155; + --color-accent: #475569; + --color-surface-invert: #020617; + + --color-foreground: #f1f5f9; + --color-muted-foreground: #94a3b8; + --color-primary-foreground: #ffffff; + + --color-border: #334155; + --color-input: #475569; + + --color-primary: #8b5cf6; + + --color-destructive: #ef4444; + --color-destructive-foreground: #ffffff; + + --color-ring: rgba(139, 92, 246, 0.3); + + --color-overlay: rgba(0, 0, 0, 0.6); + --color-popover: #334155; + --color-popover-foreground: #f1f5f9; + --color-success: #4ade80; } @custom-variant route-active { diff --git a/packages/ui/src/tag-group.tsx b/packages/ui/src/tag-group.tsx index 4acd1ebe..01e285c1 100644 --- a/packages/ui/src/tag-group.tsx +++ b/packages/ui/src/tag-group.tsx @@ -15,9 +15,9 @@ export const Tag = ({ className, ...props }: TagProps) => ( focusVisibleRingStyles(), tw` cursor-pointer rounded-sm bg-transparent px-2 py-1.5 text-xs leading-none font-medium tracking-tight - text-slate-400 + text-muted-foreground - selected:bg-white selected:text-slate-800 selected:shadow-sm + selected:bg-background selected:text-foreground selected:shadow-sm `, )} /> @@ -33,6 +33,6 @@ export interface TagGroupProps export const TagGroup = ({ children, label, ...props }: TagGroupProps) => ( {label && {label}} - {children} + {children} ); diff --git a/packages/ui/src/text-field.tsx b/packages/ui/src/text-field.tsx index b50dd077..37426d06 100644 --- a/packages/ui/src/text-field.tsx +++ b/packages/ui/src/text-field.tsx @@ -73,10 +73,10 @@ export const TextField = ({ children, className, error, label, ...props }: TextF export const textInputFieldStyles = tv({ extend: focusVisibleRingStyles, - base: tw`rounded-md border border-slate-200 px-3 py-1.5 text-md leading-5 text-slate-800`, + base: tw`rounded-md border border-border px-3 py-1.5 text-md leading-5 text-foreground`, variants: { isTableCell: { - false: tw`disabled:bg-slate-100 disabled:opacity-50`, + false: tw`disabled:bg-secondary disabled:opacity-50`, true: tw`w-full min-w-0 rounded-none border-transparent px-5 py-1.5 -outline-offset-4`, }, }, diff --git a/packages/ui/src/theme.tsx b/packages/ui/src/theme.tsx new file mode 100644 index 00000000..c05ad8c0 --- /dev/null +++ b/packages/ui/src/theme.tsx @@ -0,0 +1,67 @@ +import { createContext, ReactNode, use, useCallback, useEffect, useMemo, useState } from 'react'; + +export type Theme = 'dark' | 'light' | 'system'; +export type ResolvedTheme = 'dark' | 'light'; + +interface ThemeContextValue { + resolvedTheme: ResolvedTheme; + setTheme: (theme: Theme) => void; + theme: Theme; +} + +const ThemeContext = createContext(undefined); + +const STORAGE_KEY = 'theme'; + +const getSystemTheme = (): ResolvedTheme => + typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; + +const getStoredTheme = (): Theme => { + if (typeof window === 'undefined') return 'system'; + const stored = localStorage.getItem(STORAGE_KEY); + if (stored === 'light' || stored === 'dark' || stored === 'system') return stored; + return 'system'; +}; + +export interface ThemeProviderProps { + children: ReactNode; +} + +export const ThemeProvider = ({ children }: ThemeProviderProps) => { + const [theme, setThemeState] = useState(getStoredTheme); + const [systemTheme, setSystemTheme] = useState(getSystemTheme); + + const resolvedTheme: ResolvedTheme = theme === 'system' ? systemTheme : theme; + + const setTheme = useCallback((newTheme: Theme) => { + setThemeState(newTheme); + if (newTheme === 'system') { + localStorage.removeItem(STORAGE_KEY); + } else { + localStorage.setItem(STORAGE_KEY, newTheme); + } + }, []); + + // Listen for system preference changes & apply dark class + useEffect(() => { + document.documentElement.classList.toggle('dark', resolvedTheme === 'dark'); + + const mql = window.matchMedia('(prefers-color-scheme: dark)'); + const handler = (e: MediaQueryListEvent) => void setSystemTheme(e.matches ? 'dark' : 'light'); + mql.addEventListener('change', handler); + return () => void mql.removeEventListener('change', handler); + }, [resolvedTheme]); + + const value = useMemo( + () => ({ resolvedTheme, setTheme, theme }), + [resolvedTheme, setTheme, theme], + ); + + return {children}; +}; + +export const useTheme = (): ThemeContextValue => { + const context = use(ThemeContext); + if (!context) throw new Error('useTheme must be used within a ThemeProvider'); + return context; +}; diff --git a/packages/ui/src/toast.tsx b/packages/ui/src/toast.tsx index f7d2e211..aa22f817 100644 --- a/packages/ui/src/toast.tsx +++ b/packages/ui/src/toast.tsx @@ -25,8 +25,8 @@ export const ToastRegion = () => { {({ toast }) => ( @@ -34,7 +34,7 @@ export const ToastRegion = () => { {toast.content.title}
diff --git a/packages/ui/src/tree.tsx b/packages/ui/src/tree.tsx index cec252d8..00a15fd6 100644 --- a/packages/ui/src/tree.tsx +++ b/packages/ui/src/tree.tsx @@ -63,17 +63,17 @@ export const TreeItem = ({ focusVisibleRingStyles(), tw` group/tree-item cursor-pointer rounded-md bg-transparent px-3 py-1.5 text-md leading-5 font-medium - tracking-tight text-slate-800 + tracking-tight text-foreground - hover:bg-slate-100 + hover:bg-secondary - active:bg-slate-200 + active:bg-accent - pressed:bg-slate-200 + pressed:bg-accent - selected:bg-slate-200 + selected:bg-accent - drop-target:bg-violet-200 + drop-target:bg-primary/20 `, )} ref={(node) => { @@ -103,7 +103,7 @@ export const TreeItem = ({ ({ {children} {allowsDragging && ( )}