@design.estate/dees-catalog 10.0.0 → 11.1.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 (26) hide show
  1. package/dist_bundle/bundle.js +914 -780
  2. package/dist_bundle/bundle.js.map +1 -1
  3. package/dist_ts_web/00_commitinfo_data.js +1 -1
  4. package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.demo.js +24 -1
  5. package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.js +13 -1
  6. package/dist_ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.d.ts +62 -48
  7. package/dist_ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.demo.js +97 -57
  8. package/dist_ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.js +541 -666
  9. package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.js +19 -1
  10. package/dist_ts_web/elements/00group-harness/dees-terminal-view/dees-terminal-view.d.ts +45 -6
  11. package/dist_ts_web/elements/00group-harness/dees-terminal-view/dees-terminal-view.js +184 -27
  12. package/dist_ts_web/elements/00group-harness/interfaces.d.ts +59 -20
  13. package/dist_ts_web/services/DeesServiceLibLoader.d.ts +7 -0
  14. package/dist_ts_web/services/DeesServiceLibLoader.js +45 -22
  15. package/package.json +1 -2
  16. package/readme.md +37 -17
  17. package/ts_web/00_commitinfo_data.ts +1 -1
  18. package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.demo.ts +23 -0
  19. package/ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.ts +12 -0
  20. package/ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.demo.ts +107 -55
  21. package/ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.ts +622 -756
  22. package/ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.ts +18 -0
  23. package/ts_web/elements/00group-harness/dees-terminal-view/dees-terminal-view.ts +196 -24
  24. package/ts_web/elements/00group-harness/interfaces.ts +63 -19
  25. package/ts_web/services/DeesServiceLibLoader.ts +150 -102
  26. package/.smartconfig.json +0 -86
@@ -122196,6 +122196,20 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
122196
122196
  }
122197
122197
  return _DeesServiceLibLoader.instance;
122198
122198
  }
122199
+ /**
122200
+ * Shares one in-flight load between concurrent callers — but only for as long
122201
+ * as that load can still succeed. A rejected load clears its own cache entry,
122202
+ * so the next call makes a fresh attempt instead of replaying a stale failure
122203
+ * for the lifetime of the page. Every load below goes through this.
122204
+ */
122205
+ shareLoad(loadArg, readArg, writeArg) {
122206
+ const pending = loadArg();
122207
+ writeArg(pending);
122208
+ pending.catch(() => {
122209
+ if (readArg() === pending) writeArg(null);
122210
+ });
122211
+ return pending;
122212
+ }
122199
122213
  /**
122200
122214
  * Load xterm terminal emulator from CDN
122201
122215
  * @returns Promise resolving to xterm module with Terminal class
@@ -122207,19 +122221,24 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
122207
122221
  if (this.xtermLoadingPromise) {
122208
122222
  return this.xtermLoadingPromise;
122209
122223
  }
122210
- this.xtermLoadingPromise = (async () => {
122211
- const url = `${CDN_BASE}/xterm@${CDN_VERSIONS.xterm}/+esm`;
122212
- const module = await import(
122213
- /* @vite-ignore */
122214
- url
122215
- );
122216
- await this.injectXtermStyles();
122217
- this.xtermLib = {
122218
- Terminal: module.Terminal
122219
- };
122220
- return this.xtermLib;
122221
- })();
122222
- return this.xtermLoadingPromise;
122224
+ return this.shareLoad(
122225
+ async () => {
122226
+ const url = `${CDN_BASE}/xterm@${CDN_VERSIONS.xterm}/+esm`;
122227
+ const module = await import(
122228
+ /* @vite-ignore */
122229
+ url
122230
+ );
122231
+ await this.injectXtermStyles();
122232
+ this.xtermLib = {
122233
+ Terminal: module.Terminal
122234
+ };
122235
+ return this.xtermLib;
122236
+ },
122237
+ () => this.xtermLoadingPromise,
122238
+ (promiseArg) => {
122239
+ this.xtermLoadingPromise = promiseArg;
122240
+ }
122241
+ );
122223
122242
  }
122224
122243
  /**
122225
122244
  * Provides xterm modules from the host application instead of the CDN.
@@ -122257,18 +122276,23 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
122257
122276
  if (this.xtermFitAddonLoadingPromise) {
122258
122277
  return this.xtermFitAddonLoadingPromise;
122259
122278
  }
122260
- this.xtermFitAddonLoadingPromise = (async () => {
122261
- const url = `${CDN_BASE}/xterm-addon-fit@${CDN_VERSIONS.xtermAddonFit}/+esm`;
122262
- const module = await import(
122263
- /* @vite-ignore */
122264
- url
122265
- );
122266
- this.xtermFitAddonLib = {
122267
- FitAddon: module.FitAddon
122268
- };
122269
- return this.xtermFitAddonLib;
122270
- })();
122271
- return this.xtermFitAddonLoadingPromise;
122279
+ return this.shareLoad(
122280
+ async () => {
122281
+ const url = `${CDN_BASE}/xterm-addon-fit@${CDN_VERSIONS.xtermAddonFit}/+esm`;
122282
+ const module = await import(
122283
+ /* @vite-ignore */
122284
+ url
122285
+ );
122286
+ this.xtermFitAddonLib = {
122287
+ FitAddon: module.FitAddon
122288
+ };
122289
+ return this.xtermFitAddonLib;
122290
+ },
122291
+ () => this.xtermFitAddonLoadingPromise,
122292
+ (promiseArg) => {
122293
+ this.xtermFitAddonLoadingPromise = promiseArg;
122294
+ }
122295
+ );
122272
122296
  }
122273
122297
  /**
122274
122298
  * Load xterm-addon-search from CDN
@@ -122281,18 +122305,23 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
122281
122305
  if (this.xtermSearchAddonLoadingPromise) {
122282
122306
  return this.xtermSearchAddonLoadingPromise;
122283
122307
  }
122284
- this.xtermSearchAddonLoadingPromise = (async () => {
122285
- const url = `${CDN_BASE}/xterm-addon-search@${CDN_VERSIONS.xtermAddonSearch}/+esm`;
122286
- const module = await import(
122287
- /* @vite-ignore */
122288
- url
122289
- );
122290
- this.xtermSearchAddonLib = {
122291
- SearchAddon: module.SearchAddon
122292
- };
122293
- return this.xtermSearchAddonLib;
122294
- })();
122295
- return this.xtermSearchAddonLoadingPromise;
122308
+ return this.shareLoad(
122309
+ async () => {
122310
+ const url = `${CDN_BASE}/xterm-addon-search@${CDN_VERSIONS.xtermAddonSearch}/+esm`;
122311
+ const module = await import(
122312
+ /* @vite-ignore */
122313
+ url
122314
+ );
122315
+ this.xtermSearchAddonLib = {
122316
+ SearchAddon: module.SearchAddon
122317
+ };
122318
+ return this.xtermSearchAddonLib;
122319
+ },
122320
+ () => this.xtermSearchAddonLoadingPromise,
122321
+ (promiseArg) => {
122322
+ this.xtermSearchAddonLoadingPromise = promiseArg;
122323
+ }
122324
+ );
122296
122325
  }
122297
122326
  /**
122298
122327
  * Inject xterm CSS styles into the document head
@@ -122331,16 +122360,21 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
122331
122360
  if (this.highlightJsLoadingPromise) {
122332
122361
  return this.highlightJsLoadingPromise;
122333
122362
  }
122334
- this.highlightJsLoadingPromise = (async () => {
122335
- const url = `${CDN_BASE}/highlight.js@${CDN_VERSIONS.highlightJs}/+esm`;
122336
- const module = await import(
122337
- /* @vite-ignore */
122338
- url
122339
- );
122340
- this.highlightJsLib = module.default;
122341
- return this.highlightJsLib;
122342
- })();
122343
- return this.highlightJsLoadingPromise;
122363
+ return this.shareLoad(
122364
+ async () => {
122365
+ const url = `${CDN_BASE}/highlight.js@${CDN_VERSIONS.highlightJs}/+esm`;
122366
+ const module = await import(
122367
+ /* @vite-ignore */
122368
+ url
122369
+ );
122370
+ this.highlightJsLib = module.default;
122371
+ return this.highlightJsLib;
122372
+ },
122373
+ () => this.highlightJsLoadingPromise,
122374
+ (promiseArg) => {
122375
+ this.highlightJsLoadingPromise = promiseArg;
122376
+ }
122377
+ );
122344
122378
  }
122345
122379
  /**
122346
122380
  * Load TradingView Lightweight Charts from CDN
@@ -122353,23 +122387,28 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
122353
122387
  if (this.lightweightChartsLoadingPromise) {
122354
122388
  return this.lightweightChartsLoadingPromise;
122355
122389
  }
122356
- this.lightweightChartsLoadingPromise = (async () => {
122357
- const url = `${CDN_BASE}/lightweight-charts@${CDN_VERSIONS.lightweightCharts}/+esm`;
122358
- const module = await import(
122359
- /* @vite-ignore */
122360
- url
122361
- );
122362
- this.lightweightChartsLib = {
122363
- createChart: module.createChart,
122364
- AreaSeries: module.AreaSeries,
122365
- LineSeries: module.LineSeries,
122366
- ColorType: module.ColorType,
122367
- LineType: module.LineType,
122368
- CrosshairMode: module.CrosshairMode
122369
- };
122370
- return this.lightweightChartsLib;
122371
- })();
122372
- return this.lightweightChartsLoadingPromise;
122390
+ return this.shareLoad(
122391
+ async () => {
122392
+ const url = `${CDN_BASE}/lightweight-charts@${CDN_VERSIONS.lightweightCharts}/+esm`;
122393
+ const module = await import(
122394
+ /* @vite-ignore */
122395
+ url
122396
+ );
122397
+ this.lightweightChartsLib = {
122398
+ createChart: module.createChart,
122399
+ AreaSeries: module.AreaSeries,
122400
+ LineSeries: module.LineSeries,
122401
+ ColorType: module.ColorType,
122402
+ LineType: module.LineType,
122403
+ CrosshairMode: module.CrosshairMode
122404
+ };
122405
+ return this.lightweightChartsLib;
122406
+ },
122407
+ () => this.lightweightChartsLoadingPromise,
122408
+ (promiseArg) => {
122409
+ this.lightweightChartsLoadingPromise = promiseArg;
122410
+ }
122411
+ );
122373
122412
  }
122374
122413
  /**
122375
122414
  * Load Apache ECharts from CDN
@@ -122382,20 +122421,25 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
122382
122421
  if (this.echartsLoadingPromise) {
122383
122422
  return this.echartsLoadingPromise;
122384
122423
  }
122385
- this.echartsLoadingPromise = (async () => {
122386
- const url = `${CDN_BASE}/echarts@${CDN_VERSIONS.echarts}/dist/echarts.esm.min.js`;
122387
- const module = await import(
122388
- /* @vite-ignore */
122389
- url
122390
- );
122391
- this.echartsLib = {
122392
- init: module.init,
122393
- dispose: module.dispose,
122394
- getInstanceByDom: module.getInstanceByDom
122395
- };
122396
- return this.echartsLib;
122397
- })();
122398
- return this.echartsLoadingPromise;
122424
+ return this.shareLoad(
122425
+ async () => {
122426
+ const url = `${CDN_BASE}/echarts@${CDN_VERSIONS.echarts}/dist/echarts.esm.min.js`;
122427
+ const module = await import(
122428
+ /* @vite-ignore */
122429
+ url
122430
+ );
122431
+ this.echartsLib = {
122432
+ init: module.init,
122433
+ dispose: module.dispose,
122434
+ getInstanceByDom: module.getInstanceByDom
122435
+ };
122436
+ return this.echartsLib;
122437
+ },
122438
+ () => this.echartsLoadingPromise,
122439
+ (promiseArg) => {
122440
+ this.echartsLoadingPromise = promiseArg;
122441
+ }
122442
+ );
122399
122443
  }
122400
122444
  /**
122401
122445
  * Load Tiptap rich text editor and extensions from CDN
@@ -122408,26 +122452,31 @@ body > div[style*="top: -50000px"][style*="width: 50000px"] {
122408
122452
  if (this.tiptapLoadingPromise) {
122409
122453
  return this.tiptapLoadingPromise;
122410
122454
  }
122411
- this.tiptapLoadingPromise = (async () => {
122412
- const [coreModule, starterKitModule, underlineModule, textAlignModule, linkModule, typographyModule] = await Promise.all([
122413
- Promise.resolve().then(() => (init_dist17(), dist_exports2)),
122414
- Promise.resolve().then(() => (init_dist43(), dist_exports3)),
122415
- Promise.resolve().then(() => (init_dist44(), dist_exports4)),
122416
- Promise.resolve().then(() => (init_dist45(), dist_exports5)),
122417
- Promise.resolve().then(() => (init_dist46(), dist_exports6)),
122418
- Promise.resolve().then(() => (init_dist47(), dist_exports7))
122419
- ]);
122420
- this.tiptapLib = {
122421
- Editor: coreModule.Editor,
122422
- StarterKit: starterKitModule.default || starterKitModule.StarterKit,
122423
- Underline: underlineModule.default || underlineModule.Underline,
122424
- TextAlign: textAlignModule.default || textAlignModule.TextAlign,
122425
- Link: linkModule.default || linkModule.Link,
122426
- Typography: typographyModule.default || typographyModule.Typography
122427
- };
122428
- return this.tiptapLib;
122429
- })();
122430
- return this.tiptapLoadingPromise;
122455
+ return this.shareLoad(
122456
+ async () => {
122457
+ const [coreModule, starterKitModule, underlineModule, textAlignModule, linkModule, typographyModule] = await Promise.all([
122458
+ Promise.resolve().then(() => (init_dist17(), dist_exports2)),
122459
+ Promise.resolve().then(() => (init_dist43(), dist_exports3)),
122460
+ Promise.resolve().then(() => (init_dist44(), dist_exports4)),
122461
+ Promise.resolve().then(() => (init_dist45(), dist_exports5)),
122462
+ Promise.resolve().then(() => (init_dist46(), dist_exports6)),
122463
+ Promise.resolve().then(() => (init_dist47(), dist_exports7))
122464
+ ]);
122465
+ this.tiptapLib = {
122466
+ Editor: coreModule.Editor,
122467
+ StarterKit: starterKitModule.default || starterKitModule.StarterKit,
122468
+ Underline: underlineModule.default || underlineModule.Underline,
122469
+ TextAlign: textAlignModule.default || textAlignModule.TextAlign,
122470
+ Link: linkModule.default || linkModule.Link,
122471
+ Typography: typographyModule.default || typographyModule.Typography
122472
+ };
122473
+ return this.tiptapLib;
122474
+ },
122475
+ () => this.tiptapLoadingPromise,
122476
+ (promiseArg) => {
122477
+ this.tiptapLoadingPromise = promiseArg;
122478
+ }
122479
+ );
122431
122480
  }
122432
122481
  /**
122433
122482
  * Preload multiple libraries in parallel
@@ -165037,9 +165086,9 @@ var _DeesAppui = class _DeesAppui extends (_a75 = DeesElement, _appbarMenuItems_
165037
165086
  /**
165038
165087
  * Set the selected secondary menu item by key (for tab items only)
165039
165088
  */
165040
- setSecondaryMenuSelection(itemKey) {
165089
+ setSecondaryMenuSelection(itemKey2) {
165041
165090
  for (const group of this.secondarymenuGroups) {
165042
- const item = group.items.find((i11) => "key" in i11 && i11.key === itemKey);
165091
+ const item = group.items.find((i11) => "key" in i11 && i11.key === itemKey2);
165043
165092
  if (item && (!("type" in item) || item.type === "tab" || item.type === void 0)) {
165044
165093
  this.secondarymenuSelectedItem = item;
165045
165094
  return;
@@ -169741,6 +169790,29 @@ var toolShowcase = /* @__PURE__ */ __name(() => {
169741
169790
  startedAt: now3 - 155e3,
169742
169791
  finishedAt: now3 - 152e3
169743
169792
  }
169793
+ },
169794
+ {
169795
+ id: "demo-t4",
169796
+ role: "assistant",
169797
+ text: "Re-ran the suite to confirm the cache key fix.",
169798
+ createdAt: now3 - 14e4
169799
+ },
169800
+ // A terminal card alone in its row: expanded it takes the pane, and once it collapses it
169801
+ // returns to the reading measure like the prose around it.
169802
+ {
169803
+ id: "demo-t5",
169804
+ role: "tool",
169805
+ text: "",
169806
+ createdAt: now3 - 13e4,
169807
+ toolCall: {
169808
+ id: "call-5",
169809
+ name: "run_command",
169810
+ status: "completed",
169811
+ input: { command: "pnpm test --filter sitemap", cwd: "/srv/app" },
169812
+ output: { exitCode: 0, stdout: "14 tests passed\n0 failed" },
169813
+ startedAt: now3 - 136e3,
169814
+ finishedAt: now3 - 131e3
169815
+ }
169744
169816
  }
169745
169817
  ];
169746
169818
  }, "toolShowcase");
@@ -172494,6 +172566,7 @@ ${derivedSubtitle}` : subtitle;
172494
172566
  }
172495
172567
  updated(changedProperties) {
172496
172568
  super.updated(changedProperties);
172569
+ this.toggleAttribute("data-collapsed", !this.isOpen);
172497
172570
  if (!this.isConnected) return;
172498
172571
  this.terminalScrollHydrationFrame ??= requestAnimationFrame(() => {
172499
172572
  this.terminalScrollHydrationFrame = null;
@@ -172828,7 +172901,21 @@ __publicField(_DeesHarnessToolCard, "styles", [
172828
172901
  :host {
172829
172902
  display: block;
172830
172903
  container-type: inline-size;
172904
+ margin-inline: auto;
172905
+ max-width: 100%;
172831
172906
  font-family: var(--dees-font-family);
172907
+ transition: max-width var(--dees-transition-default) var(--dees-ease-standard);
172908
+ }
172909
+
172910
+ /*
172911
+ * A collapsed card may be held to a narrower measure than its row. The host — not the inner
172912
+ * surface — carries the constraint so the card's own inline-size container keeps measuring
172913
+ * what is actually rendered. The width comes from the context the card sits in: a transcript
172914
+ * row that holds this card alone sets it, a multi-card grid cell leaves it unset, and a
172915
+ * direct host falls back to the full row.
172916
+ */
172917
+ :host([data-collapsed]) {
172918
+ max-width: var(--dees-harness-collapsed-tool-width, 100%);
172832
172919
  }
172833
172920
 
172834
172921
  .harness-card.subtaskPreviewCard {
@@ -176428,6 +176515,18 @@ __publicField(_DeesHarnessMessageList, "styles", [
176428
176515
  width: 100%;
176429
176516
  }
176430
176517
 
176518
+ /*
176519
+ * The pane is what an open command or diff needs, not what its one-line collapsed header
176520
+ * needs, so a closed card returns to the reading measure inside its row. Only a row holding
176521
+ * a single card offers it: in a multi-card grid the cell width is the layout, and shrinking
176522
+ * one card would break the row. The card applies this itself — it alone knows whether it is
176523
+ * open — and the measure stays defined here, with the rest of the column rules.
176524
+ */
176525
+ .stack > [data-tool-layout='full-row'][data-tool-count='1'],
176526
+ .virtualItem[data-tool-layout='full-row'][data-tool-count='1'] {
176527
+ --dees-harness-collapsed-tool-width: min(var(--dees-harness-content-width, 820px), 100%);
176528
+ }
176529
+
176431
176530
  /*
176432
176531
  * One or two subagents are read like any other content, so they stay on the measure. The
176433
176532
  * pane is only worth taking once three or more cards have to sit side by side.
@@ -179337,7 +179436,7 @@ init_dist_ts26();
179337
179436
  init_templates();
179338
179437
  init_theme();
179339
179438
  init_dees_contextmenu();
179340
- var _showDetails_dec, _notice_dec, _associations_dec, _ungroupedSessionIds_dec, _groups_dec2, _selectedSessionId_dec, _sessions_dec, _a99, _DeesDemoSessionWorkspace_decorators, _init101, _sessions, _selectedSessionId, _groups2, _ungroupedSessionIds, _associations, _notice, _showDetails;
179439
+ var _showDetails_dec, _notice_dec, _associations_dec, _ungroupedItemIds_dec, _groups_dec2, _selectedSessionId_dec, _sessions_dec, _a99, _DeesDemoSessionWorkspace_decorators, _init101, _sessions, _selectedSessionId, _groups2, _ungroupedItemIds, _associations, _notice, _showDetails;
179341
179440
  var stages = {
179342
179441
  idle: { state: "normal", label: "Ready to start", description: "The brief is ready. Start the session to prepare the workspace." },
179343
179442
  starting: { state: "working", label: "Starting", description: "Opening the workspace and reading the project instructions." },
@@ -179349,13 +179448,13 @@ var stages = {
179349
179448
  paused: { state: "normal", label: "Paused", description: "Permission was declined. The requested action did not run." }
179350
179449
  };
179351
179450
  var demoSessions = /* @__PURE__ */ __name(() => [
179352
- { title: "Fix sitemap cache key", harness: { id: "codex", name: "Codex" }, stage: "approval" },
179353
- { title: "Investigate model rate limits", harness: { id: "opencode", name: "OpenCode" }, stage: "working" },
179354
- { title: "Refactor workbench layout", harness: { id: "flexharness", name: "FlexHarness" }, stage: "feedback" },
179355
- { title: "Add adaptive code diffs", harness: { id: "codex", name: "Codex" }, stage: "finished" },
179356
- { title: "Prepare the catalog release", harness: { id: "opencode", name: "OpenCode" }, stage: "error" },
179451
+ { title: "Fix sitemap cache key", harness: { id: "codex", name: "Codex" }, projectLabel: "storefront", stage: "approval" },
179452
+ { title: "Investigate model rate limits", harness: { id: "opencode", name: "OpenCode" }, projectLabel: "gateway", stage: "working" },
179453
+ { title: "Refactor workbench layout", harness: { id: "flexharness", name: "FlexHarness" }, projectLabel: "storefront", stage: "feedback" },
179454
+ { title: "Add adaptive code diffs", harness: { id: "codex", name: "Codex" }, projectLabel: "catalog", stage: "finished" },
179455
+ { title: "Prepare the catalog release", harness: { id: "opencode", name: "OpenCode" }, projectLabel: "catalog", stage: "error" },
179357
179456
  { title: "Improve agent onboarding", harness: { id: "flexharness", name: "FlexHarness" }, stage: "idle" },
179358
- { title: "Draft the migration guide", harness: { id: "codex", name: "Codex" }, stage: "starting" }
179457
+ { title: "Draft the migration guide", harness: { id: "codex", name: "Codex" }, projectLabel: "gateway", stage: "starting" }
179359
179458
  ].map((session, index3) => ({
179360
179459
  ...session,
179361
179460
  id: `session-${index3}`,
@@ -179366,9 +179465,29 @@ var demoSessions = /* @__PURE__ */ __name(() => [
179366
179465
  preview: stages[session.stage].description
179367
179466
  })), "demoSessions");
179368
179467
  var demoGroups = [
179369
- { id: "active", name: "Workspace", sessionIds: ["session-0", "session-1", "session-2", "session-6"] },
179370
- { id: "review", name: "Recent", sessionIds: ["session-3", "session-5", "session-4"] }
179468
+ {
179469
+ id: "active",
179470
+ name: "Workspace",
179471
+ itemIds: [
179472
+ { kind: "session", id: "session-0" },
179473
+ { kind: "resource", id: "terminal:build" },
179474
+ { kind: "session", id: "session-1" },
179475
+ { kind: "session", id: "session-2" },
179476
+ { kind: "session", id: "session-6" }
179477
+ ]
179478
+ },
179479
+ {
179480
+ id: "review",
179481
+ name: "Recent",
179482
+ itemIds: [
179483
+ { kind: "session", id: "session-3" },
179484
+ { kind: "session", id: "session-5" },
179485
+ { kind: "session", id: "session-4" }
179486
+ ]
179487
+ }
179371
179488
  ];
179489
+ var cloneGroups = /* @__PURE__ */ __name((groupsArg) => groupsArg.map((group) => ({ ...group, itemIds: group.itemIds.map((ref) => ({ ...ref })) })), "cloneGroups");
179490
+ var sameRef = /* @__PURE__ */ __name((leftArg, rightArg) => leftArg.kind === rightArg.kind && leftArg.id === rightArg.id, "sameRef");
179372
179491
  var demoResources = [
179373
179492
  {
179374
179493
  id: "terminal:build",
@@ -179376,6 +179495,7 @@ var demoResources = [
179376
179495
  title: "Build shell",
179377
179496
  icon: "lucide:Terminal",
179378
179497
  preview: "pnpm run build",
179498
+ projectLabel: "storefront",
179379
179499
  updatedAt: Date.now() - 9e4,
179380
179500
  state: "ready"
179381
179501
  },
@@ -179385,6 +179505,7 @@ var demoResources = [
179385
179505
  title: "Release notes",
179386
179506
  icon: "lucide:FileText",
179387
179507
  preview: "Catalog migration notes",
179508
+ projectLabel: "catalog",
179388
179509
  updatedAt: Date.now() - 18e4
179389
179510
  },
179390
179511
  {
@@ -179397,34 +179518,43 @@ var demoResources = [
179397
179518
  }
179398
179519
  ];
179399
179520
  var demoResourceAssociations = [
179400
- { resourceId: "terminal:build", sessionId: "session-4" },
179401
- { resourceId: "file:release-notes", sessionId: "session-4" }
179521
+ { resourceId: "terminal:build", target: { kind: "session", id: "session-4" } },
179522
+ { resourceId: "file:release-notes", target: { kind: "resource", id: "terminal:build" } }
179523
+ ];
179524
+ var demoEligibleSessionTargets = [
179525
+ { kind: "session", id: "session-0" },
179526
+ { kind: "session", id: "session-1" },
179527
+ { kind: "session", id: "session-2" },
179528
+ { kind: "session", id: "session-4" }
179402
179529
  ];
179403
- var demoResourceEligibleSessionIds = Object.fromEntries(
179404
- demoResources.map((resource) => [resource.id, ["session-0", "session-1", "session-2", "session-4"]])
179530
+ var demoResourceEligibleTargets = Object.fromEntries(
179531
+ demoResources.map((resource) => [
179532
+ resource.id,
179533
+ resource.kind === "terminal" ? demoEligibleSessionTargets : [...demoEligibleSessionTargets, { kind: "resource", id: "terminal:build" }]
179534
+ ])
179405
179535
  );
179406
179536
  var handleSessionSelect = /* @__PURE__ */ __name((event) => {
179407
179537
  const list5 = event.currentTarget;
179408
179538
  list5.selectedSessionId = event.detail.session.id;
179409
179539
  }, "handleSessionSelect");
179410
- var handleSessionMove = /* @__PURE__ */ __name((event) => {
179540
+ var handleItemMove = /* @__PURE__ */ __name((event) => {
179411
179541
  const list5 = event.currentTarget;
179412
- applySessionMove(list5, event.detail);
179413
- }, "handleSessionMove");
179414
- var applySessionMove = /* @__PURE__ */ __name((list5, detail) => {
179415
- const { sessionId, groupId, beforeSessionId } = detail;
179542
+ applyItemMove(list5, event.detail);
179543
+ }, "handleItemMove");
179544
+ var applyItemMove = /* @__PURE__ */ __name((list5, moveArg) => {
179545
+ const { item, toGroupId, beforeItem } = moveArg;
179416
179546
  const groups = list5.groups.map((group) => ({
179417
179547
  ...group,
179418
- sessionIds: group.sessionIds.filter((id) => id !== sessionId)
179548
+ itemIds: group.itemIds.filter((ref) => !sameRef(ref, item))
179419
179549
  }));
179420
- const ungroupedSessionIds = list5.ungroupedSessionIds.filter((id) => id !== sessionId);
179421
- const targetIds = groupId === null ? ungroupedSessionIds : groups.find((group) => group.id === groupId)?.sessionIds;
179550
+ const ungroupedItemIds = list5.ungroupedItemIds.filter((ref) => !sameRef(ref, item));
179551
+ const targetIds = toGroupId === null ? ungroupedItemIds : groups.find((group) => group.id === toGroupId)?.itemIds;
179422
179552
  if (!targetIds) return;
179423
- const anchorIndex = beforeSessionId ? targetIds.indexOf(beforeSessionId) : -1;
179424
- targetIds.splice(anchorIndex >= 0 ? anchorIndex : targetIds.length, 0, sessionId);
179553
+ const anchorIndex = beforeItem ? targetIds.findIndex((ref) => sameRef(ref, beforeItem)) : -1;
179554
+ targetIds.splice(anchorIndex >= 0 ? anchorIndex : targetIds.length, 0, { ...item });
179425
179555
  list5.groups = groups;
179426
- list5.ungroupedSessionIds = ungroupedSessionIds;
179427
- }, "applySessionMove");
179556
+ list5.ungroupedItemIds = ungroupedItemIds;
179557
+ }, "applyItemMove");
179428
179558
  var handleSessionOpen = /* @__PURE__ */ __name((event) => {
179429
179559
  const list5 = event.currentTarget;
179430
179560
  list5.selectedSessionId = event.detail.session.id;
@@ -179437,7 +179567,10 @@ var handleSessionContext = /* @__PURE__ */ __name(async (event, onChange = () =>
179437
179567
  const invokingControl = originalEvent.composedPath().find(
179438
179568
  (target) => target instanceof HTMLButtonElement
179439
179569
  );
179440
- const currentGroup = list5.groups.find((group) => group.sessionIds.includes(session.id));
179570
+ const sessionRef = { kind: "session", id: session.id };
179571
+ const currentGroup = list5.groups.find(
179572
+ (group) => group.itemIds.some((ref) => sameRef(ref, sessionRef))
179573
+ );
179441
179574
  const menu = await DeesContextmenu.openContextMenuWithOptions(originalEvent, [
179442
179575
  {
179443
179576
  name: "Open conversation",
@@ -179457,7 +179590,7 @@ var handleSessionContext = /* @__PURE__ */ __name(async (event, onChange = () =>
179457
179590
  name: `Move to ${group.name}`,
179458
179591
  iconName: "lucide:FolderInput",
179459
179592
  action: /* @__PURE__ */ __name(async () => {
179460
- applySessionMove(list5, { sessionId: session.id, groupId: group.id, index: group.sessionIds.length });
179593
+ applyItemMove(list5, { item: sessionRef, toGroupId: group.id, beforeItem: null });
179461
179594
  onChange();
179462
179595
  showDemoResult(list5, `Moved \u201C${session.title}\u201D to ${group.name}.`);
179463
179596
  }, "action")
@@ -179466,7 +179599,7 @@ var handleSessionContext = /* @__PURE__ */ __name(async (event, onChange = () =>
179466
179599
  name: "Move to Ungrouped",
179467
179600
  iconName: "lucide:FolderInput",
179468
179601
  action: /* @__PURE__ */ __name(async () => {
179469
- applySessionMove(list5, { sessionId: session.id, groupId: null, index: list5.ungroupedSessionIds.length });
179602
+ applyItemMove(list5, { item: sessionRef, toGroupId: null, beforeItem: null });
179470
179603
  onChange();
179471
179604
  showDemoResult(list5, `Moved \u201C${session.title}\u201D to Ungrouped.`);
179472
179605
  }, "action")
@@ -179486,33 +179619,34 @@ var handleResourceAssociation = /* @__PURE__ */ __name((event) => {
179486
179619
  const next2 = list5.resourceAssociations.filter(
179487
179620
  (association2) => association2.resourceId !== event.detail.resource.id
179488
179621
  );
179489
- if (event.detail.toSessionId) {
179622
+ if (event.detail.toTarget) {
179490
179623
  next2.push({
179491
179624
  resourceId: event.detail.resource.id,
179492
- sessionId: event.detail.toSessionId
179625
+ target: { ...event.detail.toTarget }
179493
179626
  });
179494
179627
  }
179495
179628
  list5.resourceAssociations = next2;
179496
179629
  }, "handleResourceAssociation");
179497
179630
  var handleResourceContext = /* @__PURE__ */ __name(async (event) => {
179498
179631
  const list5 = event.currentTarget;
179499
- const currentSessionId = event.detail.sessionId;
179500
- const eligibleSessionIds = list5.eligibleSessionIdsByResourceId[event.detail.resource.id] ?? [];
179632
+ const currentTarget = event.detail.target;
179633
+ const eligibleTargets = list5.eligibleTargetsByResourceId[event.detail.resource.id] ?? [];
179634
+ const titleFor = /* @__PURE__ */ __name((refArg) => (refArg.kind === "session" ? list5.sessions.find((session) => session.id === refArg.id)?.title : list5.resources.find((resource) => resource.id === refArg.id)?.title) ?? refArg.id, "titleFor");
179501
179635
  const menu = await DeesContextmenu.openContextMenuWithOptions(
179502
179636
  new MouseEvent("contextmenu", {
179503
179637
  clientX: event.detail.clientX,
179504
179638
  clientY: event.detail.clientY
179505
179639
  }),
179506
179640
  [
179507
- ...eligibleSessionIds.filter((sessionId) => sessionId !== currentSessionId).map((sessionId) => ({
179508
- name: `${currentSessionId ? "Reassign" : "Attach"} to ${list5.sessions.find((session) => session.id === sessionId)?.title}`,
179641
+ ...eligibleTargets.filter((target) => !currentTarget || !sameRef(target, currentTarget)).map((target) => ({
179642
+ name: `${currentTarget ? "Reassign" : "Attach"} to ${titleFor(target)}`,
179509
179643
  iconName: "lucide:Link",
179510
179644
  action: /* @__PURE__ */ __name(async () => {
179511
- event.detail.requestAssociation(sessionId);
179645
+ event.detail.requestAssociation(target);
179512
179646
  event.detail.close();
179513
179647
  }, "action")
179514
179648
  })),
179515
- ...currentSessionId ? [{
179649
+ ...currentTarget ? [{
179516
179650
  name: "Detach",
179517
179651
  iconName: "lucide:Unlink",
179518
179652
  action: /* @__PURE__ */ __name(async () => {
@@ -179534,7 +179668,7 @@ var openModal = /* @__PURE__ */ __name(async (sessions, groups) => {
179534
179668
  const content3 = document.createElement("dees-harness-session-list");
179535
179669
  content3.sessions = sessions;
179536
179670
  content3.groups = groups;
179537
- content3.ungroupedSessionIds = [];
179671
+ content3.ungroupedItemIds = [];
179538
179672
  content3.enableGrouping = true;
179539
179673
  content3.style.height = "420px";
179540
179674
  content3.addEventListener("harness-session-select", handleSessionSelect);
@@ -179542,7 +179676,7 @@ var openModal = /* @__PURE__ */ __name(async (sessions, groups) => {
179542
179676
  content3.addEventListener("harness-session-context", (event) => {
179543
179677
  void handleSessionContext(event);
179544
179678
  });
179545
- content3.addEventListener("harness-session-move", handleSessionMove);
179679
+ content3.addEventListener("harness-item-move", handleItemMove);
179546
179680
  DeesModal2.createAndShow({
179547
179681
  heading: "Conversations",
179548
179682
  content: b2`<style>${demoStyles}</style><div class="dees-demo-stack">${content3}${demoOutput("Choose a conversation action.")}</div>`,
@@ -179550,13 +179684,13 @@ var openModal = /* @__PURE__ */ __name(async (sessions, groups) => {
179550
179684
  });
179551
179685
  }, "openModal");
179552
179686
  _DeesDemoSessionWorkspace_decorators = [customElement("dees-demo-session-workspace")];
179553
- var _DeesDemoSessionWorkspace = class _DeesDemoSessionWorkspace extends (_a99 = DeesElement, _sessions_dec = [r5()], _selectedSessionId_dec = [r5()], _groups_dec2 = [r5()], _ungroupedSessionIds_dec = [r5()], _associations_dec = [r5()], _notice_dec = [r5()], _showDetails_dec = [r5()], _a99) {
179687
+ var _DeesDemoSessionWorkspace = class _DeesDemoSessionWorkspace extends (_a99 = DeesElement, _sessions_dec = [r5()], _selectedSessionId_dec = [r5()], _groups_dec2 = [r5()], _ungroupedItemIds_dec = [r5()], _associations_dec = [r5()], _notice_dec = [r5()], _showDetails_dec = [r5()], _a99) {
179554
179688
  constructor() {
179555
179689
  super(...arguments);
179556
179690
  __privateAdd(this, _sessions, __runInitializers(_init101, 8, this, demoSessions())), __runInitializers(_init101, 11, this);
179557
179691
  __privateAdd(this, _selectedSessionId, __runInitializers(_init101, 12, this, "session-0")), __runInitializers(_init101, 15, this);
179558
- __privateAdd(this, _groups2, __runInitializers(_init101, 16, this, demoGroups.map((group) => ({ ...group, sessionIds: [...group.sessionIds] })))), __runInitializers(_init101, 19, this);
179559
- __privateAdd(this, _ungroupedSessionIds, __runInitializers(_init101, 20, this, [])), __runInitializers(_init101, 23, this);
179692
+ __privateAdd(this, _groups2, __runInitializers(_init101, 16, this, cloneGroups(demoGroups))), __runInitializers(_init101, 19, this);
179693
+ __privateAdd(this, _ungroupedItemIds, __runInitializers(_init101, 20, this, [])), __runInitializers(_init101, 23, this);
179560
179694
  __privateAdd(this, _associations, __runInitializers(_init101, 24, this, [...demoResourceAssociations])), __runInitializers(_init101, 27, this);
179561
179695
  __privateAdd(this, _notice, __runInitializers(_init101, 28, this, "Interactive demo \xB7 Changes stay in this example.")), __runInitializers(_init101, 31, this);
179562
179696
  __privateAdd(this, _showDetails, __runInitializers(_init101, 32, this, false)), __runInitializers(_init101, 35, this);
@@ -179569,7 +179703,7 @@ var _DeesDemoSessionWorkspace = class _DeesDemoSessionWorkspace extends (_a99 =
179569
179703
  if (!list5) return;
179570
179704
  this.selectedSessionId = list5.selectedSessionId;
179571
179705
  this.groups = list5.groups;
179572
- this.ungroupedSessionIds = list5.ungroupedSessionIds;
179706
+ this.ungroupedItemIds = list5.ungroupedItemIds;
179573
179707
  this.associations = list5.resourceAssociations;
179574
179708
  }
179575
179709
  async select(event) {
@@ -179602,8 +179736,8 @@ var _DeesDemoSessionWorkspace = class _DeesDemoSessionWorkspace extends (_a99 =
179602
179736
  reset() {
179603
179737
  this.sessions = demoSessions();
179604
179738
  this.selectedSessionId = "session-0";
179605
- this.groups = demoGroups.map((group) => ({ ...group, sessionIds: [...group.sessionIds] }));
179606
- this.ungroupedSessionIds = [];
179739
+ this.groups = cloneGroups(demoGroups);
179740
+ this.ungroupedItemIds = [];
179607
179741
  this.associations = [...demoResourceAssociations];
179608
179742
  this.notice = "Demo reset. Choose a session or respond to the approval request.";
179609
179743
  if (this.list) {
@@ -179632,23 +179766,23 @@ var _DeesDemoSessionWorkspace = class _DeesDemoSessionWorkspace extends (_a99 =
179632
179766
  <span class="attentionSummary"><dees-icon icon="lucide:CircleAlert" iconSize="12"></dees-icon>${attentionCount} need attention</span>
179633
179767
  </div>
179634
179768
  <dees-harness-session-list
179635
- .sessions=${this.sessions} .groups=${this.groups} .ungroupedSessionIds=${this.ungroupedSessionIds}
179769
+ .sessions=${this.sessions} .groups=${this.groups} .ungroupedItemIds=${this.ungroupedItemIds}
179636
179770
  .resources=${demoResources} .resourceAssociations=${this.associations}
179637
- .eligibleSessionIdsByResourceId=${demoResourceEligibleSessionIds} .enableGrouping=${true}
179771
+ .eligibleTargetsByResourceId=${demoResourceEligibleTargets} .enableGrouping=${true}
179638
179772
  .selectedSessionId=${this.selectedSessionId}
179639
179773
  @harness-session-select=${this.select} @harness-session-open=${this.select}
179640
179774
  @harness-session-context=${(event) => handleSessionContext(event, (opened) => {
179641
179775
  this.syncList();
179642
179776
  if (opened) void this.revealSession();
179643
179777
  })}
179644
- @harness-session-move=${(event) => {
179645
- handleSessionMove(event);
179778
+ @harness-item-move=${(event) => {
179779
+ handleItemMove(event);
179646
179780
  this.syncList();
179647
179781
  }}
179648
179782
  @harness-group-create-request=${() => {
179649
- this.groups = [...this.groups, { id: `group-${this.groups.length}`, name: `Group ${this.groups.length + 1}`, sessionIds: [] }];
179783
+ this.groups = [...this.groups, { id: `group-${this.groups.length}`, name: `Group ${this.groups.length + 1}`, itemIds: [] }];
179650
179784
  if (this.list) this.list.search = "";
179651
- this.notice = "Group created. Drag a session here or use its Actions menu.";
179785
+ this.notice = "Group created. Drag a conversation or resource here, or use its Actions menu.";
179652
179786
  }}
179653
179787
  @harness-resource-select=${(event) => {
179654
179788
  event.currentTarget.selectedResourceId = event.detail.resource.id;
@@ -179738,14 +179872,14 @@ _init101 = __decoratorStart(_a99);
179738
179872
  _sessions = new WeakMap();
179739
179873
  _selectedSessionId = new WeakMap();
179740
179874
  _groups2 = new WeakMap();
179741
- _ungroupedSessionIds = new WeakMap();
179875
+ _ungroupedItemIds = new WeakMap();
179742
179876
  _associations = new WeakMap();
179743
179877
  _notice = new WeakMap();
179744
179878
  _showDetails = new WeakMap();
179745
179879
  __decorateElement(_init101, 4, "sessions", _sessions_dec, _DeesDemoSessionWorkspace, _sessions);
179746
179880
  __decorateElement(_init101, 4, "selectedSessionId", _selectedSessionId_dec, _DeesDemoSessionWorkspace, _selectedSessionId);
179747
179881
  __decorateElement(_init101, 4, "groups", _groups_dec2, _DeesDemoSessionWorkspace, _groups2);
179748
- __decorateElement(_init101, 4, "ungroupedSessionIds", _ungroupedSessionIds_dec, _DeesDemoSessionWorkspace, _ungroupedSessionIds);
179882
+ __decorateElement(_init101, 4, "ungroupedItemIds", _ungroupedItemIds_dec, _DeesDemoSessionWorkspace, _ungroupedItemIds);
179749
179883
  __decorateElement(_init101, 4, "associations", _associations_dec, _DeesDemoSessionWorkspace, _associations);
179750
179884
  __decorateElement(_init101, 4, "notice", _notice_dec, _DeesDemoSessionWorkspace, _notice);
179751
179885
  __decorateElement(_init101, 4, "showDetails", _showDetails_dec, _DeesDemoSessionWorkspace, _showDetails);
@@ -179801,12 +179935,18 @@ var demoFunc67 = /* @__PURE__ */ __name(() => demoApp(b2`<dees-demo-session-work
179801
179935
  init_dist_ts26();
179802
179936
  init_theme();
179803
179937
  init_dees_icon();
179804
- var _resourceContextResourceId_dec, _moveAnnouncement_dec, _bottomScrollShadowVisible_dec, _topScrollShadowVisible_dec, _collapsedGroupIds_dec, _expandedSessionIds_dec, _draggedItemHeight_dec, _dropSlot_dec, _resourceDropTargetSessionId_dec, _draggingResourceId_dec, _draggingSessionId_dec, _search_dec, _eligibleSessionIdsByResourceId_dec, _selectedResourceId_dec, _resourceAssociations_dec, _resources_dec, _ungroupedSessionIds_dec2, _enableGrouping_dec, _groups_dec3, _emptyText_dec2, _loading_dec5, _selectedSessionId_dec2, _sessions_dec2, _a100, _DeesHarnessSessionList_decorators, _init102, _sessions2, _selectedSessionId2, _loading5, _emptyText2, _groups3, _enableGrouping, _ungroupedSessionIds2, _resources, _resourceAssociations, _selectedResourceId, _eligibleSessionIdsByResourceId, _search, _draggingSessionId, _draggingResourceId, _resourceDropTargetSessionId, _dropSlot, _draggedItemHeight, _expandedSessionIds, _collapsedGroupIds, _topScrollShadowVisible, _bottomScrollShadowVisible, _moveAnnouncement, _resourceContextResourceId;
179938
+ var _resourceContextResourceId_dec, _moveAnnouncement_dec, _bottomScrollShadowVisible_dec, _topScrollShadowVisible_dec, _collapsedGroupIds_dec, _expandedSessionIds_dec, _draggedItemHeight_dec, _dropSlot_dec, _draggingItem_dec, _search_dec, _eligibleTargetsByResourceId_dec, _selectedResourceId_dec, _resourceAssociations_dec, _resources_dec, _ungroupedItemIds_dec2, _enableGrouping_dec, _groups_dec3, _emptyText_dec2, _loading_dec5, _selectedSessionId_dec2, _sessions_dec2, _a100, _DeesHarnessSessionList_decorators, _init102, _sessions2, _selectedSessionId2, _loading5, _emptyText2, _groups3, _enableGrouping, _ungroupedItemIds2, _resources, _resourceAssociations, _selectedResourceId, _eligibleTargetsByResourceId, _search, _draggingItem, _dropSlot, _draggedItemHeight, _expandedSessionIds, _collapsedGroupIds, _topScrollShadowVisible, _bottomScrollShadowVisible, _moveAnnouncement, _resourceContextResourceId;
179805
179939
  var { repeat: repeat6 } = directives_exports;
179806
179940
  var dragAutoScrollMaxPixelsPerSecond = 900;
179807
179941
  var dragThresholdPixels = 4;
179942
+ var itemKey = /* @__PURE__ */ __name((refArg) => `${refArg.kind}:${refArg.id}`, "itemKey");
179943
+ var itemRef = /* @__PURE__ */ __name((itemArg) => ({
179944
+ kind: itemArg.kind,
179945
+ id: itemArg.id
179946
+ }), "itemRef");
179947
+ var sameItem = /* @__PURE__ */ __name((leftArg, rightArg) => !!leftArg && !!rightArg && leftArg.kind === rightArg.kind && leftArg.id === rightArg.id, "sameItem");
179808
179948
  _DeesHarnessSessionList_decorators = [customElement("dees-harness-session-list")];
179809
- var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = DeesElement, _sessions_dec2 = [n5({ attribute: false })], _selectedSessionId_dec2 = [n5()], _loading_dec5 = [n5({ type: Boolean })], _emptyText_dec2 = [n5()], _groups_dec3 = [n5({ attribute: false })], _enableGrouping_dec = [n5({ type: Boolean })], _ungroupedSessionIds_dec2 = [n5({ attribute: false })], _resources_dec = [n5({ attribute: false })], _resourceAssociations_dec = [n5({ attribute: false })], _selectedResourceId_dec = [n5()], _eligibleSessionIdsByResourceId_dec = [n5({ attribute: false })], _search_dec = [r5()], _draggingSessionId_dec = [r5()], _draggingResourceId_dec = [r5()], _resourceDropTargetSessionId_dec = [r5()], _dropSlot_dec = [r5()], _draggedItemHeight_dec = [r5()], _expandedSessionIds_dec = [r5()], _collapsedGroupIds_dec = [r5()], _topScrollShadowVisible_dec = [r5()], _bottomScrollShadowVisible_dec = [r5()], _moveAnnouncement_dec = [r5()], _resourceContextResourceId_dec = [r5()], _a100) {
179949
+ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = DeesElement, _sessions_dec2 = [n5({ attribute: false })], _selectedSessionId_dec2 = [n5()], _loading_dec5 = [n5({ type: Boolean })], _emptyText_dec2 = [n5()], _groups_dec3 = [n5({ attribute: false })], _enableGrouping_dec = [n5({ type: Boolean })], _ungroupedItemIds_dec2 = [n5({ attribute: false })], _resources_dec = [n5({ attribute: false })], _resourceAssociations_dec = [n5({ attribute: false })], _selectedResourceId_dec = [n5()], _eligibleTargetsByResourceId_dec = [n5({ attribute: false })], _search_dec = [r5()], _draggingItem_dec = [r5()], _dropSlot_dec = [r5()], _draggedItemHeight_dec = [r5()], _expandedSessionIds_dec = [r5()], _collapsedGroupIds_dec = [r5()], _topScrollShadowVisible_dec = [r5()], _bottomScrollShadowVisible_dec = [r5()], _moveAnnouncement_dec = [r5()], _resourceContextResourceId_dec = [r5()], _a100) {
179810
179950
  constructor() {
179811
179951
  super(...arguments);
179812
179952
  __privateAdd(this, _sessions2, __runInitializers(_init102, 8, this, [])), __runInitializers(_init102, 11, this);
@@ -179815,23 +179955,21 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
179815
179955
  __privateAdd(this, _emptyText2, __runInitializers(_init102, 20, this, "No conversations yet.")), __runInitializers(_init102, 23, this);
179816
179956
  __privateAdd(this, _groups3, __runInitializers(_init102, 24, this, [])), __runInitializers(_init102, 27, this);
179817
179957
  __privateAdd(this, _enableGrouping, __runInitializers(_init102, 28, this, false)), __runInitializers(_init102, 31, this);
179818
- __privateAdd(this, _ungroupedSessionIds2, __runInitializers(_init102, 32, this, [])), __runInitializers(_init102, 35, this);
179958
+ __privateAdd(this, _ungroupedItemIds2, __runInitializers(_init102, 32, this, [])), __runInitializers(_init102, 35, this);
179819
179959
  __privateAdd(this, _resources, __runInitializers(_init102, 36, this, [])), __runInitializers(_init102, 39, this);
179820
179960
  __privateAdd(this, _resourceAssociations, __runInitializers(_init102, 40, this, [])), __runInitializers(_init102, 43, this);
179821
179961
  __privateAdd(this, _selectedResourceId, __runInitializers(_init102, 44, this, "")), __runInitializers(_init102, 47, this);
179822
- __privateAdd(this, _eligibleSessionIdsByResourceId, __runInitializers(_init102, 48, this, {})), __runInitializers(_init102, 51, this);
179962
+ __privateAdd(this, _eligibleTargetsByResourceId, __runInitializers(_init102, 48, this, {})), __runInitializers(_init102, 51, this);
179823
179963
  __privateAdd(this, _search, __runInitializers(_init102, 52, this, "")), __runInitializers(_init102, 55, this);
179824
- __privateAdd(this, _draggingSessionId, __runInitializers(_init102, 56, this, "")), __runInitializers(_init102, 59, this);
179825
- __privateAdd(this, _draggingResourceId, __runInitializers(_init102, 60, this, "")), __runInitializers(_init102, 63, this);
179826
- __privateAdd(this, _resourceDropTargetSessionId, __runInitializers(_init102, 64, this, "")), __runInitializers(_init102, 67, this);
179827
- __privateAdd(this, _dropSlot, __runInitializers(_init102, 68, this)), __runInitializers(_init102, 71, this);
179828
- __privateAdd(this, _draggedItemHeight, __runInitializers(_init102, 72, this, 0)), __runInitializers(_init102, 75, this);
179829
- __privateAdd(this, _expandedSessionIds, __runInitializers(_init102, 76, this, /* @__PURE__ */ new Set())), __runInitializers(_init102, 79, this);
179830
- __privateAdd(this, _collapsedGroupIds, __runInitializers(_init102, 80, this, /* @__PURE__ */ new Set())), __runInitializers(_init102, 83, this);
179831
- __privateAdd(this, _topScrollShadowVisible, __runInitializers(_init102, 84, this, false)), __runInitializers(_init102, 87, this);
179832
- __privateAdd(this, _bottomScrollShadowVisible, __runInitializers(_init102, 88, this, false)), __runInitializers(_init102, 91, this);
179833
- __privateAdd(this, _moveAnnouncement, __runInitializers(_init102, 92, this, "")), __runInitializers(_init102, 95, this);
179834
- __privateAdd(this, _resourceContextResourceId, __runInitializers(_init102, 96, this, "")), __runInitializers(_init102, 99, this);
179964
+ __privateAdd(this, _draggingItem, __runInitializers(_init102, 56, this)), __runInitializers(_init102, 59, this);
179965
+ __privateAdd(this, _dropSlot, __runInitializers(_init102, 60, this)), __runInitializers(_init102, 63, this);
179966
+ __privateAdd(this, _draggedItemHeight, __runInitializers(_init102, 64, this, 0)), __runInitializers(_init102, 67, this);
179967
+ __privateAdd(this, _expandedSessionIds, __runInitializers(_init102, 68, this, /* @__PURE__ */ new Set())), __runInitializers(_init102, 71, this);
179968
+ __privateAdd(this, _collapsedGroupIds, __runInitializers(_init102, 72, this, /* @__PURE__ */ new Set())), __runInitializers(_init102, 75, this);
179969
+ __privateAdd(this, _topScrollShadowVisible, __runInitializers(_init102, 76, this, false)), __runInitializers(_init102, 79, this);
179970
+ __privateAdd(this, _bottomScrollShadowVisible, __runInitializers(_init102, 80, this, false)), __runInitializers(_init102, 83, this);
179971
+ __privateAdd(this, _moveAnnouncement, __runInitializers(_init102, 84, this, "")), __runInitializers(_init102, 87, this);
179972
+ __privateAdd(this, _resourceContextResourceId, __runInitializers(_init102, 88, this, "")), __runInitializers(_init102, 91, this);
179835
179973
  __publicField(this, "reflowGeneration", 0);
179836
179974
  __publicField(this, "reflowAnimations", /* @__PURE__ */ new Set());
179837
179975
  __publicField(this, "pointerDragGesture");
@@ -179859,16 +179997,8 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
179859
179997
  eventArg.clientX - gesture.startClientX,
179860
179998
  eventArg.clientY - gesture.startClientY
179861
179999
  ) < dragThresholdPixels) return;
179862
- if (gesture.phase === "pending" && !(gesture.type === "session" ? this.startPointerDrag(eventArg, gesture) : this.startResourcePointerDrag(eventArg, gesture))) return;
180000
+ if (gesture.phase === "pending" && !this.startPointerDrag(eventArg, gesture)) return;
179863
180001
  eventArg.preventDefault();
179864
- if (gesture.type === "resource") {
179865
- this.moveResourceDragPreview(eventArg.clientX, eventArg.clientY, gesture);
179866
- this.dragAutoScrollClientX = eventArg.clientX;
179867
- this.dragAutoScrollClientY = eventArg.clientY;
179868
- this.updateDragAutoScrollVelocity();
179869
- this.updateResourceDropTargetFromPointer(eventArg.clientX, eventArg.clientY);
179870
- return;
179871
- }
179872
180002
  this.moveDragPreview(eventArg.clientX, eventArg.clientY, gesture);
179873
180003
  this.dragAutoScrollClientX = eventArg.clientX;
179874
180004
  this.dragAutoScrollClientY = eventArg.clientY;
@@ -179879,14 +180009,6 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
179879
180009
  const gesture = this.pointerDragGesture;
179880
180010
  if (!gesture || eventArg.pointerId !== gesture.pointerId) return;
179881
180011
  if (gesture.phase === "active") {
179882
- if (gesture.type === "resource") {
179883
- this.dragAutoScrollClientX = eventArg.clientX;
179884
- this.dragAutoScrollClientY = eventArg.clientY;
179885
- this.moveResourceDragPreview(eventArg.clientX, eventArg.clientY, gesture);
179886
- this.updateResourceDropTargetFromPointer(eventArg.clientX, eventArg.clientY);
179887
- this.finishPointerDrag(true);
179888
- return;
179889
- }
179890
180012
  this.dragAutoScrollClientX = eventArg.clientX;
179891
180013
  this.dragAutoScrollClientY = eventArg.clientY;
179892
180014
  this.moveDragPreview(eventArg.clientX, eventArg.clientY, gesture);
@@ -179926,14 +180048,7 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
179926
180048
  this.stopDragAutoScroll();
179927
180049
  return;
179928
180050
  }
179929
- if (gesture.type === "resource") {
179930
- this.updateResourceDropTargetFromPointer(
179931
- this.dragAutoScrollClientX,
179932
- this.dragAutoScrollClientY
179933
- );
179934
- } else {
179935
- this.updateDropSlotFromPointer();
179936
- }
180051
+ this.updateDropSlotFromPointer();
179937
180052
  this.dragAutoScrollFrame = requestAnimationFrame(this.runDragAutoScroll);
179938
180053
  }, "runDragAutoScroll"));
179939
180054
  __publicField(this, "syncScrollShadowState", /* @__PURE__ */ __name(() => {
@@ -179945,9 +180060,6 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
179945
180060
  if (this.bottomScrollShadowVisible !== bottomVisible) this.bottomScrollShadowVisible = bottomVisible;
179946
180061
  }, "syncScrollShadowState"));
179947
180062
  }
179948
- get sessionsById() {
179949
- return new Map(this.sessions.map((session) => [session.id, session]));
179950
- }
179951
180063
  get resourcesById() {
179952
180064
  const result = /* @__PURE__ */ new Map();
179953
180065
  for (const resource of this.resources) {
@@ -179956,22 +180068,111 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
179956
180068
  }
179957
180069
  return result;
179958
180070
  }
179959
- /** First valid record wins; unknown and duplicate records remain untouched but are not projected. */
180071
+ /**
180072
+ * First valid record wins; unknown records, self-references and duplicates
180073
+ * remain untouched but are not projected. Keyed by resource id.
180074
+ */
179960
180075
  get acceptedResourceAssociations() {
179961
180076
  const resourcesById = this.resourcesById;
179962
- const sessionsById = this.sessionsById;
180077
+ const itemsByKey = this.itemsByKey;
179963
180078
  const result = /* @__PURE__ */ new Map();
179964
180079
  for (const association2 of this.resourceAssociations) {
179965
- if (!resourcesById.has(association2.resourceId) || !sessionsById.has(association2.sessionId) || result.has(association2.resourceId)) continue;
179966
- result.set(association2.resourceId, association2.sessionId);
180080
+ if (!association2.target || !resourcesById.has(association2.resourceId) || !itemsByKey.has(itemKey(association2.target)) || sameItem(association2.target, { kind: "resource", id: association2.resourceId }) || result.has(association2.resourceId)) continue;
180081
+ result.set(association2.resourceId, itemRef(association2.target));
179967
180082
  }
179968
180083
  return result;
179969
180084
  }
179970
180085
  get acceptedResources() {
179971
180086
  return [...this.resourcesById.values()];
179972
180087
  }
180088
+ /** Sessions and resources as one pool of orderable items, before grouping and search. */
180089
+ get acceptedItems() {
180090
+ const result = [];
180091
+ const seenSessionIds = /* @__PURE__ */ new Set();
180092
+ for (const session of this.sessions) {
180093
+ if (!session.id || seenSessionIds.has(session.id)) continue;
180094
+ seenSessionIds.add(session.id);
180095
+ result.push({
180096
+ kind: "session",
180097
+ key: `session:${session.id}`,
180098
+ id: session.id,
180099
+ title: session.title || session.id,
180100
+ sortable: session.draggable !== false,
180101
+ timestamp: session.updatedAt ?? session.createdAt ?? 0,
180102
+ session
180103
+ });
180104
+ }
180105
+ for (const resource of this.resourcesById.values()) {
180106
+ result.push({
180107
+ kind: "resource",
180108
+ key: `resource:${resource.id}`,
180109
+ id: resource.id,
180110
+ title: resource.title || resource.id,
180111
+ sortable: resource.draggable !== false,
180112
+ timestamp: resource.updatedAt ?? resource.createdAt ?? 0,
180113
+ resource
180114
+ });
180115
+ }
180116
+ return result;
180117
+ }
180118
+ get itemsByKey() {
180119
+ return new Map(this.acceptedItems.map((item) => [item.key, item]));
180120
+ }
180121
+ /** Groups only organize the list while grouping is enabled. */
180122
+ get effectiveGroups() {
180123
+ return this.enableGrouping ? this.groups : [];
180124
+ }
180125
+ get groupedItemKeys() {
180126
+ const keys3 = /* @__PURE__ */ new Set();
180127
+ for (const group of this.effectiveGroups) {
180128
+ for (const ref of group.itemIds) keys3.add(itemKey(ref));
180129
+ }
180130
+ return keys3;
180131
+ }
180132
+ itemsForGroup(groupIdArg) {
180133
+ if (groupIdArg === null) return this.ungroupedItems;
180134
+ const group = this.effectiveGroups.find((candidate) => candidate.id === groupIdArg);
180135
+ if (!group) return [];
180136
+ const byKey = this.itemsByKey;
180137
+ const seen = /* @__PURE__ */ new Set();
180138
+ const result = [];
180139
+ for (const ref of group.itemIds) {
180140
+ const key2 = itemKey(ref);
180141
+ const item = byKey.get(key2);
180142
+ if (!item || seen.has(key2)) continue;
180143
+ seen.add(key2);
180144
+ result.push(item);
180145
+ }
180146
+ return result;
180147
+ }
180148
+ /** Pinned entries first, then explicitly ordered items, then unknown items by recency. */
180149
+ get ungroupedItems() {
180150
+ const grouped = this.groupedItemKeys;
180151
+ const available = this.acceptedItems.filter((item) => !grouped.has(item.key));
180152
+ const pinned = available.filter((item) => !item.sortable);
180153
+ const sortableByKey = new Map(
180154
+ available.filter((item) => item.sortable).map((item) => [item.key, item])
180155
+ );
180156
+ const ordered = [];
180157
+ for (const ref of this.ungroupedItemIds) {
180158
+ const item = sortableByKey.get(itemKey(ref));
180159
+ if (!item) continue;
180160
+ ordered.push(item);
180161
+ sortableByKey.delete(item.key);
180162
+ }
180163
+ const unknown3 = [...sortableByKey.values()].sort(
180164
+ (left, right) => right.timestamp - left.timestamp
180165
+ );
180166
+ return [...pinned, ...ordered, ...unknown3];
180167
+ }
179973
180168
  resourceMatchesSearch(resourceArg, queryArg) {
179974
- return [resourceArg.id, resourceArg.kind, resourceArg.title, resourceArg.preview].filter(Boolean).join(" ").toLowerCase().includes(queryArg);
180169
+ return [
180170
+ resourceArg.id,
180171
+ resourceArg.kind,
180172
+ resourceArg.title,
180173
+ resourceArg.preview,
180174
+ resourceArg.projectLabel
180175
+ ].filter(Boolean).join(" ").toLowerCase().includes(queryArg);
179975
180176
  }
179976
180177
  sessionMatchesSearch(sessionArg, queryArg) {
179977
180178
  return [
@@ -179980,99 +180181,74 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
179980
180181
  sessionArg.id,
179981
180182
  sessionArg.harness?.id,
179982
180183
  sessionArg.harness?.name,
180184
+ sessionArg.projectLabel,
179983
180185
  this.sessionStatusLabel(sessionArg),
179984
180186
  this.sessionStateLabel(sessionArg.state ?? "normal")
179985
180187
  ].filter(Boolean).join(" ").toLowerCase().includes(queryArg);
179986
180188
  }
179987
- resourcesForSession(sessionArg) {
179988
- const associations = this.acceptedResourceAssociations;
179989
- const associated = this.acceptedResources.filter(
179990
- (resource) => associations.get(resource.id) === sessionArg.id
179991
- );
179992
- const query3 = this.search.trim().toLowerCase();
179993
- if (!query3 || this.sessionMatchesSearch(sessionArg, query3)) return associated;
179994
- return associated.filter((resource) => this.resourceMatchesSearch(resource, query3));
179995
- }
179996
- get unattachedResources() {
179997
- const associations = this.acceptedResourceAssociations;
179998
- const query3 = this.search.trim().toLowerCase();
179999
- return this.acceptedResources.filter((resource) => !associations.has(resource.id) && (!query3 || this.resourceMatchesSearch(resource, query3)));
180000
- }
180001
- get groupedSessionIds() {
180002
- const ids = /* @__PURE__ */ new Set();
180003
- for (const group of this.groups) {
180004
- for (const id of group.sessionIds) ids.add(id);
180005
- }
180006
- return ids;
180007
- }
180008
- /** Pinned entries first, followed by explicitly ordered sortable sessions. */
180009
- get ungroupedSessions() {
180010
- const grouped = this.groupedSessionIds;
180011
- const available = this.sessions.filter((session) => !grouped.has(session.id));
180012
- const pinned = available.filter((session) => session.draggable === false);
180013
- const sortableById = new Map(
180014
- available.filter((session) => session.draggable !== false).map((session) => [session.id, session])
180015
- );
180016
- const ordered = [];
180017
- for (const sessionId of this.ungroupedSessionIds) {
180018
- const session = sortableById.get(sessionId);
180019
- if (!session) continue;
180020
- ordered.push(session);
180021
- sortableById.delete(sessionId);
180022
- }
180023
- const newSessions = [...sortableById.values()].sort(
180024
- (left, right) => (right.updatedAt ?? right.createdAt ?? 0) - (left.updatedAt ?? left.createdAt ?? 0)
180025
- );
180026
- return [...pinned, ...ordered, ...newSessions];
180189
+ itemMatchesSearch(itemArg, queryArg) {
180190
+ return itemArg.kind === "session" ? this.sessionMatchesSearch(itemArg.session, queryArg) : this.resourceMatchesSearch(itemArg.resource, queryArg);
180027
180191
  }
180028
- get filteredSessions() {
180192
+ /** Search flattens the list: sessions and resources match as peers, newest first. */
180193
+ get searchItems() {
180029
180194
  const query3 = this.search.trim().toLowerCase();
180030
- const associations = this.acceptedResourceAssociations;
180031
- const matchingAttachedSessionIds = new Set(
180032
- this.acceptedResources.filter((resource) => query3 && this.resourceMatchesSearch(resource, query3)).map((resource) => associations.get(resource.id)).filter((sessionId) => sessionId !== void 0)
180033
- );
180034
- const sorted = [...this.sessions].sort(
180035
- (left, right) => (right.updatedAt ?? right.createdAt ?? 0) - (left.updatedAt ?? left.createdAt ?? 0)
180195
+ const sorted = [...this.acceptedItems].sort(
180196
+ (left, right) => right.timestamp - left.timestamp
180036
180197
  );
180037
180198
  if (!query3) return sorted;
180038
- return sorted.filter(
180039
- (session) => this.sessionMatchesSearch(session, query3) || matchingAttachedSessionIds.has(session.id)
180040
- );
180199
+ return sorted.filter((item) => this.itemMatchesSearch(item, query3));
180200
+ }
180201
+ get searchActive() {
180202
+ return this.search.trim().length > 0;
180203
+ }
180204
+ sortableItems(itemsArg, excludeDraggingArg = true) {
180205
+ return itemsArg.filter((item) => item.sortable && (!excludeDraggingArg || !sameItem(this.draggingItem, item)));
180206
+ }
180207
+ groupIdForItem(refArg) {
180208
+ return this.effectiveGroups.find(
180209
+ (group) => group.itemIds.some((candidate) => sameItem(candidate, refArg))
180210
+ )?.id ?? null;
180211
+ }
180212
+ rowElementFor(refArg) {
180213
+ return Array.from(
180214
+ this.shadowRoot?.querySelectorAll("[data-item-kind][data-item-id]") ?? []
180215
+ ).find((candidate) => candidate.dataset.itemKind === refArg.kind && candidate.dataset.itemId === refArg.id);
180041
180216
  }
180042
180217
  focusSearch() {
180043
180218
  this.shadowRoot?.querySelector(".searchRow input")?.focus();
180044
180219
  }
180045
- requestResourceAssociation(resourceIdArg, toSessionIdArg, sourceArg = "action") {
180220
+ requestResourceAssociation(resourceIdArg, toTargetArg, sourceArg = "action") {
180046
180221
  const resource = this.resourcesById.get(resourceIdArg);
180047
180222
  if (!resource) return false;
180048
- const fromSessionId = this.acceptedResourceAssociations.get(resourceIdArg) ?? null;
180049
- if (fromSessionId === toSessionIdArg) return false;
180050
- if (toSessionIdArg !== null && (!this.sessionsById.has(toSessionIdArg) || !this.isResourceTargetEligible(resourceIdArg, toSessionIdArg))) return false;
180051
- if (toSessionIdArg === null && fromSessionId === null) return false;
180052
- const detail = fromSessionId === null ? {
180223
+ const fromTarget = this.acceptedResourceAssociations.get(resourceIdArg) ?? null;
180224
+ if (sameItem(fromTarget, toTargetArg)) return false;
180225
+ if (toTargetArg !== null && (!this.itemsByKey.has(itemKey(toTargetArg)) || sameItem(toTargetArg, { kind: "resource", id: resourceIdArg }) || !this.isResourceTargetEligible(resourceIdArg, toTargetArg))) return false;
180226
+ if (toTargetArg === null && fromTarget === null) return false;
180227
+ const toTarget = toTargetArg === null ? null : itemRef(toTargetArg);
180228
+ const detail = fromTarget === null ? {
180053
180229
  intent: "attach",
180054
180230
  resource,
180055
- fromSessionId: null,
180056
- toSessionId: toSessionIdArg,
180231
+ fromTarget: null,
180232
+ toTarget,
180057
180233
  source: sourceArg
180058
- } : toSessionIdArg === null ? {
180234
+ } : toTarget === null ? {
180059
180235
  intent: "detach",
180060
180236
  resource,
180061
- fromSessionId,
180062
- toSessionId: null,
180237
+ fromTarget,
180238
+ toTarget: null,
180063
180239
  source: sourceArg
180064
180240
  } : {
180065
180241
  intent: "reassign",
180066
180242
  resource,
180067
- fromSessionId,
180068
- toSessionId: toSessionIdArg,
180243
+ fromTarget,
180244
+ toTarget,
180069
180245
  source: sourceArg
180070
180246
  };
180071
180247
  this.pendingResourceAssociationRequest = {
180072
180248
  resourceId: resource.id,
180073
180249
  resourceTitle: resource.title || resource.id,
180074
- fromSessionId,
180075
- toSessionId: toSessionIdArg,
180250
+ fromTarget,
180251
+ toTarget,
180076
180252
  intent: detail.intent
180077
180253
  };
180078
180254
  this.moveAnnouncement = this.resourceAssociationAnnouncement(detail, false);
@@ -180095,9 +180271,6 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180095
180271
  }
180096
180272
  if (changedProperties.has("sessions")) {
180097
180273
  const knownIds = new Set(this.sessions.map((session) => session.id));
180098
- if (this.pointerDragGesture?.type === "session" && !knownIds.has(this.pointerDragGesture.sessionId)) {
180099
- this.finishPointerDrag(false);
180100
- }
180101
180274
  const nextExpanded = new Set(
180102
180275
  [...this.expandedSessionIds].filter((sessionId) => knownIds.has(sessionId))
180103
180276
  );
@@ -180106,22 +180279,14 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180106
180279
  }
180107
180280
  }
180108
180281
  if (changedProperties.has("resources") || changedProperties.has("resourceAssociations")) {
180109
- const gesture = this.pointerDragGesture;
180110
- if (gesture?.type === "resource") {
180111
- const currentSessionId = this.acceptedResourceAssociations.get(gesture.resourceId) ?? null;
180112
- if (!this.resourcesById.has(gesture.resourceId) || currentSessionId !== gesture.fromSessionId) {
180113
- this.finishPointerDrag(false);
180114
- }
180115
- }
180116
180282
  if (this.resourceContextResourceId && !this.resourcesById.has(this.resourceContextResourceId)) this.closeResourceContext(this.resourceContextGeneration);
180117
180283
  this.reconcilePendingResourceAssociation();
180118
180284
  }
180119
- if (changedProperties.has("sessions") || changedProperties.has("eligibleSessionIdsByResourceId")) {
180285
+ if (changedProperties.has("sessions") || changedProperties.has("resources") || changedProperties.has("search")) {
180120
180286
  const gesture = this.pointerDragGesture;
180121
- if (gesture?.type === "resource" && !this.sameSessionIds(
180122
- gesture.eligibleSessionIds,
180123
- new Set(this.eligibleSessionIds(gesture.resourceId))
180124
- )) this.finishPointerDrag(false);
180287
+ if (gesture && (this.searchActive || !this.itemsByKey.has(itemKey(gesture.item)))) {
180288
+ this.finishPointerDrag(false);
180289
+ }
180125
180290
  }
180126
180291
  }
180127
180292
  async connectedCallback() {
@@ -180141,7 +180306,6 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180141
180306
  await super.disconnectedCallback();
180142
180307
  }
180143
180308
  render() {
180144
- const groupingActive = this.enableGrouping && !this.search.trim();
180145
180309
  return b2`
180146
180310
  <div class="searchRow">
180147
180311
  <div class="searchField">
@@ -180175,11 +180339,11 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180175
180339
  <div class="listFrame">
180176
180340
  <div class="scrollShadow top ${this.topScrollShadowVisible ? "visible" : ""}"></div>
180177
180341
  <div
180178
- class="list ${this.draggingSessionId || this.draggingResourceId ? "dragActive" : ""}"
180342
+ class="list ${this.draggingItem ? "dragActive" : ""}"
180179
180343
  @scroll=${this.syncScrollShadowState}
180180
180344
  >
180181
180345
  <div class="listContent">
180182
- ${this.loading ? b2`<div class="emptyNote">Loading conversations…</div>` : groupingActive ? this.renderGroupedList() : this.renderFlatList()}
180346
+ ${this.loading ? b2`<div class="emptyNote">Loading conversations…</div>` : this.searchActive ? this.renderSearchList() : this.renderOrderedList()}
180183
180347
  </div>
180184
180348
  </div>
180185
180349
  <div class="scrollShadow bottom ${this.bottomScrollShadowVisible ? "visible" : ""}"></div>
@@ -180187,70 +180351,25 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180187
180351
  <div class="visuallyHidden" aria-live="polite">${this.moveAnnouncement}</div>
180188
180352
  `;
180189
180353
  }
180190
- renderFlatList() {
180191
- const sessions = this.filteredSessions;
180192
- const unattachedResources = this.unattachedResources;
180193
- if (!sessions.length && !unattachedResources.length) {
180194
- return b2`<div class="emptyNote">
180195
- ${this.acceptedResources.length ? this.sessions.length ? "No conversations or resources match this search." : "No resources match this search." : this.sessions.length ? "No conversations match this search." : this.emptyText}
180196
- </div>`;
180197
- }
180198
- return b2`
180199
- <div class="section">
180200
- ${repeat6(
180201
- sessions,
180202
- (session) => session.id,
180203
- (session) => this.renderItem(session, null, -1)
180204
- )}
180205
- </div>
180206
- ${this.renderUnattachedResources(unattachedResources)}
180207
- `;
180354
+ renderEmptyNote() {
180355
+ return b2`<div class="emptyNote">
180356
+ ${this.acceptedResources.length ? this.sessions.length ? "No conversations or resources match this search." : "No resources match this search." : this.sessions.length ? "No conversations match this search." : this.emptyText}
180357
+ </div>`;
180358
+ }
180359
+ renderSearchList() {
180360
+ const items = this.searchItems;
180361
+ if (!items.length) return this.renderEmptyNote();
180362
+ return this.renderSection(items, null, false);
180208
180363
  }
180209
- renderGroupedList() {
180210
- const byId = this.sessionsById;
180211
- const ungrouped = this.ungroupedSessions;
180212
- const unattachedResources = this.unattachedResources;
180213
- if (!this.sessions.length && !this.groups.length && !unattachedResources.length) {
180364
+ renderOrderedList() {
180365
+ const groups = this.effectiveGroups;
180366
+ const ungrouped = this.ungroupedItems;
180367
+ if (!this.acceptedItems.length && !groups.length) {
180214
180368
  return b2`<div class="emptyNote">${this.emptyText}</div>`;
180215
180369
  }
180216
180370
  return b2`
180217
- ${this.groups.map((group) => {
180218
- const members = group.sessionIds.map((id) => byId.get(id)).filter((session) => session !== void 0);
180219
- const collapsed = this.collapsedGroupIds.has(group.id);
180220
- const attentionCount = members.filter((session) => session.state === "attention" || session.state === "error").length;
180221
- const isHeaderDrop = this.dropSlot?.groupId === group.id && this.dropSlot.index === this.sortableSessions(members).length && collapsed;
180222
- return b2`
180223
- <button
180224
- class="groupHeader ${collapsed ? "collapsed" : ""} ${isHeaderDrop ? "dropTarget" : ""}"
180225
- data-drop-group-id=${group.id}
180226
- data-drop-index=${this.sortableSessions(members).length}
180227
- data-drop-kind="slot"
180228
- type="button"
180229
- @click=${() => this.toggleGroupCollapsed(group.id)}
180230
- @contextmenu=${(event) => {
180231
- event.preventDefault();
180232
- const detail = {
180233
- group,
180234
- clientX: event.clientX,
180235
- clientY: event.clientY,
180236
- originalEvent: event
180237
- };
180238
- this.dispatchEvent(
180239
- new CustomEvent("harness-group-context", { detail, bubbles: true, composed: true })
180240
- );
180241
- }}
180242
- >
180243
- <dees-icon class="chevron" icon="lucide:ChevronDown" iconSize="12"></dees-icon>
180244
- <span>${group.name}</span>
180245
- ${attentionCount ? b2`<span class="groupAttention" aria-label=${`${attentionCount} need attention`}>
180246
- <dees-icon aria-hidden="true" icon="lucide:CircleAlert" iconSize="11"></dees-icon>${attentionCount}
180247
- </span>` : ""}
180248
- <span class="groupCount">${members.length}</span>
180249
- </button>
180250
- ${collapsed ? "" : this.renderSection(members, group.id)}
180251
- `;
180252
- })}
180253
- ${this.groups.length && (ungrouped.length || this.draggingSessionId) ? b2`
180371
+ ${groups.map((group) => this.renderGroup(group))}
180372
+ ${groups.length && (ungrouped.length || this.draggingItem) ? b2`
180254
180373
  <button
180255
180374
  class="groupHeader"
180256
180375
  data-drop-group-id=""
@@ -180263,43 +180382,85 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180263
180382
  <span class="groupCount">${ungrouped.length}</span>
180264
180383
  </button>
180265
180384
  ` : ""}
180266
- ${this.renderSection(ungrouped, null)}
180267
- ${this.renderUnattachedResources(unattachedResources)}
180385
+ ${this.renderSection(ungrouped, null, true)}
180386
+ `;
180387
+ }
180388
+ renderGroup(groupArg) {
180389
+ const members = this.itemsForGroup(groupArg.id);
180390
+ const collapsed = this.collapsedGroupIds.has(groupArg.id);
180391
+ const attentionCount = members.filter((item) => item.kind === "session" && (item.session.state === "attention" || item.session.state === "error")).length;
180392
+ const appendIndex = this.sortableItems(members).length;
180393
+ const isHeaderDrop = this.dropSlot?.groupId === groupArg.id && this.dropSlot.index === appendIndex && collapsed;
180394
+ return b2`
180395
+ <button
180396
+ class="groupHeader ${collapsed ? "collapsed" : ""} ${isHeaderDrop ? "dropTarget" : ""}"
180397
+ data-drop-group-id=${groupArg.id}
180398
+ data-drop-index=${appendIndex}
180399
+ data-drop-kind="slot"
180400
+ type="button"
180401
+ @click=${() => this.toggleGroupCollapsed(groupArg.id)}
180402
+ @contextmenu=${(event) => {
180403
+ event.preventDefault();
180404
+ const detail = {
180405
+ group: groupArg,
180406
+ clientX: event.clientX,
180407
+ clientY: event.clientY,
180408
+ originalEvent: event
180409
+ };
180410
+ this.dispatchEvent(
180411
+ new CustomEvent("harness-group-context", { detail, bubbles: true, composed: true })
180412
+ );
180413
+ }}
180414
+ >
180415
+ <dees-icon class="chevron" icon="lucide:ChevronDown" iconSize="12"></dees-icon>
180416
+ <span>${groupArg.name}</span>
180417
+ ${attentionCount ? b2`<span class="groupAttention" aria-label=${`${attentionCount} need attention`}>
180418
+ <dees-icon aria-hidden="true" icon="lucide:CircleAlert" iconSize="11"></dees-icon>${attentionCount}
180419
+ </span>` : ""}
180420
+ <span class="groupCount">${members.length}</span>
180421
+ </button>
180422
+ ${collapsed ? "" : this.renderSection(members, groupArg.id, true)}
180268
180423
  `;
180269
180424
  }
180270
- renderSection(sessions, groupId) {
180271
- const slot = this.dropSlot;
180425
+ renderSection(itemsArg, groupIdArg, sortableArg) {
180426
+ const slot = sortableArg ? this.dropSlot : void 0;
180272
180427
  const entries = [];
180273
180428
  let logicalIndex = 0;
180274
- for (const session of sessions) {
180275
- const isDragging = session.id === this.draggingSessionId;
180276
- const sortable = session.draggable !== false;
180277
- if (slot && slot.groupId === groupId && slot.index === logicalIndex && !isDragging && sortable) {
180278
- entries.push({ type: "placeholder", key: `placeholder:${groupId ?? "ungrouped"}` });
180429
+ for (const item of itemsArg) {
180430
+ const isDragging = sameItem(this.draggingItem, item);
180431
+ const sortable = sortableArg && item.sortable;
180432
+ if (slot && slot.groupId === groupIdArg && slot.index === logicalIndex && !isDragging && sortable) {
180433
+ entries.push({ type: "placeholder", key: `placeholder:${groupIdArg ?? "ungrouped"}` });
180279
180434
  }
180280
180435
  entries.push({
180281
- type: "session",
180282
- key: `session:${session.id}`,
180283
- session,
180436
+ type: "item",
180437
+ key: item.key,
180438
+ item,
180284
180439
  logicalIndex: sortable ? logicalIndex : -1
180285
180440
  });
180286
180441
  if (sortable && !isDragging) logicalIndex += 1;
180287
180442
  }
180288
- if (slot && slot.groupId === groupId && slot.index === logicalIndex) {
180289
- entries.push({ type: "placeholder", key: `placeholder:${groupId ?? "ungrouped"}` });
180443
+ if (slot && slot.groupId === groupIdArg && slot.index === logicalIndex) {
180444
+ entries.push({ type: "placeholder", key: `placeholder:${groupIdArg ?? "ungrouped"}` });
180290
180445
  }
180291
180446
  const hasPlaceholder = entries.some((entry) => entry.type === "placeholder");
180447
+ const associations = this.acceptedResourceAssociations;
180292
180448
  return b2`
180293
- <div class="section" data-group-id=${groupId ?? ""}>
180449
+ <div class="section" data-group-id=${groupIdArg ?? ""}>
180294
180450
  ${repeat6(
180295
180451
  entries,
180296
180452
  (entry) => entry.key,
180297
- (entry) => entry.type === "placeholder" ? this.renderDropPlaceholder(groupId, slot?.index ?? 0) : this.renderItem(entry.session, groupId, entry.logicalIndex)
180453
+ (entry) => entry.type === "placeholder" ? this.renderDropPlaceholder(groupIdArg, slot?.index ?? 0) : entry.item.kind === "session" ? this.renderSessionItem(entry.item.session, groupIdArg, entry.logicalIndex) : this.renderResourceItem(
180454
+ entry.item.resource,
180455
+ groupIdArg,
180456
+ entry.logicalIndex,
180457
+ associations.get(entry.item.id) ?? null
180458
+ )
180298
180459
  )}
180299
- ${this.draggingSessionId && !hasPlaceholder && this.sortableSessions(sessions).length === 0 ? b2`
180460
+ ${this.draggingItem && sortableArg && !hasPlaceholder && this.sortableItems(itemsArg).length === 0 ? b2`
180300
180461
  <div
180301
180462
  class="emptyDropZone"
180302
- data-drop-group-id=${groupId ?? ""}
180463
+ data-drop-group-id=${groupIdArg ?? ""}
180303
180464
  data-drop-index="0"
180304
180465
  data-drop-kind="slot"
180305
180466
  ></div>
@@ -180314,30 +180475,57 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180314
180475
  data-drop-group-id=${groupId ?? ""}
180315
180476
  data-drop-index=${index3}
180316
180477
  data-drop-kind="slot"
180317
- style=${`height: ${Math.max(46, this.draggedItemHeight)}px`}
180478
+ style=${`height: ${Math.max(24, this.draggedItemHeight)}px`}
180318
180479
  ></div>
180319
180480
  `;
180320
180481
  }
180321
- renderItem(session, groupId, index3) {
180322
- const draggable = this.enableGrouping && session.draggable !== false && index3 >= 0;
180482
+ /** One grip for both kinds; only its size and extra class differ. */
180483
+ renderDragHandle(refArg, titleArg, groupIdArg, logicalIndexArg) {
180484
+ const isResource = refArg.kind === "resource";
180485
+ return b2`
180486
+ <button
180487
+ class="dragHandle ${isResource ? "resourceDragHandle" : ""}"
180488
+ type="button"
180489
+ title="Drag to reorder; use arrow, Home, or End keys for keyboard reordering"
180490
+ aria-label=${`Reorder ${titleArg}`}
180491
+ aria-keyshortcuts="ArrowUp ArrowDown Home End"
180492
+ @pointerdown=${(event) => {
180493
+ this.handlePointerDown(event, refArg, groupIdArg, logicalIndexArg);
180494
+ }}
180495
+ @pointermove=${this.handlePointerMove}
180496
+ @pointerup=${this.handlePointerUp}
180497
+ @pointercancel=${this.handlePointerCancel}
180498
+ @lostpointercapture=${this.handleLostPointerCapture}
180499
+ @keydown=${(event) => {
180500
+ this.handleKeyboardMove(event, refArg, titleArg, groupIdArg);
180501
+ }}
180502
+ >
180503
+ <dees-icon icon="lucide:GripVertical" iconSize=${isResource ? "13" : "14"}></dees-icon>
180504
+ </button>
180505
+ `;
180506
+ }
180507
+ renderSessionItem(session, groupId, index3) {
180508
+ const draggable = index3 >= 0;
180323
180509
  const expanded = this.expandedSessionIds.has(session.id);
180324
180510
  const state4 = session.state ?? "normal";
180325
180511
  const statusLabel = this.sessionStatusLabel(session);
180326
180512
  const active = (state4 === "normal" || state4 === "working") && (session.working ?? state4 === "working");
180327
180513
  const detailsId = `session-details-${session.id}`;
180328
180514
  const dropIndex = index3 < 0 ? 0 : index3;
180329
- const resources = this.resourcesForSession(session);
180330
- const isResourceDropTarget = this.resourceDropTargetSessionId === session.id;
180515
+ const title = session.title || session.id;
180516
+ const projectLabel = session.projectLabel?.trim() ?? "";
180331
180517
  return b2`
180332
180518
  <article
180333
- class="item state-${state4} ${active ? "active" : ""} ${expanded ? "expanded" : ""} ${session.id === this.selectedSessionId ? "selected" : ""} ${this.draggingSessionId === session.id ? "dragging" : ""} ${isResourceDropTarget ? "resourceDropTarget" : ""}"
180519
+ class="item state-${state4} ${active ? "active" : ""} ${expanded ? "expanded" : ""} ${session.id === this.selectedSessionId ? "selected" : ""} ${sameItem(this.draggingItem, { kind: "session", id: session.id }) ? "dragging" : ""}"
180334
180520
  data-session-id=${session.id}
180521
+ data-item-kind="session"
180522
+ data-item-id=${session.id}
180335
180523
  data-drop-group-id=${groupId ?? ""}
180336
180524
  data-drop-index=${dropIndex}
180337
180525
  data-drop-kind=${index3 < 0 ? "slot" : "card"}
180338
180526
  @dblclick=${(event) => {
180339
180527
  if (this.eventTargetsExpandToggle(event) || this.eventTargetsDragHandle(event) || this.eventTargetsSessionActions(event)) return;
180340
- this.emit("harness-session-open", session);
180528
+ this.emitSession("harness-session-open", session);
180341
180529
  }}
180342
180530
  @contextmenu=${(event) => {
180343
180531
  if (this.eventTargetsExpandToggle(event)) return;
@@ -180356,38 +180544,18 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180356
180544
  >
180357
180545
  <div class="itemInner">
180358
180546
  <div class="itemHeader ${draggable ? "draggable" : ""}">
180359
- ${draggable ? b2`
180360
- <button
180361
- class="dragHandle"
180362
- type="button"
180363
- title="Drag to reorder; use arrow, Home, or End keys for keyboard reordering"
180364
- aria-label=${`Reorder ${session.title || session.id}`}
180365
- aria-keyshortcuts="ArrowUp ArrowDown Home End"
180366
- @pointerdown=${(event) => {
180367
- this.handlePointerDown(event, session, groupId, index3);
180368
- }}
180369
- @pointermove=${this.handlePointerMove}
180370
- @pointerup=${this.handlePointerUp}
180371
- @pointercancel=${this.handlePointerCancel}
180372
- @lostpointercapture=${this.handleLostPointerCapture}
180373
- @keydown=${(event) => {
180374
- this.handleKeyboardMove(event, session, groupId);
180375
- }}
180376
- >
180377
- <dees-icon icon="lucide:GripVertical" iconSize="14"></dees-icon>
180378
- </button>
180379
- ` : ""}
180547
+ ${draggable ? this.renderDragHandle({ kind: "session", id: session.id }, title, groupId, index3) : ""}
180380
180548
  <button
180381
180549
  class="itemPrimary"
180382
180550
  type="button"
180383
180551
  aria-keyshortcuts="ContextMenu Shift+F10"
180384
180552
  aria-current=${session.id === this.selectedSessionId ? "true" : "false"}
180385
- @click=${() => this.emit("harness-session-select", session)}
180553
+ @click=${() => this.emitSession("harness-session-select", session)}
180386
180554
  @keydown=${(event) => {
180387
180555
  if (this.handleSessionContextKey(event)) return;
180388
180556
  if (event.key !== "Enter") return;
180389
180557
  event.preventDefault();
180390
- this.emit("harness-session-open", session);
180558
+ this.emitSession("harness-session-open", session);
180391
180559
  }}
180392
180560
  >
180393
180561
  <span class="stateDot" aria-hidden="true">
@@ -180396,7 +180564,7 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180396
180564
  <span class="itemCopy">
180397
180565
  <span class="itemTitle">
180398
180566
  ${session.icon ? b2`<dees-icon class="itemIcon" icon=${session.icon} iconSize="13"></dees-icon>` : ""}
180399
- <span class="titleText">${session.title || session.id}</span>
180567
+ <span class="titleText">${title}</span>
180400
180568
  </span>
180401
180569
  <span class="itemStatus" title=${statusLabel}>${statusLabel}</span>
180402
180570
  <span class="itemIdentity">
@@ -180405,6 +180573,10 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180405
180573
  <span class="harnessName" title=${session.harness.name}>${session.harness.name}</span>
180406
180574
  <span aria-hidden="true">·</span>
180407
180575
  ` : ""}
180576
+ ${projectLabel ? b2`
180577
+ <span class="projectName" title=${projectLabel}>${projectLabel}</span>
180578
+ <span aria-hidden="true">·</span>
180579
+ ` : ""}
180408
180580
  <span class="itemTime">${harnessFormatRelativeTime(session.updatedAt ?? session.createdAt)}</span>
180409
180581
  </span>
180410
180582
  </span>
@@ -180413,7 +180585,7 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180413
180585
  class="sessionActions"
180414
180586
  type="button"
180415
180587
  title="Actions"
180416
- aria-label=${`Actions for ${session.title || session.id}`}
180588
+ aria-label=${`Actions for ${title}`}
180417
180589
  aria-haspopup="menu"
180418
180590
  aria-keyshortcuts="ContextMenu Shift+F10"
180419
180591
  @click=${this.invokeSessionContext}
@@ -180435,16 +180607,6 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180435
180607
  <dees-icon icon="lucide:ChevronDown" iconSize="15"></dees-icon>
180436
180608
  </button>
180437
180609
  </div>
180438
- ${isResourceDropTarget ? b2`<div class="resourceDropLabel">Associate resource with ${session.title || session.id}</div>` : ""}
180439
- ${resources.length ? b2`
180440
- <ul class="resourceChildren" aria-label=${`Resources for ${session.title || session.id}`}>
180441
- ${repeat6(
180442
- resources,
180443
- (resource) => resource.id,
180444
- (resource) => this.renderResource(resource, session.id)
180445
- )}
180446
- </ul>
180447
- ` : ""}
180448
180610
  ${expanded ? b2`
180449
180611
  <div class="itemDetails" id=${detailsId}>
180450
180612
  ${session.preview ? b2`<div class="itemPreview">${session.preview}</div>` : ""}
@@ -180457,77 +180619,55 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180457
180619
  </article>
180458
180620
  `;
180459
180621
  }
180460
- renderUnattachedResources(resourcesArg) {
180461
- if (!resourcesArg.length) return b2``;
180462
- return b2`
180463
- <section class="resourceSection" aria-labelledby="unattached-resources-heading">
180464
- <h2 class="resourceSectionTitle" id="unattached-resources-heading">Resources</h2>
180465
- <ul class="resourceList" aria-label="Unattached resources">
180466
- ${repeat6(
180467
- resourcesArg,
180468
- (resource) => resource.id,
180469
- (resource) => this.renderResource(resource, null)
180470
- )}
180471
- </ul>
180472
- </section>
180473
- `;
180474
- }
180475
- renderResource(resourceArg, sessionIdArg) {
180622
+ renderResourceItem(resourceArg, groupIdArg, indexArg, attachedTarget) {
180476
180623
  const selected = resourceArg.id === this.selectedResourceId;
180477
- const dragging = resourceArg.id === this.draggingResourceId;
180478
- const eligibleTargetCount = this.eligibleSessionIds(resourceArg.id).filter(
180479
- (sessionId) => sessionId !== sessionIdArg
180480
- ).length;
180624
+ const draggable = indexArg >= 0;
180625
+ const dragging = sameItem(this.draggingItem, { kind: "resource", id: resourceArg.id });
180481
180626
  const contextOpen = resourceArg.id === this.resourceContextResourceId;
180627
+ const attachedTitle = attachedTarget ? this.itemTitleFor(attachedTarget) : "";
180628
+ const title = resourceArg.title || resourceArg.id;
180629
+ const projectLabel = resourceArg.projectLabel?.trim() ?? "";
180630
+ const dropIndex = indexArg < 0 ? 0 : indexArg;
180482
180631
  return b2`
180483
- <li
180484
- class="resourceRow ${selected ? "selected" : ""} ${dragging ? "resourceDragging" : ""}"
180632
+ <article
180633
+ class="resourceRow ${selected ? "selected" : ""} ${draggable ? "draggable" : ""} ${dragging ? "dragging" : ""}"
180485
180634
  data-resource-id=${resourceArg.id}
180486
- data-resource-session-id=${sessionIdArg ?? ""}
180635
+ data-item-kind="resource"
180636
+ data-item-id=${resourceArg.id}
180637
+ data-resource-target=${attachedTarget ? `${attachedTarget.kind}:${attachedTarget.id}` : ""}
180638
+ data-drop-group-id=${groupIdArg ?? ""}
180639
+ data-drop-index=${dropIndex}
180640
+ data-drop-kind=${indexArg < 0 ? "slot" : "card"}
180487
180641
  @dblclick=${(event) => {
180488
180642
  event.stopPropagation();
180489
- if (this.eventTargetsResourceControl(event)) return;
180490
- this.emitResource("harness-resource-open", resourceArg, sessionIdArg);
180643
+ if (this.eventTargetsResourceControl(event) || this.eventTargetsDragHandle(event)) return;
180644
+ this.emitResource("harness-resource-open", resourceArg, attachedTarget);
180491
180645
  }}
180492
180646
  @contextmenu=${(event) => {
180493
180647
  event.preventDefault();
180494
180648
  event.stopPropagation();
180495
180649
  const focusTarget = event.composedPath().find((target) => target instanceof HTMLElement && target.matches("button:not(:disabled)")) ?? event.currentTarget.querySelector(".resourcePrimary") ?? event.currentTarget;
180496
- this.emitResourceContext(event, resourceArg, sessionIdArg, "pointer", focusTarget);
180650
+ this.emitResourceContext(event, resourceArg, attachedTarget, "pointer", focusTarget);
180497
180651
  }}
180498
180652
  >
180499
- <button
180500
- class="resourceDragHandle"
180501
- type="button"
180502
- title=${eligibleTargetCount ? "Drag to associate with an eligible conversation" : "No eligible conversations"}
180503
- aria-label=${`Associate ${resourceArg.title || resourceArg.id} by dragging`}
180504
- aria-keyshortcuts="ContextMenu Shift+F10"
180505
- ?disabled=${eligibleTargetCount === 0}
180506
- @pointerdown=${(event) => {
180507
- this.handleResourcePointerDown(event, resourceArg, sessionIdArg);
180508
- }}
180509
- @pointermove=${this.handlePointerMove}
180510
- @pointerup=${this.handlePointerUp}
180511
- @pointercancel=${this.handlePointerCancel}
180512
- @lostpointercapture=${this.handleLostPointerCapture}
180513
- @keydown=${(event) => {
180514
- this.handleResourceContextKey(event, resourceArg, sessionIdArg);
180515
- }}
180516
- >
180517
- <dees-icon icon="lucide:GripVertical" iconSize="13"></dees-icon>
180518
- </button>
180653
+ ${draggable ? this.renderDragHandle(
180654
+ { kind: "resource", id: resourceArg.id },
180655
+ title,
180656
+ groupIdArg,
180657
+ indexArg
180658
+ ) : ""}
180519
180659
  <button
180520
180660
  class="resourcePrimary"
180521
180661
  type="button"
180522
180662
  aria-current=${selected ? "true" : "false"}
180523
- @click=${() => this.emitResource("harness-resource-select", resourceArg, sessionIdArg)}
180663
+ @click=${() => this.emitResource("harness-resource-select", resourceArg, attachedTarget)}
180524
180664
  @keydown=${(event) => {
180525
180665
  if (event.key === "Enter") {
180526
180666
  event.preventDefault();
180527
- this.emitResource("harness-resource-open", resourceArg, sessionIdArg);
180667
+ this.emitResource("harness-resource-open", resourceArg, attachedTarget);
180528
180668
  return;
180529
180669
  }
180530
- this.handleResourceContextKey(event, resourceArg, sessionIdArg);
180670
+ this.handleResourceContextKey(event, resourceArg, attachedTarget);
180531
180671
  }}
180532
180672
  >
180533
180673
  <dees-icon
@@ -180536,32 +180676,45 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180536
180676
  iconSize="14"
180537
180677
  ></dees-icon>
180538
180678
  <span class="resourceCopy">
180539
- <span class="resourceTitle">${resourceArg.title || resourceArg.id}</span>
180679
+ <span class="resourceTitle">${title}</span>
180540
180680
  ${resourceArg.preview ? b2`<span class="resourcePreview">${resourceArg.preview}</span>` : ""}
180541
180681
  <span class="resourceMeta">
180542
- ${resourceArg.kind}${resourceArg.state ? ` \xB7 ${resourceArg.state}` : ""}${resourceArg.updatedAt || resourceArg.createdAt ? ` \xB7 ${harnessFormatRelativeTime(resourceArg.updatedAt ?? resourceArg.createdAt)}` : ""}
180682
+ ${resourceArg.kind}${projectLabel ? ` \xB7 ${projectLabel}` : ""}${resourceArg.state ? ` \xB7 ${resourceArg.state}` : ""}${resourceArg.updatedAt || resourceArg.createdAt ? ` \xB7 ${harnessFormatRelativeTime(resourceArg.updatedAt ?? resourceArg.createdAt)}` : ""}
180543
180683
  </span>
180684
+ ${attachedTarget ? b2`
180685
+ <span class="resourceAttachment" title=${`Attached to ${attachedTitle}`}>
180686
+ <dees-icon aria-hidden="true" icon="lucide:Link" iconSize="10"></dees-icon>
180687
+ <span class="visuallyHidden">Attached to </span>
180688
+ <span class="resourceAttachmentName">${attachedTitle}</span>
180689
+ </span>
180690
+ ` : ""}
180544
180691
  </span>
180545
180692
  </button>
180546
180693
  <button
180547
180694
  class="resourceActions"
180548
180695
  type="button"
180549
180696
  title="Resource actions"
180550
- aria-label=${`Actions for ${resourceArg.title || resourceArg.id}`}
180697
+ aria-label=${`Actions for ${title}`}
180551
180698
  aria-haspopup="menu"
180552
180699
  aria-expanded=${contextOpen ? "true" : "false"}
180553
180700
  aria-keyshortcuts="ContextMenu Shift+F10"
180554
180701
  @click=${(event) => {
180555
180702
  event.stopPropagation();
180556
- this.emitResourceContext(event, resourceArg, sessionIdArg, "action", event.currentTarget);
180703
+ this.emitResourceContext(
180704
+ event,
180705
+ resourceArg,
180706
+ attachedTarget,
180707
+ "action",
180708
+ event.currentTarget
180709
+ );
180557
180710
  }}
180558
180711
  @keydown=${(event) => {
180559
- this.handleResourceContextKey(event, resourceArg, sessionIdArg);
180712
+ this.handleResourceContextKey(event, resourceArg, attachedTarget);
180560
180713
  }}
180561
180714
  >
180562
180715
  <dees-icon icon="lucide:Ellipsis" iconSize="15"></dees-icon>
180563
180716
  </button>
180564
- </li>
180717
+ </article>
180565
180718
  `;
180566
180719
  }
180567
180720
  invokeSessionContext(event) {
@@ -180599,7 +180752,7 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180599
180752
  }
180600
180753
  eventTargetsResourceControl(event) {
180601
180754
  return event.composedPath().some(
180602
- (target) => target instanceof HTMLElement && (target.classList.contains("resourceDragHandle") || target.classList.contains("resourceActions"))
180755
+ (target) => target instanceof HTMLElement && target.classList.contains("resourceActions")
180603
180756
  );
180604
180757
  }
180605
180758
  sessionStateLabel(state4) {
@@ -180648,28 +180801,17 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180648
180801
  }
180649
180802
  this.collapsedGroupIds = next2;
180650
180803
  }
180651
- sortableSessions(sessionsArg, excludeDraggingArg = true) {
180652
- return sessionsArg.filter((session) => session.draggable !== false && (!excludeDraggingArg || session.id !== this.draggingSessionId));
180653
- }
180654
- sessionsForGroup(groupIdArg) {
180655
- if (groupIdArg === null) return this.ungroupedSessions;
180656
- const group = this.groups.find((candidate) => candidate.id === groupIdArg);
180657
- if (!group) return [];
180658
- const byId = this.sessionsById;
180659
- return group.sessionIds.map((sessionId) => byId.get(sessionId)).filter((session) => session !== void 0);
180660
- }
180661
- handlePointerDown(eventArg, sessionArg, groupIdArg, logicalIndexArg) {
180804
+ handlePointerDown(eventArg, refArg, groupIdArg, logicalIndexArg) {
180662
180805
  if (this.pointerDragGesture || eventArg.button !== 0 || !eventArg.isPrimary || logicalIndexArg < 0) return;
180663
180806
  const handle3 = eventArg.currentTarget;
180664
- const source = handle3.closest(".item");
180807
+ const source = handle3.closest(".item, .resourceRow");
180665
180808
  if (!source) return;
180666
180809
  const sourceRect = source.getBoundingClientRect();
180667
180810
  this.pointerDragGesture = {
180668
- type: "session",
180669
180811
  pointerId: eventArg.pointerId,
180670
180812
  handle: handle3,
180671
180813
  source,
180672
- sessionId: sessionArg.id,
180814
+ item: itemRef(refArg),
180673
180815
  sourceGroupId: groupIdArg,
180674
180816
  sourceLogicalIndex: logicalIndexArg,
180675
180817
  startClientX: eventArg.clientX,
@@ -180686,34 +180828,6 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180686
180828
  this.finishPointerDrag(false);
180687
180829
  }
180688
180830
  }
180689
- handleResourcePointerDown(eventArg, resourceArg, fromSessionIdArg) {
180690
- if (this.pointerDragGesture || eventArg.button !== 0 || !eventArg.isPrimary || this.eligibleSessionIds(resourceArg.id).every((sessionId) => sessionId === fromSessionIdArg)) return;
180691
- const handle3 = eventArg.currentTarget;
180692
- const source = handle3.closest(".resourceRow");
180693
- if (!source) return;
180694
- const sourceRect = source.getBoundingClientRect();
180695
- this.pointerDragGesture = {
180696
- type: "resource",
180697
- pointerId: eventArg.pointerId,
180698
- handle: handle3,
180699
- source,
180700
- resourceId: resourceArg.id,
180701
- fromSessionId: fromSessionIdArg,
180702
- eligibleSessionIds: new Set(this.eligibleSessionIds(resourceArg.id)),
180703
- startClientX: eventArg.clientX,
180704
- startClientY: eventArg.clientY,
180705
- grabOffsetX: eventArg.clientX - sourceRect.left,
180706
- grabOffsetY: eventArg.clientY - sourceRect.top,
180707
- phase: "pending"
180708
- };
180709
- window.addEventListener("keydown", this.handleDragKeyDown, true);
180710
- window.addEventListener("blur", this.handleDragWindowBlur);
180711
- try {
180712
- handle3.setPointerCapture(eventArg.pointerId);
180713
- } catch {
180714
- this.finishPointerDrag(false);
180715
- }
180716
- }
180717
180831
  startPointerDrag(eventArg, gestureArg) {
180718
180832
  const sourceRect = gestureArg.source.getBoundingClientRect();
180719
180833
  gestureArg.source.classList.add("dragging");
@@ -180730,32 +180844,15 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180730
180844
  gestureArg.phase = "active";
180731
180845
  this.reflowGeneration += 1;
180732
180846
  this.cancelReflowAnimations();
180733
- this.draggedItemHeight = Math.ceil(sourceRect.height);
180734
- this.draggingSessionId = gestureArg.sessionId;
180735
- const remainingCount = this.sortableSessions(this.sessionsForGroup(gestureArg.sourceGroupId)).length;
180847
+ this.draggedItemHeight = sourceRect.height;
180848
+ this.draggingItem = itemRef(gestureArg.item);
180849
+ const remainingCount = this.sortableItems(this.itemsForGroup(gestureArg.sourceGroupId)).length;
180736
180850
  this.dropSlot = {
180737
180851
  groupId: gestureArg.sourceGroupId,
180738
180852
  index: Math.min(gestureArg.sourceLogicalIndex, remainingCount)
180739
180853
  };
180740
180854
  return true;
180741
180855
  }
180742
- startResourcePointerDrag(eventArg, gestureArg) {
180743
- const sourceRect = gestureArg.source.getBoundingClientRect();
180744
- gestureArg.source.classList.add("resourceDragging");
180745
- gestureArg.source.style.setProperty("--resource-drag-preview-width", `${sourceRect.width}px`);
180746
- this.moveResourceDragPreview(eventArg.clientX, eventArg.clientY, gestureArg);
180747
- gestureArg.source.setAttribute("popover", "manual");
180748
- try {
180749
- gestureArg.source.showPopover();
180750
- } catch {
180751
- this.finishPointerDrag(false);
180752
- return false;
180753
- }
180754
- gestureArg.phase = "active";
180755
- this.draggingResourceId = gestureArg.resourceId;
180756
- this.resourceDropTargetSessionId = "";
180757
- return true;
180758
- }
180759
180856
  moveDragPreview(clientXArg, clientYArg, gestureArg) {
180760
180857
  gestureArg.source.style.setProperty(
180761
180858
  "--drag-preview-x",
@@ -180766,38 +180863,20 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180766
180863
  `${clientYArg - gestureArg.grabOffsetY}px`
180767
180864
  );
180768
180865
  }
180769
- moveResourceDragPreview(clientXArg, clientYArg, gestureArg) {
180770
- gestureArg.source.style.setProperty(
180771
- "--resource-drag-preview-x",
180772
- `${clientXArg - gestureArg.grabOffsetX}px`
180773
- );
180774
- gestureArg.source.style.setProperty(
180775
- "--resource-drag-preview-y",
180776
- `${clientYArg - gestureArg.grabOffsetY}px`
180777
- );
180778
- }
180779
- updateResourceDropTargetFromPointer(clientXArg, clientYArg) {
180780
- const gesture = this.pointerDragGesture;
180781
- if (gesture?.type !== "resource" || gesture.phase !== "active") return;
180782
- const hit = this.shadowRoot?.elementFromPoint(clientXArg, clientYArg);
180783
- const target = hit?.closest(".item[data-session-id]");
180784
- const sessionId = target?.dataset.sessionId ?? "";
180785
- this.resourceDropTargetSessionId = sessionId && sessionId !== gesture.fromSessionId && this.isResourceTargetEligible(gesture.resourceId, sessionId) ? sessionId : "";
180786
- }
180787
180866
  setDropSlot(groupIdArg, indexArg) {
180788
180867
  const current = this.dropSlot;
180789
180868
  if (current && current.groupId === groupIdArg && current.index === indexArg) return;
180790
- const previousRects = this.captureCardRects();
180869
+ const previousRects = this.captureRowRects();
180791
180870
  const generation = ++this.reflowGeneration;
180792
180871
  this.dropSlot = { groupId: groupIdArg, index: indexArg };
180793
- void this.animateCardReflow(previousRects, generation);
180872
+ void this.animateRowReflow(previousRects, generation);
180794
180873
  }
180795
180874
  clearDropSlot() {
180796
180875
  if (!this.dropSlot) return;
180797
- const previousRects = this.captureCardRects();
180876
+ const previousRects = this.captureRowRects();
180798
180877
  const generation = ++this.reflowGeneration;
180799
180878
  this.dropSlot = void 0;
180800
- void this.animateCardReflow(previousRects, generation);
180879
+ void this.animateRowReflow(previousRects, generation);
180801
180880
  }
180802
180881
  updateDragAutoScrollVelocity() {
180803
180882
  const container = this.scrollContainer;
@@ -180842,6 +180921,7 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180842
180921
  }
180843
180922
  }
180844
180923
  const selector = "[data-drop-group-id][data-drop-index][data-drop-kind]";
180924
+ const dragging = this.draggingItem;
180845
180925
  let target;
180846
180926
  const offsets = this.dragAutoScrollVelocity === 0 ? [0, -8, 8, -16, 16, -32, 32] : [0, 8, 16, 32, 48, 64, 96].map(
180847
180927
  (value2) => value2 * (this.dragAutoScrollVelocity < 0 ? 1 : -1)
@@ -180849,7 +180929,7 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180849
180929
  for (const offset of offsets) {
180850
180930
  const hit = this.shadowRoot?.elementFromPoint(pointerX, pointerY + offset);
180851
180931
  const candidate = hit?.closest(selector);
180852
- if (candidate && candidate.dataset.sessionId !== this.draggingSessionId && Number(candidate.dataset.dropIndex) >= 0) {
180932
+ if (candidate && !(dragging && candidate.dataset.itemKind === dragging.kind && candidate.dataset.itemId === dragging.id) && Number(candidate.dataset.dropIndex) >= 0) {
180853
180933
  target = candidate;
180854
180934
  break;
180855
180935
  }
@@ -180886,83 +180966,79 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180886
180966
  this.dragAutoScrollVelocity = 0;
180887
180967
  this.dragAutoScrollLastFrame = 0;
180888
180968
  }
180889
- createMoveDetail(sessionIdArg, groupIdArg, indexArg) {
180890
- const targetSessions = this.sortableSessions(this.sessionsForGroup(groupIdArg));
180891
- const beforeSessionId = targetSessions[Math.max(0, indexArg)]?.id;
180892
- const sourceGroupId = this.groups.find((group) => group.sessionIds.includes(sessionIdArg))?.id ?? null;
180893
- const sourceSessions = this.sortableSessions(this.sessionsForGroup(sourceGroupId), false);
180894
- const sourceIndex = sourceSessions.findIndex((session) => session.id === sessionIdArg);
180895
- const compatibilityIndex = sourceGroupId === groupIdArg && sourceIndex >= 0 && indexArg > sourceIndex ? indexArg + 1 : indexArg;
180969
+ createMoveDetail(refArg, toGroupIdArg, indexArg, sourceArg) {
180970
+ const targetItems = this.sortableItems(this.itemsForGroup(toGroupIdArg));
180971
+ const beforeItem = targetItems[Math.max(0, indexArg)];
180896
180972
  return {
180897
- sessionId: sessionIdArg,
180898
- groupId: groupIdArg,
180899
- index: Math.max(0, compatibilityIndex),
180900
- ...beforeSessionId ? { beforeSessionId } : {}
180973
+ item: itemRef(refArg),
180974
+ fromGroupId: this.groupIdForItem(refArg),
180975
+ toGroupId: toGroupIdArg,
180976
+ beforeItem: beforeItem ? itemRef(beforeItem) : null,
180977
+ source: sourceArg
180901
180978
  };
180902
180979
  }
180903
- handleKeyboardMove(eventArg, sessionArg, groupIdArg) {
180980
+ emitItemMove(detailArg) {
180981
+ this.dispatchEvent(
180982
+ new CustomEvent("harness-item-move", { detail: detailArg, bubbles: true, composed: true })
180983
+ );
180984
+ }
180985
+ handleKeyboardMove(eventArg, refArg, titleArg, groupIdArg) {
180904
180986
  if (!["ArrowUp", "ArrowDown", "Home", "End"].includes(eventArg.key)) return;
180905
180987
  eventArg.preventDefault();
180906
180988
  eventArg.stopPropagation();
180907
- const sessions = this.sortableSessions(this.sessionsForGroup(groupIdArg), false);
180908
- const sourceIndex = sessions.findIndex((session) => session.id === sessionArg.id);
180989
+ const items = this.sortableItems(this.itemsForGroup(groupIdArg), false);
180990
+ const sourceIndex = items.findIndex((item) => sameItem(item, refArg));
180909
180991
  if (sourceIndex < 0) return;
180910
- const sectionIds = [...this.groups.map((group) => group.id), null];
180992
+ const sectionIds = [...this.effectiveGroups.map((group) => group.id), null];
180911
180993
  const sectionIndex = sectionIds.indexOf(groupIdArg);
180912
- const adjacentGroupId = eventArg.key === "ArrowUp" && sourceIndex === 0 && sectionIndex > 0 ? sectionIds[sectionIndex - 1] : eventArg.key === "ArrowDown" && sourceIndex === sessions.length - 1 && sectionIndex >= 0 && sectionIndex < sectionIds.length - 1 ? sectionIds[sectionIndex + 1] : void 0;
180994
+ const adjacentGroupId = eventArg.key === "ArrowUp" && sourceIndex === 0 && sectionIndex > 0 ? sectionIds[sectionIndex - 1] : eventArg.key === "ArrowDown" && sourceIndex === items.length - 1 && sectionIndex >= 0 && sectionIndex < sectionIds.length - 1 ? sectionIds[sectionIndex + 1] : void 0;
180913
180995
  if (adjacentGroupId !== void 0) {
180914
- const targetSessions = this.sortableSessions(this.sessionsForGroup(adjacentGroupId), false);
180915
- const targetIndex2 = eventArg.key === "ArrowUp" ? targetSessions.length : 0;
180916
- const beforeSession2 = targetSessions[targetIndex2];
180917
- const detail2 = {
180918
- sessionId: sessionArg.id,
180919
- groupId: adjacentGroupId,
180920
- index: targetIndex2,
180921
- ...beforeSession2 ? { beforeSessionId: beforeSession2.id } : {}
180922
- };
180923
- const groupName = adjacentGroupId === null ? "Ungrouped" : this.groups.find((group) => group.id === adjacentGroupId)?.name ?? adjacentGroupId;
180924
- this.moveAnnouncement = `Moved ${sessionArg.title || sessionArg.id} to ${groupName}.`;
180925
- this.keyboardMoveFocus = { sessionId: sessionArg.id, groupId: adjacentGroupId };
180926
- this.dispatchEvent(
180927
- new CustomEvent("harness-session-move", { detail: detail2, bubbles: true, composed: true })
180928
- );
180996
+ const targetItems = this.sortableItems(this.itemsForGroup(adjacentGroupId), false);
180997
+ const targetIndex2 = eventArg.key === "ArrowUp" ? targetItems.length : 0;
180998
+ const beforeItem2 = targetItems[targetIndex2];
180999
+ const groupName = adjacentGroupId === null ? "Ungrouped" : this.effectiveGroups.find((group) => group.id === adjacentGroupId)?.name ?? adjacentGroupId;
181000
+ this.moveAnnouncement = `Moved ${titleArg} to ${groupName}.`;
181001
+ this.keyboardMoveFocus = { item: itemRef(refArg), groupId: adjacentGroupId };
181002
+ this.emitItemMove({
181003
+ item: itemRef(refArg),
181004
+ fromGroupId: groupIdArg,
181005
+ toGroupId: adjacentGroupId,
181006
+ beforeItem: beforeItem2 ? itemRef(beforeItem2) : null,
181007
+ source: "keyboard"
181008
+ });
180929
181009
  return;
180930
181010
  }
180931
- const remaining = sessions.filter((session) => session.id !== sessionArg.id);
181011
+ const remaining = items.filter((item) => !sameItem(item, refArg));
180932
181012
  const targetIndex = eventArg.key === "Home" ? 0 : eventArg.key === "End" ? remaining.length : eventArg.key === "ArrowUp" ? Math.max(0, sourceIndex - 1) : Math.min(remaining.length, sourceIndex + 1);
180933
181013
  if (targetIndex === sourceIndex) return;
180934
- const beforeSession = remaining[targetIndex];
180935
- const compatibilityIndex = targetIndex > sourceIndex ? targetIndex + 1 : targetIndex;
180936
- const detail = {
180937
- sessionId: sessionArg.id,
180938
- groupId: groupIdArg,
180939
- index: compatibilityIndex,
180940
- ...beforeSession ? { beforeSessionId: beforeSession.id } : {}
180941
- };
180942
- this.moveAnnouncement = beforeSession ? `Moved ${sessionArg.title || sessionArg.id} before ${beforeSession.title || beforeSession.id}.` : `Moved ${sessionArg.title || sessionArg.id} to the end.`;
180943
- this.dispatchEvent(
180944
- new CustomEvent("harness-session-move", { detail, bubbles: true, composed: true })
180945
- );
181014
+ const beforeItem = remaining[targetIndex];
181015
+ this.moveAnnouncement = beforeItem ? `Moved ${titleArg} before ${beforeItem.title}.` : `Moved ${titleArg} to the end.`;
181016
+ this.emitItemMove({
181017
+ item: itemRef(refArg),
181018
+ fromGroupId: groupIdArg,
181019
+ toGroupId: groupIdArg,
181020
+ beforeItem: beforeItem ? itemRef(beforeItem) : null,
181021
+ source: "keyboard"
181022
+ });
181023
+ }
181024
+ rowKey(rowArg) {
181025
+ return `${rowArg.dataset.itemKind}:${rowArg.dataset.itemId}`;
180946
181026
  }
180947
- captureCardRects() {
181027
+ captureRowRects() {
180948
181028
  const result = /* @__PURE__ */ new Map();
180949
- for (const item of this.shadowRoot?.querySelectorAll(".item[data-session-id]") ?? []) {
180950
- if (item.dataset.sessionId === this.draggingSessionId) continue;
180951
- result.set(item.dataset.sessionId ?? "", item.getBoundingClientRect());
181029
+ const draggingKey = this.draggingItem ? itemKey(this.draggingItem) : "";
181030
+ for (const row of this.shadowRoot?.querySelectorAll("[data-item-kind][data-item-id]") ?? []) {
181031
+ const key2 = this.rowKey(row);
181032
+ if (key2 === draggingKey) continue;
181033
+ result.set(key2, row.getBoundingClientRect());
180952
181034
  }
180953
181035
  return result;
180954
181036
  }
180955
181037
  restoreKeyboardMoveFocus() {
180956
181038
  const pending = this.keyboardMoveFocus;
180957
181039
  if (!pending) return;
180958
- const currentGroupId = this.groups.find(
180959
- (group) => group.sessionIds.includes(pending.sessionId)
180960
- )?.id ?? null;
180961
- if (currentGroupId !== pending.groupId) return;
180962
- const item = Array.from(
180963
- this.shadowRoot?.querySelectorAll(".item[data-session-id]") ?? []
180964
- ).find((candidate) => candidate.dataset.sessionId === pending.sessionId);
180965
- const handle3 = item?.querySelector(".dragHandle");
181040
+ if (this.groupIdForItem(pending.item) !== pending.groupId) return;
181041
+ const handle3 = this.rowElementFor(pending.item)?.querySelector(".dragHandle");
180966
181042
  if (handle3) {
180967
181043
  handle3.focus();
180968
181044
  this.keyboardMoveFocus = void 0;
@@ -180976,43 +181052,52 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
180976
181052
  this.keyboardMoveFocus = void 0;
180977
181053
  }
180978
181054
  }
180979
- eligibleSessionIds(resourceIdArg) {
180980
- const knownSessionIds = new Set(this.sessions.map((session) => session.id));
181055
+ /** Known, de-duplicated targets the host allows; an absent entry means unrestricted. */
181056
+ eligibleTargets(resourceIdArg) {
181057
+ const declared = this.eligibleTargetsByResourceId[resourceIdArg];
181058
+ if (!declared) return void 0;
181059
+ const itemsByKey = this.itemsByKey;
180981
181060
  const result = [];
180982
181061
  const seen = /* @__PURE__ */ new Set();
180983
- for (const sessionId of this.eligibleSessionIdsByResourceId[resourceIdArg] ?? []) {
180984
- if (!knownSessionIds.has(sessionId) || seen.has(sessionId)) continue;
180985
- seen.add(sessionId);
180986
- result.push(sessionId);
181062
+ for (const target of declared) {
181063
+ const key2 = target && itemKey(target);
181064
+ if (!key2 || seen.has(key2) || !itemsByKey.has(key2) || sameItem(target, { kind: "resource", id: resourceIdArg })) continue;
181065
+ seen.add(key2);
181066
+ result.push(itemRef(target));
180987
181067
  }
180988
181068
  return result;
180989
181069
  }
180990
- sameSessionIds(firstArg, secondArg) {
180991
- return firstArg.size === secondArg.size && [...firstArg].every((sessionId) => secondArg.has(sessionId));
181070
+ isResourceTargetEligible(resourceIdArg, targetArg) {
181071
+ const eligible = this.eligibleTargets(resourceIdArg);
181072
+ return eligible === void 0 || eligible.some((candidate) => sameItem(candidate, targetArg));
181073
+ }
181074
+ itemTitleFor(refArg) {
181075
+ return this.itemsByKey.get(itemKey(refArg))?.title ?? refArg.id;
180992
181076
  }
180993
- isResourceTargetEligible(resourceIdArg, sessionIdArg) {
180994
- return this.eligibleSessionIds(resourceIdArg).includes(sessionIdArg);
181077
+ emitSession(nameArg, sessionArg) {
181078
+ const detail = { session: sessionArg };
181079
+ this.dispatchEvent(new CustomEvent(nameArg, { detail, bubbles: true, composed: true }));
180995
181080
  }
180996
- emitResource(nameArg, resourceArg, sessionIdArg) {
181081
+ emitResource(nameArg, resourceArg, targetArg) {
180997
181082
  const detail = {
180998
181083
  resource: resourceArg,
180999
- sessionId: sessionIdArg
181084
+ target: targetArg
181000
181085
  };
181001
181086
  this.dispatchEvent(new CustomEvent(nameArg, { detail, bubbles: true, composed: true }));
181002
181087
  }
181003
- handleResourceContextKey(eventArg, resourceArg, sessionIdArg) {
181088
+ handleResourceContextKey(eventArg, resourceArg, targetArg) {
181004
181089
  if (!(eventArg.key === "ContextMenu" || eventArg.key === "F10" && eventArg.shiftKey)) return;
181005
181090
  eventArg.preventDefault();
181006
181091
  eventArg.stopPropagation();
181007
181092
  this.emitResourceContext(
181008
181093
  eventArg,
181009
181094
  resourceArg,
181010
- sessionIdArg,
181095
+ targetArg,
181011
181096
  "keyboard",
181012
181097
  eventArg.currentTarget
181013
181098
  );
181014
181099
  }
181015
- emitResourceContext(eventArg, resourceArg, sessionIdArg, sourceArg, focusTargetArg) {
181100
+ emitResourceContext(eventArg, resourceArg, targetArg, sourceArg, focusTargetArg) {
181016
181101
  const rect = focusTargetArg.getBoundingClientRect();
181017
181102
  const contextGeneration = ++this.resourceContextGeneration;
181018
181103
  this.resourceContextResourceId = resourceArg.id;
@@ -181024,14 +181109,14 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
181024
181109
  ].find((className) => focusTargetArg.classList.contains(className)) ?? "resourcePrimary";
181025
181110
  const detail = {
181026
181111
  resource: resourceArg,
181027
- sessionId: sessionIdArg,
181112
+ target: targetArg,
181028
181113
  clientX: sourceArg === "pointer" && eventArg instanceof MouseEvent ? eventArg.clientX : rect.left + rect.width / 2,
181029
181114
  clientY: sourceArg === "pointer" && eventArg instanceof MouseEvent ? eventArg.clientY : rect.bottom,
181030
181115
  source: sourceArg,
181031
181116
  originalEvent: eventArg,
181032
- requestAssociation: /* @__PURE__ */ __name((toSessionIdArg) => this.requestResourceAssociation(
181117
+ requestAssociation: /* @__PURE__ */ __name((toTargetArg) => this.requestResourceAssociation(
181033
181118
  resourceArg.id,
181034
- toSessionIdArg,
181119
+ toTargetArg,
181035
181120
  sourceArg === "keyboard" ? "keyboard" : "action"
181036
181121
  ), "requestAssociation"),
181037
181122
  close: /* @__PURE__ */ __name(() => this.closeResourceContext(contextGeneration), "close")
@@ -181053,9 +181138,7 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
181053
181138
  focusTarget.focus();
181054
181139
  return;
181055
181140
  }
181056
- const row = Array.from(
181057
- this.shadowRoot?.querySelectorAll(".resourceRow[data-resource-id]") ?? []
181058
- ).find((candidate) => candidate.dataset.resourceId === resourceId);
181141
+ const row = this.rowElementFor({ kind: "resource", id: resourceId });
181059
181142
  row?.querySelector(`.${focusClass || "resourcePrimary"}`)?.focus();
181060
181143
  });
181061
181144
  }
@@ -181072,40 +181155,41 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
181072
181155
  this.pendingResourceAssociationRequest = void 0;
181073
181156
  return;
181074
181157
  }
181075
- const currentSessionId = this.acceptedResourceAssociations.get(pending.resourceId) ?? null;
181076
- if (currentSessionId !== pending.toSessionId) {
181077
- if (currentSessionId !== pending.fromSessionId) {
181078
- const controlledSession = currentSessionId ? this.sessionsById.get(currentSessionId)?.title ?? currentSessionId : "unattached";
181079
- this.moveAnnouncement = `Association request for ${pending.resourceTitle} was not applied; controlled association is ${controlledSession}.`;
181158
+ const currentTarget = this.acceptedResourceAssociations.get(pending.resourceId) ?? null;
181159
+ if (!sameItem(currentTarget, pending.toTarget) && !(currentTarget === null && pending.toTarget === null)) {
181160
+ if (!sameItem(currentTarget, pending.fromTarget) && !(currentTarget === null && pending.fromTarget === null)) {
181161
+ const controlledTitle = currentTarget ? this.itemTitleFor(currentTarget) : "unattached";
181162
+ this.moveAnnouncement = `Association request for ${pending.resourceTitle} was not applied; controlled association is ${controlledTitle}.`;
181080
181163
  this.pendingResourceAssociationRequest = void 0;
181081
181164
  }
181082
181165
  return;
181083
181166
  }
181084
- const fromSession = pending.fromSessionId ? this.sessionsById.get(pending.fromSessionId)?.title ?? pending.fromSessionId : "";
181085
- const toSession = pending.toSessionId ? this.sessionsById.get(pending.toSessionId)?.title ?? pending.toSessionId : "";
181086
- this.moveAnnouncement = pending.intent === "attach" ? `${pending.resourceTitle} is now attached to ${toSession}.` : pending.intent === "detach" ? `${pending.resourceTitle} is now detached from ${fromSession}.` : `${pending.resourceTitle} is now reassigned from ${fromSession} to ${toSession}.`;
181167
+ const fromTitle = pending.fromTarget ? this.itemTitleFor(pending.fromTarget) : "";
181168
+ const toTitle = pending.toTarget ? this.itemTitleFor(pending.toTarget) : "";
181169
+ this.moveAnnouncement = pending.intent === "attach" ? `${pending.resourceTitle} is now attached to ${toTitle}.` : pending.intent === "detach" ? `${pending.resourceTitle} is now detached from ${fromTitle}.` : `${pending.resourceTitle} is now reassigned from ${fromTitle} to ${toTitle}.`;
181087
181170
  this.pendingResourceAssociationRequest = void 0;
181088
181171
  }
181089
181172
  resourceAssociationAnnouncement(detailArg, completedArg) {
181090
181173
  const resourceTitle = detailArg.resource.title || detailArg.resource.id;
181091
- const fromTitle = detailArg.fromSessionId ? this.sessionsById.get(detailArg.fromSessionId)?.title ?? detailArg.fromSessionId : "";
181092
- const toTitle = detailArg.toSessionId ? this.sessionsById.get(detailArg.toSessionId)?.title ?? detailArg.toSessionId : "";
181174
+ const fromTitle = detailArg.fromTarget ? this.itemTitleFor(detailArg.fromTarget) : "";
181175
+ const toTitle = detailArg.toTarget ? this.itemTitleFor(detailArg.toTarget) : "";
181093
181176
  const verb = completedArg ? "completed" : "requested";
181094
181177
  return detailArg.intent === "attach" ? `Attach ${verb} for ${resourceTitle} to ${toTitle}.` : detailArg.intent === "detach" ? `Detach ${verb} for ${resourceTitle} from ${fromTitle}.` : `Reassign ${verb} for ${resourceTitle} from ${fromTitle} to ${toTitle}.`;
181095
181178
  }
181096
- async animateCardReflow(previousRectsArg, generationArg) {
181179
+ async animateRowReflow(previousRectsArg, generationArg) {
181097
181180
  await this.updateComplete;
181098
181181
  if (generationArg !== this.reflowGeneration || !this.isConnected || globalThis.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return;
181099
181182
  this.cancelReflowAnimations();
181100
- for (const item of this.shadowRoot?.querySelectorAll(".item[data-session-id]") ?? []) {
181101
- const sessionId = item.dataset.sessionId ?? "";
181102
- if (!sessionId || sessionId === this.draggingSessionId) continue;
181103
- const previous3 = previousRectsArg.get(sessionId);
181183
+ const draggingKey = this.draggingItem ? itemKey(this.draggingItem) : "";
181184
+ for (const row of this.shadowRoot?.querySelectorAll("[data-item-kind][data-item-id]") ?? []) {
181185
+ const key2 = this.rowKey(row);
181186
+ if (key2 === draggingKey) continue;
181187
+ const previous3 = previousRectsArg.get(key2);
181104
181188
  if (!previous3) continue;
181105
- const current = item.getBoundingClientRect();
181189
+ const current = row.getBoundingClientRect();
181106
181190
  const deltaY = previous3.top - current.top;
181107
181191
  if (Math.abs(deltaY) < 0.5) continue;
181108
- const animation = item.animate(
181192
+ const animation = row.animate(
181109
181193
  [
181110
181194
  { transform: `translateY(${deltaY}px)` },
181111
181195
  { transform: "translateY(0)" }
@@ -181125,11 +181209,7 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
181125
181209
  finishPointerDrag(commitArg) {
181126
181210
  const gesture = this.pointerDragGesture;
181127
181211
  if (!gesture) return;
181128
- const sessionMoveDetail = gesture.type === "session" && commitArg && gesture.phase === "active" && this.dropSlot ? this.createMoveDetail(gesture.sessionId, this.dropSlot.groupId, this.dropSlot.index) : void 0;
181129
- const resourceRequest = gesture.type === "resource" && commitArg && gesture.phase === "active" && this.resourceDropTargetSessionId ? {
181130
- resourceId: gesture.resourceId,
181131
- toSessionId: this.resourceDropTargetSessionId
181132
- } : void 0;
181212
+ const moveDetail = commitArg && gesture.phase === "active" && this.dropSlot ? this.createMoveDetail(gesture.item, this.dropSlot.groupId, this.dropSlot.index, "pointer") : void 0;
181133
181213
  this.pointerDragGesture = void 0;
181134
181214
  window.removeEventListener("keydown", this.handleDragKeyDown, true);
181135
181215
  window.removeEventListener("blur", this.handleDragWindowBlur);
@@ -181138,19 +181218,14 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
181138
181218
  this.cancelReflowAnimations();
181139
181219
  if (gesture.source.matches(":popover-open")) gesture.source.hidePopover();
181140
181220
  gesture.source.removeAttribute("popover");
181141
- gesture.source.classList.remove("dragging", "resourceDragging");
181221
+ gesture.source.classList.remove("dragging");
181142
181222
  for (const property of [
181143
181223
  "--drag-preview-width",
181144
181224
  "--drag-preview-height",
181145
181225
  "--drag-preview-x",
181146
- "--drag-preview-y",
181147
- "--resource-drag-preview-width",
181148
- "--resource-drag-preview-x",
181149
- "--resource-drag-preview-y"
181226
+ "--drag-preview-y"
181150
181227
  ]) gesture.source.style.removeProperty(property);
181151
- this.draggingSessionId = "";
181152
- this.draggingResourceId = "";
181153
- this.resourceDropTargetSessionId = "";
181228
+ this.draggingItem = void 0;
181154
181229
  this.draggedItemHeight = 0;
181155
181230
  this.dropSlot = void 0;
181156
181231
  try {
@@ -181159,21 +181234,7 @@ var _DeesHarnessSessionList = class _DeesHarnessSessionList extends (_a100 = Dee
181159
181234
  }
181160
181235
  } catch {
181161
181236
  }
181162
- if (sessionMoveDetail) {
181163
- this.dispatchEvent(
181164
- new CustomEvent("harness-session-move", {
181165
- detail: sessionMoveDetail,
181166
- bubbles: true,
181167
- composed: true
181168
- })
181169
- );
181170
- }
181171
- if (resourceRequest) {
181172
- this.requestResourceAssociation(resourceRequest.resourceId, resourceRequest.toSessionId, "drop");
181173
- }
181174
- }
181175
- emit(name, session) {
181176
- this.dispatchEvent(new CustomEvent(name, { detail: { session }, bubbles: true, composed: true }));
181237
+ if (moveDetail) this.emitItemMove(moveDetail);
181177
181238
  }
181178
181239
  };
181179
181240
  _init102 = __decoratorStart(_a100);
@@ -181183,15 +181244,13 @@ _loading5 = new WeakMap();
181183
181244
  _emptyText2 = new WeakMap();
181184
181245
  _groups3 = new WeakMap();
181185
181246
  _enableGrouping = new WeakMap();
181186
- _ungroupedSessionIds2 = new WeakMap();
181247
+ _ungroupedItemIds2 = new WeakMap();
181187
181248
  _resources = new WeakMap();
181188
181249
  _resourceAssociations = new WeakMap();
181189
181250
  _selectedResourceId = new WeakMap();
181190
- _eligibleSessionIdsByResourceId = new WeakMap();
181251
+ _eligibleTargetsByResourceId = new WeakMap();
181191
181252
  _search = new WeakMap();
181192
- _draggingSessionId = new WeakMap();
181193
- _draggingResourceId = new WeakMap();
181194
- _resourceDropTargetSessionId = new WeakMap();
181253
+ _draggingItem = new WeakMap();
181195
181254
  _dropSlot = new WeakMap();
181196
181255
  _draggedItemHeight = new WeakMap();
181197
181256
  _expandedSessionIds = new WeakMap();
@@ -181206,15 +181265,13 @@ __decorateElement(_init102, 4, "loading", _loading_dec5, _DeesHarnessSessionList
181206
181265
  __decorateElement(_init102, 4, "emptyText", _emptyText_dec2, _DeesHarnessSessionList, _emptyText2);
181207
181266
  __decorateElement(_init102, 4, "groups", _groups_dec3, _DeesHarnessSessionList, _groups3);
181208
181267
  __decorateElement(_init102, 4, "enableGrouping", _enableGrouping_dec, _DeesHarnessSessionList, _enableGrouping);
181209
- __decorateElement(_init102, 4, "ungroupedSessionIds", _ungroupedSessionIds_dec2, _DeesHarnessSessionList, _ungroupedSessionIds2);
181268
+ __decorateElement(_init102, 4, "ungroupedItemIds", _ungroupedItemIds_dec2, _DeesHarnessSessionList, _ungroupedItemIds2);
181210
181269
  __decorateElement(_init102, 4, "resources", _resources_dec, _DeesHarnessSessionList, _resources);
181211
181270
  __decorateElement(_init102, 4, "resourceAssociations", _resourceAssociations_dec, _DeesHarnessSessionList, _resourceAssociations);
181212
181271
  __decorateElement(_init102, 4, "selectedResourceId", _selectedResourceId_dec, _DeesHarnessSessionList, _selectedResourceId);
181213
- __decorateElement(_init102, 4, "eligibleSessionIdsByResourceId", _eligibleSessionIdsByResourceId_dec, _DeesHarnessSessionList, _eligibleSessionIdsByResourceId);
181272
+ __decorateElement(_init102, 4, "eligibleTargetsByResourceId", _eligibleTargetsByResourceId_dec, _DeesHarnessSessionList, _eligibleTargetsByResourceId);
181214
181273
  __decorateElement(_init102, 4, "search", _search_dec, _DeesHarnessSessionList, _search);
181215
- __decorateElement(_init102, 4, "draggingSessionId", _draggingSessionId_dec, _DeesHarnessSessionList, _draggingSessionId);
181216
- __decorateElement(_init102, 4, "draggingResourceId", _draggingResourceId_dec, _DeesHarnessSessionList, _draggingResourceId);
181217
- __decorateElement(_init102, 4, "resourceDropTargetSessionId", _resourceDropTargetSessionId_dec, _DeesHarnessSessionList, _resourceDropTargetSessionId);
181274
+ __decorateElement(_init102, 4, "draggingItem", _draggingItem_dec, _DeesHarnessSessionList, _draggingItem);
181218
181275
  __decorateElement(_init102, 4, "dropSlot", _dropSlot_dec, _DeesHarnessSessionList, _dropSlot);
181219
181276
  __decorateElement(_init102, 4, "draggedItemHeight", _draggedItemHeight_dec, _DeesHarnessSessionList, _draggedItemHeight);
181220
181277
  __decorateElement(_init102, 4, "expandedSessionIds", _expandedSessionIds_dec, _DeesHarnessSessionList, _expandedSessionIds);
@@ -181391,7 +181448,9 @@ __publicField(_DeesHarnessSessionList, "styles", [
181391
181448
  border-color var(--dees-transition-fast) var(--dees-ease-standard);
181392
181449
  }
181393
181450
 
181394
- .item.dragging {
181451
+ /* One drag preview for both item kinds. */
181452
+ .item.dragging,
181453
+ .resourceRow.dragging {
181395
181454
  position: fixed;
181396
181455
  inset: 0 auto auto 0;
181397
181456
  width: var(--drag-preview-width);
@@ -181434,12 +181493,6 @@ __publicField(_DeesHarnessSessionList, "styles", [
181434
181493
  box-shadow: inset 3px 0 var(--card-accent);
181435
181494
  }
181436
181495
 
181437
- .item.resourceDropTarget {
181438
- outline: 2px solid var(--dees-color-accent-primary);
181439
- outline-offset: 2px;
181440
- box-shadow: 0 0 0 5px var(--dees-color-focus-ring), var(--dees-shadow-lg);
181441
- }
181442
-
181443
181496
  .itemHeader {
181444
181497
  display: grid;
181445
181498
  grid-template-columns: minmax(0, 1fr) 30px 30px;
@@ -181536,7 +181589,8 @@ __publicField(_DeesHarnessSessionList, "styles", [
181536
181589
  line-height: 14px;
181537
181590
  }
181538
181591
 
181539
- .harnessName {
181592
+ .harnessName,
181593
+ .projectName {
181540
181594
  min-width: 0;
181541
181595
  overflow: hidden;
181542
181596
  text-overflow: ellipsis;
@@ -181616,75 +181670,31 @@ __publicField(_DeesHarnessSessionList, "styles", [
181616
181670
  font-size: var(--dees-font-caption2-size, 10px);
181617
181671
  }
181618
181672
 
181619
- .resourceDropLabel {
181620
- margin: 0 10px 10px;
181621
- padding: 6px 9px;
181622
- border-radius: var(--dees-radius-md);
181623
- background: color-mix(in srgb, var(--dees-color-accent-primary) 14%, transparent);
181624
- color: var(--dees-color-accent-primary);
181625
- font-size: var(--dees-font-caption1-size, 11px);
181626
- font-weight: 680;
181627
- text-align: center;
181628
- }
181629
-
181630
- .resourceChildren {
181631
- display: grid;
181632
- gap: 4px;
181633
- margin: 0 8px 8px 24px;
181634
- padding: 0;
181635
- list-style: none;
181636
- }
181637
-
181638
- .resourceSection {
181639
- margin-top: var(--dees-spacing-md);
181640
- }
181641
-
181642
- .resourceSectionTitle {
181643
- margin: 0 0 7px;
181644
- padding: var(--dees-spacing-xs) 6px;
181645
- color: var(--dees-color-text-secondary);
181646
- font-size: var(--dees-font-caption1-size, 11px);
181647
- font-weight: 600;
181648
- }
181649
-
181650
- .resourceList {
181651
- display: grid;
181652
- gap: var(--dees-spacing-sm);
181653
- margin: 0;
181654
- padding: 0;
181655
- list-style: none;
181656
- }
181657
-
181673
+ /* Resource rows are peers of session cards in the same ordered section. */
181658
181674
  .resourceRow {
181675
+ position: relative;
181659
181676
  display: grid;
181660
- grid-template-columns: 26px minmax(0, 1fr) 38px;
181677
+ grid-template-columns: minmax(0, 1fr) 38px;
181661
181678
  min-width: 0;
181662
181679
  overflow: hidden;
181663
181680
  border: 1px solid var(--dees-color-border-subtle);
181664
181681
  border-radius: var(--dees-radius-lg);
181682
+ corner-shape: var(--dees-corner-shape);
181665
181683
  background: color-mix(in srgb, var(--dees-color-bg-secondary) 76%, transparent);
181666
181684
  color: var(--dees-color-text-primary);
181667
181685
  box-shadow: var(--dees-shadow-xs);
181668
181686
  box-sizing: border-box;
181669
181687
  }
181670
181688
 
181689
+ .resourceRow.draggable {
181690
+ grid-template-columns: 26px minmax(0, 1fr) 38px;
181691
+ }
181692
+
181671
181693
  .resourceRow.selected {
181672
181694
  border-color: var(--dees-color-accent-primary);
181673
181695
  background: color-mix(in srgb, var(--dees-color-accent-primary) 10%, var(--dees-color-bg-secondary));
181674
181696
  }
181675
181697
 
181676
- .resourceRow.resourceDragging {
181677
- position: fixed;
181678
- inset: 0 auto auto 0;
181679
- width: var(--resource-drag-preview-width);
181680
- max-width: none;
181681
- margin: 0;
181682
- opacity: 0.96;
181683
- pointer-events: none;
181684
- transform: translate3d(var(--resource-drag-preview-x), var(--resource-drag-preview-y), 0);
181685
- box-shadow: var(--dees-shadow-xl);
181686
- }
181687
-
181688
181698
  .resourceDragHandle,
181689
181699
  .resourcePrimary,
181690
181700
  .resourceActions {
@@ -181696,22 +181706,7 @@ __publicField(_DeesHarnessSessionList, "styles", [
181696
181706
  }
181697
181707
 
181698
181708
  .resourceDragHandle {
181699
- display: grid;
181700
- place-items: center;
181701
181709
  min-width: 26px;
181702
- padding: 0;
181703
- color: var(--dees-color-text-muted);
181704
- cursor: grab;
181705
- touch-action: none;
181706
- }
181707
-
181708
- .resourceDragHandle:disabled {
181709
- opacity: 0.35;
181710
- cursor: default;
181711
- }
181712
-
181713
- .resourceDragHandle:not(:disabled):active {
181714
- cursor: grabbing;
181715
181710
  }
181716
181711
 
181717
181712
  .resourcePrimary {
@@ -181765,6 +181760,29 @@ __publicField(_DeesHarnessSessionList, "styles", [
181765
181760
  white-space: nowrap;
181766
181761
  }
181767
181762
 
181763
+ /* Decorative marker for the controlled association; never interactive. */
181764
+ .resourceAttachment {
181765
+ display: flex;
181766
+ min-width: 0;
181767
+ align-items: center;
181768
+ gap: 4px;
181769
+ margin-top: 2px;
181770
+ color: var(--dees-color-link);
181771
+ font-size: var(--dees-font-caption2-size, 10px);
181772
+ font-weight: 600;
181773
+ }
181774
+
181775
+ .resourceAttachment dees-icon {
181776
+ flex: 0 0 auto;
181777
+ }
181778
+
181779
+ .resourceAttachmentName {
181780
+ min-width: 0;
181781
+ overflow: hidden;
181782
+ text-overflow: ellipsis;
181783
+ white-space: nowrap;
181784
+ }
181785
+
181768
181786
  .resourceActions {
181769
181787
  display: grid;
181770
181788
  place-items: center;
@@ -181802,7 +181820,7 @@ __publicField(_DeesHarnessSessionList, "styles", [
181802
181820
  .emptyDropZone {
181803
181821
  display: grid;
181804
181822
  place-items: center;
181805
- min-height: 46px;
181823
+ overflow: hidden;
181806
181824
  border: 1px dashed color-mix(in srgb, var(--dees-color-accent-primary) 68%, transparent);
181807
181825
  border-radius: var(--dees-radius-xl);
181808
181826
  background: color-mix(in srgb, var(--dees-color-accent-primary) 8%, transparent);
@@ -181810,9 +181828,14 @@ __publicField(_DeesHarnessSessionList, "styles", [
181810
181828
  box-sizing: border-box;
181811
181829
  }
181812
181830
 
181831
+ /* The placeholder matches the dragged row exactly; only the empty zone has a floor. */
181832
+ .emptyDropZone {
181833
+ min-height: 46px;
181834
+ }
181835
+
181813
181836
  .dropPlaceholder::after,
181814
181837
  .emptyDropZone::after {
181815
- content: 'Drop conversation here';
181838
+ content: 'Drop here';
181816
181839
  font-size: var(--dees-font-caption2-size, 10px);
181817
181840
  font-weight: 650;
181818
181841
  letter-spacing: 0.02em;
@@ -181955,6 +181978,10 @@ __publicField(_DeesHarnessSessionList, "styles", [
181955
181978
  .expandToggle { margin: 0 3px 0 0; }
181956
181979
 
181957
181980
  .resourceRow {
181981
+ grid-template-columns: minmax(0, 1fr) 44px;
181982
+ }
181983
+
181984
+ .resourceRow.draggable {
181958
181985
  grid-template-columns: 44px minmax(0, 1fr) 44px;
181959
181986
  }
181960
181987
 
@@ -181971,10 +181998,6 @@ __publicField(_DeesHarnessSessionList, "styles", [
181971
181998
  }
181972
181999
 
181973
182000
  @media (max-width: 280px) {
181974
- .resourceChildren {
181975
- margin-left: 8px;
181976
- }
181977
-
181978
182001
  .resourcePreview {
181979
182002
  display: none;
181980
182003
  }
@@ -182015,10 +182038,13 @@ init_dist_ts26();
182015
182038
  init_theme();
182016
182039
  init_services();
182017
182040
  var _disabled_dec10, _fontSize_dec, _a101, _DeesTerminalView_decorators, _init103, _fontSize, _disabled10;
182041
+ var maxPendingWriteBytes = 8 * 1024 * 1024;
182042
+ var RIS = "\x1Bc";
182043
+ var chunkByteLength = /* @__PURE__ */ __name((chunkArg) => typeof chunkArg === "string" ? chunkArg.length * 2 : chunkArg.byteLength, "chunkByteLength");
182018
182044
  _DeesTerminalView_decorators = [customElement("dees-terminal-view")];
182019
182045
  var _DeesTerminalView = class _DeesTerminalView extends (_a101 = DeesElement, _fontSize_dec = [n5({ type: Number })], _disabled_dec10 = [n5({ type: Boolean })], _a101) {
182020
182046
  constructor() {
182021
- super(...arguments);
182047
+ super();
182022
182048
  __privateAdd(this, _fontSize, __runInitializers(_init103, 8, this, 13)), __runInitializers(_init103, 11, this);
182023
182049
  __privateAdd(this, _disabled10, __runInitializers(_init103, 12, this, false)), __runInitializers(_init103, 15, this);
182024
182050
  __publicField(this, "terminal");
@@ -182026,45 +182052,138 @@ var _DeesTerminalView = class _DeesTerminalView extends (_a101 = DeesElement, _f
182026
182052
  __publicField(this, "resizeObserver");
182027
182053
  __publicField(this, "inputDisposable");
182028
182054
  __publicField(this, "lastReportedSize", "");
182029
- // Output that arrives before xterm finished loading is replayed in order.
182055
+ // Output that arrives before xterm is live is replayed in order, bounded by
182056
+ // maxPendingWriteBytes and retained across a disconnect.
182030
182057
  __publicField(this, "pendingWrites", []);
182058
+ __publicField(this, "pendingWriteBytes", 0);
182059
+ __publicField(this, "pendingWritesDropped", false);
182031
182060
  __publicField(this, "setupPromise");
182061
+ __publicField(this, "readyPending", true);
182062
+ __publicField(this, "readyResolve", /* @__PURE__ */ __name(() => void 0, "readyResolve"));
182063
+ __publicField(this, "readyReject", /* @__PURE__ */ __name(() => void 0, "readyReject"));
182064
+ /**
182065
+ * Resolves once the terminal of the current connection is live; a fresh
182066
+ * promise is created for every connection. It rejects with the load error
182067
+ * when xterm cannot be loaded (`terminal-error` carries the same error), and
182068
+ * when the element is disconnected before the terminal came up.
182069
+ */
182070
+ __publicField(this, "ready");
182071
+ this.ready = this.createReadyPromise();
182072
+ this.registerStartupFunction(async (signalArg) => {
182073
+ await this.ensureTerminal(signalArg);
182074
+ });
182075
+ this.registerGarbageFunction(() => {
182076
+ this.teardownTerminal();
182077
+ });
182032
182078
  }
182033
182079
  render() {
182034
182080
  return b2`<div class="terminalHost"></div>`;
182035
182081
  }
182036
- async firstUpdated() {
182037
- this.setupPromise = this.setupTerminal();
182038
- await this.setupPromise;
182082
+ connectedCallback() {
182083
+ if (!this.readyPending) this.ready = this.createReadyPromise();
182084
+ return super.connectedCallback();
182039
182085
  }
182040
182086
  /** Feed remote output into the terminal; safe to call before setup finishes. */
182041
182087
  write(dataArg) {
182042
- if (this.terminal) {
182043
- this.terminal.write(dataArg);
182044
- } else {
182045
- this.pendingWrites.push(dataArg);
182088
+ if (this.terminal && this.isConnected) {
182089
+ this.writeToTerminal(this.terminal, dataArg);
182090
+ return;
182046
182091
  }
182092
+ this.bufferWrite(dataArg);
182047
182093
  }
182048
182094
  /** Reset the screen and scrollback, e.g. before a full replay. */
182049
182095
  clear() {
182050
- this.pendingWrites = [];
182051
- this.terminal?.reset();
182096
+ const terminal = this.terminal;
182097
+ if (terminal && this.isConnected) {
182098
+ this.writeToTerminal(terminal, RIS);
182099
+ return;
182100
+ }
182101
+ this.pendingWrites = [RIS];
182102
+ this.pendingWriteBytes = chunkByteLength(RIS);
182103
+ this.pendingWritesDropped = false;
182052
182104
  }
182053
182105
  focus() {
182054
182106
  this.terminal?.focus();
182055
182107
  }
182056
- /** Current grid size, or undefined while xterm is still loading. */
182108
+ /** Current grid size, or undefined while no terminal is live. */
182057
182109
  get currentSize() {
182058
182110
  if (!this.terminal) return void 0;
182059
182111
  return { rows: this.terminal.rows, cols: this.terminal.cols };
182060
182112
  }
182061
- async setupTerminal() {
182113
+ createReadyPromise() {
182114
+ this.readyPending = true;
182115
+ const promise = new Promise((resolveArg, rejectArg) => {
182116
+ this.readyResolve = () => {
182117
+ this.readyPending = false;
182118
+ resolveArg();
182119
+ };
182120
+ this.readyReject = (reasonArg) => {
182121
+ this.readyPending = false;
182122
+ rejectArg(reasonArg);
182123
+ };
182124
+ });
182125
+ promise.catch(() => void 0);
182126
+ return promise;
182127
+ }
182128
+ bufferWrite(chunkArg) {
182129
+ this.pendingWrites.push(chunkArg);
182130
+ this.pendingWriteBytes += chunkByteLength(chunkArg);
182131
+ while (this.pendingWriteBytes > maxPendingWriteBytes && this.pendingWrites.length > 1) {
182132
+ const droppedChunk = this.pendingWrites.shift();
182133
+ this.pendingWriteBytes -= chunkByteLength(droppedChunk);
182134
+ this.pendingWritesDropped = true;
182135
+ }
182136
+ }
182137
+ flushPendingWrites(terminalArg) {
182138
+ const pending = this.pendingWrites;
182139
+ const dropped = this.pendingWritesDropped;
182140
+ this.pendingWrites = [];
182141
+ this.pendingWriteBytes = 0;
182142
+ this.pendingWritesDropped = false;
182143
+ if (dropped && !this.writeToTerminal(terminalArg, RIS)) return;
182144
+ for (const chunk of pending) {
182145
+ if (!this.writeToTerminal(terminalArg, chunk)) return;
182146
+ }
182147
+ }
182148
+ /** xterm throws at its own flow-control watermark; that has to reach the host. */
182149
+ writeToTerminal(terminalArg, chunkArg) {
182150
+ try {
182151
+ terminalArg.write(chunkArg);
182152
+ return true;
182153
+ } catch (error) {
182154
+ this.reportError(error);
182155
+ return false;
182156
+ }
182157
+ }
182158
+ reportError(errorArg) {
182159
+ if (this.readyPending) this.readyReject(errorArg);
182160
+ const detail = { error: errorArg };
182161
+ this.dispatchEvent(
182162
+ new CustomEvent("terminal-error", { detail, bubbles: true, composed: true })
182163
+ );
182164
+ }
182165
+ ensureTerminal(signalArg) {
182166
+ if (this.terminal) return Promise.resolve();
182167
+ if (!this.setupPromise) this.setupPromise = this.setupTerminal(signalArg);
182168
+ return this.setupPromise;
182169
+ }
182170
+ async setupTerminal(signalArg) {
182171
+ try {
182172
+ await this.createTerminal(signalArg);
182173
+ } catch (error) {
182174
+ this.reportError(error);
182175
+ } finally {
182176
+ if (!this.terminal) this.setupPromise = void 0;
182177
+ }
182178
+ }
182179
+ async createTerminal(signalArg) {
182180
+ await this.updateComplete;
182062
182181
  const libLoader = DeesServiceLibLoader.getInstance();
182063
182182
  const [xterm, fit] = await Promise.all([
182064
182183
  libLoader.loadXterm(),
182065
182184
  libLoader.loadXtermFitAddon()
182066
182185
  ]);
182067
- if (!this.isConnected) return;
182186
+ if (signalArg.aborted || !this.isConnected) return;
182068
182187
  const host = this.shadowRoot?.querySelector(".terminalHost");
182069
182188
  if (!host) return;
182070
182189
  const computed2 = getComputedStyle(this);
@@ -182084,9 +182203,18 @@ var _DeesTerminalView = class _DeesTerminalView extends (_a101 = DeesElement, _f
182084
182203
  cursor: themeColor("--dees-color-accent-primary", "#e6e6e6")
182085
182204
  }
182086
182205
  });
182087
- const fitAddon = new fit.FitAddon();
182088
- terminal.loadAddon(fitAddon);
182089
- terminal.open(host);
182206
+ let fitAddon;
182207
+ try {
182208
+ fitAddon = new fit.FitAddon();
182209
+ terminal.loadAddon(fitAddon);
182210
+ terminal.open(host);
182211
+ } catch (error) {
182212
+ try {
182213
+ terminal.dispose();
182214
+ } catch {
182215
+ }
182216
+ throw error;
182217
+ }
182090
182218
  this.terminal = terminal;
182091
182219
  this.fitAddon = fitAddon;
182092
182220
  this.inputDisposable = terminal.onData((dataArg) => {
@@ -182096,11 +182224,11 @@ var _DeesTerminalView = class _DeesTerminalView extends (_a101 = DeesElement, _f
182096
182224
  new CustomEvent("terminal-input", { detail, bubbles: true, composed: true })
182097
182225
  );
182098
182226
  });
182099
- for (const pending of this.pendingWrites) terminal.write(pending);
182100
- this.pendingWrites = [];
182227
+ this.flushPendingWrites(terminal);
182101
182228
  this.refit();
182102
182229
  this.resizeObserver = new ResizeObserver(() => this.refit());
182103
182230
  this.resizeObserver.observe(host);
182231
+ this.readyResolve();
182104
182232
  }
182105
182233
  refit() {
182106
182234
  if (!this.terminal || !this.fitAddon) return;
@@ -182120,8 +182248,7 @@ var _DeesTerminalView = class _DeesTerminalView extends (_a101 = DeesElement, _f
182120
182248
  new CustomEvent("terminal-resize", { detail, bubbles: true, composed: true })
182121
182249
  );
182122
182250
  }
182123
- async disconnectedCallback() {
182124
- await super.disconnectedCallback();
182251
+ teardownTerminal() {
182125
182252
  this.resizeObserver?.disconnect();
182126
182253
  this.resizeObserver = void 0;
182127
182254
  this.inputDisposable?.dispose();
@@ -182129,7 +182256,13 @@ var _DeesTerminalView = class _DeesTerminalView extends (_a101 = DeesElement, _f
182129
182256
  this.terminal?.dispose();
182130
182257
  this.terminal = void 0;
182131
182258
  this.fitAddon = void 0;
182132
- this.pendingWrites = [];
182259
+ this.lastReportedSize = "";
182260
+ this.setupPromise = void 0;
182261
+ if (this.readyPending) {
182262
+ this.readyReject(
182263
+ new Error("dees-terminal-view was disconnected before the terminal became ready")
182264
+ );
182265
+ }
182133
182266
  }
182134
182267
  };
182135
182268
  _init103 = __decoratorStart(_a101);
@@ -223678,7 +223811,7 @@ init_group_runtime();
223678
223811
  // ts_web/00_commitinfo_data.ts
223679
223812
  var commitinfo = {
223680
223813
  name: "@design.estate/dees-catalog",
223681
- version: "10.0.0",
223814
+ version: "11.1.0",
223682
223815
  description: "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript."
223683
223816
  };
223684
223817
 
@@ -223889,6 +224022,7 @@ export {
223889
224022
  isInViewport,
223890
224023
  makeStreamingSimulator,
223891
224024
  markdownStyles,
224025
+ maxPendingWriteBytes,
223892
224026
  mosaicEqualize,
223893
224027
  mosaicFindLeaf,
223894
224028
  mosaicFindLeafBySurface,