@worca/ui 0.42.0 → 0.43.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/app/styles.css CHANGED
@@ -9241,7 +9241,10 @@ body.help-mode-active sl-tab {
9241
9241
  border-right: none;
9242
9242
  }
9243
9243
 
9244
- /* File column header — sticky left, wider than data cells */
9244
+ /* File column header — sticky left, wider than data cells.
9245
+ ``position: relative`` so the drag handle (.access-col-file-resizer)
9246
+ can pin to the right edge with position:absolute. ``sticky`` already
9247
+ establishes a containing block for absolute children. */
9245
9248
  .access-col-file-header {
9246
9249
  position: sticky;
9247
9250
  left: 0;
@@ -9254,6 +9257,37 @@ body.help-mode-active sl-tab {
9254
9257
  border-right: 2px solid var(--border, #e2e8f0);
9255
9258
  }
9256
9259
 
9260
+ /* Drag handle for resizing the file column. Pinned to the right edge of
9261
+ the header; on pointerdown it captures the pointer and rewrites the
9262
+ shared --fa-file-col-width CSS var on the .run-file-access root, which
9263
+ the grid template + sticky cells both read so they move in lockstep. */
9264
+ .access-col-file-resizer {
9265
+ position: absolute;
9266
+ top: 0;
9267
+ right: -3px;
9268
+ width: 7px;
9269
+ height: 100%;
9270
+ cursor: col-resize;
9271
+ user-select: none;
9272
+ touch-action: none;
9273
+ z-index: 5;
9274
+ background: transparent;
9275
+ }
9276
+
9277
+ .access-col-file-resizer:hover,
9278
+ .access-col-file-resizer--active {
9279
+ background: rgba(59, 130, 246, 0.35);
9280
+ }
9281
+
9282
+ /* Body-level cursor lock during a drag (set via JS) — keeps the cursor as
9283
+ col-resize even when the pointer leaves the handle's 7px hit area, which
9284
+ is otherwise easy to do at faster drag speeds. */
9285
+ .access-col-file-resizing,
9286
+ .access-col-file-resizing * {
9287
+ cursor: col-resize !important;
9288
+ user-select: none !important;
9289
+ }
9290
+
9257
9291
  .access-col-header--collapsed {
9258
9292
  background: rgba(59, 130, 246, 0.06);
9259
9293
  color: #3b82f6;
@@ -9338,10 +9372,12 @@ body.help-mode-active sl-tab {
9338
9372
  justify-content: flex-start;
9339
9373
  gap: 6px;
9340
9374
  padding: 3px 8px;
9341
- /* One indent step == a dir's [chevron 18px + gap 6px], so a child's content
9342
- (file name, or sub-folder chevron) starts exactly under its parent's
9343
- folder icon. */
9344
- padding-left: calc(8px + var(--depth, 0) * 24px);
9375
+ /* One indent step == the full chevron + folder-icon prefix on a dir row:
9376
+ chevron 18px + gap 6px + folder-icon 14px + gap 6px = 44px. A child file
9377
+ row (no chevron/folder prefix) lands its name at exactly the x-position
9378
+ of its parent folder's name, and a nested dir row's text aligns with
9379
+ its grandparent's text — what a treegrid is supposed to look like. */
9380
+ padding-left: calc(8px + var(--depth, 0) * 44px);
9345
9381
  background: inherit;
9346
9382
  border-right: 2px solid var(--border, #e2e8f0);
9347
9383
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@worca/ui",
3
- "version": "0.42.0",
3
+ "version": "0.43.0",
4
4
  "description": "Pipeline monitoring UI for worca-cc",
5
5
  "license": "MIT",
6
6
  "author": "Sinisha Djukic",
@@ -361,6 +361,14 @@ function fragmentRecordsToFileAccess(records, repoRoot) {
361
361
  * Relativise an absolute fragment path against the repo root (a live stand-in
362
362
  * for the Python GitPathOracle respelling). Paths already relative, or outside
363
363
  * the repo, are returned unchanged; the repo root itself maps to null.
364
+ *
365
+ * Tolerant recovery: when ``repoRoot`` is a worktree under
366
+ * ``<project>/.worktrees/<id>`` and ``rawPath`` is an absolute path pointing
367
+ * at a sibling clone of the same project (e.g. the main checkout), strip the
368
+ * prefix up to and including the project basename so the entry groups under
369
+ * the project tree instead of rendering the full absolute path. Mirrors the
370
+ * Python ``_recover_basename_tail`` in ``path_canon.py`` so live and
371
+ * completion-time views agree.
364
372
  */
365
373
  function canonicalizePath(rawPath, repoRoot) {
366
374
  if (!rawPath) return null;
@@ -368,7 +376,31 @@ function canonicalizePath(rawPath, repoRoot) {
368
376
  return rawPath.slice(repoRoot.length + 1);
369
377
  }
370
378
  if (rawPath === repoRoot) return null;
371
- return rawPath;
379
+ return recoverWorktreeBasenameTail(rawPath, repoRoot) ?? rawPath;
380
+ }
381
+
382
+ /**
383
+ * Recover repo-relative tail from a raw absolute path that fell outside a
384
+ * worktree root. Returns null when the heuristic doesn't apply so the caller
385
+ * can fall back to the raw path unchanged.
386
+ */
387
+ function recoverWorktreeBasenameTail(rawPath, repoRoot) {
388
+ if (!rawPath || !repoRoot) return null;
389
+ const rootParts = repoRoot.split('/');
390
+ const wtIdx = rootParts.indexOf('.worktrees');
391
+ if (wtIdx <= 0) return null;
392
+ const projectBasename = rootParts[wtIdx - 1];
393
+ if (!projectBasename) return null;
394
+ const rawParts = rawPath.split('/');
395
+ // Scan from the right — the tail closest to the leaf is the most likely
396
+ // intended target when the basename appears more than once.
397
+ for (let i = rawParts.length - 1; i >= 0; i--) {
398
+ if (rawParts[i] === projectBasename) {
399
+ const tail = rawParts.slice(i + 1).join('/');
400
+ return tail || null;
401
+ }
402
+ }
403
+ return null;
372
404
  }
373
405
 
374
406
  function ensureFile(fileData, path) {
@@ -451,7 +483,47 @@ function buildTree(fileData) {
451
483
 
452
484
  // Rollup dir totals and cells bottom-up, then serialise to arrays.
453
485
  rollupDir(root);
454
- return [...root.children.values()].map(serializeNode);
486
+ return collapseSingleChildDirs(
487
+ [...root.children.values()].map(serializeNode),
488
+ );
489
+ }
490
+
491
+ /**
492
+ * Collapse chains of single-child intermediate directories into one synthetic
493
+ * node. Without this, a stray absolute path (e.g. /Volumes/Apps/dev/.../foo.js)
494
+ * renders as a six-deep chain of single-row dirs that buries the filename.
495
+ *
496
+ * Rule: walk bottom-up, then while a dir's only child is also a dir, merge:
497
+ * adopt the child's children/cells/totals (which equal the parent's, since
498
+ * the parent rolled up from the single child) and visually concatenate the
499
+ * names so the collapsed segments stay visible without burning rows.
500
+ *
501
+ * The collapsed node keeps the deeper child's ``path`` so per-file drawer
502
+ * lookups (which key on the file's full path) continue to work.
503
+ */
504
+ function collapseSingleChildDirs(nodes) {
505
+ return nodes.map(collapseNode);
506
+ }
507
+
508
+ function collapseNode(node) {
509
+ if (node.type !== 'dir') return node;
510
+ // Recurse first so the rule applies bottom-up.
511
+ let current = { ...node, children: node.children.map(collapseNode) };
512
+ while (current.children.length === 1 && current.children[0].type === 'dir') {
513
+ const child = current.children[0];
514
+ current = {
515
+ ...current,
516
+ path: child.path,
517
+ name: `${current.name}/${child.name}`,
518
+ children: child.children,
519
+ // cells/totals of a parent with a single dir child equal the child's
520
+ // (rollup invariant), so adopting the child's keeps the row identical
521
+ // to what it would have rendered before the collapse.
522
+ cells: child.cells,
523
+ totals: child.totals,
524
+ };
525
+ }
526
+ return current;
455
527
  }
456
528
 
457
529
  function rollupDir(node) {
@@ -313,6 +313,12 @@ function exportBundle(projectRoot, id, mode = 'standalone') {
313
313
  const dir = mkdtempSync(join(tmpdir(), 'worca-bundle-'));
314
314
  try {
315
315
  const bundlePath = join(dir, `${id}-bundle.out`);
316
+ // --include-models / --include-pricing: bundle the project's worca.models
317
+ // and worca.pricing entries that the template references (e.g. a custom
318
+ // "glm-ds" alias). The CLI already filters to *referenced* aliases via
319
+ // collect_referenced_model_aliases, so built-ins the importer already has
320
+ // (opus/sonnet/haiku) aren't shipped redundantly. Without these flags a
321
+ // bundle that names a custom alias is broken on import.
316
322
  runWorcaTemplates(projectRoot, [
317
323
  'export',
318
324
  '--to',
@@ -321,6 +327,8 @@ function exportBundle(projectRoot, id, mode = 'standalone') {
321
327
  id,
322
328
  '--mode',
323
329
  normalizedMode,
330
+ '--include-models',
331
+ '--include-pricing',
324
332
  ]);
325
333
  if (existsSync(bundlePath)) {
326
334
  const buf = readFileSync(bundlePath);
@@ -354,7 +362,15 @@ function exportBundle(projectRoot, id, mode = 'standalone') {
354
362
  function exportGist(projectRoot, id) {
355
363
  const stdout = runWorcaTemplates(
356
364
  projectRoot,
357
- ['export', '--to', 'gist', '--templates', id],
365
+ [
366
+ 'export',
367
+ '--to',
368
+ 'gist',
369
+ '--templates',
370
+ id,
371
+ '--include-models',
372
+ '--include-pricing',
373
+ ],
358
374
  { timeout: 30000 },
359
375
  );
360
376
  // The CLI prints the gist URL (from `gh gist create`) as its final stdout line.