@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/main.bundle.js +1433 -1422
- package/app/main.bundle.js.map +3 -3
- package/app/styles.css +41 -5
- package/package.json +1 -1
- package/server/file-access-aggregator.js +74 -2
- package/server/templates-routes.js +17 -1
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 ==
|
|
9342
|
-
|
|
9343
|
-
folder
|
|
9344
|
-
|
|
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
|
@@ -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
|
|
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
|
-
[
|
|
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.
|