@bahulam/code 0.1.5 → 0.1.7

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.
Files changed (179) hide show
  1. package/LICENSE +1 -1
  2. package/NOTICE +6 -6
  3. package/README.md +10 -17
  4. package/package.json +9 -9
  5. package/src/agents/loader.mjs +1 -1
  6. package/src/agents/workflow_loader.mjs +2 -2
  7. package/src/auth/tarang-auth.mjs +3 -2
  8. package/src/config/model-catalog-default.json +4 -3
  9. package/src/config/model-catalog.mjs +2 -0
  10. package/src/context/prose-chunker.mjs +162 -5
  11. package/src/core/attachments.mjs +65 -3
  12. package/src/core/bundled-runtime.mjs +1 -1
  13. package/src/core/headless.mjs +14 -0
  14. package/src/core/jsonl-writer.mjs +3 -3
  15. package/src/core/lint-resolver.mjs +472 -0
  16. package/src/core/local-agent.mjs +15 -4
  17. package/src/core/local-store.mjs +4 -0
  18. package/src/core/output-filter.mjs +21 -25
  19. package/src/core/pricing.mjs +10 -3
  20. package/src/core/stream-client.mjs +112 -16
  21. package/src/core/tool-executor.mjs +123 -31
  22. package/src/daemon/http-dashboard.mjs +400 -0
  23. package/src/daemon/session-core.mjs +1 -0
  24. package/src/local-service/agent-relay.mjs +868 -0
  25. package/src/local-service/approval-bridge.mjs +222 -0
  26. package/src/local-service/browser.mjs +24 -0
  27. package/src/local-service/command.mjs +155 -0
  28. package/src/local-service/file-access.mjs +393 -0
  29. package/src/local-service/machine.mjs +86 -0
  30. package/src/local-service/paths.mjs +29 -0
  31. package/src/local-service/preview-converters.mjs +260 -0
  32. package/src/local-service/server.mjs +2644 -0
  33. package/src/local-service/session-store.mjs +221 -0
  34. package/src/onboarding/preflight.mjs +1 -1
  35. package/src/terminal/analytics.mjs +2 -6
  36. package/src/terminal/ansi.mjs +11 -3
  37. package/src/terminal/main.mjs +37 -11
  38. package/src/terminal/model-catalog-display.mjs +111 -0
  39. package/src/terminal/repl-format.mjs +4 -1
  40. package/src/terminal/repl-model-form.mjs +81 -14
  41. package/src/terminal/repl-render.mjs +35 -5
  42. package/src/terminal/repl-state.mjs +12 -0
  43. package/src/terminal/repl.mjs +255 -55
  44. package/src/terminal/tool-display.mjs +32 -0
  45. package/src/terminal/watch-state.mjs +118 -0
  46. package/src/tools/analyze-image.mjs +158 -0
  47. package/src/tools/bash.mjs +11 -9
  48. package/src/tools/generate-image.mjs +411 -0
  49. package/src/tools/lint.mjs +24 -43
  50. package/src/tools/project-overview.mjs +5 -2
  51. package/src/tools/registry.mjs +4 -0
  52. package/src/ui/icons.mjs +2 -0
  53. package/src/ui/palette.mjs +1 -1
  54. package/src/ui/slash-commands.mjs +4 -6
  55. package/src/ui/tool-card.mjs +7 -3
  56. package/pulse/app/activity/page.tsx +0 -190
  57. package/pulse/app/api/activity/route.ts +0 -138
  58. package/pulse/app/api/benchmark/route.ts +0 -113
  59. package/pulse/app/api/benchmarks/route.ts +0 -195
  60. package/pulse/app/api/costs/route.ts +0 -88
  61. package/pulse/app/api/export/route.ts +0 -77
  62. package/pulse/app/api/history/route.ts +0 -11
  63. package/pulse/app/api/import/route.ts +0 -31
  64. package/pulse/app/api/memory/route.ts +0 -50
  65. package/pulse/app/api/plans/route.ts +0 -9
  66. package/pulse/app/api/projects/[slug]/route.ts +0 -96
  67. package/pulse/app/api/projects/route.ts +0 -121
  68. package/pulse/app/api/sessions/[id]/replay/route.ts +0 -20
  69. package/pulse/app/api/sessions/[id]/route.ts +0 -31
  70. package/pulse/app/api/sessions/route.ts +0 -112
  71. package/pulse/app/api/settings/route.ts +0 -14
  72. package/pulse/app/api/stats/route.ts +0 -143
  73. package/pulse/app/api/todos/route.ts +0 -9
  74. package/pulse/app/api/tools/route.ts +0 -160
  75. package/pulse/app/benchmarks/page.tsx +0 -224
  76. package/pulse/app/costs/page.tsx +0 -179
  77. package/pulse/app/export/page.tsx +0 -465
  78. package/pulse/app/favicon.ico +0 -0
  79. package/pulse/app/globals.css +0 -263
  80. package/pulse/app/help/page.tsx +0 -143
  81. package/pulse/app/history/page.tsx +0 -157
  82. package/pulse/app/layout.tsx +0 -46
  83. package/pulse/app/memory/page.tsx +0 -365
  84. package/pulse/app/overview-client.tsx +0 -393
  85. package/pulse/app/page.tsx +0 -14
  86. package/pulse/app/plans/page.tsx +0 -308
  87. package/pulse/app/projects/[slug]/page.tsx +0 -390
  88. package/pulse/app/projects/page.tsx +0 -110
  89. package/pulse/app/sessions/[id]/page.tsx +0 -243
  90. package/pulse/app/sessions/page.tsx +0 -39
  91. package/pulse/app/settings/page.tsx +0 -188
  92. package/pulse/app/todos/page.tsx +0 -211
  93. package/pulse/app/tools/page.tsx +0 -249
  94. package/pulse/cli.js +0 -164
  95. package/pulse/components/activity/day-of-week-chart.tsx +0 -35
  96. package/pulse/components/activity/streak-card.tsx +0 -36
  97. package/pulse/components/costs/cache-efficiency-panel.tsx +0 -76
  98. package/pulse/components/costs/cost-by-project-chart.tsx +0 -48
  99. package/pulse/components/costs/cost-over-time-chart.tsx +0 -95
  100. package/pulse/components/costs/model-token-table.tsx +0 -60
  101. package/pulse/components/global-search.tsx +0 -193
  102. package/pulse/components/keyboard-nav-provider.tsx +0 -23
  103. package/pulse/components/layout/bottom-nav.tsx +0 -53
  104. package/pulse/components/layout/client-layout.tsx +0 -31
  105. package/pulse/components/layout/sidebar-context.tsx +0 -50
  106. package/pulse/components/layout/sidebar.tsx +0 -183
  107. package/pulse/components/layout/top-bar.tsx +0 -121
  108. package/pulse/components/overview/activity-heatmap.tsx +0 -107
  109. package/pulse/components/overview/conversation-table.tsx +0 -148
  110. package/pulse/components/overview/model-breakdown-donut.tsx +0 -95
  111. package/pulse/components/overview/peak-hours-chart.tsx +0 -87
  112. package/pulse/components/overview/project-activity-donut.tsx +0 -96
  113. package/pulse/components/overview/stat-card.tsx +0 -102
  114. package/pulse/components/overview/usage-over-time-chart.tsx +0 -166
  115. package/pulse/components/projects/project-card.tsx +0 -175
  116. package/pulse/components/sessions/replay/assistant-markdown.tsx +0 -94
  117. package/pulse/components/sessions/replay/compaction-card.tsx +0 -25
  118. package/pulse/components/sessions/replay/session-sidebar.tsx +0 -231
  119. package/pulse/components/sessions/replay/token-accumulation-chart.tsx +0 -98
  120. package/pulse/components/sessions/replay/tool-call-badge.tsx +0 -127
  121. package/pulse/components/sessions/replay/turn-cards.tsx +0 -220
  122. package/pulse/components/sessions/replay/user-tool-result.tsx +0 -158
  123. package/pulse/components/sessions/session-badges.tsx +0 -49
  124. package/pulse/components/sessions/session-table.tsx +0 -299
  125. package/pulse/components/theme-provider.tsx +0 -44
  126. package/pulse/components/tools/feature-adoption-table.tsx +0 -58
  127. package/pulse/components/tools/mcp-server-panel.tsx +0 -45
  128. package/pulse/components/tools/tool-ranking-chart.tsx +0 -57
  129. package/pulse/components/tools/version-history-table.tsx +0 -32
  130. package/pulse/components/ui/alert.tsx +0 -66
  131. package/pulse/components/ui/badge.tsx +0 -48
  132. package/pulse/components/ui/breadcrumb.tsx +0 -109
  133. package/pulse/components/ui/button.tsx +0 -64
  134. package/pulse/components/ui/calendar.tsx +0 -220
  135. package/pulse/components/ui/card.tsx +0 -92
  136. package/pulse/components/ui/command.tsx +0 -158
  137. package/pulse/components/ui/dialog.tsx +0 -158
  138. package/pulse/components/ui/input.tsx +0 -21
  139. package/pulse/components/ui/popover.tsx +0 -89
  140. package/pulse/components/ui/progress.tsx +0 -31
  141. package/pulse/components/ui/select.tsx +0 -190
  142. package/pulse/components/ui/separator.tsx +0 -28
  143. package/pulse/components/ui/sheet.tsx +0 -143
  144. package/pulse/components/ui/skeleton.tsx +0 -13
  145. package/pulse/components/ui/table.tsx +0 -116
  146. package/pulse/components/ui/tabs.tsx +0 -91
  147. package/pulse/components/ui/tooltip.tsx +0 -57
  148. package/pulse/components/use-global-keyboard-nav.ts +0 -79
  149. package/pulse/components.json +0 -23
  150. package/pulse/eslint.config.mjs +0 -18
  151. package/pulse/lib/bahulam-paths.ts +0 -23
  152. package/pulse/lib/claude-reader.ts +0 -592
  153. package/pulse/lib/decode.ts +0 -129
  154. package/pulse/lib/pricing.ts +0 -102
  155. package/pulse/lib/replay-parser.ts +0 -165
  156. package/pulse/lib/tool-categories.ts +0 -140
  157. package/pulse/lib/utils.ts +0 -6
  158. package/pulse/next-env.d.ts +0 -6
  159. package/pulse/next.config.ts +0 -16
  160. package/pulse/package.json +0 -45
  161. package/pulse/postcss.config.mjs +0 -7
  162. package/pulse/public/activity.png +0 -0
  163. package/pulse/public/cc-lens.png +0 -0
  164. package/pulse/public/command-k.png +0 -0
  165. package/pulse/public/costs.png +0 -0
  166. package/pulse/public/dashboard-dark.png +0 -0
  167. package/pulse/public/dashboard-white.png +0 -0
  168. package/pulse/public/export.png +0 -0
  169. package/pulse/public/file.svg +0 -1
  170. package/pulse/public/globe.svg +0 -1
  171. package/pulse/public/next.svg +0 -1
  172. package/pulse/public/projects.png +0 -0
  173. package/pulse/public/session-chat.png +0 -0
  174. package/pulse/public/todos.png +0 -0
  175. package/pulse/public/tools.png +0 -0
  176. package/pulse/public/vercel.svg +0 -1
  177. package/pulse/public/window.svg +0 -1
  178. package/pulse/tsconfig.json +0 -34
  179. package/pulse/types/claude.ts +0 -294
@@ -1,95 +0,0 @@
1
- 'use client'
2
-
3
- import { useMemo, useState } from 'react'
4
- import { AreaChart, Area, XAxis, YAxis, Tooltip, CartesianGrid, ResponsiveContainer, Legend } from 'recharts'
5
- import { formatCost } from '@/lib/decode'
6
- import type { DailyCost } from '@/types/claude'
7
-
8
- const MODEL_COLORS: Record<string, string> = {
9
- 'claude-opus-4-6': '#d97706',
10
- 'claude-opus-4-5-20251101': '#a78bfa',
11
- 'claude-sonnet-4-6': 'var(--viz-sky)',
12
- 'claude-haiku-4-5': '#34d399',
13
- }
14
-
15
- function colorForModel(m: string): string {
16
- for (const [key, col] of Object.entries(MODEL_COLORS)) {
17
- if (m.includes(key.split('-').slice(2).join('-'))) return col
18
- }
19
- return '#7a8494'
20
- }
21
-
22
- function shortModel(m: string): string {
23
- if (m.includes('opus-4-6')) return 'Opus 4.6'
24
- if (m.includes('opus-4-5')) return 'Opus 4.5'
25
- if (m.includes('sonnet-4-6')) return 'Sonnet 4.6'
26
- if (m.includes('haiku-4-5')) return 'Haiku 4.5'
27
- return m
28
- }
29
-
30
- interface Props {
31
- daily: DailyCost[]
32
- }
33
-
34
- type Window = 30 | 90 | 365
35
-
36
- export function CostOverTimeChart({ daily }: Props) {
37
- const [window, setWindow] = useState<Window>(90)
38
-
39
- const { data, models } = useMemo(() => {
40
- const sorted = [...daily].sort((a, b) => a.date.localeCompare(b.date))
41
- const sliced = sorted.slice(-window)
42
- const modelSet = new Set<string>()
43
- for (const d of sliced) Object.keys(d.costs ?? {}).forEach(m => modelSet.add(m))
44
- const models = [...modelSet]
45
- return {
46
- data: sliced.map(d => ({
47
- date: d.date.slice(5), // MM-DD
48
- ...Object.fromEntries(models.map(m => [m, d.costs[m] ?? 0])),
49
- total: d.total,
50
- })),
51
- models,
52
- }
53
- }, [daily, window])
54
-
55
- return (
56
- <div>
57
- <div className="flex items-center justify-between mb-3">
58
- <h3 className="text-[13px] font-bold text-muted-foreground uppercase tracking-widest">Cost Over Time</h3>
59
- <div className="flex gap-1">
60
- {([30, 90, 365] as Window[]).map(w => (
61
- <button
62
- key={w}
63
- onClick={() => setWindow(w)}
64
- className={`px-2 py-0.5 rounded text-[12px] transition-colors ${window === w ? 'bg-primary text-black font-bold' : 'text-muted-foreground hover:text-foreground border border-border'}`}
65
- >
66
- {w === 365 ? 'All' : `${w}d`}
67
- </button>
68
- ))}
69
- </div>
70
- </div>
71
- <ResponsiveContainer width="100%" height={200}>
72
- <AreaChart data={data} margin={{ top: 4, right: 8, bottom: 0, left: 0 }}>
73
- <CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
74
- <XAxis dataKey="date" tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }} tickLine={false} axisLine={false} interval="preserveStartEnd" />
75
- <YAxis tick={{ fontSize: 11, fill: 'var(--muted-foreground)' }} tickLine={false} axisLine={false} tickFormatter={v => `$${v.toFixed(2)}`} width={48} />
76
- <Tooltip
77
- contentStyle={{ background: 'var(--card)', border: '1px solid var(--border)', borderRadius: 4, fontSize: 12 }}
78
- formatter={(val: number | undefined, name?: string) => [formatCost(val ?? 0), shortModel(name ?? '')]}
79
- />
80
- {models.map(m => (
81
- <Area
82
- key={m}
83
- type="monotone"
84
- dataKey={m}
85
- stackId="1"
86
- stroke={colorForModel(m)}
87
- fill={colorForModel(m) + '30'}
88
- strokeWidth={1.5}
89
- />
90
- ))}
91
- </AreaChart>
92
- </ResponsiveContainer>
93
- </div>
94
- )
95
- }
@@ -1,60 +0,0 @@
1
- import { formatTokens, formatCost } from '@/lib/decode'
2
- import type { ModelCostBreakdown } from '@/types/claude'
3
-
4
- function shortModel(m: string): string {
5
- if (m.includes('opus-4-6')) return 'claude-opus-4.6'
6
- if (m.includes('opus-4-5')) return 'claude-opus-4.5'
7
- if (m.includes('sonnet-4-6')) return 'claude-sonnet-4.6'
8
- if (m.includes('haiku-4-5')) return 'claude-haiku-4.5'
9
- return m
10
- }
11
-
12
- interface Props {
13
- models: ModelCostBreakdown[]
14
- }
15
-
16
- export function ModelTokenTable({ models }: Props) {
17
- const totals = models.reduce((acc, m) => ({
18
- input: acc.input + m.input_tokens,
19
- output: acc.output + m.output_tokens,
20
- cacheWrite: acc.cacheWrite + m.cache_write_tokens,
21
- cacheRead: acc.cacheRead + m.cache_read_tokens,
22
- cost: acc.cost + m.estimated_cost,
23
- }), { input: 0, output: 0, cacheWrite: 0, cacheRead: 0, cost: 0 })
24
-
25
- return (
26
- <div className="overflow-x-auto">
27
- <table className="w-full text-[13px] font-mono">
28
- <thead>
29
- <tr className="border-b border-border">
30
- {['Model', 'Input', 'Output', 'Cache W', 'Cache R', 'Cost'].map(h => (
31
- <th key={h} className={`py-2 text-[12px] font-bold text-muted-foreground uppercase tracking-wider ${h === 'Model' ? 'text-left' : 'text-right'}`}>
32
- {h}
33
- </th>
34
- ))}
35
- </tr>
36
- </thead>
37
- <tbody>
38
- {models.map(m => (
39
- <tr key={m.model} className="border-b border-border/30 hover:bg-muted/50 transition-colors">
40
- <td className="py-2 text-foreground/80">{shortModel(m.model)}</td>
41
- <td className="py-2 text-right text-blue-700 dark:text-[#60a5fa]">{formatTokens(m.input_tokens)}</td>
42
- <td className="py-2 text-right text-[#d97706]">{formatTokens(m.output_tokens)}</td>
43
- <td className="py-2 text-right text-[#a78bfa]">{formatTokens(m.cache_write_tokens)}</td>
44
- <td className="py-2 text-right text-[#34d399]">{formatTokens(m.cache_read_tokens)}</td>
45
- <td className="py-2 text-right text-[#d97706] font-bold">{formatCost(m.estimated_cost)}</td>
46
- </tr>
47
- ))}
48
- <tr className="border-t border-border font-bold">
49
- <td className="py-2 text-muted-foreground">TOTAL</td>
50
- <td className="py-2 text-right text-blue-700 dark:text-[#60a5fa]">{formatTokens(totals.input)}</td>
51
- <td className="py-2 text-right text-[#d97706]">{formatTokens(totals.output)}</td>
52
- <td className="py-2 text-right text-[#a78bfa]">{formatTokens(totals.cacheWrite)}</td>
53
- <td className="py-2 text-right text-[#34d399]">{formatTokens(totals.cacheRead)}</td>
54
- <td className="py-2 text-right text-[#d97706]">{formatCost(totals.cost)}</td>
55
- </tr>
56
- </tbody>
57
- </table>
58
- </div>
59
- )
60
- }
@@ -1,193 +0,0 @@
1
- 'use client'
2
-
3
- import { useState, useEffect } from 'react'
4
- import { useRouter } from 'next/navigation'
5
- import { LayoutDashboard, FileText, FolderOpen, Layers, Brain } from 'lucide-react'
6
- import {
7
- CommandDialog,
8
- CommandEmpty,
9
- CommandGroup,
10
- CommandInput,
11
- CommandItem,
12
- CommandList,
13
- CommandSeparator,
14
- CommandShortcut,
15
- } from '@/components/ui/command'
16
- import { projectDisplayName } from '@/lib/decode'
17
-
18
- const PAGES = [
19
- { label: 'Overview', href: '/' },
20
- { label: 'Projects', href: '/projects' },
21
- { label: 'Sessions', href: '/sessions' },
22
- { label: 'Costs', href: '/costs' },
23
- { label: 'Tools', href: '/tools' },
24
- { label: 'Activity', href: '/activity' },
25
- { label: 'History', href: '/history' },
26
- { label: 'Todos', href: '/todos' },
27
- { label: 'Plans', href: '/plans' },
28
- { label: 'Memory', href: '/memory' },
29
- { label: 'Settings', href: '/settings' },
30
- { label: 'Export', href: '/export' },
31
- ]
32
-
33
- export function GlobalSearch() {
34
- const [open, setOpen] = useState(false)
35
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
- const [sessions, setSessions] = useState<any[]>([])
37
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
- const [projects, setProjects] = useState<any[]>([])
39
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
- const [plans, setPlans] = useState<any[]>([])
41
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
- const [memories, setMemories] = useState<any[]>([])
43
- const [loaded, setLoaded] = useState(false)
44
- const router = useRouter()
45
-
46
- // Lazy-load data on first open
47
- useEffect(() => {
48
- if (!open || loaded) return
49
- Promise.all([
50
- fetch('/api/sessions').then(r => r.json()),
51
- fetch('/api/projects').then(r => r.json()),
52
- fetch('/api/plans').then(r => r.json()),
53
- fetch('/api/memory').then(r => r.json()),
54
- ]).then(([s, p, pl, m]) => {
55
- setSessions(s.sessions ?? [])
56
- setProjects(p.projects ?? [])
57
- setPlans(pl.plans ?? [])
58
- setMemories(m.memories ?? [])
59
- setLoaded(true)
60
- }).catch(() => setLoaded(true))
61
- }, [open, loaded])
62
-
63
- // Listen for open-search custom event (fired by TopBar button)
64
- useEffect(() => {
65
- const handler = () => setOpen(true)
66
- window.addEventListener('open-search', handler)
67
- return () => window.removeEventListener('open-search', handler)
68
- }, [])
69
-
70
- // Cmd+K / Ctrl+K shortcut
71
- useEffect(() => {
72
- const handler = (e: KeyboardEvent) => {
73
- if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
74
- e.preventDefault()
75
- setOpen(o => !o)
76
- }
77
- }
78
- window.addEventListener('keydown', handler)
79
- return () => window.removeEventListener('keydown', handler)
80
- }, [])
81
-
82
- function navigate(href: string) {
83
- router.push(href)
84
- setOpen(false)
85
- }
86
-
87
- return (
88
- <CommandDialog open={open} onOpenChange={setOpen}>
89
- <CommandInput placeholder="Search pages, sessions, projects..." />
90
- <CommandList>
91
- <CommandEmpty>No results found.</CommandEmpty>
92
-
93
- <CommandGroup heading="Pages">
94
- {PAGES.map(p => (
95
- <CommandItem key={p.href} value={p.label} onSelect={() => navigate(p.href)}>
96
- <LayoutDashboard className="w-3.5 h-3.5 text-muted-foreground shrink-0" />
97
- {p.label}
98
- <CommandShortcut>page</CommandShortcut>
99
- </CommandItem>
100
- ))}
101
- </CommandGroup>
102
-
103
- {sessions.length > 0 && (
104
- <>
105
- <CommandSeparator />
106
- <CommandGroup heading="Sessions">
107
- {sessions.slice(0, 80).map(s => (
108
- <CommandItem
109
- key={s.session_id}
110
- value={`${s.slug ?? ''} ${s.first_prompt ?? ''} ${projectDisplayName(s.project_path ?? '')}`}
111
- onSelect={() => navigate(`/sessions/${s.session_id}`)}
112
- >
113
- <FileText className="w-3.5 h-3.5 text-blue-700 dark:text-blue-400 shrink-0" />
114
- <div className="flex-1 min-w-0">
115
- <span className="text-sm">{s.slug ?? s.session_id.slice(0, 12)}</span>
116
- {s.first_prompt && (
117
- <p className="text-xs text-muted-foreground truncate">{s.first_prompt.slice(0, 60)}</p>
118
- )}
119
- </div>
120
- <CommandShortcut>session</CommandShortcut>
121
- </CommandItem>
122
- ))}
123
- </CommandGroup>
124
- </>
125
- )}
126
-
127
- {projects.length > 0 && (
128
- <>
129
- <CommandSeparator />
130
- <CommandGroup heading="Projects">
131
- {projects.map(p => (
132
- <CommandItem
133
- key={p.slug}
134
- value={`${p.display_name} ${p.slug}`}
135
- onSelect={() => navigate(`/projects/${p.slug}`)}
136
- >
137
- <FolderOpen className="w-3.5 h-3.5 text-green-400 shrink-0" />
138
- <span className="flex-1 truncate">{p.display_name}</span>
139
- <CommandShortcut>{p.session_count} sessions</CommandShortcut>
140
- </CommandItem>
141
- ))}
142
- </CommandGroup>
143
- </>
144
- )}
145
-
146
- {plans.length > 0 && (
147
- <>
148
- <CommandSeparator />
149
- <CommandGroup heading="Plans">
150
- {plans.map((p, i) => (
151
- <CommandItem
152
- key={`plan-${i}`}
153
- value={`${p.name} ${(p.content ?? '').slice(0, 200)}`}
154
- onSelect={() => navigate('/plans')}
155
- >
156
- <Layers className="w-3.5 h-3.5 text-purple-400 shrink-0" />
157
- <span className="flex-1 truncate">{p.name}</span>
158
- <CommandShortcut>plan</CommandShortcut>
159
- </CommandItem>
160
- ))}
161
- </CommandGroup>
162
- </>
163
- )}
164
-
165
- {memories.length > 0 && (
166
- <>
167
- <CommandSeparator />
168
- <CommandGroup heading="Memory">
169
- {memories.slice(0, 40).map((m, i) => (
170
- <CommandItem
171
- key={`mem-${i}`}
172
- value={`${m.name ?? ''} ${m.description ?? ''} ${(m.body ?? '').slice(0, 100)}`}
173
- onSelect={() => navigate('/memory')}
174
- >
175
- <Brain className="w-3.5 h-3.5 text-amber-400 shrink-0" />
176
- <span className="flex-1 truncate">{m.name}</span>
177
- <CommandShortcut>{m.type}</CommandShortcut>
178
- </CommandItem>
179
- ))}
180
- </CommandGroup>
181
- </>
182
- )}
183
- </CommandList>
184
-
185
- <div className="border-t border-border px-3 py-2 flex items-center gap-4 text-[10px] text-muted-foreground/40 font-mono">
186
- <span>↑↓ navigate</span>
187
- <span>↵ open</span>
188
- <span>esc close</span>
189
- <span className="ml-auto">⌘K toggle</span>
190
- </div>
191
- </CommandDialog>
192
- )
193
- }
@@ -1,23 +0,0 @@
1
- 'use client'
2
-
3
- import { GlobalSearch } from './global-search'
4
- import { useGlobalKeyboardNav } from './use-global-keyboard-nav'
5
-
6
- function GModeIndicator() {
7
- const gMode = useGlobalKeyboardNav()
8
- if (!gMode) return null
9
- return (
10
- <div className="fixed bottom-20 right-4 md:bottom-4 z-50 px-3 py-1.5 bg-background border border-primary/60 rounded text-sm font-mono text-primary shadow-lg animate-in fade-in-0 duration-100 pointer-events-none">
11
- g —
12
- </div>
13
- )
14
- }
15
-
16
- export function KeyboardNavProvider() {
17
- return (
18
- <>
19
- <GlobalSearch />
20
- <GModeIndicator />
21
- </>
22
- )
23
- }
@@ -1,53 +0,0 @@
1
- 'use client'
2
-
3
- import Link from 'next/link'
4
- import { usePathname } from 'next/navigation'
5
- import {
6
- LayoutDashboard, MessageSquare, DollarSign,
7
- FolderOpen, Activity, Moon, Sun, Zap,
8
- } from 'lucide-react'
9
- import { useTheme } from '@/components/theme-provider'
10
- import { cn } from '@/lib/utils'
11
-
12
- const NAV = [
13
- { href: '/', label: 'Overview', icon: LayoutDashboard },
14
- { href: '/sessions', label: 'Sessions', icon: MessageSquare },
15
- { href: '/costs', label: 'Costs', icon: DollarSign },
16
- { href: '/projects', label: 'Projects', icon: FolderOpen },
17
- { href: '/activity', label: 'Activity', icon: Activity },
18
- { href: '/benchmarks', label: 'Benchmarks', icon: Zap },
19
- ]
20
-
21
- export function BottomNav() {
22
- const pathname = usePathname()
23
- const { theme, toggle } = useTheme()
24
-
25
- return (
26
- <nav className="md:hidden fixed bottom-0 left-0 right-0 z-50 bg-sidebar border-t border-sidebar-border flex">
27
- {NAV.map(({ href, label, icon: Icon }) => {
28
- const active = pathname === href
29
- return (
30
- <Link
31
- key={href}
32
- href={href}
33
- className={cn(
34
- 'flex-1 flex flex-col items-center justify-center py-2.5 gap-1 transition-colors',
35
- active ? 'text-sidebar-primary' : 'text-sidebar-foreground/40 hover:text-sidebar-foreground',
36
- )}
37
- >
38
- <Icon className="w-4 h-4" />
39
- <span className="text-[10px] font-medium leading-none">{label}</span>
40
- </Link>
41
- )
42
- })}
43
- <button
44
- onClick={toggle}
45
- aria-label="Toggle theme"
46
- className="flex-1 flex flex-col items-center justify-center py-2.5 gap-1 transition-colors text-sidebar-foreground/40 hover:text-sidebar-foreground cursor-pointer"
47
- >
48
- {theme === 'dark' ? <Sun className="w-4 h-4" /> : <Moon className="w-4 h-4" />}
49
- <span className="text-[10px] font-medium leading-none">Theme</span>
50
- </button>
51
- </nav>
52
- )
53
- }
@@ -1,31 +0,0 @@
1
- 'use client'
2
-
3
- import { useSidebar } from '@/components/layout/sidebar-context'
4
-
5
- export function ClientLayout({ children }: { children: React.ReactNode }) {
6
- const { collapsed } = useSidebar()
7
- return (
8
- <main
9
- className={[
10
- 'flex-1 min-h-screen overflow-x-hidden bg-background pb-16 md:pb-0',
11
- 'transition-[margin] duration-300',
12
- collapsed ? 'md:ml-14' : 'md:ml-56',
13
- ].join(' ')}
14
- >
15
- {children}
16
- <footer className="border-t border-border/50 py-3 px-6 flex items-center justify-center mb-16 md:mb-0">
17
- <p className="text-xs text-muted-foreground">
18
- Made by{' '}
19
- <a
20
- href="https://github.com/Arindam200"
21
- target="_blank"
22
- rel="noopener noreferrer"
23
- className="text-muted-foreground hover:text-foreground underline underline-offset-2 transition-colors"
24
- >
25
- Arindam
26
- </a>
27
- </p>
28
- </footer>
29
- </main>
30
- )
31
- }
@@ -1,50 +0,0 @@
1
- 'use client'
2
-
3
- import { createContext, useContext, useEffect, useState } from 'react'
4
-
5
- interface SidebarContextType {
6
- collapsed: boolean
7
- toggle: () => void
8
- mobileOpen: boolean
9
- setMobileOpen: (v: boolean) => void
10
- }
11
-
12
- const SidebarContext = createContext<SidebarContextType>({
13
- collapsed: false,
14
- toggle: () => {},
15
- mobileOpen: false,
16
- setMobileOpen: () => {},
17
- })
18
-
19
- export function SidebarProvider({ children }: { children: React.ReactNode }) {
20
- const [collapsed, setCollapsed] = useState(false)
21
- const [mobileOpen, setMobileOpen] = useState(false)
22
-
23
- useEffect(() => {
24
- const id = window.setTimeout(() => {
25
- try {
26
- setCollapsed(localStorage.getItem('cc-sidebar-collapsed') === 'true')
27
- } catch {}
28
- }, 0)
29
-
30
- return () => window.clearTimeout(id)
31
- }, [])
32
-
33
- function toggle() {
34
- setCollapsed(prev => {
35
- const next = !prev
36
- try { localStorage.setItem('cc-sidebar-collapsed', String(next)) } catch {}
37
- return next
38
- })
39
- }
40
-
41
- return (
42
- <SidebarContext.Provider value={{ collapsed, toggle, mobileOpen, setMobileOpen }}>
43
- {children}
44
- </SidebarContext.Provider>
45
- )
46
- }
47
-
48
- export function useSidebar() {
49
- return useContext(SidebarContext)
50
- }