termcast 1.5.0 → 1.7.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.
Files changed (106) hide show
  1. package/dist/build.d.ts.map +1 -1
  2. package/dist/build.js +22 -5
  3. package/dist/build.js.map +1 -1
  4. package/dist/compile.d.ts.map +1 -1
  5. package/dist/compile.js +7 -1
  6. package/dist/compile.js.map +1 -1
  7. package/dist/components/bar-chart.d.ts.map +1 -1
  8. package/dist/components/bar-chart.js +14 -3
  9. package/dist/components/bar-chart.js.map +1 -1
  10. package/dist/components/bar-graph.d.ts +4 -4
  11. package/dist/components/bar-graph.d.ts.map +1 -1
  12. package/dist/components/bar-graph.js +23 -5
  13. package/dist/components/bar-graph.js.map +1 -1
  14. package/dist/components/candle-chart.d.ts +15 -0
  15. package/dist/components/candle-chart.d.ts.map +1 -1
  16. package/dist/components/candle-chart.js +41 -3
  17. package/dist/components/candle-chart.js.map +1 -1
  18. package/dist/components/chart-tooltip.d.ts +83 -0
  19. package/dist/components/chart-tooltip.d.ts.map +1 -0
  20. package/dist/components/chart-tooltip.js +127 -0
  21. package/dist/components/chart-tooltip.js.map +1 -0
  22. package/dist/components/dotted-line-graph.d.ts +11 -0
  23. package/dist/components/dotted-line-graph.d.ts.map +1 -1
  24. package/dist/components/dotted-line-graph.js +43 -2
  25. package/dist/components/dotted-line-graph.js.map +1 -1
  26. package/dist/components/graph.d.ts +11 -0
  27. package/dist/components/graph.d.ts.map +1 -1
  28. package/dist/components/graph.js +53 -4
  29. package/dist/components/graph.js.map +1 -1
  30. package/dist/components/horizontal-bar-graph.d.ts.map +1 -1
  31. package/dist/components/horizontal-bar-graph.js +16 -5
  32. package/dist/components/horizontal-bar-graph.js.map +1 -1
  33. package/dist/components/list.d.ts +7 -0
  34. package/dist/components/list.d.ts.map +1 -1
  35. package/dist/components/list.js +75 -14
  36. package/dist/components/list.js.map +1 -1
  37. package/dist/examples/chart-tooltips.d.ts +2 -0
  38. package/dist/examples/chart-tooltips.d.ts.map +1 -0
  39. package/dist/examples/chart-tooltips.js +16 -0
  40. package/dist/examples/chart-tooltips.js.map +1 -0
  41. package/dist/examples/list-detail-height-ratchet.d.ts +2 -0
  42. package/dist/examples/list-detail-height-ratchet.d.ts.map +1 -0
  43. package/dist/examples/list-detail-height-ratchet.js +26 -0
  44. package/dist/examples/list-detail-height-ratchet.js.map +1 -0
  45. package/dist/extensions/dev.d.ts.map +1 -1
  46. package/dist/extensions/dev.js +1 -0
  47. package/dist/extensions/dev.js.map +1 -1
  48. package/dist/globals.js +8 -0
  49. package/dist/globals.js.map +1 -1
  50. package/dist/package-json.d.ts +2 -0
  51. package/dist/package-json.d.ts.map +1 -1
  52. package/dist/package-json.js +20 -17
  53. package/dist/package-json.js.map +1 -1
  54. package/dist/profiler.d.ts +2 -0
  55. package/dist/profiler.d.ts.map +1 -0
  56. package/dist/profiler.js +390 -0
  57. package/dist/profiler.js.map +1 -0
  58. package/package.json +14 -15
  59. package/src/build.tsx +27 -5
  60. package/src/cli.tsx +0 -0
  61. package/src/compile.tsx +9 -1
  62. package/src/compile.vitest.tsx +8 -8
  63. package/src/components/bar-chart.tsx +23 -3
  64. package/src/components/bar-graph.tsx +32 -13
  65. package/src/components/candle-chart.tsx +63 -16
  66. package/src/components/chart-tooltip.tsx +191 -0
  67. package/src/components/dotted-line-graph.tsx +49 -3
  68. package/src/components/graph.tsx +76 -18
  69. package/src/components/horizontal-bar-graph.tsx +24 -4
  70. package/src/components/list.tsx +93 -20
  71. package/src/examples/action-shortcut.vitest.tsx +4 -4
  72. package/src/examples/actions-context.vitest.tsx +2 -2
  73. package/src/examples/bar-graph-weekly.vitest.tsx +97 -97
  74. package/src/examples/chart-tooltips.tsx +54 -0
  75. package/src/examples/form-basic.vitest.tsx +8 -8
  76. package/src/examples/github.vitest.tsx +19 -28
  77. package/src/examples/graph-bar-chart.vitest.tsx +40 -40
  78. package/src/examples/graph-polymarket.vitest.tsx +24 -24
  79. package/src/examples/graph-row.vitest.tsx +8 -8
  80. package/src/examples/graph-styles.vitest.tsx +65 -65
  81. package/src/examples/horizontal-bar-graph-weekly.vitest.tsx +52 -52
  82. package/src/examples/list-detail-height-ratchet.tsx +48 -0
  83. package/src/examples/list-detail-height-ratchet.vitest.tsx +161 -0
  84. package/src/examples/list-detail-metadata.vitest.tsx +49 -49
  85. package/src/examples/list-dropdown-default.vitest.tsx +27 -27
  86. package/src/examples/list-fetch-data.vitest.tsx +3 -3
  87. package/src/examples/list-item-accessories.vitest.tsx +2 -2
  88. package/src/examples/list-loading-empty-view.vitest.tsx +1 -1
  89. package/src/examples/list-no-actions.vitest.tsx +3 -3
  90. package/src/examples/list-scrollbox.vitest.tsx +6 -6
  91. package/src/examples/list-spacing-mode.vitest.tsx +3 -3
  92. package/src/examples/list-with-detail.vitest.tsx +11 -11
  93. package/src/examples/list-with-dropdown.vitest.tsx +7 -7
  94. package/src/examples/list-with-sections.vitest.tsx +32 -32
  95. package/src/examples/list-with-toast.vitest.tsx +4 -4
  96. package/src/examples/simple-candle-chart.vitest.tsx +63 -61
  97. package/src/examples/simple-grid.vitest.tsx +13 -13
  98. package/src/examples/simple-navigation.vitest.tsx +25 -25
  99. package/src/examples/simple-progress-bar.vitest.tsx +8 -8
  100. package/src/examples/swift-extension.vitest.tsx +3 -3
  101. package/src/examples/toast-action.vitest.tsx +4 -4
  102. package/src/extensions/dev.tsx +2 -1
  103. package/src/extensions/dev.vitest.tsx +17 -17
  104. package/src/globals.ts +9 -0
  105. package/src/package-json.tsx +24 -23
  106. package/src/profiler.tsx +487 -0
@@ -0,0 +1,390 @@
1
+ // React component profiler for termcast.
2
+ //
3
+ // Captures React 19.2+ PerformanceMeasure entries emitted by the development
4
+ // reconciler (react-reconciler/cjs/react-reconciler.development.js) and writes
5
+ // a .cpuprofile file on process exit. Analyze with profano:
6
+ //
7
+ // TERMCAST_REACT_PROFILE=1 termcast dev ./my-extension
8
+ // bunx profano ./tmp/react-profile-*.cpuprofile --sort self
9
+ // bunx profano ./tmp/react-profile-*.cpuprofile --sort total
10
+ //
11
+ // The reconciler emits performance.measure() calls with:
12
+ // - name: "\u200b" + componentName (component renders)
13
+ // - name: trigger string like "Mount", "Cascading Update", etc.
14
+ // - detail.devtools.track: "Components ⚛" for component renders
15
+ //
16
+ // NOTE: React also emits some entries via console.timeStamp() which are not
17
+ // captured by PerformanceObserver. This profiler captures a useful subset of
18
+ // React's performance track data, not every component render.
19
+ //
20
+ // The profile builds a best-effort call tree from time containment: if measure
21
+ // A fully contains measure B in time, B becomes a child of A. Partially
22
+ // overlapping measures are attached to the nearest containing ancestor.
23
+ // This gives meaningful self vs total times in profano output:
24
+ // - total time = all time inside a measure (including children)
25
+ // - self time = time not attributed to any child measure
26
+ //
27
+ // Requirements:
28
+ // - React 19.2+ in development mode (NODE_ENV !== 'production')
29
+ // - PerformanceObserver available (Node.js 16+, Bun)
30
+ import fs from 'node:fs';
31
+ import path from 'node:path';
32
+ import { logger } from './logger';
33
+ const measures = [];
34
+ let observerInstalled = false;
35
+ let profileWritten = false;
36
+ let perfObserver = null;
37
+ function writeProfileOnce() {
38
+ if (profileWritten) {
39
+ return;
40
+ }
41
+ profileWritten = true;
42
+ // Drain any queued records not yet delivered by the observer callback,
43
+ // so the last measures before exit are captured.
44
+ if (perfObserver) {
45
+ for (const entry of perfObserver.takeRecords()) {
46
+ const detail = entry.detail;
47
+ if (!detail?.devtools?.track) {
48
+ continue;
49
+ }
50
+ measures.push({
51
+ name: entry.name,
52
+ duration: entry.duration,
53
+ startTime: entry.startTime,
54
+ track: detail.devtools.track,
55
+ });
56
+ }
57
+ }
58
+ writeProfile();
59
+ }
60
+ export function installProfiler() {
61
+ if (observerInstalled) {
62
+ return;
63
+ }
64
+ if (typeof PerformanceObserver === 'undefined') {
65
+ logger.error('PerformanceObserver not available, profiling disabled');
66
+ return;
67
+ }
68
+ observerInstalled = true;
69
+ perfObserver = new PerformanceObserver((list) => {
70
+ for (const entry of list.getEntries()) {
71
+ const detail = entry.detail;
72
+ if (!detail?.devtools?.track) {
73
+ continue;
74
+ }
75
+ measures.push({
76
+ name: entry.name,
77
+ duration: entry.duration,
78
+ startTime: entry.startTime,
79
+ track: detail.devtools.track,
80
+ });
81
+ }
82
+ });
83
+ perfObserver.observe({ type: 'measure', buffered: true });
84
+ // Hook into the devtools fiber tree to collect component source locations
85
+ // from _debugStack on each commit. This builds the componentSourceMap
86
+ // incrementally as components render.
87
+ installFiberHook();
88
+ // Write profile on exit signals. The named function reference is used so
89
+ // removeListener actually removes the correct handler, preventing recursion
90
+ // when process.kill re-raises the signal.
91
+ const handleSignal = (signal) => {
92
+ writeProfileOnce();
93
+ process.removeListener(signal, handleSignal);
94
+ process.kill(process.pid, signal);
95
+ };
96
+ process.on('SIGINT', handleSignal);
97
+ process.on('SIGTERM', handleSignal);
98
+ process.on('exit', writeProfileOnce);
99
+ logger.log('React profiler installed. Profile will be written on exit.');
100
+ }
101
+ // Build a call tree from time containment: if span A fully contains span B,
102
+ // B is a child of A. Each unique (track, name) pair can appear at multiple
103
+ // tree positions when it occurs inside different parent spans.
104
+ //
105
+ // The algorithm: sort spans longest-first so parents come before children.
106
+ // For each span, walk the tree from root finding the deepest ancestor that
107
+ // contains it, then attach a new node there. Each tree node gets a unique ID
108
+ // even if the same component name appears multiple times (different call sites).
109
+ function buildCallTree({ spans, sourceMap }) {
110
+ const ROOT_ID = 1;
111
+ const IDLE_ID = 2;
112
+ const nodes = [
113
+ {
114
+ id: ROOT_ID,
115
+ callFrame: { functionName: '(root)', scriptId: '0', url: '', lineNumber: -1, columnNumber: -1 },
116
+ children: [IDLE_ID],
117
+ },
118
+ {
119
+ id: IDLE_ID,
120
+ callFrame: { functionName: '(idle)', scriptId: '0', url: '', lineNumber: -1, columnNumber: -1 },
121
+ children: [],
122
+ },
123
+ ];
124
+ let nextId = 3;
125
+ const rootEntry = {
126
+ nodeId: ROOT_ID,
127
+ startUs: -Infinity,
128
+ endUs: Infinity,
129
+ children: [],
130
+ };
131
+ // Sort spans by duration descending so parents (longer) are inserted first
132
+ const indexed = spans.map((s, i) => ({ ...s, originalIndex: i }));
133
+ indexed.sort((a, b) => (b.endUs - b.startUs) - (a.endUs - a.startUs));
134
+ // Map from original span index to the leaf node ID for sampling
135
+ const spanToLeafId = new Map();
136
+ for (const span of indexed) {
137
+ // Find deepest ancestor in the tree that fully contains this span
138
+ const parent = findDeepestContainer(rootEntry, span.startUs, span.endUs);
139
+ const id = nextId++;
140
+ const newEntry = {
141
+ nodeId: id,
142
+ startUs: span.startUs,
143
+ endUs: span.endUs,
144
+ children: [],
145
+ };
146
+ // Resolve source file path from the component name.
147
+ // Falls back to the React track name (e.g. "Components ⚛") for scheduler
148
+ // events and components not found in source.
149
+ // scriptId is stable per source identity so profano aggregates repeated
150
+ // renders of the same component into one row.
151
+ const sourcePath = sourceMap.get(span.name);
152
+ const sourceMatch = sourcePath ? /^(.*):(\d+)$/.exec(sourcePath) : null;
153
+ const url = sourceMatch ? sourceMatch[1] : (sourcePath || span.track);
154
+ const lineNumber = sourceMatch ? Number(sourceMatch[2]) : -1;
155
+ const scriptId = sourcePath || `${span.track}:${span.name}`;
156
+ nodes.push({
157
+ id,
158
+ callFrame: {
159
+ functionName: span.name,
160
+ scriptId,
161
+ url,
162
+ lineNumber,
163
+ columnNumber: -1,
164
+ },
165
+ children: [],
166
+ });
167
+ // Add as child of parent in both the tree and the profile nodes
168
+ parent.children.push(newEntry);
169
+ const parentNode = nodes.find((n) => n.id === parent.nodeId);
170
+ if (!parentNode.children.includes(id)) {
171
+ parentNode.children.push(id);
172
+ }
173
+ spanToLeafId.set(span.originalIndex, id);
174
+ }
175
+ return { nodes, spanToLeafId };
176
+ }
177
+ function findDeepestContainer(entry, startUs, endUs) {
178
+ // Check children for a tighter fit
179
+ for (const child of entry.children) {
180
+ if (child.startUs <= startUs && child.endUs >= endUs) {
181
+ return findDeepestContainer(child, startUs, endUs);
182
+ }
183
+ }
184
+ return entry;
185
+ }
186
+ // Component name → source file:line mapping built from React's fiber _debugStack.
187
+ // Populated at runtime by hooking into __REACT_DEVTOOLS_GLOBAL_HOOK__.onCommitFiberRoot
188
+ // which gives us the actual fiber tree with debug stack traces. Each fiber's _debugStack
189
+ // is an Error whose second stack frame points to where the JSX element was created.
190
+ // This is much more accurate than regex scanning source files.
191
+ const componentSourceMap = new Map();
192
+ // Extract "file:line" from a fiber's _debugStack Error.
193
+ // The stack trace format varies between Bun and Node:
194
+ // at functionName (/path/to/file.tsx:42:5) — named frame
195
+ // at /path/to/file.tsx:42:5 — anonymous frame
196
+ // We want the first non-React-internal frame. node_modules paths are kept
197
+ // so components from third-party packages show their real location.
198
+ function parseFrameLocation(frame) {
199
+ // Named frame: at fn (/path/file.tsx:1:2)
200
+ const parenthesized = /\((.+):(\d+):\d+\)\s*$/.exec(frame);
201
+ if (parenthesized) {
202
+ return { filePath: parenthesized[1], line: parenthesized[2] };
203
+ }
204
+ // Anonymous frame: at /path/file.tsx:1:2
205
+ const bare = /^\s*at (.+):(\d+):\d+\s*$/.exec(frame);
206
+ if (bare) {
207
+ return { filePath: bare[1], line: bare[2] };
208
+ }
209
+ return null;
210
+ }
211
+ function extractSourceFromFiber(fiber) {
212
+ const debugStack = fiber._debugStack;
213
+ if (!debugStack) {
214
+ return null;
215
+ }
216
+ const stack = debugStack.stack || String(debugStack);
217
+ const frames = stack.split('\n');
218
+ for (const frame of frames) {
219
+ // Skip only React internals, keep everything else including node_modules
220
+ if (frame.includes('react.development') ||
221
+ frame.includes('react-jsx') ||
222
+ frame.includes('react-reconciler')) {
223
+ continue;
224
+ }
225
+ const loc = parseFrameLocation(frame);
226
+ if (loc) {
227
+ const relativePath = path.relative(process.cwd(), loc.filePath);
228
+ return `${relativePath}:${loc.line}`;
229
+ }
230
+ }
231
+ return null;
232
+ }
233
+ // Get display name from a fiber, handling memo/forwardRef wrappers.
234
+ // React wraps components in objects with .type or .render for these HOCs.
235
+ function getFiberComponentName(fiber) {
236
+ const type = fiber.type;
237
+ if (!type) {
238
+ return null;
239
+ }
240
+ // Direct function component or class
241
+ if (type.displayName || type.name) {
242
+ return type.displayName || type.name;
243
+ }
244
+ // memo(Component) — type is { $$typeof: REACT_MEMO_TYPE, type: innerComponent }
245
+ if (type.type?.displayName || type.type?.name) {
246
+ return type.type.displayName || type.type.name;
247
+ }
248
+ // forwardRef(Component) — type is { $$typeof: REACT_FORWARD_REF_TYPE, render: fn }
249
+ if (type.render?.displayName || type.render?.name) {
250
+ return type.render.displayName || type.render.name;
251
+ }
252
+ return null;
253
+ }
254
+ // Iterative fiber tree walk to avoid stack overflow on large flat lists.
255
+ // Sibling chains can be hundreds deep; recursion would overflow.
256
+ function walkFiberTree(root) {
257
+ const stack = root ? [root] : [];
258
+ while (stack.length > 0) {
259
+ const fiber = stack.pop();
260
+ const name = getFiberComponentName(fiber);
261
+ if (name && !componentSourceMap.has(name)) {
262
+ const source = extractSourceFromFiber(fiber);
263
+ if (source) {
264
+ componentSourceMap.set(name, source);
265
+ }
266
+ }
267
+ if (fiber.sibling) {
268
+ stack.push(fiber.sibling);
269
+ }
270
+ if (fiber.child) {
271
+ stack.push(fiber.child);
272
+ }
273
+ }
274
+ }
275
+ function installFiberHook() {
276
+ const hook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
277
+ if (!hook) {
278
+ return;
279
+ }
280
+ // Preserve all arguments and `this` so React Refresh and other hooks
281
+ // that depend on priorityLevel and didError continue to work.
282
+ const originalOnCommit = hook.onCommitFiberRoot;
283
+ hook.onCommitFiberRoot = function (...args) {
284
+ const root = args[1];
285
+ try {
286
+ walkFiberTree(root?.current);
287
+ }
288
+ finally {
289
+ return originalOnCommit?.apply(this, args);
290
+ }
291
+ };
292
+ }
293
+ function writeProfile() {
294
+ if (measures.length === 0) {
295
+ logger.log('No React performance measures captured, skipping profile write');
296
+ return;
297
+ }
298
+ const TICK = 1000; // microseconds per sample (1ms resolution)
299
+ // componentSourceMap was populated incrementally by the fiber hook during rendering
300
+ const sourceMap = componentSourceMap;
301
+ const sorted = [...measures].sort((a, b) => a.startTime - b.startTime);
302
+ const t0 = sorted[0].startTime;
303
+ const endUs = Math.round((Math.max(...sorted.map((m) => m.startTime + m.duration)) - t0) * 1000);
304
+ // Convert measures to spans with microsecond timestamps
305
+ const spans = sorted.map((m) => ({
306
+ startUs: Math.round((m.startTime - t0) * 1000),
307
+ endUs: Math.round((m.startTime + m.duration - t0) * 1000),
308
+ name: m.name.replace('\u200b', ''),
309
+ track: m.track,
310
+ }));
311
+ // Build call tree from time containment, passing sourceMap for file paths
312
+ const { nodes, spanToLeafId } = buildCallTree({ spans, sourceMap });
313
+ // Generate samples only over active span windows and compress idle gaps.
314
+ // Instead of iterating every tick across the full timeline (which is O(ticks * spans)
315
+ // and can hang for long sessions), collect all span boundaries, sort them, and only
316
+ // sample within active windows. Idle gaps between windows become a single idle sample
317
+ // with a large timeDelta.
318
+ const samples = [];
319
+ const timeDeltas = [];
320
+ const IDLE_ID = 2;
321
+ // Collect unique boundary times from all spans
322
+ const boundaries = new Set();
323
+ for (const span of spans) {
324
+ boundaries.add(span.startUs);
325
+ boundaries.add(span.endUs);
326
+ }
327
+ // Add timeline start/end
328
+ boundaries.add(0);
329
+ boundaries.add(endUs);
330
+ const sortedBoundaries = [...boundaries].sort((a, b) => a - b);
331
+ // Sort spans narrowest-first for fast deepest-leaf lookup
332
+ const spansByNarrowest = spans
333
+ .map((s, i) => ({ ...s, idx: i }))
334
+ .sort((a, b) => (a.endUs - a.startUs) - (b.endUs - b.startUs));
335
+ // For each window between consecutive boundaries, determine if any span is
336
+ // active. If yes, sample at TICK resolution. If no, emit one idle sample.
337
+ for (let w = 0; w < sortedBoundaries.length - 1; w++) {
338
+ const windowStart = sortedBoundaries[w];
339
+ const windowEnd = sortedBoundaries[w + 1];
340
+ if (windowStart >= windowEnd) {
341
+ continue;
342
+ }
343
+ // Check if any span is active at the midpoint of this window
344
+ const mid = windowStart + Math.floor((windowEnd - windowStart) / 2);
345
+ let hasActiveSpan = false;
346
+ for (const span of spansByNarrowest) {
347
+ if (mid >= span.startUs && mid < span.endUs) {
348
+ hasActiveSpan = true;
349
+ break;
350
+ }
351
+ }
352
+ if (!hasActiveSpan) {
353
+ // Compress idle window into a single sample
354
+ samples.push(IDLE_ID);
355
+ timeDeltas.push(windowEnd - windowStart);
356
+ continue;
357
+ }
358
+ // Sample at TICK resolution within this active window.
359
+ // Use Math.min so the last sample's timeDelta covers only the remainder,
360
+ // preventing inflation when the window is shorter than TICK or not divisible.
361
+ for (let t = windowStart; t < windowEnd; t += TICK) {
362
+ const nextT = Math.min(t + TICK, windowEnd);
363
+ let leafId = IDLE_ID;
364
+ for (const span of spansByNarrowest) {
365
+ if (t >= span.startUs && t < span.endUs) {
366
+ leafId = spanToLeafId.get(span.idx) ?? IDLE_ID;
367
+ break;
368
+ }
369
+ }
370
+ samples.push(leafId);
371
+ timeDeltas.push(nextT - t);
372
+ }
373
+ }
374
+ const profile = {
375
+ nodes,
376
+ samples,
377
+ startTime: 0,
378
+ endTime: endUs,
379
+ timeDeltas,
380
+ };
381
+ // Write to ./tmp/react-profile.cpuprofile relative to cwd
382
+ const outDir = path.join(process.cwd(), 'tmp');
383
+ fs.mkdirSync(outDir, { recursive: true });
384
+ const outPath = path.join(outDir, `react-profile-${Date.now()}.cpuprofile`);
385
+ fs.writeFileSync(outPath, JSON.stringify(profile));
386
+ const activeSamples = samples.filter((s) => s !== 2).length;
387
+ logger.log(`Wrote React profile: ${outPath} (${measures.length} measures, ${nodes.length} nodes, ${activeSamples} active / ${samples.length} total samples)`);
388
+ console.error(`\nReact profile written: ${outPath}\nAnalyze with: npx profano ${outPath} --sort self`);
389
+ }
390
+ //# sourceMappingURL=profiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiler.js","sourceRoot":"","sources":["../src/profiler.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,18 +1,9 @@
1
1
  {
2
2
  "name": "termcast",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Raycast for the terminal",
5
5
  "repository": "https://github.com/remorses/termcast",
6
6
  "types": "./dist/index.d.ts",
7
- "scripts": {
8
- "build": "tsc",
9
- "prepublishOnly": "bun x tsc && chmod +x dist/cli.js",
10
- "release": "cd .. && bun changeset version && /Users/morse/Documents/GitHub/changesets/packages/cli/bin.js publish # my own fork to support github releases",
11
- "see-raycast-types": "zed node_modules/@raycast/api/types/index.d.ts",
12
- "play": "bun src/cli.tsx",
13
- "test": "bun test && bun e2e",
14
- "e2e": "vitest -u"
15
- },
16
7
  "bin": {
17
8
  "termcast": "./dist/cli.js"
18
9
  },
@@ -94,7 +85,6 @@
94
85
  "@parcel/watcher": "^2.5.6",
95
86
  "@tanstack/react-query": "^5.85.5",
96
87
  "@tanstack/react-query-persist-client": "^5.85.5",
97
- "@termcast/utils": "^2.2.8",
98
88
  "change-case": "^5.4.4",
99
89
  "colord": "^2.9.3",
100
90
  "goke": "^6.6.0",
@@ -107,7 +97,8 @@
107
97
  "react-refresh": "^0.18.0",
108
98
  "simple-plist": "^1.3.1",
109
99
  "string-dedent": "^3.0.2",
110
- "zustand": "^5.0.8"
100
+ "zustand": "^5.0.8",
101
+ "@termcast/utils": "^2.2.8"
111
102
  },
112
103
  "peerDependencies": {
113
104
  "@opentui/core": "^0.2.12",
@@ -126,10 +117,18 @@
126
117
  "bun-pty": "0.4.8",
127
118
  "zigpty": "^0.0.4",
128
119
  "react": "^19.2.4",
129
- "tuistory": "^0.7.0",
130
- "vitest": "^4.0.16"
120
+ "vitest": "^4.0.16",
121
+ "tuistory": "^0.8.1"
131
122
  },
132
123
  "optionalDependencies": {
133
124
  "sharp": "^0.34.5"
125
+ },
126
+ "scripts": {
127
+ "build": "tsc",
128
+ "release": "cd .. && pnpm changeset version && /Users/morse/Documents/GitHub/changesets/packages/cli/bin.js publish # my own fork to support github releases",
129
+ "see-raycast-types": "zed node_modules/@raycast/api/types/index.d.ts",
130
+ "play": "bun src/cli.tsx",
131
+ "test": "bun test && pnpm e2e",
132
+ "e2e": "vitest -u"
134
133
  }
135
- }
134
+ }
package/src/build.tsx CHANGED
@@ -244,13 +244,35 @@ export async function buildExtensionCommands({
244
244
  packageJsonPath: path.join(resolvedPath, 'package.json'),
245
245
  })
246
246
 
247
- // Filter existing command files as entrypoints
248
- const entrypoints = commandsData.commands
249
- .filter((cmd) => cmd.exists)
250
- .map((cmd) => cmd.filePath)
247
+ // Build entrypoints. For directory-style entries (e.g. src/tui/index.tsx),
248
+ // generate a wrapper file named {command}.tsx in the bundle dir so Bun
249
+ // outputs {command}.js instead of index.js. This avoids collisions when
250
+ // multiple commands use directory entries (Bun fails with "Multiple files
251
+ // share the same output path ./index.js" otherwise).
252
+ const existingCommands = commandsData.commands.filter((cmd) => cmd.exists)
253
+ const entrypoints = existingCommands.map((cmd) => {
254
+ const isDirectoryEntry = path.basename(cmd.filePath).startsWith('index.')
255
+ if (isDirectoryEntry) {
256
+ const wrapperPath = path.join(bundleDir, `${cmd.name}.tsx`)
257
+ fs.writeFileSync(
258
+ wrapperPath,
259
+ `export * from ${JSON.stringify(cmd.filePath)};\nexport { default } from ${JSON.stringify(cmd.filePath)};\n`,
260
+ )
261
+ return wrapperPath
262
+ }
263
+ return cmd.filePath
264
+ })
251
265
 
252
266
  if (entrypoints.length === 0) {
253
- throw new Error('No command files found to build')
267
+ const tried = commandsData.commands
268
+ .map((cmd) => {
269
+ const paths = cmd.checkedPaths.map((p) => ` ${p}`).join('\n')
270
+ return ` command "${cmd.name}" checked:\n${paths}`
271
+ })
272
+ .join('\n')
273
+ throw new Error(
274
+ `No command files found to build.\n${tried}\n\nEach command in package.json "commands" needs a matching file: {name}.tsx or {name}/index.tsx in the project root or src/`,
275
+ )
254
276
  }
255
277
 
256
278
  logger.log(`Building ${entrypoints.length} commands...`)
package/src/cli.tsx CHANGED
File without changes
package/src/compile.tsx CHANGED
@@ -190,7 +190,15 @@ export async function compileExtension({
190
190
  if (!entry) {
191
191
  const existingCommands = commands.filter((cmd) => cmd.exists)
192
192
  if (existingCommands.length === 0) {
193
- throw new Error('No command files found to build')
193
+ const tried = commands
194
+ .map((cmd) => {
195
+ const paths = cmd.checkedPaths.map((p) => ` ${p}`).join('\n')
196
+ return ` command "${cmd.name}" checked:\n${paths}`
197
+ })
198
+ .join('\n')
199
+ throw new Error(
200
+ `No command files found to build.\n${tried}\n\nEach command in package.json "commands" needs a matching file: {name}.tsx or {name}/index.tsx in the project root or src/`,
201
+ )
194
202
  }
195
203
  logger.log(`Compiling ${existingCommands.length} commands...`)
196
204
  } else {
@@ -90,13 +90,13 @@ test('compile extension and run executable', async () => {
90
90
 
91
91
  Commands
92
92
  ›List Items Displays a simple list with some items view
93
- Sear...temsSearch and filter...gh a list of items view
93
+ Search ItemsSearch and filter...h a list of items view
94
94
  Google Oauth view
95
- useP...DemoShows how to use ...rom @raycast/utils view
96
- Sh...ateShows the current a...tate in JSON format view
97
-
98
-
99
- run command ↑↓ navigate ^k actions :vim
95
+ usePr...e Dem Shows how to use...m @raycast/utils view
96
+ Show Stat Shows the current ...ate in JSON format view
97
+ With ...mentsDemonstrates com...ssword, dropdown) view
98
+ Quic...tionCopies current t...t showing a view no-view
99
+ Throw ErrorCommand that thro...rror at root scope view
100
100
  "
101
101
  `)
102
102
  }, 60000)
@@ -142,7 +142,7 @@ test('compiled executable can run command', async () => {
142
142
  ○ Fifth Item This is the fifth item
143
143
 
144
144
 
145
- ✓ Copied to Clipboard First Item
145
+
146
146
  "
147
147
  `)
148
148
  }, 60000)
@@ -198,7 +198,7 @@ test('compiled executable can navigate back', async () => {
198
198
  ○ Fifth Item This is the fifth item
199
199
 
200
200
 
201
- ↵ copy item title ↑↓ navigate ^k actions :vim
201
+
202
202
  "
203
203
  `)
204
204
  }, 60000)
@@ -12,9 +12,11 @@
12
12
  * primary, accent, info, success, warning, error, secondary (cycles with %)
13
13
  */
14
14
 
15
- import React, { ReactNode, useMemo } from 'react'
15
+ import React, { ReactNode, useMemo, useRef } from 'react'
16
+ import type { MouseEvent as OpenTUIMouseEvent } from '@opentui/core'
16
17
  import { useTheme, getThemePalette } from 'termcast/src/theme'
17
18
  import { Color, resolveColor } from 'termcast/src/colors'
19
+ import { ChartTooltip, useChartTooltip, formatTooltipLine } from 'termcast/src/components/chart-tooltip'
18
20
 
19
21
  // ── Types ────────────────────────────────────────────────────────────
20
22
 
@@ -173,6 +175,8 @@ function LabelRow({ segments, labelMap, position, color }: {
173
175
  const BarChart: BarChartType = (props) => {
174
176
  const theme = useTheme()
175
177
  const { height = 1, showLabels = true, children } = props
178
+ const containerRef = useRef<any>(null)
179
+ const { tooltip, show: showTooltip, hide: hideTooltip } = useChartTooltip()
176
180
 
177
181
  // Collect segment data from BarChart.Segment children
178
182
  const segments = useMemo<SegmentData[]>(() => {
@@ -242,7 +246,8 @@ const BarChart: BarChartType = (props) => {
242
246
  const belowMap = new Map(below.map((l) => [l.segmentIndex, l]))
243
247
 
244
248
  return (
245
- <box flexDirection="column" width="100%" flexShrink={0}>
249
+ <box ref={containerRef} flexDirection="column" width="100%" flexShrink={0} onMouseOut={hideTooltip}>
250
+ <ChartTooltip tooltip={tooltip} containerRef={containerRef} />
246
251
  <LabelRow
247
252
  segments={visibleSegments}
248
253
  labelMap={aboveMap}
@@ -252,7 +257,22 @@ const BarChart: BarChartType = (props) => {
252
257
  <box flexDirection="row" height={height} width="100%" flexShrink={0}>
253
258
  {visibleSegments.map((seg, i) => {
254
259
  return (
255
- <box key={i} flexGrow={seg.value} flexShrink={0} backgroundColor={seg.resolvedColor} height={height} />
260
+ <box
261
+ key={i}
262
+ flexGrow={seg.value}
263
+ flexShrink={0}
264
+ backgroundColor={seg.resolvedColor}
265
+ height={height}
266
+ onMouseMove={(evt: OpenTUIMouseEvent) => {
267
+ const pct = formatValue(seg.value, total)
268
+ const label = seg.label || `#${i + 1}`
269
+ showTooltip({
270
+ x: evt.x,
271
+ y: evt.y,
272
+ lines: [formatTooltipLine(label, `${seg.value} (${pct})`)],
273
+ })
274
+ }}
275
+ />
256
276
  )
257
277
  })}
258
278
  </box>