@bahulam/code 0.1.6 → 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 (178) 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 +9 -4
  21. package/src/core/tool-executor.mjs +105 -29
  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 +231 -53
  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/registry.mjs +4 -0
  51. package/src/ui/icons.mjs +2 -0
  52. package/src/ui/palette.mjs +1 -1
  53. package/src/ui/slash-commands.mjs +4 -6
  54. package/src/ui/tool-card.mjs +7 -3
  55. package/pulse/app/activity/page.tsx +0 -190
  56. package/pulse/app/api/activity/route.ts +0 -138
  57. package/pulse/app/api/benchmark/route.ts +0 -113
  58. package/pulse/app/api/benchmarks/route.ts +0 -195
  59. package/pulse/app/api/costs/route.ts +0 -88
  60. package/pulse/app/api/export/route.ts +0 -77
  61. package/pulse/app/api/history/route.ts +0 -11
  62. package/pulse/app/api/import/route.ts +0 -31
  63. package/pulse/app/api/memory/route.ts +0 -50
  64. package/pulse/app/api/plans/route.ts +0 -9
  65. package/pulse/app/api/projects/[slug]/route.ts +0 -96
  66. package/pulse/app/api/projects/route.ts +0 -121
  67. package/pulse/app/api/sessions/[id]/replay/route.ts +0 -20
  68. package/pulse/app/api/sessions/[id]/route.ts +0 -31
  69. package/pulse/app/api/sessions/route.ts +0 -112
  70. package/pulse/app/api/settings/route.ts +0 -14
  71. package/pulse/app/api/stats/route.ts +0 -143
  72. package/pulse/app/api/todos/route.ts +0 -9
  73. package/pulse/app/api/tools/route.ts +0 -160
  74. package/pulse/app/benchmarks/page.tsx +0 -224
  75. package/pulse/app/costs/page.tsx +0 -179
  76. package/pulse/app/export/page.tsx +0 -465
  77. package/pulse/app/favicon.ico +0 -0
  78. package/pulse/app/globals.css +0 -263
  79. package/pulse/app/help/page.tsx +0 -143
  80. package/pulse/app/history/page.tsx +0 -157
  81. package/pulse/app/layout.tsx +0 -46
  82. package/pulse/app/memory/page.tsx +0 -365
  83. package/pulse/app/overview-client.tsx +0 -393
  84. package/pulse/app/page.tsx +0 -14
  85. package/pulse/app/plans/page.tsx +0 -308
  86. package/pulse/app/projects/[slug]/page.tsx +0 -390
  87. package/pulse/app/projects/page.tsx +0 -110
  88. package/pulse/app/sessions/[id]/page.tsx +0 -243
  89. package/pulse/app/sessions/page.tsx +0 -39
  90. package/pulse/app/settings/page.tsx +0 -188
  91. package/pulse/app/todos/page.tsx +0 -211
  92. package/pulse/app/tools/page.tsx +0 -249
  93. package/pulse/cli.js +0 -164
  94. package/pulse/components/activity/day-of-week-chart.tsx +0 -35
  95. package/pulse/components/activity/streak-card.tsx +0 -36
  96. package/pulse/components/costs/cache-efficiency-panel.tsx +0 -76
  97. package/pulse/components/costs/cost-by-project-chart.tsx +0 -48
  98. package/pulse/components/costs/cost-over-time-chart.tsx +0 -95
  99. package/pulse/components/costs/model-token-table.tsx +0 -60
  100. package/pulse/components/global-search.tsx +0 -193
  101. package/pulse/components/keyboard-nav-provider.tsx +0 -23
  102. package/pulse/components/layout/bottom-nav.tsx +0 -53
  103. package/pulse/components/layout/client-layout.tsx +0 -31
  104. package/pulse/components/layout/sidebar-context.tsx +0 -50
  105. package/pulse/components/layout/sidebar.tsx +0 -183
  106. package/pulse/components/layout/top-bar.tsx +0 -121
  107. package/pulse/components/overview/activity-heatmap.tsx +0 -107
  108. package/pulse/components/overview/conversation-table.tsx +0 -148
  109. package/pulse/components/overview/model-breakdown-donut.tsx +0 -95
  110. package/pulse/components/overview/peak-hours-chart.tsx +0 -87
  111. package/pulse/components/overview/project-activity-donut.tsx +0 -96
  112. package/pulse/components/overview/stat-card.tsx +0 -102
  113. package/pulse/components/overview/usage-over-time-chart.tsx +0 -166
  114. package/pulse/components/projects/project-card.tsx +0 -175
  115. package/pulse/components/sessions/replay/assistant-markdown.tsx +0 -94
  116. package/pulse/components/sessions/replay/compaction-card.tsx +0 -25
  117. package/pulse/components/sessions/replay/session-sidebar.tsx +0 -231
  118. package/pulse/components/sessions/replay/token-accumulation-chart.tsx +0 -98
  119. package/pulse/components/sessions/replay/tool-call-badge.tsx +0 -127
  120. package/pulse/components/sessions/replay/turn-cards.tsx +0 -220
  121. package/pulse/components/sessions/replay/user-tool-result.tsx +0 -158
  122. package/pulse/components/sessions/session-badges.tsx +0 -49
  123. package/pulse/components/sessions/session-table.tsx +0 -299
  124. package/pulse/components/theme-provider.tsx +0 -44
  125. package/pulse/components/tools/feature-adoption-table.tsx +0 -58
  126. package/pulse/components/tools/mcp-server-panel.tsx +0 -45
  127. package/pulse/components/tools/tool-ranking-chart.tsx +0 -57
  128. package/pulse/components/tools/version-history-table.tsx +0 -32
  129. package/pulse/components/ui/alert.tsx +0 -66
  130. package/pulse/components/ui/badge.tsx +0 -48
  131. package/pulse/components/ui/breadcrumb.tsx +0 -109
  132. package/pulse/components/ui/button.tsx +0 -64
  133. package/pulse/components/ui/calendar.tsx +0 -220
  134. package/pulse/components/ui/card.tsx +0 -92
  135. package/pulse/components/ui/command.tsx +0 -158
  136. package/pulse/components/ui/dialog.tsx +0 -158
  137. package/pulse/components/ui/input.tsx +0 -21
  138. package/pulse/components/ui/popover.tsx +0 -89
  139. package/pulse/components/ui/progress.tsx +0 -31
  140. package/pulse/components/ui/select.tsx +0 -190
  141. package/pulse/components/ui/separator.tsx +0 -28
  142. package/pulse/components/ui/sheet.tsx +0 -143
  143. package/pulse/components/ui/skeleton.tsx +0 -13
  144. package/pulse/components/ui/table.tsx +0 -116
  145. package/pulse/components/ui/tabs.tsx +0 -91
  146. package/pulse/components/ui/tooltip.tsx +0 -57
  147. package/pulse/components/use-global-keyboard-nav.ts +0 -79
  148. package/pulse/components.json +0 -23
  149. package/pulse/eslint.config.mjs +0 -18
  150. package/pulse/lib/bahulam-paths.ts +0 -23
  151. package/pulse/lib/claude-reader.ts +0 -592
  152. package/pulse/lib/decode.ts +0 -129
  153. package/pulse/lib/pricing.ts +0 -102
  154. package/pulse/lib/replay-parser.ts +0 -165
  155. package/pulse/lib/tool-categories.ts +0 -140
  156. package/pulse/lib/utils.ts +0 -6
  157. package/pulse/next-env.d.ts +0 -6
  158. package/pulse/next.config.ts +0 -16
  159. package/pulse/package.json +0 -45
  160. package/pulse/postcss.config.mjs +0 -7
  161. package/pulse/public/activity.png +0 -0
  162. package/pulse/public/cc-lens.png +0 -0
  163. package/pulse/public/command-k.png +0 -0
  164. package/pulse/public/costs.png +0 -0
  165. package/pulse/public/dashboard-dark.png +0 -0
  166. package/pulse/public/dashboard-white.png +0 -0
  167. package/pulse/public/export.png +0 -0
  168. package/pulse/public/file.svg +0 -1
  169. package/pulse/public/globe.svg +0 -1
  170. package/pulse/public/next.svg +0 -1
  171. package/pulse/public/projects.png +0 -0
  172. package/pulse/public/session-chat.png +0 -0
  173. package/pulse/public/todos.png +0 -0
  174. package/pulse/public/tools.png +0 -0
  175. package/pulse/public/vercel.svg +0 -1
  176. package/pulse/public/window.svg +0 -1
  177. package/pulse/tsconfig.json +0 -34
  178. 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
- }