@workerdeck/ui 0.6.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/README.md +74 -0
- package/build/index.d.mts +927 -0
- package/build/index.mjs +5236 -0
- package/build/index.mjs.map +1 -0
- package/package.json +73 -0
- package/src/components/agent/Composer.tsx +139 -0
- package/src/components/agent/Conversation.tsx +59 -0
- package/src/components/agent/FileCard.tsx +50 -0
- package/src/components/agent/Loader.tsx +21 -0
- package/src/components/agent/Message.tsx +44 -0
- package/src/components/agent/ModelSelect.tsx +87 -0
- package/src/components/agent/PermissionModeSelect.tsx +94 -0
- package/src/components/agent/PermissionPrompt.tsx +52 -0
- package/src/components/agent/QuestionPrompt.tsx +193 -0
- package/src/components/agent/Reasoning.tsx +58 -0
- package/src/components/agent/Response.tsx +31 -0
- package/src/components/agent/SessionList.tsx +93 -0
- package/src/components/agent/SessionPanel.tsx +149 -0
- package/src/components/agent/StatusBar.tsx +140 -0
- package/src/components/agent/ToolCallCard.tsx +94 -0
- package/src/components/agent/Transcript.tsx +114 -0
- package/src/components/agent/status.ts +16 -0
- package/src/components/prompt-area/animated-placeholder.tsx +42 -0
- package/src/components/prompt-area/clipboard-helpers.ts +206 -0
- package/src/components/prompt-area/cursor-helpers.ts +244 -0
- package/src/components/prompt-area/dom-helpers.ts +721 -0
- package/src/components/prompt-area/file-strip.tsx +250 -0
- package/src/components/prompt-area/html-to-markdown.ts +278 -0
- package/src/components/prompt-area/image-strip.tsx +49 -0
- package/src/components/prompt-area/index.ts +23 -0
- package/src/components/prompt-area/prompt-area-engine.ts +705 -0
- package/src/components/prompt-area/prompt-area-list-ops.ts +499 -0
- package/src/components/prompt-area/prompt-area.tsx +375 -0
- package/src/components/prompt-area/remove-button.tsx +37 -0
- package/src/components/prompt-area/segment-helpers.ts +62 -0
- package/src/components/prompt-area/trigger-popover.tsx +139 -0
- package/src/components/prompt-area/trigger-presets.ts +143 -0
- package/src/components/prompt-area/types.ts +360 -0
- package/src/components/prompt-area/use-markdown-mode.ts +113 -0
- package/src/components/prompt-area/use-prompt-area-events.ts +470 -0
- package/src/components/prompt-area/use-prompt-area-state.ts +131 -0
- package/src/components/prompt-area/use-prompt-area.ts +1507 -0
- package/src/components/prompt-area/use-trigger-search.ts +115 -0
- package/src/components/ui/AlertDialog.tsx +56 -0
- package/src/components/ui/Badge.tsx +42 -0
- package/src/components/ui/Button.tsx +47 -0
- package/src/components/ui/Card.tsx +29 -0
- package/src/components/ui/CodeBlock.tsx +31 -0
- package/src/components/ui/CopyButton.tsx +28 -0
- package/src/components/ui/Input.tsx +20 -0
- package/src/components/ui/ProgressRing.tsx +49 -0
- package/src/components/ui/Select.tsx +80 -0
- package/src/components/ui/Sonner.tsx +22 -0
- package/src/components/ui/Spinner.tsx +6 -0
- package/src/components/ui/Textarea.tsx +21 -0
- package/src/components/ui/Tooltip.tsx +34 -0
- package/src/index.ts +99 -0
- package/src/lib/format.ts +67 -0
- package/src/lib/utils.ts +33 -0
- package/src/styles/theme.css +413 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Primitives
|
|
2
|
+
export { Button, buttonVariants, type ButtonProps } from './components/ui/Button.tsx'
|
|
3
|
+
export { Badge, badgeVariants, type BadgeProps } from './components/ui/Badge.tsx'
|
|
4
|
+
export { Card, CardContent, CardHeader, CardTitle } from './components/ui/Card.tsx'
|
|
5
|
+
export { Input } from './components/ui/Input.tsx'
|
|
6
|
+
export { Textarea } from './components/ui/Textarea.tsx'
|
|
7
|
+
export {
|
|
8
|
+
Select,
|
|
9
|
+
SelectContent,
|
|
10
|
+
SelectItem,
|
|
11
|
+
SelectItemText,
|
|
12
|
+
SelectTrigger,
|
|
13
|
+
SelectValue,
|
|
14
|
+
} from './components/ui/Select.tsx'
|
|
15
|
+
export {
|
|
16
|
+
AlertDialog,
|
|
17
|
+
AlertDialogClose,
|
|
18
|
+
AlertDialogContent,
|
|
19
|
+
AlertDialogDescription,
|
|
20
|
+
AlertDialogTitle,
|
|
21
|
+
AlertDialogTrigger,
|
|
22
|
+
} from './components/ui/AlertDialog.tsx'
|
|
23
|
+
export { Tip, TooltipContent, TooltipProvider } from './components/ui/Tooltip.tsx'
|
|
24
|
+
export { Toaster, toast } from './components/ui/Sonner.tsx'
|
|
25
|
+
export { CopyButton, type CopyButtonProps } from './components/ui/CopyButton.tsx'
|
|
26
|
+
export { Spinner } from './components/ui/Spinner.tsx'
|
|
27
|
+
export { CodeBlock, type CodeBlockProps } from './components/ui/CodeBlock.tsx'
|
|
28
|
+
export { ProgressRing, type ProgressRingProps } from './components/ui/ProgressRing.tsx'
|
|
29
|
+
// Prompt input (vendored just-marketing/prompt-area, themed to these tokens)
|
|
30
|
+
export {
|
|
31
|
+
PromptArea,
|
|
32
|
+
usePromptAreaState,
|
|
33
|
+
commandTrigger,
|
|
34
|
+
mentionTrigger,
|
|
35
|
+
hashtagTrigger,
|
|
36
|
+
segmentsToPlainText,
|
|
37
|
+
plainTextToSegments,
|
|
38
|
+
isSegmentsEmpty,
|
|
39
|
+
getChipsByTrigger,
|
|
40
|
+
type PromptAreaHandle,
|
|
41
|
+
type PromptAreaProps,
|
|
42
|
+
type Segment,
|
|
43
|
+
type ChipSegment,
|
|
44
|
+
type TextSegment,
|
|
45
|
+
type TriggerConfig,
|
|
46
|
+
type TriggerSuggestion,
|
|
47
|
+
} from './components/prompt-area/index.ts'
|
|
48
|
+
|
|
49
|
+
// Agent-control components
|
|
50
|
+
export { SessionPanel, type SessionPanelProps } from './components/agent/SessionPanel.tsx'
|
|
51
|
+
export { Transcript, type TranscriptProps } from './components/agent/Transcript.tsx'
|
|
52
|
+
export {
|
|
53
|
+
Conversation,
|
|
54
|
+
ConversationContent,
|
|
55
|
+
ConversationScrollButton,
|
|
56
|
+
type ConversationProps,
|
|
57
|
+
} from './components/agent/Conversation.tsx'
|
|
58
|
+
export { Message, MessageContent, type MessageProps } from './components/agent/Message.tsx'
|
|
59
|
+
export { Response, type ResponseProps } from './components/agent/Response.tsx'
|
|
60
|
+
export { Reasoning, type ReasoningProps } from './components/agent/Reasoning.tsx'
|
|
61
|
+
export { Loader } from './components/agent/Loader.tsx'
|
|
62
|
+
export { ToolCallCard, type ToolCallCardProps, type ToolCallItem } from './components/agent/ToolCallCard.tsx'
|
|
63
|
+
export { FileCard, type FileCardProps, type FileDeliveredItem } from './components/agent/FileCard.tsx'
|
|
64
|
+
export { PermissionPrompt, type PermissionPromptProps } from './components/agent/PermissionPrompt.tsx'
|
|
65
|
+
export {
|
|
66
|
+
QuestionPrompt,
|
|
67
|
+
QUESTION_BEHAVIORS,
|
|
68
|
+
parseUserQuestions,
|
|
69
|
+
type QuestionPromptProps,
|
|
70
|
+
type QuestionBehaviorMeta,
|
|
71
|
+
} from './components/agent/QuestionPrompt.tsx'
|
|
72
|
+
export { Composer, type ComposerProps } from './components/agent/Composer.tsx'
|
|
73
|
+
export { ModelSelect, type ModelSelectProps } from './components/agent/ModelSelect.tsx'
|
|
74
|
+
export {
|
|
75
|
+
PERMISSION_MODES,
|
|
76
|
+
PermissionModeSelect,
|
|
77
|
+
type PermissionModeMeta,
|
|
78
|
+
type PermissionModeSelectProps,
|
|
79
|
+
} from './components/agent/PermissionModeSelect.tsx'
|
|
80
|
+
export { StatusBar, type StatusBarProps } from './components/agent/StatusBar.tsx'
|
|
81
|
+
export {
|
|
82
|
+
SessionList,
|
|
83
|
+
SessionListItem,
|
|
84
|
+
type SessionListItemProps,
|
|
85
|
+
type SessionListProps,
|
|
86
|
+
} from './components/agent/SessionList.tsx'
|
|
87
|
+
export { STATUS_META } from './components/agent/status.ts'
|
|
88
|
+
|
|
89
|
+
// Utilities
|
|
90
|
+
export { cn } from './lib/utils.ts'
|
|
91
|
+
export {
|
|
92
|
+
formatBytes,
|
|
93
|
+
formatCost,
|
|
94
|
+
formatCountdown,
|
|
95
|
+
formatDuration,
|
|
96
|
+
formatRelativeTime,
|
|
97
|
+
formatTokens,
|
|
98
|
+
toolInputPreview,
|
|
99
|
+
} from './lib/format.ts'
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export function formatCost(usd: number | undefined): string {
|
|
2
|
+
if (usd === undefined || Number.isNaN(usd)) return '—'
|
|
3
|
+
if (usd === 0) return '$0.00'
|
|
4
|
+
if (usd < 0.01) return '<$0.01'
|
|
5
|
+
return `$${usd.toFixed(2)}`
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function formatDuration(ms: number): string {
|
|
9
|
+
if (ms < 1000) return `${Math.round(ms)}ms`
|
|
10
|
+
const s = ms / 1000
|
|
11
|
+
if (s < 60) return `${s.toFixed(1)}s`
|
|
12
|
+
const m = Math.floor(s / 60)
|
|
13
|
+
return `${m}m ${Math.round(s % 60)}s`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Compact token count, Claude Code-style: 850 → "850", 359_000 → "359.0k", 1_200_000 → "1.2M". */
|
|
17
|
+
export function formatTokens(tokens: number): string {
|
|
18
|
+
if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M`
|
|
19
|
+
if (tokens >= 1000) return `${(tokens / 1000).toFixed(1)}k`
|
|
20
|
+
return String(Math.round(tokens))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function formatBytes(bytes: number): string {
|
|
24
|
+
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
|
|
25
|
+
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`
|
|
26
|
+
return `${bytes} B`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Countdown to an epoch-ms deadline: "2h 18m", "12m", "<1m"; "now" once passed. */
|
|
30
|
+
export function formatCountdown(untilEpochMs: number, now = Date.now()): string {
|
|
31
|
+
const remaining = untilEpochMs - now
|
|
32
|
+
if (remaining <= 0) return 'now'
|
|
33
|
+
const minutes = Math.ceil(remaining / 60_000)
|
|
34
|
+
if (minutes < 1) return '<1m'
|
|
35
|
+
if (minutes < 60) return `${minutes}m`
|
|
36
|
+
const days = Math.floor(minutes / (60 * 24))
|
|
37
|
+
if (days >= 1) return `${days}d ${Math.floor((minutes % (60 * 24)) / 60)}h`
|
|
38
|
+
return `${Math.floor(minutes / 60)}h ${minutes % 60}m`
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function formatRelativeTime(epochMs: number | undefined, now = Date.now()): string {
|
|
42
|
+
if (!epochMs) return '—'
|
|
43
|
+
const diff = Math.max(0, now - epochMs)
|
|
44
|
+
const s = Math.floor(diff / 1000)
|
|
45
|
+
if (s < 60) return 'just now'
|
|
46
|
+
const m = Math.floor(s / 60)
|
|
47
|
+
if (m < 60) return `${m}m ago`
|
|
48
|
+
const h = Math.floor(m / 60)
|
|
49
|
+
if (h < 24) return `${h}h ago`
|
|
50
|
+
const d = Math.floor(h / 24)
|
|
51
|
+
return `${d}d ago`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Compact one-line preview of a tool input for card headers. */
|
|
55
|
+
export function toolInputPreview(input: unknown, max = 80): string {
|
|
56
|
+
if (input === null || input === undefined) return ''
|
|
57
|
+
if (typeof input === 'object') {
|
|
58
|
+
const o = input as Record<string, unknown>
|
|
59
|
+
const primary =
|
|
60
|
+
o.command ?? o.file_path ?? o.path ?? o.url ?? o.pattern ?? o.query ?? o.description
|
|
61
|
+
if (typeof primary === 'string') {
|
|
62
|
+
return primary.length > max ? primary.slice(0, max - 1) + '…' : primary
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const text = JSON.stringify(input) ?? ''
|
|
66
|
+
return text.length > max ? text.slice(0, max - 1) + '…' : text
|
|
67
|
+
}
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { clsx, type ClassValue } from 'clsx'
|
|
2
|
+
import { extendTailwindMerge } from 'tailwind-merge'
|
|
3
|
+
|
|
4
|
+
// Register the custom text-* font-size utilities as a font-size class group, or
|
|
5
|
+
// tailwind-merge collapses a size class and a color class (e.g. `text-label
|
|
6
|
+
// text-muted-foreground`) into one bucket and silently drops the size.
|
|
7
|
+
const twMerge = extendTailwindMerge({
|
|
8
|
+
extend: {
|
|
9
|
+
classGroups: {
|
|
10
|
+
'font-size': [
|
|
11
|
+
{
|
|
12
|
+
text: [
|
|
13
|
+
'display-xl',
|
|
14
|
+
'display-lg',
|
|
15
|
+
'display-md',
|
|
16
|
+
'display-sm',
|
|
17
|
+
'heading-1',
|
|
18
|
+
'heading-2',
|
|
19
|
+
'heading-3',
|
|
20
|
+
'body',
|
|
21
|
+
'body-sm',
|
|
22
|
+
'label',
|
|
23
|
+
'code',
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
export function cn(...inputs: ClassValue[]) {
|
|
32
|
+
return twMerge(clsx(inputs))
|
|
33
|
+
}
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @workerdeck/ui theme — design tokens + Tailwind v4 theme mapping.
|
|
3
|
+
*
|
|
4
|
+
* Consumer wiring (the app's tailwind entry CSS):
|
|
5
|
+
*
|
|
6
|
+
* @import 'tailwindcss';
|
|
7
|
+
* @import '@workerdeck/ui/theme.css';
|
|
8
|
+
* @source '../node_modules/@workerdeck/ui'; (or the package src path in a monorepo)
|
|
9
|
+
*
|
|
10
|
+
* Do NOT import tailwindcss here — the consumer owns that. Theme switching is driven by
|
|
11
|
+
* <html data-theme="light|dark"> (set it before first paint to avoid a flash).
|
|
12
|
+
*
|
|
13
|
+
* Two tiers: a raw palette swapped by [data-theme], then `@theme inline` mapping raw
|
|
14
|
+
* tokens to Tailwind utilities (`inline` preserves the var() reference so flipping
|
|
15
|
+
* data-theme re-themes with zero JS).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
:root,
|
|
19
|
+
[data-theme='light'] {
|
|
20
|
+
/* ---------- Backgrounds ---------- */
|
|
21
|
+
--bg: #ffffff;
|
|
22
|
+
--bg-surface: #fafafa;
|
|
23
|
+
--bg-surface-hover: #f2f2f2;
|
|
24
|
+
--bg-code: #f5f5f5;
|
|
25
|
+
--bg-elevated: #ffffffcc;
|
|
26
|
+
|
|
27
|
+
/* ---------- Borders ---------- */
|
|
28
|
+
--border: #ebebeb;
|
|
29
|
+
--border-light: #e0e0e0;
|
|
30
|
+
--border-strong: #c9c9c9;
|
|
31
|
+
|
|
32
|
+
/* ---------- Foreground / Text ---------- */
|
|
33
|
+
--fg-1: #171717;
|
|
34
|
+
--fg-2: #454545;
|
|
35
|
+
--fg-3: #737373;
|
|
36
|
+
--fg-4: #a1a1a1;
|
|
37
|
+
--fg-disabled: #c9c9c9;
|
|
38
|
+
|
|
39
|
+
/* ---------- Accent = the inverse of the canvas ---------- */
|
|
40
|
+
--accent: #171717;
|
|
41
|
+
--accent-hover: #000000;
|
|
42
|
+
--accent-dim: #737373;
|
|
43
|
+
--accent-bg: #f2f2f2;
|
|
44
|
+
--accent-ring: #17171733;
|
|
45
|
+
|
|
46
|
+
/* ---------- Semantic ---------- */
|
|
47
|
+
--success: #0f7b4f;
|
|
48
|
+
--success-bg: #edfaf3;
|
|
49
|
+
--warning: #a35200;
|
|
50
|
+
--warning-bg: #fff6ed;
|
|
51
|
+
--danger: #d93036;
|
|
52
|
+
--danger-bg: #fef1f1;
|
|
53
|
+
--info: #0072f5;
|
|
54
|
+
--info-bg: #eff6ff;
|
|
55
|
+
|
|
56
|
+
/* ---------- Elevation (flat by default; shadows are for overlays only) ---------- */
|
|
57
|
+
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
58
|
+
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);
|
|
59
|
+
--shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
|
|
60
|
+
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
61
|
+
--shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.1);
|
|
62
|
+
|
|
63
|
+
/* ---------- Inset frame ---------- */
|
|
64
|
+
--shine: #dcdcdc;
|
|
65
|
+
--frame-shadow: var(--shadow-xs);
|
|
66
|
+
|
|
67
|
+
/* ---------- aliases (bridge for the @theme utilities) ---------- */
|
|
68
|
+
--surface: var(--bg-surface);
|
|
69
|
+
--sidebar: #f4f4f4;
|
|
70
|
+
--text: var(--fg-1);
|
|
71
|
+
--text-muted: var(--fg-3);
|
|
72
|
+
--accent-tint: var(--accent-bg);
|
|
73
|
+
--accent-fg: #ffffff;
|
|
74
|
+
--warn: var(--warning);
|
|
75
|
+
--ring: var(--fg-1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
[data-theme='dark'] {
|
|
79
|
+
/* ---------- Backgrounds ---------- */
|
|
80
|
+
--bg: #000000;
|
|
81
|
+
--bg-surface: #0a0a0a;
|
|
82
|
+
--bg-surface-hover: #141414;
|
|
83
|
+
--bg-code: #141414;
|
|
84
|
+
--bg-elevated: #0a0a0acc;
|
|
85
|
+
|
|
86
|
+
/* ---------- Borders ---------- */
|
|
87
|
+
--border: #1f1f1f;
|
|
88
|
+
--border-light: #292929;
|
|
89
|
+
--border-strong: #3d3d3d;
|
|
90
|
+
|
|
91
|
+
/* ---------- Foreground / Text ---------- */
|
|
92
|
+
--fg-1: #ededed;
|
|
93
|
+
--fg-2: #a1a1a1;
|
|
94
|
+
--fg-3: #8f8f8f;
|
|
95
|
+
--fg-4: #6e6e6e;
|
|
96
|
+
--fg-disabled: #454545;
|
|
97
|
+
|
|
98
|
+
/* ---------- Accent = the inverse of the canvas ---------- */
|
|
99
|
+
--accent: #ededed;
|
|
100
|
+
--accent-hover: #ffffff;
|
|
101
|
+
--accent-dim: #8f8f8f;
|
|
102
|
+
--accent-bg: #1f1f1f;
|
|
103
|
+
--accent-ring: #ffffff3d;
|
|
104
|
+
|
|
105
|
+
/* ---------- Semantic ---------- */
|
|
106
|
+
--success: #3ecf8e;
|
|
107
|
+
--success-bg: #0d2a1d;
|
|
108
|
+
--warning: #f5a623;
|
|
109
|
+
--warning-bg: #2a1f04;
|
|
110
|
+
--danger: #ff6166;
|
|
111
|
+
--danger-bg: #2a1114;
|
|
112
|
+
--info: #0091ff;
|
|
113
|
+
--info-bg: #0a1f33;
|
|
114
|
+
|
|
115
|
+
/* ---------- Elevation ---------- */
|
|
116
|
+
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.5);
|
|
117
|
+
--shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.5);
|
|
118
|
+
--shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
|
|
119
|
+
--shadow-md: 0 8px 20px rgba(0, 0, 0, 0.6);
|
|
120
|
+
--shadow-lg: 0 24px 48px rgba(0, 0, 0, 0.7);
|
|
121
|
+
|
|
122
|
+
/* ---------- Inset frame ---------- */
|
|
123
|
+
--shine: #303030;
|
|
124
|
+
--frame-shadow:
|
|
125
|
+
0 4px 24px rgba(0, 0, 0, 0.55),
|
|
126
|
+
0 2px 6px rgba(0, 0, 0, 0.45);
|
|
127
|
+
|
|
128
|
+
/* ---------- aliases ---------- */
|
|
129
|
+
--surface: var(--bg-surface);
|
|
130
|
+
--sidebar: #131313;
|
|
131
|
+
--text: var(--fg-1);
|
|
132
|
+
--text-muted: var(--fg-3);
|
|
133
|
+
--accent-tint: var(--accent-bg);
|
|
134
|
+
--accent-fg: #0a0a0a;
|
|
135
|
+
--warn: var(--warning);
|
|
136
|
+
--ring: var(--fg-1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Theme-independent tokens — type tracking, easing, fonts.
|
|
140
|
+
* Hosts get real fonts by importing @fontsource/inter + @fontsource/jetbrains-mono;
|
|
141
|
+
* the stacks below degrade gracefully without them. */
|
|
142
|
+
:root {
|
|
143
|
+
--tracking-tight: -0.02em;
|
|
144
|
+
--tracking-normal: 0;
|
|
145
|
+
--tracking-wide: 0.04em;
|
|
146
|
+
--tracking-caps: 0.1em;
|
|
147
|
+
|
|
148
|
+
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
|
|
149
|
+
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
|
150
|
+
|
|
151
|
+
--cw-font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
152
|
+
--cw-font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', 'Cascadia Code', Menlo, Consolas, monospace;
|
|
153
|
+
|
|
154
|
+
--motion-fast: 120ms;
|
|
155
|
+
--motion-base: 150ms;
|
|
156
|
+
--motion-slow: 220ms;
|
|
157
|
+
|
|
158
|
+
/* Stock-name bridge → raw custom properties (shadcn idiom compatibility). */
|
|
159
|
+
--primary: var(--accent);
|
|
160
|
+
--primary-foreground: var(--accent-fg);
|
|
161
|
+
--secondary: var(--surface);
|
|
162
|
+
--secondary-foreground: var(--text);
|
|
163
|
+
--muted: var(--sidebar);
|
|
164
|
+
--muted-foreground: var(--text-muted);
|
|
165
|
+
--background: var(--bg);
|
|
166
|
+
--foreground: var(--text);
|
|
167
|
+
--destructive: var(--danger);
|
|
168
|
+
--radius: var(--radius-md);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* Drive dark: off the same <html data-theme> the tokens swap on. */
|
|
172
|
+
@custom-variant dark (&:where([data-theme='dark'], [data-theme='dark'] *));
|
|
173
|
+
|
|
174
|
+
@theme inline {
|
|
175
|
+
/* Semantic colors → utilities. */
|
|
176
|
+
--color-bg: var(--bg);
|
|
177
|
+
--color-surface: var(--surface);
|
|
178
|
+
--color-sidebar: var(--sidebar);
|
|
179
|
+
--color-border: var(--border);
|
|
180
|
+
--color-text: var(--text);
|
|
181
|
+
--color-accent: var(--accent);
|
|
182
|
+
--color-accent-hover: var(--accent-hover);
|
|
183
|
+
--color-accent-tint: var(--accent-tint);
|
|
184
|
+
--color-accent-fg: var(--accent-fg);
|
|
185
|
+
--color-success: var(--success);
|
|
186
|
+
--color-warn: var(--warn);
|
|
187
|
+
--color-danger: var(--danger);
|
|
188
|
+
--color-ring: var(--ring);
|
|
189
|
+
|
|
190
|
+
--color-surface-hover: var(--bg-surface-hover);
|
|
191
|
+
/* Named code-bg (not code) so it can't shadow the --text-code font-size token:
|
|
192
|
+
with a --color-code defined, `text-code` would resolve to the color namespace
|
|
193
|
+
and paint text in the code *background* color — invisible in both themes. */
|
|
194
|
+
--color-code-bg: var(--bg-code);
|
|
195
|
+
--color-border-light: var(--border-light);
|
|
196
|
+
--color-border-strong: var(--border-strong);
|
|
197
|
+
--color-fg-1: var(--fg-1);
|
|
198
|
+
--color-fg-2: var(--fg-2);
|
|
199
|
+
--color-fg-3: var(--fg-3);
|
|
200
|
+
--color-fg-4: var(--fg-4);
|
|
201
|
+
--color-accent-bg: var(--accent-bg);
|
|
202
|
+
--color-accent-dim: var(--accent-dim);
|
|
203
|
+
--color-success-bg: var(--success-bg);
|
|
204
|
+
--color-warning: var(--warning);
|
|
205
|
+
--color-warning-bg: var(--warning-bg);
|
|
206
|
+
--color-danger-bg: var(--danger-bg);
|
|
207
|
+
--color-info: var(--info);
|
|
208
|
+
--color-info-bg: var(--info-bg);
|
|
209
|
+
|
|
210
|
+
/* Stock-name bridge → utilities (bg-primary / text-muted-foreground / ...). */
|
|
211
|
+
--color-primary: var(--primary);
|
|
212
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
213
|
+
--color-secondary: var(--secondary);
|
|
214
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
215
|
+
--color-muted: var(--muted);
|
|
216
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
217
|
+
--color-background: var(--background);
|
|
218
|
+
--color-foreground: var(--foreground);
|
|
219
|
+
--color-destructive: var(--destructive);
|
|
220
|
+
|
|
221
|
+
/* Font roles */
|
|
222
|
+
--font-sans: var(--cw-font-sans);
|
|
223
|
+
--font-mono: var(--cw-font-mono);
|
|
224
|
+
|
|
225
|
+
/* Type scale — size + line-height. Display sizes carry tight tracking. */
|
|
226
|
+
--text-display-xl: 2.5rem;
|
|
227
|
+
--text-display-xl--line-height: 3rem;
|
|
228
|
+
--text-display-xl--letter-spacing: -0.02em;
|
|
229
|
+
--text-display-lg: 2rem;
|
|
230
|
+
--text-display-lg--line-height: 2.5rem;
|
|
231
|
+
--text-display-lg--letter-spacing: -0.02em;
|
|
232
|
+
--text-display-md: 1.5rem;
|
|
233
|
+
--text-display-md--line-height: 2rem;
|
|
234
|
+
--text-display-md--letter-spacing: -0.02em;
|
|
235
|
+
--text-display-sm: 1.25rem;
|
|
236
|
+
--text-display-sm--line-height: 1.75rem;
|
|
237
|
+
--text-display-sm--letter-spacing: -0.02em;
|
|
238
|
+
--text-heading-1: 1.5rem;
|
|
239
|
+
--text-heading-1--line-height: 2rem;
|
|
240
|
+
--text-heading-2: 1.25rem;
|
|
241
|
+
--text-heading-2--line-height: 1.75rem;
|
|
242
|
+
--text-heading-3: 1rem;
|
|
243
|
+
--text-heading-3--line-height: 1.5rem;
|
|
244
|
+
--text-body: 0.9375rem;
|
|
245
|
+
--text-body--line-height: 1.5rem;
|
|
246
|
+
--text-body-sm: 0.8125rem;
|
|
247
|
+
--text-body-sm--line-height: 1.25rem;
|
|
248
|
+
--text-label: 0.75rem;
|
|
249
|
+
--text-label--line-height: 1rem;
|
|
250
|
+
--text-label--letter-spacing: 0.02em;
|
|
251
|
+
--text-code: 0.84375rem;
|
|
252
|
+
--text-code--line-height: 1.375rem;
|
|
253
|
+
|
|
254
|
+
/* Radii — 6px is the workhorse. */
|
|
255
|
+
--radius-sm: 4px;
|
|
256
|
+
--radius-md: 6px;
|
|
257
|
+
--radius-lg: 8px;
|
|
258
|
+
--radius-full: 9999px;
|
|
259
|
+
|
|
260
|
+
/* Motion */
|
|
261
|
+
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
|
262
|
+
--ease-emphasized: cubic-bezier(0.3, 0, 0, 1);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@layer base {
|
|
266
|
+
* {
|
|
267
|
+
border-color: var(--border);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
html {
|
|
271
|
+
-webkit-text-size-adjust: 100%;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
body {
|
|
275
|
+
margin: 0;
|
|
276
|
+
background-color: var(--bg);
|
|
277
|
+
color: var(--text);
|
|
278
|
+
font-family: var(--cw-font-sans);
|
|
279
|
+
font-size: var(--text-body);
|
|
280
|
+
line-height: 1.5rem;
|
|
281
|
+
-webkit-font-smoothing: antialiased;
|
|
282
|
+
text-rendering: optimizeLegibility;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/* Standard focus ring: accent-derived, 2px + 2px offset. */
|
|
286
|
+
:focus-visible {
|
|
287
|
+
outline: 2px solid var(--ring);
|
|
288
|
+
outline-offset: 2px;
|
|
289
|
+
border-radius: 2px;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
@media (prefers-reduced-motion: reduce) {
|
|
293
|
+
*,
|
|
294
|
+
*::before,
|
|
295
|
+
*::after {
|
|
296
|
+
animation-duration: 0.001ms !important;
|
|
297
|
+
animation-iteration-count: 1 !important;
|
|
298
|
+
transition-duration: 0.001ms !important;
|
|
299
|
+
scroll-behavior: auto !important;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/* Lucide icons ship a hardcoded 24px; default them to 16px. :where() keeps specificity 0. */
|
|
304
|
+
:where(svg.lucide) {
|
|
305
|
+
width: 1rem;
|
|
306
|
+
height: 1rem;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/* The inset frame's gradient "shine" edge (app shells). Do NOT add bg-* on top. */
|
|
311
|
+
.frame-shine {
|
|
312
|
+
border: 1px solid transparent;
|
|
313
|
+
background:
|
|
314
|
+
linear-gradient(var(--bg), var(--bg)) padding-box,
|
|
315
|
+
linear-gradient(
|
|
316
|
+
135deg,
|
|
317
|
+
var(--shine) 0%,
|
|
318
|
+
color-mix(in srgb, var(--shine) 55%, transparent) 55%,
|
|
319
|
+
color-mix(in srgb, var(--shine) 30%, transparent) 100%
|
|
320
|
+
)
|
|
321
|
+
border-box;
|
|
322
|
+
box-shadow: var(--frame-shadow);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* ── prompt-area (vendored from just-marketing/prompt-area, MIT) ──────────────
|
|
326
|
+
Hand-authored styles its contentEditable chips/markers depend on; colors ride
|
|
327
|
+
the stock-name bridge vars above (--secondary/--foreground). */
|
|
328
|
+
.prompt-area-chip {
|
|
329
|
+
display: inline-flex;
|
|
330
|
+
align-items: center;
|
|
331
|
+
padding: 1px 6px;
|
|
332
|
+
border-radius: 4px;
|
|
333
|
+
font-size: 0.8125rem;
|
|
334
|
+
font-weight: 500;
|
|
335
|
+
cursor: pointer;
|
|
336
|
+
user-select: none;
|
|
337
|
+
vertical-align: baseline;
|
|
338
|
+
margin: 0 1px;
|
|
339
|
+
background-color: var(--secondary);
|
|
340
|
+
color: var(--foreground);
|
|
341
|
+
transition:
|
|
342
|
+
background-color 0.2s ease,
|
|
343
|
+
transform 0.15s cubic-bezier(0.4, 0, 0.2, 1),
|
|
344
|
+
box-shadow 0.2s ease;
|
|
345
|
+
position: relative;
|
|
346
|
+
overflow: hidden;
|
|
347
|
+
}
|
|
348
|
+
.prompt-area-chip:hover {
|
|
349
|
+
background-color: color-mix(in srgb, var(--secondary) 80%, var(--foreground) 20%);
|
|
350
|
+
box-shadow: 0 1px 4px color-mix(in srgb, var(--foreground) 10%, transparent);
|
|
351
|
+
}
|
|
352
|
+
.prompt-area-chip:active {
|
|
353
|
+
transform: scale(0.93);
|
|
354
|
+
background-color: color-mix(in srgb, var(--secondary) 70%, var(--foreground) 30%);
|
|
355
|
+
box-shadow: 0 0 0 transparent;
|
|
356
|
+
transition:
|
|
357
|
+
background-color 0.05s ease,
|
|
358
|
+
transform 0.08s cubic-bezier(0.4, 0, 0.2, 1),
|
|
359
|
+
box-shadow 0.05s ease;
|
|
360
|
+
}
|
|
361
|
+
@keyframes prompt-area-chip-ripple {
|
|
362
|
+
0% {
|
|
363
|
+
transform: scale(0);
|
|
364
|
+
opacity: 0.4;
|
|
365
|
+
}
|
|
366
|
+
100% {
|
|
367
|
+
transform: scale(2.5);
|
|
368
|
+
opacity: 0;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
.prompt-area-chip-ripple {
|
|
372
|
+
position: absolute;
|
|
373
|
+
border-radius: 50%;
|
|
374
|
+
width: 100%;
|
|
375
|
+
aspect-ratio: 1;
|
|
376
|
+
background: color-mix(in srgb, var(--foreground) 15%, transparent);
|
|
377
|
+
pointer-events: none;
|
|
378
|
+
animation: prompt-area-chip-ripple 0.45s ease-out forwards;
|
|
379
|
+
}
|
|
380
|
+
.prompt-area-md-marker {
|
|
381
|
+
font-size: 0;
|
|
382
|
+
display: inline;
|
|
383
|
+
}
|
|
384
|
+
.prompt-area-list-bullet {
|
|
385
|
+
font-size: 0;
|
|
386
|
+
display: inline-flex;
|
|
387
|
+
align-items: center;
|
|
388
|
+
vertical-align: middle;
|
|
389
|
+
transform: translateY(-1.5px);
|
|
390
|
+
margin-right: 4px;
|
|
391
|
+
}
|
|
392
|
+
.prompt-area-list-bullet::before {
|
|
393
|
+
content: '';
|
|
394
|
+
width: var(--prompt-area-bullet-size, 6px);
|
|
395
|
+
height: var(--prompt-area-bullet-size, 6px);
|
|
396
|
+
border-radius: 50%;
|
|
397
|
+
background: currentColor;
|
|
398
|
+
}
|
|
399
|
+
.prompt-area-list-indent {
|
|
400
|
+
display: inline-block;
|
|
401
|
+
white-space: pre;
|
|
402
|
+
}
|
|
403
|
+
.prompt-area-chip--inline {
|
|
404
|
+
padding: 0;
|
|
405
|
+
border-radius: 0;
|
|
406
|
+
margin: 0;
|
|
407
|
+
font-weight: 700;
|
|
408
|
+
}
|
|
409
|
+
@media (prefers-reduced-motion: reduce) {
|
|
410
|
+
.prompt-area-editor {
|
|
411
|
+
transition: none !important;
|
|
412
|
+
}
|
|
413
|
+
}
|