specrails-desktop 2.59.0 → 2.59.1
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/client/dist/assets/{ActivityFeedPage-DBFt8ZHP.js → ActivityFeedPage-vAY9C71W.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-DiQx0W67.js → AgentBrowserCapture-CpwjGXQm.js} +1 -1
- package/client/dist/assets/{AgentModeAnalyticsPane-BmDSXp_J.js → AgentModeAnalyticsPane-BHeoJVxQ.js} +2 -2
- package/client/dist/assets/{AgentModeCodePane-BLQLPSwr.js → AgentModeCodePane-B0Nf3YkN.js} +2 -2
- package/client/dist/assets/{AgentModeJobsPane-BbTXdKUa.js → AgentModeJobsPane-CpbIItY_.js} +1 -1
- package/client/dist/assets/{AgentsPage-Dei1jGR8.js → AgentsPage-BVJAFXn3.js} +1 -1
- package/client/dist/assets/{AnalyticsPage-BhkeGIGn.js → AnalyticsPage-DDWees-K.js} +1 -1
- package/client/dist/assets/{CodePage-B_9yYWUx.js → CodePage-CzV5tkvS.js} +1 -1
- package/client/dist/assets/{DesktopAnalyticsPage-DAcfEJac.js → DesktopAnalyticsPage-DfRHNK7i.js} +1 -1
- package/client/dist/assets/{DocsDialog-WCuiYgsc.js → DocsDialog-BxduBYbh.js} +1 -1
- package/client/dist/assets/{DocsPage-bGVYTMTU.js → DocsPage-DOVeRJn_.js} +1 -1
- package/client/dist/assets/{ExportDropdown-CsodZ-yb.js → ExportDropdown-BnKVV8UK.js} +1 -1
- package/client/dist/assets/{InteractiveJobComposer-D5fh_eVL.js → InteractiveJobComposer-Da9dvM-0.js} +1 -1
- package/client/dist/assets/{JobDetailModal-DlpXF61O.js → JobDetailModal-DhwKbZGe.js} +1 -1
- package/client/dist/assets/{JobDetailPage-2yR19_Lb.js → JobDetailPage-MtPE-E_1.js} +1 -1
- package/client/dist/assets/{JobsPage-CS9IR6Dq.js → JobsPage-0vdlbNik.js} +1 -1
- package/client/dist/assets/{LoopBuilderPage-BwiAtmOr.js → LoopBuilderPage-DCUHozXh.js} +1 -1
- package/client/dist/assets/{LoopPreviewModal--upwvgTv.js → LoopPreviewModal-Df-S8NGf.js} +1 -1
- package/client/dist/assets/{LoopWindowSurface-DRnUixtl.js → LoopWindowSurface-sdb7QbSo.js} +1 -1
- package/client/dist/assets/{LoopsPage-Xm3nRToQ.js → LoopsPage-CDfCC9Px.js} +1 -1
- package/client/dist/assets/{MinimizedChatsContext-D_8L4EWZ.js → MinimizedChatsContext-DKlPf54Y.js} +1 -1
- package/client/dist/assets/{PluginsPage-CAFQ1B4Q.js → PluginsPage-iL0G3rQQ.js} +1 -1
- package/client/dist/assets/{ProjectSettingsDialog-DYSO4v90.js → ProjectSettingsDialog-DOkjT7xv.js} +1 -1
- package/client/dist/assets/{RepositoryDeliveries-CbQmW7Qx.js → RepositoryDeliveries-CJVpOUpZ.js} +1 -1
- package/client/dist/assets/RepositoryScopeSelector-BeM-2gIZ.js +2 -0
- package/client/dist/assets/{ReviewPacketPage-SDBhYdQB.js → ReviewPacketPage-EBCim2w_.js} +1 -1
- package/client/dist/assets/{TemplatePreviewModal-DHBUds51.js → TemplatePreviewModal-1-92UGwN.js} +1 -1
- package/client/dist/assets/{TicketDetailModalContext-UAeNm7BB.js → TicketDetailModalContext-CkVXuHxU.js} +1 -1
- package/client/dist/assets/{Trans-DExd3Z00.js → Trans-CzhPjnjB.js} +1 -1
- package/client/dist/assets/{agent-DrM4qned.js → agent--8QAfoXL.js} +1 -1
- package/client/dist/assets/{agent-CW74ODDP.js → agent-3HxXkIWX.js} +1 -1
- package/client/dist/assets/{agent-BElLtdKP.js → agent-BjttZLIl.js} +1 -1
- package/client/dist/assets/{agent-C-m9VugX.js → agent-CpFCz32T.js} +1 -1
- package/client/dist/assets/{agent-KdIXq3w5.js → agent-DVnw6ybO.js} +1 -1
- package/client/dist/assets/{agent-CGSKxfZk.js → agent-Z5s_GTP0.js} +1 -1
- package/client/dist/assets/{agent-DeCAZDhL.js → agent-hv57-zyG.js} +1 -1
- package/client/dist/assets/{agent-jK8mCisM.js → agent-xGrZ_aN1.js} +1 -1
- package/client/dist/assets/{formatDistanceToNow-aw7H5ORC.js → formatDistanceToNow-DlQaXc7c.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-NyC7gZJX.js → getTimezoneOffsetInMilliseconds-DxUFGcVq.js} +1 -1
- package/client/dist/assets/index-Bfunvi6w.css +2 -0
- package/client/dist/assets/{index-JSTZVJfe.js → index-Czov72g7.js} +43 -43
- package/client/dist/assets/{jira-api-DlXaRwA3.js → jira-api-CbJvbRYx.js} +1 -1
- package/client/dist/assets/{project-repositories-DXhb9FP4.js → project-repositories-BkBD3rxx.js} +1 -1
- package/client/dist/assets/{spending-BdcyVkcd.js → spending-CYotH-9N.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-DoWQv7l1.js → subscriptionUsage-BH73RO86.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-DQ02J25O.js → subscriptionUsage-BWTeWlHx.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-N9hB-ARf.js → subscriptionUsage-CBh82zVa.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-DWF3zKp4.js → subscriptionUsage-CHcQ7lLe.js} +1 -1
- package/client/dist/assets/subscriptionUsage-CdlE5Mg1.js +1 -0
- package/client/dist/assets/{subscriptionUsage-Cf87DeS_.js → subscriptionUsage-DUPBmSQi.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-DkIf6Zuh.js → subscriptionUsage-DrlwrJ17.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-D6u7VBdz.js → subscriptionUsage-EFu0mIK3.js} +1 -1
- package/client/dist/assets/{useDesktop-D07TvesS.js → useDesktop-DpxVGfUC.js} +1 -1
- package/client/dist/assets/{useRuntimeRuns-pGLmtAC6.js → useRuntimeRuns-CfGRbhr9.js} +1 -1
- package/client/dist/assets/{useSharedWebSocket-TzKtPRp9.js → useSharedWebSocket-C17TGqT8.js} +2 -2
- package/client/dist/index.html +17 -17
- package/docs/internals/api-reference.md +1 -0
- package/docs/internals/core-runtime-updates.md +9 -2
- package/docs/internals/desktop-owned-workflows.md +23 -0
- package/docs/internals/mission-rail-cards.md +15 -0
- package/docs/internals/operations-runbook.md +6 -0
- package/docs/internals/safe-pr-review-flow.md +25 -7
- package/docs/internals/source-map.md +3 -1
- package/docs/internals/visual-design-audit.md +5 -0
- package/package.json +1 -1
- package/server/dist/core-update-manager.js +1 -1
- package/server/dist/db/migrations.js +9 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-bridge.js +114 -5
- package/server/dist/modules/delivery/runtime/multi-repo-execution.js +7 -5
- package/server/dist/modules/delivery/runtime/rail-isolated-launch.js +65 -30
- package/server/dist/modules/delivery/runtime/rail-pr-store.js +3 -3
- package/server/dist/modules/delivery/runtime/rail-relaunch.js +154 -0
- package/server/dist/modules/delivery/runtime/rails-router.js +109 -10
- package/server/dist/modules/execution/runtime/revision-seed.js +6 -2
- package/server/dist/modules/loops/runtime/loop-agent-defaults.json +1 -1
- package/server/dist/modules/loops/runtime/loop-definition-events.js +1 -1
- package/server/dist/modules/loops/runtime/loop-implement-recipe.js +8 -4
- package/server/dist/modules/loops/runtime/loop-run-manager.js +27 -17
- package/server/dist/modules/subscription-usage/adapters/claude.js +30 -20
- package/server/dist/modules/subscription-usage/application.js +2 -1
- package/server/dist/modules/subscription-usage/domain.js +35 -1
- package/server/dist/modules/subscription-usage/runtime/usage-service.js +1 -1
- package/server/dist/worktree-manager.js +114 -6
- package/server/dist/worktree-overlay.js +4 -4
- package/client/dist/assets/RepositoryScopeSelector-DKzvIGSZ.js +0 -2
- package/client/dist/assets/index-CahchzS5.css +0 -2
- package/client/dist/assets/subscriptionUsage-zRcRPHkP.js +0 -1
|
@@ -210,7 +210,22 @@ async function createWorktree(git, input) {
|
|
|
210
210
|
// it out into a worktree (resume from the committed partial work).
|
|
211
211
|
// 3. neither → create a fresh branch off base.
|
|
212
212
|
const existing = await listWorktrees(git, input.repoDir);
|
|
213
|
-
|
|
213
|
+
let mounted = existing.find((p) => canonicalWorktreePath(p) === canonicalWorktreePath(wt));
|
|
214
|
+
if (input.reuseBranchWorktree) {
|
|
215
|
+
// New allocations have run-specific paths. A continuation must find the
|
|
216
|
+
// exact recorded branch, never borrow a ticket-keyed mount on another ref.
|
|
217
|
+
mounted = undefined;
|
|
218
|
+
for (const candidate of existing) {
|
|
219
|
+
const head = await git.run(['rev-parse', '--abbrev-ref', 'HEAD'], candidate);
|
|
220
|
+
if (head.code === 0 && head.stdout.trim() === branch) {
|
|
221
|
+
mounted = candidate;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (mounted) {
|
|
227
|
+
if (input.fresh)
|
|
228
|
+
throw new Error(`Fresh launch cannot reuse existing worktree ${mounted}`);
|
|
214
229
|
// The still-mounted worktree may be checked out on a DIFFERENT branch than
|
|
215
230
|
// the caller's preferred name (the path is keyed by ticketId only, so a
|
|
216
231
|
// stale mount from a prior run of the same ticket collides here). The
|
|
@@ -218,16 +233,18 @@ async function createWorktree(git, input) {
|
|
|
218
233
|
// — reporting the caller's preferred name recorded a branch that never
|
|
219
234
|
// existed, so the later `git push` had no ref and the PR delivery wedged
|
|
220
235
|
// at local-only. Detached HEAD / a git failure falls back to the input.
|
|
221
|
-
const head = await git.run(['rev-parse', '--abbrev-ref', 'HEAD'],
|
|
236
|
+
const head = await git.run(['rev-parse', '--abbrev-ref', 'HEAD'], mounted);
|
|
222
237
|
const actual = head.code === 0 ? head.stdout.trim() : '';
|
|
223
238
|
const actualBranch = actual && actual !== 'HEAD' ? actual : branch;
|
|
224
239
|
if (input.refreshFromBaseRef && actualBranch === branch) {
|
|
225
|
-
await fastForwardExistingBranch(git, input.repoDir, branch, input.baseRef,
|
|
240
|
+
await fastForwardExistingBranch(git, input.repoDir, branch, input.baseRef, mounted);
|
|
226
241
|
}
|
|
227
|
-
await ensurePrNeverStageExcludes(git,
|
|
228
|
-
return { branch: actualBranch, worktreePath:
|
|
242
|
+
await ensurePrNeverStageExcludes(git, mounted);
|
|
243
|
+
return { branch: actualBranch, worktreePath: mounted, worktreeCreated: false, branchCreated: false };
|
|
229
244
|
}
|
|
230
245
|
const hasBranch = (await git.run(['rev-parse', '--verify', '--quiet', `refs/heads/${branch}`], input.repoDir)).code === 0;
|
|
246
|
+
if (hasBranch && input.fresh)
|
|
247
|
+
throw new Error(`Fresh launch cannot reuse existing branch ${branch}`);
|
|
231
248
|
if (hasBranch && input.refreshFromBaseRef) {
|
|
232
249
|
await fastForwardExistingBranch(git, input.repoDir, branch, input.baseRef);
|
|
233
250
|
}
|
|
@@ -322,8 +339,94 @@ async function auditStagedPaths(git, worktreePath, excludePaths, phase) {
|
|
|
322
339
|
const parsed = parseNulTerminatedPaths(result, phase);
|
|
323
340
|
if (parsed.error)
|
|
324
341
|
return { forbidden: [], error: parsed.error };
|
|
342
|
+
try {
|
|
343
|
+
const deliverable = parsed.paths.filter(candidate => !forbiddenStagedPaths([candidate], excludePaths).length);
|
|
344
|
+
const external = deliverable.filter(candidate => {
|
|
345
|
+
const target = path.join(worktreePath, candidate);
|
|
346
|
+
let stat;
|
|
347
|
+
try {
|
|
348
|
+
stat = fs.lstatSync(target);
|
|
349
|
+
}
|
|
350
|
+
catch (err) {
|
|
351
|
+
if (err.code === 'ENOENT')
|
|
352
|
+
return false; // staged deletion
|
|
353
|
+
throw err;
|
|
354
|
+
}
|
|
355
|
+
if (!stat.isSymbolicLink())
|
|
356
|
+
return false;
|
|
357
|
+
const link = fs.readlinkSync(target);
|
|
358
|
+
const relative = path.relative(worktreePath, link);
|
|
359
|
+
return path.isAbsolute(link) && (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative));
|
|
360
|
+
});
|
|
361
|
+
if (external.length)
|
|
362
|
+
return { forbidden: [], error: `External absolute symlinks cannot be delivered: ${describeForbiddenPaths(external)}. Working files were preserved.` };
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
return { forbidden: [], error: `Could not inspect staged symlinks: ${err instanceof Error ? err.message : String(err)}` };
|
|
366
|
+
}
|
|
325
367
|
return { forbidden: forbiddenStagedPaths(parsed.paths, excludePaths) };
|
|
326
368
|
}
|
|
369
|
+
/** Automated delivery must not publish an unfinished change introduced by this
|
|
370
|
+
* checkout. Baseline active changes and archived changes remain legitimate. */
|
|
371
|
+
async function deliveryHygieneError(git, worktreePath, baseRef) {
|
|
372
|
+
// Agents may create their own commits before settlement. Index-only checks
|
|
373
|
+
// cannot catch an overlay link already in HEAD, even when it is now excluded.
|
|
374
|
+
const changed = parseNulTerminatedPaths(await gitRun(git, ['diff', '--name-only', '--no-renames', '-z', baseRef, 'HEAD', '--'], worktreePath), 'Committed delivery audit');
|
|
375
|
+
if (changed.error)
|
|
376
|
+
return changed.error;
|
|
377
|
+
if (changed.paths.length) {
|
|
378
|
+
const tree = parseNulTerminatedPaths(await gitRun(git, ['ls-tree', '-r', '-z', 'HEAD'], worktreePath), 'Committed symlink audit');
|
|
379
|
+
if (tree.error)
|
|
380
|
+
return tree.error;
|
|
381
|
+
const paths = new Set(changed.paths);
|
|
382
|
+
for (const entry of tree.paths) {
|
|
383
|
+
const tab = entry.indexOf('\t'), metadata = entry.slice(0, tab).split(' '), rel = entry.slice(tab + 1);
|
|
384
|
+
if (metadata[0] !== '120000' || !paths.has(rel))
|
|
385
|
+
continue;
|
|
386
|
+
if (tab < 0 || metadata[1] !== 'blob' || !/^[a-f0-9]{40,64}$/i.test(metadata[2]))
|
|
387
|
+
return 'Committed symlink audit returned malformed tree evidence';
|
|
388
|
+
const blob = await gitRun(git, ['cat-file', 'blob', metadata[2]], worktreePath);
|
|
389
|
+
if (blob.code !== 0)
|
|
390
|
+
return `Could not inspect committed symlink ${JSON.stringify(rel)}`;
|
|
391
|
+
const relative = path.relative(worktreePath, blob.stdout);
|
|
392
|
+
if (path.isAbsolute(blob.stdout) && (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))) {
|
|
393
|
+
return `External absolute symlink already committed on the delivery branch: ${JSON.stringify(rel)}. Review and remove it before delivery; working files were preserved.`;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const baseline = await gitRun(git, ['ls-tree', '-d', '--name-only', '-z', `${baseRef}:openspec/changes`], worktreePath);
|
|
398
|
+
// A missing subtree is normal, but an invalid baseline is never a pass.
|
|
399
|
+
if (baseline.code !== 0) {
|
|
400
|
+
const exists = await gitRun(git, ['rev-parse', '--verify', `${baseRef}^{commit}`], worktreePath);
|
|
401
|
+
if (exists.code !== 0)
|
|
402
|
+
return `Could not inspect OpenSpec delivery baseline: ${gitFailure(exists, baseRef)}`;
|
|
403
|
+
const root = await gitRun(git, ['ls-tree', '-r', '--name-only', '-z', baseRef, '--', 'openspec/changes'], worktreePath);
|
|
404
|
+
if (root.code !== 0 || root.stdout)
|
|
405
|
+
return `Could not inspect OpenSpec delivery baseline: ${gitFailure(baseline, baseRef)}`;
|
|
406
|
+
}
|
|
407
|
+
const parsed = baseline.code === 0 ? parseNulTerminatedPaths(baseline, 'OpenSpec baseline audit') : { paths: [] };
|
|
408
|
+
if (parsed.error)
|
|
409
|
+
return parsed.error;
|
|
410
|
+
const known = new Set(parsed.paths);
|
|
411
|
+
try {
|
|
412
|
+
const directory = path.join(worktreePath, 'openspec', 'changes');
|
|
413
|
+
let entries;
|
|
414
|
+
try {
|
|
415
|
+
entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
if (err.code === 'ENOENT')
|
|
419
|
+
return undefined;
|
|
420
|
+
throw err;
|
|
421
|
+
}
|
|
422
|
+
const active = entries.filter(entry => entry.name !== 'archive' && (entry.isDirectory() || entry.isSymbolicLink()) && !known.has(entry.name)).map(entry => `openspec/changes/${entry.name}`);
|
|
423
|
+
if (active.length)
|
|
424
|
+
return `OpenSpec delivery blocked by new active changes: ${describeForbiddenPaths(active)}. Archive the intended change and review unrelated work before delivery; files were preserved.`;
|
|
425
|
+
}
|
|
426
|
+
catch (err) {
|
|
427
|
+
return `Could not inspect active OpenSpec changes: ${err instanceof Error ? err.message : String(err)}`;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
327
430
|
function forbiddenResetPathspecs(excludePaths) {
|
|
328
431
|
return [
|
|
329
432
|
'--',
|
|
@@ -341,7 +444,12 @@ function describeForbiddenPaths(paths) {
|
|
|
341
444
|
* agent paths may stay untracked in the worktree because they must never land on
|
|
342
445
|
* the ticket branch or PR.
|
|
343
446
|
*/
|
|
344
|
-
async function commitWorktreeAndVerify(git, worktreePath, message, excludePaths = []) {
|
|
447
|
+
async function commitWorktreeAndVerify(git, worktreePath, message, excludePaths = [], baseRef) {
|
|
448
|
+
if (baseRef) {
|
|
449
|
+
const error = await deliveryHygieneError(git, worktreePath, baseRef);
|
|
450
|
+
if (error)
|
|
451
|
+
return { staged: false, committed: false, clean: false, dirty: [], error };
|
|
452
|
+
}
|
|
345
453
|
const pathspecs = commitPathspecs(excludePaths);
|
|
346
454
|
// The add is deliberately PLAIN — no exclude pathspecs. `git add` exits 1
|
|
347
455
|
// with "The following paths are ignored by one of your .gitignore files"
|
|
@@ -137,11 +137,11 @@ function overlayRootsOf(input) {
|
|
|
137
137
|
* manifest entry may authenticate against. Empty for unauthorised rels. */
|
|
138
138
|
function sourceCandidatesForOverlayEntry(input, rel) {
|
|
139
139
|
const roots = overlayRootsOf(input);
|
|
140
|
-
if (rel === '.mcp.json' ||
|
|
140
|
+
if (rel === '.mcp.json' || [input.instructionsFilename, 'CLAUDE.md', 'AGENTS.md', 'GEMINI.md'].includes(rel)) {
|
|
141
141
|
return roots.map((root) => path_1.default.join(root, rel));
|
|
142
142
|
}
|
|
143
|
-
const providerPrefix =
|
|
144
|
-
if (!
|
|
143
|
+
const providerPrefix = [input.providerDir, '.claude', '.codex', '.gemini', '.kimi-code'].map(dir => `${dir}/`).find(prefix => rel.startsWith(prefix));
|
|
144
|
+
if (!providerPrefix)
|
|
145
145
|
return [];
|
|
146
146
|
const providerRel = rel.slice(providerPrefix.length);
|
|
147
147
|
if (!providerRel || providerRel === 'worktrees' || providerRel.startsWith('worktrees/'))
|
|
@@ -247,7 +247,7 @@ function captureOverlayCleanupEvidence(input, candidates, includeManifest = fals
|
|
|
247
247
|
if (dereferencedDigest(source) === destinationDigest)
|
|
248
248
|
return true;
|
|
249
249
|
// A bootstrap refresh is still an exact, reproducible source copy.
|
|
250
|
-
if (
|
|
250
|
+
if (![input.instructionsFilename, 'CLAUDE.md', 'AGENTS.md', 'GEMINI.md', '.kimi-code/AGENTS.md'].includes(rel) || !destinationStat.isFile())
|
|
251
251
|
return false;
|
|
252
252
|
try {
|
|
253
253
|
return fs_1.default.readFileSync(destination, 'utf8') === refreshLegacyInstructions(fs_1.default.readFileSync(source, 'utf8'));
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./jsx-runtime-zu2_FqZY.js";import{n as t}from"./createLucideIcon-Bjbbp_KS.js";import{r as n}from"./useDesktop-D07TvesS.js";import{t as r}from"./project-repositories-DXhb9FP4.js";var i=e();function a({value:e,onChange:a,disabled:o=!1,repositories:s,workspaceOnly:c=!1,workspaceSelection:l,onWorkspaceChange:u}){let{t:d}=t(`common`),{activeProjectId:f,projects:p}=n(),m=s??r(p.find(e=>e.id===f)),h=m.find(e=>e.isPrimary),g=e??(h?[h.id]:[]),_=g.some(e=>!m.some(t=>t.id===e));if(c){let e=m.filter(e=>g.includes(e.id)&&(e.workspacePaths?.length||e.workspacePath));return e.length?(0,i.jsxs)(`fieldset`,{className:`space-y-2`,disabled:o,children:[(0,i.jsx)(`legend`,{className:`text-xs font-medium`,children:d(`repositories.launchWorkspaces`)}),(0,i.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:d(`repositories.launchWorkspaceHint`)}),e.map(e=>{let t=e.workspacePaths??[e.workspacePath],n=l?.[e.id]??t;return(0,i.jsxs)(`div`,{className:`space-y-1`,children:[(0,i.jsx)(`span`,{className:`text-xs font-medium`,children:e.name}),t.map(t=>(0,i.jsxs)(`label`,{className:`flex items-center gap-2 text-xs`,title:t,children:[(0,i.jsx)(`input`,{type:`checkbox`,checked:n.includes(t),disabled:o||n.length===1&&n[0]===t,onChange:r=>u?.({...Object.fromEntries(Object.entries(l??{}).filter(([e])=>g.includes(e))),[e.id]:r.target.checked?[...n,t]:n.filter(e=>e!==t)})}),(0,i.jsx)(`span`,{className:`break-all`,children:t})]},t))]},e.id)})]}):null}return m.length<=1&&!_?null:(0,i.jsxs)(`fieldset`,{className:`space-y-2`,disabled:o,children:[(0,i.jsx)(`legend`,{className:`mb-1 text-xs font-medium`,children:d(`repositories.specScope`)}),(0,i.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:d(`repositories.scopeHint`)}),(0,i.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[m.map(e=>(0,i.jsxs)(`label`,{title:e.workspacePaths?.join(`
|
|
2
|
-
`)??e.workspacePath??e.path,className:`flex items-center gap-2 rounded border border-border px-2 py-1.5 text-xs`,children:[(0,i.jsx)(`input`,{type:`checkbox`,checked:g.includes(e.id),disabled:o||e.kind===`folder`&&!e.isPrimary&&!g.includes(e.id)||g.length===1&&g[0]===e.id,onChange:t=>a(t.target.checked?[...g,e.id]:g.filter(t=>t!==e.id))}),e.name,e.workspacePaths?.map(e=>(0,i.jsx)(`span`,{className:`text-muted-foreground`,children:e},e)),!e.workspacePaths&&e.workspacePath&&(0,i.jsx)(`span`,{className:`text-muted-foreground`,children:e.workspacePath}),e.kind===`folder`&&!e.isPrimary&&(0,i.jsx)(`span`,{className:`text-muted-foreground`,children:d(`repositories.contextOnly`)})]},e.id)),g.filter(e=>!m.some(t=>t.id===e)).map(e=>(0,i.jsxs)(`label`,{className:`flex items-center gap-2 rounded border border-destructive px-2 py-1.5 text-xs`,children:[(0,i.jsx)(`input`,{type:`checkbox`,checked:!0,disabled:o||g.length===1,onChange:()=>a(g.filter(t=>t!==e))}),d(`repositories.unavailable`),`: `,e]},e))]}),_&&(0,i.jsx)(`p`,{role:`alert`,className:`text-xs text-destructive`,children:d(`repositories.invalidSelection`)})]})}export{a as t};
|