@ai-ecoverse/slicc-spectrum 1.86.5 → 1.86.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/slicc-ui.js CHANGED
@@ -10134,6 +10134,103 @@ var ViewSet = class extends ViewState {
10134
10134
  return (event2) => this.toggle(id, event2.currentTarget.open);
10135
10135
  }
10136
10136
  };
10137
+ function keyOf(row) {
10138
+ return row.getAttribute("data-id") ?? row.getAttribute("data-path");
10139
+ }
10140
+ function within(row, box) {
10141
+ const rect = row.getBoundingClientRect();
10142
+ return rect.height > 0 && rect.top >= box.top && rect.bottom <= box.bottom;
10143
+ }
10144
+ var ViewScroll = class {
10145
+ #state;
10146
+ #options;
10147
+ #el = null;
10148
+ #pending = false;
10149
+ #set = -1;
10150
+ #holder = null;
10151
+ #resize = new ResizeObserver(() => {
10152
+ this.#pending = true;
10153
+ this.#restore();
10154
+ });
10155
+ #scrolled = () => this.#save();
10156
+ constructor(host4, key2, options) {
10157
+ this.#state = new ViewState(host4, key2, () => ({ spot: null }));
10158
+ this.#options = options;
10159
+ host4.addController(this);
10160
+ }
10161
+ #rows(el) {
10162
+ const rows2 = this.#options.rows;
10163
+ const found = rows2 ? [...(el.shadowRoot ?? el).querySelectorAll(rows2)] : [];
10164
+ return found.filter((row) => keyOf(row) !== null);
10165
+ }
10166
+ #bind(el) {
10167
+ if (el === this.#el) return;
10168
+ this.#el?.removeEventListener("scroll", this.#scrolled);
10169
+ this.#resize.disconnect();
10170
+ this.#el = el;
10171
+ this.#pending = true;
10172
+ this.#set = -1;
10173
+ if (!el) return;
10174
+ el.style.overflowAnchor = "none";
10175
+ el.addEventListener("scroll", this.#scrolled, { passive: true });
10176
+ this.#resize.observe(el);
10177
+ }
10178
+ #save() {
10179
+ const el = this.#el;
10180
+ if (!el?.clientHeight || Math.abs(el.scrollTop - this.#set) < 1) return;
10181
+ this.#set = -1;
10182
+ this.#pending = false;
10183
+ const box = el.getBoundingClientRect();
10184
+ const current = this.#options.current;
10185
+ const chosen = current ? (el.shadowRoot ?? el).querySelector(current)?.closest("[data-id], [data-path]") ?? null : null;
10186
+ const rows2 = this.#rows(el);
10187
+ const row = (chosen && within(chosen, box) ? chosen : null) ?? rows2.find((item2) => within(item2, box)) ?? rows2.find((item2) => item2.getBoundingClientRect().bottom > box.top);
10188
+ this.#state.value.spot = {
10189
+ top: el.scrollTop,
10190
+ key: row ? keyOf(row) : null,
10191
+ offset: row ? row.getBoundingClientRect().top - box.top : 0,
10192
+ end: el.scrollTop > 0 && el.scrollTop >= el.scrollHeight - el.clientHeight - 1
10193
+ };
10194
+ }
10195
+ #restore() {
10196
+ const el = this.#el;
10197
+ if (!el?.clientHeight || !this.#pending) return;
10198
+ const spot = this.#state.value.spot;
10199
+ if (!spot) {
10200
+ this.#pending = false;
10201
+ return;
10202
+ }
10203
+ const max2 = el.scrollHeight - el.clientHeight;
10204
+ const rows2 = this.#rows(el);
10205
+ const row = spot.key ? rows2.find((item2) => keyOf(item2) === spot.key) : void 0;
10206
+ let target2 = spot.end ? max2 : spot.top;
10207
+ if (row && !spot.end) {
10208
+ const rect = row.getBoundingClientRect();
10209
+ const offset3 = Math.min(spot.offset, Math.max(0, el.clientHeight - rect.height));
10210
+ target2 = el.scrollTop + rect.top - el.getBoundingClientRect().top - offset3;
10211
+ }
10212
+ el.scrollTop = Math.min(target2, max2);
10213
+ this.#set = el.scrollTop;
10214
+ const gone3 = !spot.end && !!spot.key && !row;
10215
+ if (gone3 ? rows2.length > 0 : target2 <= max2 + 1) this.#pending = false;
10216
+ }
10217
+ hostUpdated() {
10218
+ const el = this.#options.scroller();
10219
+ this.#bind(el);
10220
+ if (!el) return;
10221
+ const holder = this.#state.value;
10222
+ if (holder !== this.#holder) {
10223
+ if (this.#holder && !holder.spot) el.scrollTop = 0;
10224
+ this.#holder = holder;
10225
+ this.#pending = true;
10226
+ }
10227
+ for (const child of (el.shadowRoot ?? el).children) this.#resize.observe(child);
10228
+ this.#restore();
10229
+ }
10230
+ hostDisconnected() {
10231
+ this.#bind(null);
10232
+ }
10233
+ };
10137
10234
 
10138
10235
  // src/app/files.ts
10139
10236
  function relative(path) {
@@ -10269,6 +10366,7 @@ var errorCss = i`
10269
10366
  color: var(--swc-negative-content-color-default);
10270
10367
  }
10271
10368
  `;
10369
+ var blank = { scroll: 0, selected: [], focused: null, shown: false, end: false };
10272
10370
  var SliccFiles = class extends ModelElement {
10273
10371
  static properties = {
10274
10372
  ...ModelElement.properties,
@@ -10288,6 +10386,8 @@ var SliccFiles = class extends ModelElement {
10288
10386
  #reveal = null;
10289
10387
  #folders = new ViewState(this, "files", () => null);
10290
10388
  #applied = null;
10389
+ #view = new ViewState(this, "files:view", () => ({ tree: null }));
10390
+ #holder = null;
10291
10391
  constructor() {
10292
10392
  super();
10293
10393
  this.count = 0;
@@ -10411,6 +10511,7 @@ var SliccFiles = class extends ModelElement {
10411
10511
  `
10412
10512
  ];
10413
10513
  subscribe(model) {
10514
+ if (this.#holder && this.#view.value !== this.#holder) this.#entries = [];
10414
10515
  void model.files.list().then((entries) => this.#load(entries));
10415
10516
  this.#mounts(model.files);
10416
10517
  return [
@@ -10443,14 +10544,20 @@ var SliccFiles = class extends ModelElement {
10443
10544
  const tree = this.#tree();
10444
10545
  const changes2 = this.model ? changesOf(this.model).changes() : [];
10445
10546
  if (!tree) return;
10446
- if (tree.paths.length === 0)
10547
+ const holder = this.#view.value;
10548
+ const moved2 = !!this.#holder && holder !== this.#holder;
10549
+ if (tree.paths.length === 0 || moved2) {
10447
10550
  tree.expanded = this.#folders.value ?? ancestors(changes2.map((change) => change.path));
10551
+ const view = holder.tree ?? (moved2 ? blank : null);
10552
+ if (view) tree.view = view;
10553
+ this.#holder = holder;
10554
+ }
10448
10555
  tree.paths = treePaths(this.#entries);
10449
10556
  tree.gitStatus = treeStatus(changes2);
10450
10557
  this.#show();
10451
10558
  if (this.#pending) {
10452
10559
  this.#pending = false;
10453
- requestAnimationFrame(() => tree.focus());
10560
+ requestAnimationFrame(() => this.focusOn("slicc-file-tree"));
10454
10561
  }
10455
10562
  }
10456
10563
  #show() {
@@ -10615,8 +10722,14 @@ var SliccFiles = class extends ModelElement {
10615
10722
  @tree-matches=${this.#matches}
10616
10723
  @tree-search-close=${this.#closed}
10617
10724
  @tree-expand=${this.#expand}
10725
+ @tree-view=${this.#viewed}
10618
10726
  ></slicc-file-tree>`;
10619
10727
  }
10728
+ #viewed(event2) {
10729
+ event2.stopPropagation();
10730
+ const holder = this.#view.value;
10731
+ if (holder === this.#holder) holder.tree = event2.detail;
10732
+ }
10620
10733
  #expand(event2) {
10621
10734
  this.#applied = event2.detail.expanded;
10622
10735
  this.#folders.value = event2.detail.expanded;
@@ -10838,6 +10951,10 @@ function freezerOrder(rows2) {
10838
10951
  return [...rows2].sort((a23, b13) => Number(!!b13.live) - Number(!!a23.live) || b13.frozenAt - a23.frozenAt);
10839
10952
  }
10840
10953
  var SliccFreezer = class extends ModelElement {
10954
+ #scroll = new ViewScroll(this, "freezer:scroll", {
10955
+ scroller: () => this.renderRoot.querySelector(".list"),
10956
+ rows: ".card[data-id]"
10957
+ });
10841
10958
  static styles = [
10842
10959
  shared,
10843
10960
  panelCss,
@@ -10871,6 +10988,7 @@ var SliccFreezer = class extends ModelElement {
10871
10988
  -webkit-box-orient: vertical;
10872
10989
  -webkit-line-clamp: 2;
10873
10990
  overflow: hidden;
10991
+ text-overflow: ellipsis;
10874
10992
  overflow-wrap: anywhere;
10875
10993
  line-height: var(--swc-line-height-100);
10876
10994
  }
@@ -11096,6 +11214,10 @@ var SliccMonitor = class extends ModelElement {
11096
11214
  copied: { state: true }
11097
11215
  };
11098
11216
  #sections = new ViewState(this, "monitor", () => ({}));
11217
+ #scroll = new ViewScroll(this, "monitor:scroll", {
11218
+ scroller: () => this.renderRoot.querySelector(".body"),
11219
+ rows: ".section[data-id], .row[data-id]"
11220
+ });
11099
11221
  constructor() {
11100
11222
  super();
11101
11223
  this.copied = null;
@@ -11565,6 +11687,11 @@ var SliccAgents = class extends ModelElement {
11565
11687
  static properties = { ...ModelElement.properties, errors: { state: true } };
11566
11688
  #expanded = new ViewSet(this, "agents");
11567
11689
  #revealed = new ViewState(this, "agents:revealed", () => "");
11690
+ #scroll = new ViewScroll(this, "agents:scroll", {
11691
+ scroller: () => this.renderRoot.querySelector(".list"),
11692
+ rows: "li[data-id]",
11693
+ current: "li[aria-selected='true']"
11694
+ });
11568
11695
  #pointer = false;
11569
11696
  #modality = (event2) => {
11570
11697
  this.#pointer = event2.type === "pointerdown";
@@ -22431,10 +22558,10 @@ function getBuiltInFileIconName(token2) {
22431
22558
  function isColoredBuiltInIconSet(set) {
22432
22559
  return set !== "none" && COLORED_SETS.has(set);
22433
22560
  }
22434
- function resolveBuiltInFileIconToken(set, fileName, extensionCandidates) {
22561
+ function resolveBuiltInFileIconToken(set, fileName2, extensionCandidates) {
22435
22562
  if (set === "minimal" || set === "none") return;
22436
22563
  const isComplete = set === "complete";
22437
- const fileNameToken = BUILT_IN_FILE_NAME_TOKENS[fileName.toLowerCase()];
22564
+ const fileNameToken = BUILT_IN_FILE_NAME_TOKENS[fileName2.toLowerCase()];
22438
22565
  if (fileNameToken != null) {
22439
22566
  if (isComplete || STANDARD_TIER_TOKENS.has(fileNameToken)) return fileNameToken;
22440
22567
  }
@@ -22487,8 +22614,8 @@ var normalizeIconRuleKey = (value) => value.trim().toLowerCase();
22487
22614
  var getBaseFileName = (path) => {
22488
22615
  return path.split("/").at(-1) ?? path;
22489
22616
  };
22490
- var getExtensionCandidates = (fileName) => {
22491
- const segments = fileName.toLowerCase().split(".");
22617
+ var getExtensionCandidates = (fileName2) => {
22618
+ const segments = fileName2.toLowerCase().split(".");
22492
22619
  const candidates2 = [];
22493
22620
  for (let index = 1; index < segments.length; index += 1) candidates2.push(segments.slice(index).join("."));
22494
22621
  return candidates2;
@@ -22507,23 +22634,23 @@ function createFileTreeIconResolver(icons3) {
22507
22634
  const normalizedIcons = normalizeFileTreeIcons(icons3);
22508
22635
  const iconRemap = normalizedIcons.remap;
22509
22636
  const iconByFileName = /* @__PURE__ */ new Map();
22510
- for (const [fileName, icon] of Object.entries(normalizedIcons.byFileName ?? {})) iconByFileName.set(fileName.toLowerCase(), icon);
22637
+ for (const [fileName2, icon] of Object.entries(normalizedIcons.byFileName ?? {})) iconByFileName.set(fileName2.toLowerCase(), icon);
22511
22638
  const iconByFileExtension = /* @__PURE__ */ new Map();
22512
22639
  for (const [extension, icon] of Object.entries(normalizedIcons.byFileExtension ?? {})) iconByFileExtension.set(normalizeIconRuleKey(extension), icon);
22513
22640
  const iconByFileNameContains = Object.entries(normalizedIcons.byFileNameContains ?? {}).map(([needle, icon]) => [needle.toLowerCase(), icon]);
22514
22641
  const resolveIcon = (name, filePath) => {
22515
22642
  if (name === "file-tree-icon-file" && filePath != null) {
22516
- const fileName = getBaseFileName(filePath);
22517
- const lowerFileName = fileName.toLowerCase();
22643
+ const fileName2 = getBaseFileName(filePath);
22644
+ const lowerFileName = fileName2.toLowerCase();
22518
22645
  const fileNameEntry = iconByFileName.get(lowerFileName);
22519
22646
  if (fileNameEntry != null) return remapEntryToIcon(fileNameEntry, name);
22520
22647
  for (const [needle, matchEntry] of iconByFileNameContains) if (lowerFileName.includes(needle)) return remapEntryToIcon(matchEntry, name);
22521
- const extensionCandidates = getExtensionCandidates(fileName);
22648
+ const extensionCandidates = getExtensionCandidates(fileName2);
22522
22649
  for (const extension of extensionCandidates) {
22523
22650
  const extensionEntry = iconByFileExtension.get(extension);
22524
22651
  if (extensionEntry != null) return remapEntryToIcon(extensionEntry, name);
22525
22652
  }
22526
- const builtInToken = resolveBuiltInFileIconToken(normalizedIcons.set, fileName, extensionCandidates);
22653
+ const builtInToken = resolveBuiltInFileIconToken(normalizedIcons.set, fileName2, extensionCandidates);
22527
22654
  if (builtInToken != null && normalizedIcons.set !== "none") {
22528
22655
  if (builtInToken === "default") {
22529
22656
  const remappedEntry2 = iconRemap?.[name];
@@ -35402,6 +35529,11 @@ var SliccBrowser = class extends ModelElement {
35402
35529
  #expanded = new ViewSet(this, "browser");
35403
35530
  #recent = new ViewState(this, "browser:recent", () => false);
35404
35531
  #chosen = new ViewState(this, "browser:tab", () => void 0);
35532
+ #scroll = new ViewScroll(this, "browser:scroll", {
35533
+ scroller: () => this.renderRoot.querySelector(".cards")?.parentElement ?? null,
35534
+ rows: ".cards > li[data-id]",
35535
+ current: ".cards .window[aria-current='true']"
35536
+ });
35405
35537
  constructor() {
35406
35538
  super();
35407
35539
  this.address = "";
@@ -36456,6 +36588,10 @@ var SliccChanges = class extends ModelElement {
36456
36588
  copied: { state: true },
36457
36589
  uncopied: { state: true }
36458
36590
  };
36591
+ #scroll = new ViewScroll(this, "changes:scroll", {
36592
+ scroller: () => this.renderRoot.querySelector(".bar ~ ul"),
36593
+ rows: "li[data-path]"
36594
+ });
36459
36595
  constructor() {
36460
36596
  super();
36461
36597
  this.error = "";
@@ -40198,6 +40334,11 @@ var SliccMemory = class extends ModelElement {
40198
40334
  editing: { state: true }
40199
40335
  };
40200
40336
  #open = new ViewState(this, "memory", () => null);
40337
+ #scroll = new ViewScroll(this, "memory:scroll", {
40338
+ scroller: () => this.renderRoot.querySelector(".list"),
40339
+ rows: ".row[data-id]",
40340
+ current: ".row[data-open]"
40341
+ });
40201
40342
  constructor() {
40202
40343
  super();
40203
40344
  this.query = "";
@@ -41844,6 +41985,7 @@ var SliccSettings = class extends ThemedElement {
41844
41985
  problem: { state: true },
41845
41986
  blocked: { state: true }
41846
41987
  };
41988
+ #scroll = new ViewScroll(this, "settings:scroll", { scroller: () => this, rows: "[data-id]" });
41847
41989
  constructor() {
41848
41990
  super();
41849
41991
  this.pending = /* @__PURE__ */ new Set();
@@ -58827,21 +58969,21 @@ var EXTENSION_TO_FILE_FORMAT = {
58827
58969
  zsh: "zsh",
58828
58970
  sty: "tex"
58829
58971
  };
58830
- function getFiletypeFromFileName(fileName) {
58831
- const format = matchFiletypeFromName(fileName);
58972
+ function getFiletypeFromFileName(fileName2) {
58973
+ const format = matchFiletypeFromName(fileName2);
58832
58974
  if (format != null) return format;
58833
- const baseName = fileName.split(/[/\\]/).pop() ?? fileName;
58834
- return baseName === fileName ? "text" : matchFiletypeFromName(baseName) ?? "text";
58975
+ const baseName = fileName2.split(/[/\\]/).pop() ?? fileName2;
58976
+ return baseName === fileName2 ? "text" : matchFiletypeFromName(baseName) ?? "text";
58835
58977
  }
58836
- function matchFiletypeFromName(fileName) {
58837
- if (CUSTOM_EXTENSION_TO_FILE_FORMAT.has(fileName)) return CUSTOM_EXTENSION_TO_FILE_FORMAT.get(fileName) ?? "text";
58838
- if (Object.hasOwn(EXTENSION_TO_FILE_FORMAT, fileName)) return EXTENSION_TO_FILE_FORMAT[fileName] ?? null;
58839
- const compoundMatch = fileName.match(/\.([^/\\]+\.[^/\\]+)$/);
58978
+ function matchFiletypeFromName(fileName2) {
58979
+ if (CUSTOM_EXTENSION_TO_FILE_FORMAT.has(fileName2)) return CUSTOM_EXTENSION_TO_FILE_FORMAT.get(fileName2) ?? "text";
58980
+ if (Object.hasOwn(EXTENSION_TO_FILE_FORMAT, fileName2)) return EXTENSION_TO_FILE_FORMAT[fileName2] ?? null;
58981
+ const compoundMatch = fileName2.match(/\.([^/\\]+\.[^/\\]+)$/);
58840
58982
  if (compoundMatch != null) {
58841
58983
  if (CUSTOM_EXTENSION_TO_FILE_FORMAT.has(compoundMatch[1])) return CUSTOM_EXTENSION_TO_FILE_FORMAT.get(compoundMatch[1]) ?? "text";
58842
58984
  if (Object.hasOwn(EXTENSION_TO_FILE_FORMAT, compoundMatch[1])) return EXTENSION_TO_FILE_FORMAT[compoundMatch[1]] ?? "text";
58843
58985
  }
58844
- const simpleMatch = fileName.match(/\.([^.]+)$/)?.[1] ?? "";
58986
+ const simpleMatch = fileName2.match(/\.([^.]+)$/)?.[1] ?? "";
58845
58987
  if (CUSTOM_EXTENSION_TO_FILE_FORMAT.has(simpleMatch)) return CUSTOM_EXTENSION_TO_FILE_FORMAT.get(simpleMatch) ?? "text";
58846
58988
  return Object.hasOwn(EXTENSION_TO_FILE_FORMAT, simpleMatch) ? EXTENSION_TO_FILE_FORMAT[simpleMatch] ?? "text" : null;
58847
58989
  }
@@ -61797,22 +61939,22 @@ var NAMED_ESCAPES = {
61797
61939
  };
61798
61940
  function parseQuotedDiffFileName(input) {
61799
61941
  if (input[0] !== '"') return;
61800
- let fileName = "";
61942
+ let fileName2 = "";
61801
61943
  let index = 1;
61802
61944
  while (index < input.length) {
61803
61945
  const char = input[index];
61804
61946
  if (char === '"') return {
61805
- fileName,
61947
+ fileName: fileName2,
61806
61948
  rawLength: index + 1
61807
61949
  };
61808
61950
  if (char !== "\\") {
61809
- fileName += char;
61951
+ fileName2 += char;
61810
61952
  index++;
61811
61953
  continue;
61812
61954
  }
61813
61955
  const escaped = NAMED_ESCAPES[input[index + 1]];
61814
61956
  if (escaped != null) {
61815
- fileName += escaped;
61957
+ fileName2 += escaped;
61816
61958
  index += 2;
61817
61959
  continue;
61818
61960
  }
@@ -61823,7 +61965,7 @@ function parseQuotedDiffFileName(input) {
61823
61965
  bytes.push(byte);
61824
61966
  index += 4;
61825
61967
  } while (input[index] === "\\" && input[index + 1] >= "0" && input[index + 1] <= "7");
61826
- fileName += filenameDecoder.decode(new Uint8Array(bytes));
61968
+ fileName2 += filenameDecoder.decode(new Uint8Array(bytes));
61827
61969
  }
61828
61970
  }
61829
61971
  function readOctalByte(input, index) {
@@ -62017,12 +62159,12 @@ function _processFile(fileDiffString, { cacheKey, isGitDiff = GIT_DIFF_FILE_BREA
62017
62159
  const filenameMatch = line.startsWith("---") || line.startsWith("+++") ? line.match(FILENAME_HEADER_REGEX) : null;
62018
62160
  if (filenameMatch != null) {
62019
62161
  const [, type, rawFileName] = filenameMatch;
62020
- const fileName = decodeDiffFileName(rawFileName, isGitDiff);
62021
- if (type === "---" && fileName !== "/dev/null") {
62022
- const detachedFileName = detachString(fileName);
62162
+ const fileName2 = decodeDiffFileName(rawFileName, isGitDiff);
62163
+ if (type === "---" && fileName2 !== "/dev/null") {
62164
+ const detachedFileName = detachString(fileName2);
62023
62165
  currentFile.prevName = detachedFileName;
62024
62166
  currentFile.name = detachedFileName;
62025
- } else if (type === "+++" && fileName !== "/dev/null") currentFile.name = detachString(fileName);
62167
+ } else if (type === "+++" && fileName2 !== "/dev/null") currentFile.name = detachString(fileName2);
62026
62168
  } else if (isGitDiff) {
62027
62169
  if (line.startsWith("new mode ")) currentFile.mode = detachString(line.slice(8).trim());
62028
62170
  if (line.startsWith("old mode ")) currentFile.prevMode = detachString(line.slice(8).trim());
@@ -85474,8 +85616,19 @@ file-tree-container {
85474
85616
  color-scheme: inherit;
85475
85617
  }
85476
85618
  `;
85619
+ var gestures = ["wheel", "touchmove", "pointerdown", "keydown"];
85477
85620
  var folderName = "[data-item-type='folder'] > [data-item-section='content']";
85621
+ var fileName = "[data-item-type='file'] > [data-item-section='content']";
85478
85622
  var rows = `
85623
+ [data-type='item'][data-item-focused='true']:focus:not(:focus-visible)::before {
85624
+ display: none;
85625
+ }
85626
+ @media (forced-colors: active) {
85627
+ [data-type='item'][data-item-selected='true'] {
85628
+ outline: var(--swc-border-width-100) solid Highlight;
85629
+ outline-offset: calc(-1 * var(--swc-border-width-100));
85630
+ }
85631
+ }
85479
85632
  ${folderName} {
85480
85633
  display: block;
85481
85634
  white-space: nowrap;
@@ -85492,6 +85645,20 @@ ${folderName} [data-truncate-content='visible'] {
85492
85645
  display: inline;
85493
85646
  direction: inherit;
85494
85647
  }
85648
+ ${fileName} [data-truncate-container] {
85649
+ white-space: nowrap;
85650
+ text-overflow: ellipsis;
85651
+ }
85652
+ ${fileName} :is([data-truncate-grid], [data-truncate-grid] > div:not([data-truncate-marker-cell], [data-truncate-fill])) {
85653
+ display: contents;
85654
+ }
85655
+ ${fileName} :is([data-truncate-content='overflow'], [data-truncate-marker-cell], [data-truncate-fill]) {
85656
+ display: none;
85657
+ }
85658
+ ${fileName} [data-truncate-content='visible'] {
85659
+ display: inline;
85660
+ direction: inherit;
85661
+ }
85495
85662
  `;
85496
85663
  function titled(root2) {
85497
85664
  for (const row of root2.querySelectorAll("[data-type='item'][aria-label]")) {
@@ -85512,6 +85679,26 @@ var SliccFileTree = class extends HTMLElement {
85512
85679
  #query = "";
85513
85680
  #chosen = [];
85514
85681
  #applying = false;
85682
+ #want = null;
85683
+ #back = null;
85684
+ #hold = null;
85685
+ #quiet = false;
85686
+ #told = "";
85687
+ #scroller = null;
85688
+ #top = 0;
85689
+ #height = 0;
85690
+ #set = -1;
85691
+ #user = false;
85692
+ #fought = -1;
85693
+ #gesture = () => {
85694
+ this.#user = true;
85695
+ };
85696
+ #resize = new ResizeObserver(() => {
85697
+ this.#fought = -1;
85698
+ this.#measure();
85699
+ this.#scrollBack();
85700
+ });
85701
+ #scrolled = () => this.#onScroll();
85515
85702
  #titles = null;
85516
85703
  get paths() {
85517
85704
  return this.#paths;
@@ -85522,6 +85709,7 @@ var SliccFileTree = class extends HTMLElement {
85522
85709
  this.#tree?.resetPaths(value, { initialExpandedPaths: expanded });
85523
85710
  this.#research();
85524
85711
  this.#matched();
85712
+ this.#restore();
85525
85713
  }
85526
85714
  #research() {
85527
85715
  const query = this.#query;
@@ -85604,7 +85792,95 @@ var SliccFileTree = class extends HTMLElement {
85604
85792
  this.#applying = false;
85605
85793
  this.#chosen = this.#expandedNow();
85606
85794
  }
85795
+ get view() {
85796
+ const tree = this.#tree;
85797
+ const back = this.#back;
85798
+ const index = tree?.getFocusedIndex() ?? -1;
85799
+ const height = tree?.getItemHeight() ?? 0;
85800
+ const top = this.#top;
85801
+ const bottom = top + this.#height;
85802
+ const rows2 = (tree?.getVisibleCount() ?? 0) * height;
85803
+ return {
85804
+ scroll: back ? back.scroll : top,
85805
+ selected: tree?.getSelectedPaths() ?? [],
85806
+ focused: tree?.getFocusedPath() ?? null,
85807
+ shown: back ? back.shown : index >= 0 && index * height >= top && (index + 1) * height <= bottom,
85808
+ end: back ? back.end : top > 0 && bottom >= rows2 - 1
85809
+ };
85810
+ }
85811
+ set view(value) {
85812
+ this.#want = value;
85813
+ this.#back = value;
85814
+ this.#restore();
85815
+ }
85816
+ #restore() {
85817
+ const tree = this.#tree;
85818
+ const want = this.#want;
85819
+ if (want && tree && this.#paths.length > 0) {
85820
+ this.#want = null;
85821
+ this.#quiet = true;
85822
+ for (const path of tree.getSelectedPaths())
85823
+ if (!want.selected.includes(path)) tree.getItem(path)?.deselect();
85824
+ for (const path of want.selected) tree.getItem(path)?.select();
85825
+ if (want.focused) tree.focusPath(want.focused);
85826
+ this.#quiet = false;
85827
+ }
85828
+ this.#scrollBack();
85829
+ }
85830
+ #scrollBack() {
85831
+ const back = this.#back ?? this.#hold;
85832
+ const el = this.#scroller;
85833
+ const tree = this.#tree;
85834
+ if (!back || !el || !tree || this.#want || !el.clientHeight) return;
85835
+ if (el.scrollHeight + 1 < tree.getVisibleCount() * tree.getItemHeight()) return;
85836
+ this.#back = null;
85837
+ this.#hold = back;
85838
+ const index = tree.getFocusedIndex();
85839
+ const height = tree.getItemHeight();
85840
+ const max2 = el.scrollHeight - el.clientHeight;
85841
+ let top = back.end ? max2 : Math.min(back.scroll, max2);
85842
+ if (back.shown && index >= 0)
85843
+ top = Math.min(Math.max(top, (index + 1) * height - el.clientHeight), index * height);
85844
+ el.scrollTop = top;
85845
+ this.#set = el.scrollTop;
85846
+ this.#user = false;
85847
+ this.#measure();
85848
+ }
85849
+ #measure() {
85850
+ const el = this.#scroller;
85851
+ if (!el?.clientHeight) return;
85852
+ this.#top = el.scrollTop;
85853
+ this.#height = el.clientHeight;
85854
+ }
85855
+ #onScroll() {
85856
+ this.#measure();
85857
+ const el = this.#scroller;
85858
+ if (!el?.clientHeight || this.#want || Math.abs(el.scrollTop - this.#set) < 1) return;
85859
+ if ((this.#back || this.#hold) && !this.#user) {
85860
+ if (!this.#hold || el.scrollTop === this.#fought) return;
85861
+ this.#fought = el.scrollTop;
85862
+ this.#scrollBack();
85863
+ return;
85864
+ }
85865
+ this.#set = -1;
85866
+ this.#back = null;
85867
+ this.#hold = null;
85868
+ this.#tell(true);
85869
+ }
85870
+ #tell(always = false) {
85871
+ if (this.#want || this.#quiet || this.#paths.length === 0 || !this.isConnected) return;
85872
+ const view = this.view;
85873
+ const told = `${view.selected.join("\n")}
85874
+
85875
+ ${view.focused}`;
85876
+ if (!always && told === this.#told) return;
85877
+ this.#told = told;
85878
+ this.dispatchEvent(
85879
+ new CustomEvent("tree-view", { detail: view, bubbles: true, composed: true })
85880
+ );
85881
+ }
85607
85882
  #changed() {
85883
+ this.#tell();
85608
85884
  if (this.#applying || !this.#tree || this.#query) return;
85609
85885
  const expanded = this.#expandedNow();
85610
85886
  if (expanded.join("\n") === this.#chosen.join("\n")) return;
@@ -85642,9 +85918,34 @@ var SliccFileTree = class extends HTMLElement {
85642
85918
  this.#title();
85643
85919
  this.#chosen = this.#query ? this.#expanded : this.#expandedNow();
85644
85920
  this.#tree.subscribe(() => this.#changed());
85921
+ this.#watch();
85645
85922
  this.#matched();
85923
+ this.#restore();
85924
+ }
85925
+ #watch() {
85926
+ const root2 = this.#tree?.getFileTreeContainer()?.shadowRoot;
85927
+ const el = root2?.querySelector("[data-file-tree-virtualized-scroll]") ?? null;
85928
+ this.#scroller = el;
85929
+ if (!el) return;
85930
+ el.addEventListener("scroll", this.#scrolled, { passive: true });
85931
+ for (const type of gestures) el.addEventListener(type, this.#gesture, { passive: true });
85932
+ this.#set = -1;
85933
+ this.#user = false;
85934
+ this.#resize.observe(el);
85935
+ const list5 = el.querySelector("[data-file-tree-virtualized-list]");
85936
+ if (list5) this.#resize.observe(list5);
85646
85937
  }
85647
85938
  disconnectedCallback() {
85939
+ if (this.#paths.length > 0 && !this.#want) {
85940
+ const view = this.view;
85941
+ this.#want = view;
85942
+ this.#back = view;
85943
+ }
85944
+ this.#scroller?.removeEventListener("scroll", this.#scrolled);
85945
+ for (const type of gestures) this.#scroller?.removeEventListener(type, this.#gesture);
85946
+ this.#scroller = null;
85947
+ this.#hold = null;
85948
+ this.#resize.disconnect();
85648
85949
  if (this.#paths.length > 0) this.#expanded = this.#held();
85649
85950
  this.#titles?.disconnect();
85650
85951
  this.#titles = null;
@@ -85672,7 +85973,7 @@ var SliccFileTree = class extends HTMLElement {
85672
85973
  );
85673
85974
  }
85674
85975
  #selected(paths) {
85675
- if (paths.length === 1) this.#open(paths[0]);
85976
+ if (paths.length === 1 && !this.#quiet) this.#open(paths[0]);
85676
85977
  }
85677
85978
  #keydown(event2) {
85678
85979
  if (event2.defaultPrevented) return;
@@ -85690,21 +85991,22 @@ var SliccFileTree = class extends HTMLElement {
85690
85991
  if (event2.key !== "Enter") return;
85691
85992
  this.#open(this.#tree?.getFocusedPath());
85692
85993
  }
85693
- focus() {
85994
+ focus(options) {
85694
85995
  const tree = this.#tree;
85695
85996
  const path = tree?.getFocusedPath() ?? tree?.getVisibleRows(0, 1)[0]?.path;
85696
- if (path) this.reveal(path);
85997
+ if (path) this.reveal(path, true, options);
85697
85998
  }
85698
85999
  #row(path) {
85699
86000
  const container = this.#tree?.getFileTreeContainer();
85700
86001
  return container?.shadowRoot?.querySelector(`[data-item-path="${CSS.escape(path)}"]`) ?? null;
85701
86002
  }
85702
- reveal(path, focus = true) {
86003
+ reveal(path, focus = true, options) {
86004
+ this.#hold = null;
85703
86005
  this.#tree?.scrollToPath(path, { focus });
85704
86006
  if (!focus) return;
85705
86007
  const row = this.#row(path);
85706
- if (row) row.focus();
85707
- else requestAnimationFrame(() => this.#row(path)?.focus());
86008
+ if (row) row.focus(options);
86009
+ else requestAnimationFrame(() => this.#row(path)?.focus(options));
85708
86010
  }
85709
86011
  };
85710
86012
  function defineFileTree() {