dsh-vscode-workbench 0.1.3 → 0.1.5

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/lib/client.js CHANGED
@@ -4418,22 +4418,21 @@ Please report this to https://github.com/markedjs/marked.`, e) {
4418
4418
  }
4419
4419
  //#endregion
4420
4420
  //#region src/client/DshMark.tsx
4421
- /** Reuse the official DSH whale already rendered by the host brand plugin. */
4421
+ /** Bundled DSH fish logo — self-contained, no dependence on host DOM. */
4422
+ const WHALE_VIEWBOX = "0 0 23.16 17.04";
4423
+ const WHALE_PATH = "M22.9168 1.43018C22.6713 1.31018 22.5658 1.53918 22.4223 1.65519C22.3733 1.69269 22.3318 1.74169 22.2903 1.78669C21.9317 2.1697 21.5127 2.42121 20.9657 2.39121C20.1657 2.34621 19.4827 2.59771 18.8787 3.20973C18.7502 2.45521 18.3236 2.0047 17.6746 1.71569C17.3351 1.56568 16.9916 1.41518 16.7536 1.08867C16.5876 0.856163 16.5421 0.597155 16.4591 0.341647C16.4061 0.187643 16.3536 0.0301382 16.1761 0.00363739C15.9836 -0.0263635 15.9081 0.135141 15.8326 0.270145C15.5306 0.822162 15.4136 1.43018 15.4251 2.0462C15.4516 3.43174 16.0366 4.53527 17.1991 5.3203C17.3311 5.4103 17.3651 5.5003 17.3236 5.63181C17.2441 5.90231 17.1501 6.16482 17.0671 6.43533C17.0141 6.60784 16.9351 6.64584 16.7501 6.57033C16.1121 6.30383 15.5611 5.90931 15.074 5.4328C14.2475 4.63328 13.5 3.75075 12.568 3.05973C12.349 2.89822 12.13 2.74822 11.9034 2.60522C10.9524 1.68169 12.028 0.923165 12.277 0.833162C12.5375 0.739159 12.3675 0.41615 11.5259 0.42015C10.6844 0.42365 9.91439 0.705658 8.93286 1.08117C8.78935 1.13767 8.63835 1.17867 8.48384 1.21267C7.59332 1.04367 6.66829 1.00617 5.70226 1.11517C3.88321 1.31768 2.43016 2.1777 1.36213 3.64575C0.0790928 5.4103 -0.222916 7.41536 0.146595 9.50642C0.535106 11.7105 1.66014 13.535 3.38869 14.9616C5.18125 16.4406 7.24581 17.1657 9.60138 17.0266C11.0319 16.9441 12.6245 16.7526 14.421 15.2321C14.874 15.4576 15.3496 15.5476 16.1381 15.6151C16.7456 15.6716 17.3306 15.5851 17.7836 15.4911C18.4931 15.3411 18.4441 14.6841 18.1876 14.5636C16.1081 13.595 16.5646 13.9891 16.1496 13.67C17.2061 12.42 18.8202 10.1979 19.3182 7.17235C19.3672 6.83834 19.4297 6.36783 19.4222 6.09732C19.4182 5.93231 19.4562 5.86831 19.6447 5.84931C20.1657 5.78931 20.6712 5.64681 21.1357 5.3913C22.4833 4.65528 23.0268 3.44624 23.1548 1.9972C23.1738 1.77569 23.1508 1.54668 22.9168 1.43018ZM11.1749 14.4736C9.15936 12.889 8.18184 12.3675 7.77832 12.39C7.40081 12.4125 7.46881 12.8445 7.55182 13.126C7.63882 13.404 7.75182 13.5955 7.91033 13.8396C8.01983 14.0011 8.09533 14.2411 7.80083 14.4216C7.15181 14.8231 6.02327 14.2866 5.97027 14.2601C4.65673 13.4865 3.5587 12.4655 2.78467 11.069C2.03715 9.72493 1.60314 8.28289 1.53164 6.74384C1.51264 6.37233 1.62214 6.24082 1.99215 6.17332C2.47916 6.08332 2.98118 6.06432 3.46769 6.13582C5.52476 6.43633 7.27581 7.35586 8.74385 8.8129C9.58188 9.64243 10.2159 10.634 10.8689 11.6025C11.5634 12.631 12.3105 13.611 13.262 14.4146C13.598 14.6961 13.866 14.9101 14.1225 15.0681C13.349 15.1546 12.058 15.1731 11.1749 14.4746L11.1749 14.4736ZM12.141 8.25988C12.141 8.09488 12.273 7.96338 12.439 7.96338C12.4765 7.96338 12.5105 7.97088 12.541 7.98188C12.5825 7.99688 12.6205 8.01938 12.6505 8.05338C12.7035 8.10588 12.7335 8.18088 12.7335 8.25988C12.7335 8.42489 12.6015 8.55639 12.4355 8.55639C12.2695 8.55639 12.141 8.42489 12.141 8.25988ZM15.1415 9.79893C14.949 9.87793 14.7565 9.94544 14.5715 9.95294C14.2845 9.96794 13.9715 9.85143 13.8015 9.70893C13.5375 9.48742 13.3485 9.36342 13.2695 8.97691C13.2355 8.8119 13.2545 8.55639 13.2845 8.40989C13.3525 8.09438 13.277 7.89187 13.0545 7.70787C12.8735 7.55786 12.643 7.51636 12.39 7.51636C12.2955 7.51636 12.209 7.47486 12.1445 7.44136C12.039 7.38886 11.9519 7.25735 12.035 7.09585C12.0615 7.04335 12.19 6.91584 12.22 6.89334C12.5635 6.69784 12.9595 6.76184 13.326 6.90834C13.6655 7.04735 13.9225 7.30236 14.292 7.66287C14.6695 8.09838 14.7375 8.21838 14.9525 8.54539C15.1225 8.8009 15.277 9.06341 15.3831 9.36392C15.4471 9.55142 15.3641 9.70493 15.1415 9.79893Z";
4422
4424
  function DshMark({ size = 18 }) {
4423
- const mount = (0, react.useRef)(null);
4424
- (0, react.useLayoutEffect)(() => {
4425
- const source = [...document.querySelectorAll("svg")].find((svg) => svg.getAttribute("viewBox") === "0 0 23.16 17.04" && !mount.current?.contains(svg));
4426
- if (source === void 0 || mount.current === null) return;
4427
- const logo = source.cloneNode(true);
4428
- logo.setAttribute("width", String(size));
4429
- logo.setAttribute("height", String(size * 17.04 / 23.16));
4430
- mount.current.replaceChildren(logo);
4431
- }, [size]);
4432
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4425
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
4433
4426
  className: "dvw-dsh-mark",
4434
- ref: mount,
4427
+ width: size,
4428
+ height: size * 17.04 / 23.16,
4429
+ viewBox: WHALE_VIEWBOX,
4430
+ fill: "none",
4435
4431
  "aria-hidden": "true",
4436
- children: "DSH"
4432
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
4433
+ d: WHALE_PATH,
4434
+ fill: "currentColor"
4435
+ })
4437
4436
  });
4438
4437
  }
4439
4438
  //#endregion
@@ -4474,6 +4473,17 @@ Please report this to https://github.com/markedjs/marked.`, e) {
4474
4473
  const path = tab.title ?? tab.diffSourcePath ?? tab.path.replace(/^diff:/, "");
4475
4474
  return path.split(/[\\/]/).at(-1) ?? path;
4476
4475
  }
4476
+ /** Invoke `fn` at most once per `ms` after the latest call, always running the freshest callback. */
4477
+ function useDebouncedCallback(fn, ms) {
4478
+ const fnRef = (0, react.useRef)(fn);
4479
+ fnRef.current = fn;
4480
+ const timer = (0, react.useRef)();
4481
+ (0, react.useEffect)(() => () => clearTimeout(timer.current), []);
4482
+ return (0, react.useCallback)(() => {
4483
+ clearTimeout(timer.current);
4484
+ timer.current = setTimeout(() => fnRef.current(), ms);
4485
+ }, [ms]);
4486
+ }
4477
4487
  function useDshDarkTheme() {
4478
4488
  const read = () => document.body.hasAttribute("data-ds-dark-theme");
4479
4489
  const [dark, setDark] = (0, react.useState)(read);
@@ -4487,9 +4497,13 @@ Please report this to https://github.com/markedjs/marked.`, e) {
4487
4497
  }, []);
4488
4498
  return dark;
4489
4499
  }
4490
- function Tree({ sessionId, path, depth, expanded, selected, onToggle, onOpen, onChange }) {
4500
+ function Tree({ sessionId, path, depth, expanded, selected, selectedDirectory, statuses, onToggle, onSelectDirectory, onOpen, onChange, onUpload }) {
4491
4501
  const [rows, setRows] = (0, react.useState)([]);
4492
4502
  const [error, setError] = (0, react.useState)();
4503
+ const [dropTarget, setDropTarget] = (0, react.useState)(false);
4504
+ const [context, setContext] = (0, react.useState)();
4505
+ const [renaming, setRenaming] = (0, react.useState)();
4506
+ const [pendingDelete, setPendingDelete] = (0, react.useState)();
4493
4507
  const load = (0, react.useCallback)(() => {
4494
4508
  api("fs.list", {
4495
4509
  sessionId,
@@ -4502,72 +4516,223 @@ Please report this to https://github.com/markedjs/marked.`, e) {
4502
4516
  window.addEventListener("dvw-files-changed", handler);
4503
4517
  return () => window.removeEventListener("dvw-files-changed", handler);
4504
4518
  }, [load]);
4505
- const menu = async (entry) => {
4506
- const action = prompt(`操作 ${entry.name}: rename / delete / new-file / new-folder`);
4519
+ (0, react.useEffect)(() => {
4520
+ if (context === void 0) return;
4521
+ const close = () => setContext(void 0);
4522
+ const escape = (event) => {
4523
+ if (event.key === "Escape") close();
4524
+ };
4525
+ window.addEventListener("pointerdown", close);
4526
+ window.addEventListener("keydown", escape);
4527
+ return () => {
4528
+ window.removeEventListener("pointerdown", close);
4529
+ window.removeEventListener("keydown", escape);
4530
+ };
4531
+ }, [context]);
4532
+ (0, react.useEffect)(() => {
4533
+ if (pendingDelete === void 0) return;
4534
+ const escape = (event) => {
4535
+ if (event.key === "Escape") setPendingDelete(void 0);
4536
+ };
4537
+ window.addEventListener("keydown", escape);
4538
+ return () => window.removeEventListener("keydown", escape);
4539
+ }, [pendingDelete]);
4540
+ const rename = async (entry) => {
4541
+ const value = renaming?.path === entry.path ? renaming.value.trim() : "";
4542
+ setRenaming(void 0);
4543
+ if (value === "" || value === entry.name) return;
4507
4544
  try {
4508
- if (action === "rename") {
4509
- const name = prompt("新名称", entry.name);
4510
- if (name) await api("fs.rename", {
4511
- sessionId,
4512
- path: entry.path,
4513
- nextPath: [...entry.path.split("/").slice(0, -1), name].filter(Boolean).join("/")
4514
- });
4515
- }
4516
- if (action === "delete" && confirm(`确定删除 ${entry.path}?`)) await api("fs.delete", {
4545
+ await api("fs.rename", {
4546
+ sessionId,
4547
+ path: entry.path,
4548
+ nextPath: renamedPath(entry.path, value)
4549
+ });
4550
+ load();
4551
+ onChange();
4552
+ } catch (error) {
4553
+ alert(error instanceof Error ? error.message : String(error));
4554
+ }
4555
+ };
4556
+ const remove = async () => {
4557
+ if (pendingDelete === void 0) return;
4558
+ const entry = pendingDelete;
4559
+ setPendingDelete(void 0);
4560
+ try {
4561
+ await api("fs.delete", {
4517
4562
  sessionId,
4518
4563
  path: entry.path
4519
4564
  });
4520
- if (action === "new-file" || action === "new-folder") {
4521
- const name = prompt("名称");
4522
- if (name) await api("fs.create", {
4523
- sessionId,
4524
- path: `${entry.kind === "directory" ? entry.path : entry.path.split("/").slice(0, -1).join("/")}/${name}`.replace(/^\//, ""),
4525
- kind: action === "new-folder" ? "directory" : "file"
4526
- });
4527
- }
4528
4565
  load();
4529
4566
  onChange();
4530
4567
  } catch (error) {
4531
4568
  alert(error instanceof Error ? error.message : String(error));
4532
4569
  }
4533
4570
  };
4534
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4535
- className: "dvw-error",
4536
- children: error
4537
- }), rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4538
- className: "dvw-tree-row",
4539
- "data-selected": selected === row.path,
4540
- style: { paddingLeft: 6 + depth * 13 },
4541
- onClick: () => row.kind === "directory" ? onToggle(row.path) : onOpen(row.path),
4542
- onContextMenu: (event) => {
4543
- event.preventDefault();
4544
- menu(row);
4545
- },
4546
- children: [
4547
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4548
- className: "dvw-chevron",
4549
- children: row.kind === "directory" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: expanded.has(row.path) ? "chevron-down" : "chevron-right" })
4550
- }),
4551
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
4552
- name: row.kind === "directory" ? expanded.has(row.path) ? "folder-opened" : "folder" : fileIcon(row.name),
4553
- className: `dvw-file-icon ${row.kind === "directory" ? "folder" : ""}`
4554
- }),
4555
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: row.name })
4556
- ]
4557
- }), row.kind === "directory" && expanded.has(row.path) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tree, {
4558
- sessionId,
4559
- path: row.path,
4560
- depth: depth + 1,
4561
- expanded,
4562
- selected,
4563
- onToggle,
4564
- onOpen,
4565
- onChange
4566
- })] }, row.path))] });
4571
+ const drop = (event, directory) => {
4572
+ event.preventDefault();
4573
+ event.stopPropagation();
4574
+ setDropTarget(false);
4575
+ if (event.dataTransfer.files.length > 0) onUpload(directory, event.dataTransfer.files);
4576
+ };
4577
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4578
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4579
+ className: "dvw-error",
4580
+ children: error
4581
+ }),
4582
+ rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4583
+ className: "dvw-tree-row",
4584
+ "data-dvw-upload-directory": row.kind === "directory" ? row.path : void 0,
4585
+ "data-selected": selected === row.path || selectedDirectory === row.path,
4586
+ "data-drop-target": dropTarget && row.kind === "directory",
4587
+ style: { paddingLeft: 6 + depth * 13 },
4588
+ onClick: () => row.kind === "directory" ? (onSelectDirectory(row.path), onToggle(row.path)) : onOpen(row.path),
4589
+ onContextMenu: (event) => {
4590
+ event.preventDefault();
4591
+ if (row.kind === "directory") onSelectDirectory(row.path);
4592
+ setContext({
4593
+ entry: row,
4594
+ x: event.clientX,
4595
+ y: event.clientY
4596
+ });
4597
+ },
4598
+ onDragEnter: (event) => {
4599
+ if (row.kind === "directory" && event.dataTransfer.types.includes("Files")) {
4600
+ event.preventDefault();
4601
+ event.stopPropagation();
4602
+ setDropTarget(true);
4603
+ }
4604
+ },
4605
+ onDragOver: (event) => {
4606
+ if (row.kind === "directory" && event.dataTransfer.types.includes("Files")) {
4607
+ event.preventDefault();
4608
+ event.stopPropagation();
4609
+ setDropTarget(true);
4610
+ }
4611
+ },
4612
+ onDragLeave: () => setDropTarget(false),
4613
+ onDrop: (event) => row.kind === "directory" && drop(event, row.path),
4614
+ children: [
4615
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4616
+ className: "dvw-chevron",
4617
+ children: row.kind === "directory" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: expanded.has(row.path) ? "chevron-down" : "chevron-right" })
4618
+ }),
4619
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
4620
+ name: row.kind === "directory" ? expanded.has(row.path) ? "folder-opened" : "folder" : fileIcon(row.name),
4621
+ className: `dvw-file-icon ${row.kind === "directory" ? "folder" : ""}`
4622
+ }),
4623
+ renaming?.path === row.path ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4624
+ className: "dvw-tree-rename",
4625
+ value: renaming.value,
4626
+ autoFocus: true,
4627
+ onClick: (event) => event.stopPropagation(),
4628
+ onChange: (event) => setRenaming({
4629
+ path: row.path,
4630
+ value: event.target.value
4631
+ }),
4632
+ onKeyDown: (event) => {
4633
+ if (event.key === "Enter") rename(row);
4634
+ if (event.key === "Escape") setRenaming(void 0);
4635
+ },
4636
+ onBlur: () => setRenaming(void 0)
4637
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4638
+ className: "dvw-tree-name",
4639
+ children: row.name
4640
+ }),
4641
+ row.kind === "file" && statuses.has(row.path) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4642
+ className: "dvw-tree-status",
4643
+ "data-status": statuses.get(row.path),
4644
+ children: statuses.get(row.path)
4645
+ })
4646
+ ]
4647
+ }), row.kind === "directory" && expanded.has(row.path) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tree, {
4648
+ sessionId,
4649
+ path: row.path,
4650
+ depth: depth + 1,
4651
+ expanded,
4652
+ selected,
4653
+ selectedDirectory,
4654
+ statuses,
4655
+ onToggle,
4656
+ onSelectDirectory,
4657
+ onOpen,
4658
+ onChange,
4659
+ onUpload
4660
+ })] }, row.path)),
4661
+ context && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4662
+ className: "dvw-context-menu",
4663
+ role: "menu",
4664
+ style: {
4665
+ left: context.x,
4666
+ top: context.y
4667
+ },
4668
+ onPointerDown: (event) => event.stopPropagation(),
4669
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4670
+ role: "menuitem",
4671
+ onClick: () => {
4672
+ setRenaming({
4673
+ path: context.entry.path,
4674
+ value: context.entry.name
4675
+ });
4676
+ setContext(void 0);
4677
+ },
4678
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "edit" }), "重命名"]
4679
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4680
+ className: "dvw-context-delete",
4681
+ role: "menuitem",
4682
+ onClick: () => {
4683
+ setPendingDelete(context.entry);
4684
+ setContext(void 0);
4685
+ },
4686
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "trash" }), "删除"]
4687
+ })]
4688
+ }),
4689
+ pendingDelete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4690
+ className: "dvw-confirm-backdrop",
4691
+ role: "presentation",
4692
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4693
+ className: "dvw-confirm",
4694
+ role: "alertdialog",
4695
+ "aria-modal": "true",
4696
+ "aria-labelledby": "dvw-delete-title",
4697
+ "aria-describedby": "dvw-delete-message",
4698
+ children: [
4699
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h2", {
4700
+ id: "dvw-delete-title",
4701
+ children: ["删除 ", pendingDelete.kind === "directory" ? "文件夹" : "文件"]
4702
+ }),
4703
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
4704
+ id: "dvw-delete-message",
4705
+ children: [
4706
+ "是否删除“",
4707
+ pendingDelete.name,
4708
+ "”?"
4709
+ ]
4710
+ }),
4711
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4712
+ className: "dvw-confirm-note",
4713
+ children: "此操作无法撤销。"
4714
+ }),
4715
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4716
+ autoFocus: true,
4717
+ onClick: () => setPendingDelete(void 0),
4718
+ children: "取消"
4719
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4720
+ className: "dvw-danger-button",
4721
+ onClick: () => void remove(),
4722
+ children: "删除"
4723
+ })] })
4724
+ ]
4725
+ })
4726
+ })
4727
+ ] });
4567
4728
  }
4568
4729
  function Explorer(props) {
4569
4730
  const [expanded, setExpanded] = (0, react.useState)(/* @__PURE__ */ new Set());
4570
4731
  const [revision, setRevision] = (0, react.useState)(0);
4732
+ const [statuses, setStatuses] = (0, react.useState)(/* @__PURE__ */ new Map());
4733
+ const [dropTarget, setDropTarget] = (0, react.useState)(false);
4734
+ const [selectedDirectory, setSelectedDirectory] = (0, react.useState)("");
4735
+ const picker = (0, react.useRef)(null);
4571
4736
  (0, react.useEffect)(() => {
4572
4737
  if (props.cwd) try {
4573
4738
  setExpanded(new Set(JSON.parse(localStorage.getItem(`dvw:tree:${props.cwd}`) ?? "[]")));
@@ -4578,6 +4743,16 @@ Please report this to https://github.com/markedjs/marked.`, e) {
4578
4743
  (0, react.useEffect)(() => {
4579
4744
  if (props.cwd) localStorage.setItem(`dvw:tree:${props.cwd}`, JSON.stringify([...expanded]));
4580
4745
  }, [props.cwd, expanded]);
4746
+ const refreshStatus = (0, react.useCallback)(() => {
4747
+ api("git.status", { sessionId: props.sessionId }).then((status) => setStatuses(new Map(status.changes.map((change) => [change.path, gitStatusCode(change)])))).catch(() => setStatuses(/* @__PURE__ */ new Map()));
4748
+ }, [props.sessionId]);
4749
+ const refreshStatusDebounced = useDebouncedCallback(refreshStatus, 500);
4750
+ (0, react.useEffect)(() => {
4751
+ refreshStatus();
4752
+ const handler = () => refreshStatusDebounced();
4753
+ window.addEventListener("dvw-files-changed", handler);
4754
+ return () => window.removeEventListener("dvw-files-changed", handler);
4755
+ }, [refreshStatus, refreshStatusDebounced]);
4581
4756
  const create = async (kind) => {
4582
4757
  const path = prompt(kind === "file" ? "新文件路径" : "新目录路径");
4583
4758
  if (!path) return;
@@ -4593,11 +4768,111 @@ Please report this to https://github.com/markedjs/marked.`, e) {
4593
4768
  alert(String(error));
4594
4769
  }
4595
4770
  };
4771
+ const upload = (0, react.useCallback)(async (directory, files) => {
4772
+ try {
4773
+ await Promise.all([...files].map(async (file) => {
4774
+ const query = new URLSearchParams({
4775
+ sessionId: props.sessionId,
4776
+ directory,
4777
+ name: file.name
4778
+ });
4779
+ const response = await fetch(`/dsh-vscode/upload?${query}`, {
4780
+ method: "POST",
4781
+ headers: { "content-type": file.type || "application/octet-stream" },
4782
+ body: file
4783
+ });
4784
+ const answer = await response.json();
4785
+ if (!response.ok || !answer.ok) throw new Error(answer.error?.message ?? "upload failed");
4786
+ }));
4787
+ setRevision((value) => value + 1);
4788
+ window.dispatchEvent(new Event("dvw-files-changed"));
4789
+ } catch (error) {
4790
+ alert(error instanceof Error ? error.message : String(error));
4791
+ }
4792
+ }, [props.sessionId]);
4793
+ (0, react.useEffect)(() => {
4794
+ let activeTarget;
4795
+ const filesDragged = (event) => Array.from(event.dataTransfer?.types ?? []).includes("Files");
4796
+ const targetDirectory = (event) => event.target instanceof Element ? event.target.closest("[data-dvw-upload-directory]") : null;
4797
+ const clearTarget = () => {
4798
+ activeTarget?.removeAttribute("data-dvw-native-drop-target");
4799
+ activeTarget = void 0;
4800
+ };
4801
+ const intercept = (event) => {
4802
+ if (!filesDragged(event)) return;
4803
+ const target = targetDirectory(event);
4804
+ if (!target) return;
4805
+ event.preventDefault();
4806
+ event.stopImmediatePropagation();
4807
+ if (event.type === "dragleave") {
4808
+ if (!(event.relatedTarget instanceof Node && target.contains(event.relatedTarget))) clearTarget();
4809
+ return;
4810
+ }
4811
+ clearTarget();
4812
+ activeTarget = target;
4813
+ target.dataset.dvwNativeDropTarget = "true";
4814
+ if (event.type === "drop" && event.dataTransfer?.files.length) {
4815
+ const files = event.dataTransfer.files;
4816
+ clearTarget();
4817
+ window.dispatchEvent(new CustomEvent("dvw-upload-files", { detail: {
4818
+ directory: target.dataset.dvwUploadDirectory ?? "",
4819
+ files
4820
+ } }));
4821
+ }
4822
+ };
4823
+ document.addEventListener("dragenter", intercept, true);
4824
+ document.addEventListener("dragover", intercept, true);
4825
+ document.addEventListener("dragleave", intercept, true);
4826
+ document.addEventListener("drop", intercept, true);
4827
+ const uploadFiles = (event) => {
4828
+ const detail = event.detail;
4829
+ if (detail?.files?.length) upload(detail.directory, detail.files);
4830
+ };
4831
+ window.addEventListener("dvw-upload-files", uploadFiles);
4832
+ return () => {
4833
+ clearTarget();
4834
+ document.removeEventListener("dragenter", intercept, true);
4835
+ document.removeEventListener("dragover", intercept, true);
4836
+ document.removeEventListener("dragleave", intercept, true);
4837
+ document.removeEventListener("drop", intercept, true);
4838
+ window.removeEventListener("dvw-upload-files", uploadFiles);
4839
+ };
4840
+ }, [upload]);
4841
+ const drop = (event) => {
4842
+ event.preventDefault();
4843
+ setDropTarget(false);
4844
+ if (event.dataTransfer.files.length > 0) upload("", event.dataTransfer.files);
4845
+ };
4846
+ const destination = uploadDestination(selectedDirectory);
4847
+ const treeDrag = (event) => {
4848
+ if (event.dataTransfer.types.includes("Files")) {
4849
+ event.preventDefault();
4850
+ event.stopPropagation();
4851
+ setDropTarget(true);
4852
+ }
4853
+ };
4596
4854
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4597
4855
  className: "dvw-side-head",
4598
4856
  children: ["资源管理器", /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4599
4857
  className: "dvw-toolbar",
4600
4858
  children: [
4859
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4860
+ ref: picker,
4861
+ className: "dvw-file-picker",
4862
+ type: "file",
4863
+ accept: "*/*",
4864
+ multiple: true,
4865
+ onChange: (event) => {
4866
+ if (event.currentTarget.files) upload(destination, event.currentTarget.files);
4867
+ event.currentTarget.value = "";
4868
+ }
4869
+ }),
4870
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4871
+ className: "dvw-icon",
4872
+ title: `上传文件到 ${destination || "工作区根目录"}`,
4873
+ onClick: () => picker.current?.click(),
4874
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "cloud-upload" })
4875
+ }),
4601
4876
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4602
4877
  className: "dvw-icon",
4603
4878
  title: "新建文件",
@@ -4623,22 +4898,45 @@ Please report this to https://github.com/markedjs/marked.`, e) {
4623
4898
  })]
4624
4899
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4625
4900
  className: "dvw-tree",
4901
+ "data-dvw-upload-directory": "",
4902
+ "data-drop-target": dropTarget,
4903
+ onDragEnter: treeDrag,
4904
+ onDragOver: treeDrag,
4905
+ onDragLeave: () => setDropTarget(false),
4906
+ onDrop: drop,
4626
4907
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tree, {
4627
4908
  sessionId: props.sessionId,
4628
4909
  path: "",
4629
4910
  depth: 0,
4630
4911
  expanded,
4631
4912
  selected: props.selected,
4913
+ selectedDirectory,
4914
+ statuses,
4632
4915
  onToggle: (path) => setExpanded((current) => {
4633
4916
  const next = new Set(current);
4634
4917
  next.has(path) ? next.delete(path) : next.add(path);
4635
4918
  return next;
4636
4919
  }),
4920
+ onSelectDirectory: setSelectedDirectory,
4637
4921
  onOpen: props.onOpen,
4638
- onChange: () => setRevision((x) => x + 1)
4922
+ onChange: () => setRevision((x) => x + 1),
4923
+ onUpload: (directory, files) => void upload(directory, files)
4639
4924
  })
4640
4925
  }, revision)] });
4641
4926
  }
4927
+ /** Return the workspace-relative directory used by the file picker. */
4928
+ function uploadDestination(selectedDirectory) {
4929
+ return selectedDirectory ?? "";
4930
+ }
4931
+ /** Replace the final path component while preserving its workspace-relative directory. */
4932
+ function renamedPath(path, name) {
4933
+ return [...path.split("/").slice(0, -1), name].filter(Boolean).join("/");
4934
+ }
4935
+ function gitStatusCode(change) {
4936
+ if (change.untracked) return "A";
4937
+ const code = change.worktree !== "." ? change.worktree : change.index;
4938
+ return code === "?" ? "A" : code;
4939
+ }
4642
4940
  function MarkdownOutline({ headings, onClose, onHeading }) {
4643
4941
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
4644
4942
  className: "dvw-outline dvw-editor-outline",
@@ -5033,12 +5331,13 @@ Please report this to https://github.com/markedjs/marked.`, e) {
5033
5331
  setError(void 0);
5034
5332
  }).catch((error) => setError(String(error)));
5035
5333
  }, [props.sessionId]);
5334
+ const refreshDebounced = useDebouncedCallback(refresh, 500);
5036
5335
  (0, react.useEffect)(refresh, [refresh]);
5037
5336
  (0, react.useEffect)(() => {
5038
- const fn = () => refresh();
5337
+ const fn = () => refreshDebounced();
5039
5338
  window.addEventListener("dvw-files-changed", fn);
5040
5339
  return () => window.removeEventListener("dvw-files-changed", fn);
5041
- }, [refresh]);
5340
+ }, [refreshDebounced]);
5042
5341
  const action = async (name, path, value) => {
5043
5342
  setBusy(true);
5044
5343
  try {
@@ -5267,15 +5566,12 @@ Please report this to https://github.com/markedjs/marked.`, e) {
5267
5566
  ] })
5268
5567
  ] });
5269
5568
  }
5270
- function Launcher({ wide, service }) {
5271
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
5569
+ function Launcher({ service }) {
5570
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5272
5571
  className: "dvw-launch",
5273
5572
  title: "打开 VS Code 工作台",
5274
5573
  onClick: () => service.show(),
5275
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
5276
- name: "files",
5277
- className: "dvw-launch-icon"
5278
- }), wide && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "VS Code 工作台" })]
5574
+ children: "VS Code 工作台"
5279
5575
  });
5280
5576
  }
5281
5577
  function Workbench({ service, useSessions }) {
@@ -5489,6 +5785,14 @@ Please report this to https://github.com/markedjs/marked.`, e) {
5489
5785
  } : tab));
5490
5786
  window.setTimeout(() => window.dispatchEvent(new CustomEvent("dvw-markdown-heading", { detail: index })), 50);
5491
5787
  };
5788
+ const suppressAttachmentDrop = (event) => {
5789
+ if (!event.dataTransfer.types.includes("Files")) return;
5790
+ const target = event.target;
5791
+ if (!(target instanceof Element && target.closest(".dvw-tree") !== null)) {
5792
+ event.preventDefault();
5793
+ event.stopPropagation();
5794
+ }
5795
+ };
5492
5796
  if (!snapshot.visible) return null;
5493
5797
  if (sessionId === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5494
5798
  className: "dvw-overlay",
@@ -5522,6 +5826,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
5522
5826
  },
5523
5827
  role: "dialog",
5524
5828
  "aria-label": "VS Code 工作台",
5829
+ onDragEnterCapture: suppressAttachmentDrop,
5830
+ onDragOverCapture: suppressAttachmentDrop,
5831
+ onDropCapture: suppressAttachmentDrop,
5525
5832
  children: [
5526
5833
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DshConversationPanel, {
5527
5834
  visible: chatVisible && !settingsOpen,
@@ -5999,20 +6306,20 @@ Please report this to https://github.com/markedjs/marked.`, e) {
5999
6306
  const CSS = String.raw`
6000
6307
  @font-face{font-family:codicon;font-display:block;src:url('/dsh-vscode/codicons/codicon.ttf') format('truetype')}
6001
6308
  .codicon{display:inline-block;font:normal normal normal 16px/1 codicon;text-align:center;text-decoration:none;text-rendering:auto;-webkit-font-smoothing:antialiased;user-select:none}
6002
- .codicon-add:before{content:"\ea60"}.codicon-git-branch:before,.codicon-source-control:before{content:"\ea68"}.codicon-search:before{content:"\ea6d"}.codicon-close:before{content:"\ea76"}.codicon-close-all:before{content:"\eac1"}.codicon-file:before{content:"\ea7b"}.codicon-new-file:before{content:"\ea7f"}.codicon-new-folder:before{content:"\ea80"}.codicon-folder:before{content:"\ea83"}.codicon-trash:before{content:"\ea81"}.codicon-code:before{content:"\eac4"}.codicon-check:before{content:"\eab2"}.codicon-chevron-down:before{content:"\eab4"}.codicon-chevron-right:before{content:"\eab6"}.codicon-color-mode:before{content:"\eac6"}.codicon-diff:before,.codicon-diff-sidebyside:before{content:"\eae1"}.codicon-diff-single:before{content:"\ec22"}.codicon-discard:before{content:"\eae2"}.codicon-file-code:before{content:"\eae9"}.codicon-file-media:before{content:"\eaea"}.codicon-file-pdf:before{content:"\eaeb"}.codicon-file-zip:before{content:"\eaef"}.codicon-files:before{content:"\eaf0"}.codicon-folder-opened:before{content:"\eaf7"}.codicon-git-commit:before{content:"\eafc"}.codicon-graph:before{content:"\eb03"}.codicon-home:before{content:"\eb06"}.codicon-list-tree:before{content:"\eb86"}.codicon-preview:before{content:"\eb2f"}.codicon-refresh:before{content:"\eb37"}.codicon-remove:before{content:"\eb3b"}.codicon-check-all:before{content:"\ebb1"}
6309
+ .codicon-add:before{content:"\ea60"}.codicon-git-branch:before,.codicon-source-control:before{content:"\ea68"}.codicon-search:before{content:"\ea6d"}.codicon-edit:before{content:"\ea73"}.codicon-close:before{content:"\ea76"}.codicon-close-all:before{content:"\eac1"}.codicon-cloud-upload:before{content:"\eac3"}.codicon-file:before{content:"\ea7b"}.codicon-new-file:before{content:"\ea7f"}.codicon-new-folder:before{content:"\ea80"}.codicon-folder:before{content:"\ea83"}.codicon-trash:before{content:"\ea81"}.codicon-code:before{content:"\eac4"}.codicon-check:before{content:"\eab2"}.codicon-chevron-down:before{content:"\eab4"}.codicon-chevron-right:before{content:"\eab6"}.codicon-color-mode:before{content:"\eac6"}.codicon-diff:before,.codicon-diff-sidebyside:before{content:"\eae1"}.codicon-diff-single:before{content:"\ec22"}.codicon-discard:before{content:"\eae2"}.codicon-file-code:before{content:"\eae9"}.codicon-file-media:before{content:"\eaea"}.codicon-file-pdf:before{content:"\eaeb"}.codicon-file-zip:before{content:"\eaef"}.codicon-files:before{content:"\eaf0"}.codicon-folder-opened:before{content:"\eaf7"}.codicon-git-commit:before{content:"\eafc"}.codicon-graph:before{content:"\eb03"}.codicon-home:before{content:"\eb06"}.codicon-list-tree:before{content:"\eb86"}.codicon-preview:before{content:"\eb2f"}.codicon-refresh:before{content:"\eb37"}.codicon-remove:before{content:"\eb3b"}.codicon-check-all:before{content:"\ebb1"}
6003
6310
  .codicon-layout-sidebar-right:before{content:"\ebf4"}.codicon-layout-sidebar-right-off:before{content:"\ec00"}.codicon-settings:before{content:"\eb51"}.codicon-sparkle:before{content:"\ec10"}
6004
- .dvw-launch{display:flex;align-items:center;gap:10px;width:100%;height:36px;padding:0 9px;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-secondary,#aaa);cursor:pointer;font:inherit}.dvw-launch:hover{background:var(--dsw-alias-interactive-bg-hover,#292929);color:var(--dsw-alias-label-primary,#fff)}.dvw-launch-icon{width:22px;font-size:19px}
6311
+ .dvw-launch{display:flex;align-items:center;gap:10px;width:100%;height:36px;padding:0 9px;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-secondary,#aaa);cursor:pointer;font:inherit}.dvw-launch:hover{background:var(--dsw-alias-interactive-bg-hover,#292929);color:var(--dsw-alias-label-primary,#fff)}.dvw-launcher-mount{position:fixed;z-index:20;right:16px;bottom:16px}.dvw-launcher-mount .dvw-launch{width:auto;height:32px;padding:0 12px;justify-content:center;border:1px solid var(--dsw-alias-border-l3,#e5e7eb);border-radius:6px;background:var(--dsw-alias-bg-layer-1,#fff);box-shadow:0 1px 2px rgba(0,0,0,.06);color:var(--dsw-alias-label-primary,#333)}body[data-ds-dark-theme] .dvw-launcher-mount .dvw-launch{color:var(--dsw-alias-label-primary,#ddd)}@media(max-width:640px){.dvw-launcher-mount{right:8px;bottom:8px}.dvw-launcher-mount .dvw-launch{height:30px;padding:0 10px;font-size:12px}}
6005
6312
  .dvw-overlay{--dvw-side-width:210px;--dvw-base:var(--dsw-alias-bg-base,#181818);--dvw-surface-1:var(--dsw-alias-bg-layer-1,#1e1e1e);--dvw-surface-2:var(--dsw-alias-bg-layer-2,#252526);--dvw-surface-3:var(--dsw-alias-bg-layer-3,#2d2d30);--dvw-text:var(--dsw-alias-label-primary,#ccc);--dvw-text-2:var(--dsw-alias-label-secondary,#aaa);--dvw-text-3:var(--dsw-alias-label-tertiary,#858585);--dvw-border:var(--dsw-alias-border-l2,#333);--dvw-border-soft:var(--dsw-alias-border-l3,#292929);--dvw-hover:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.08));--dvw-active:var(--dsw-alias-interactive-bg-active,rgba(255,255,255,.14));--dvw-accent:var(--dsw-alias-state-business-primary,#3b82f6);--dvw-error:var(--dsw-alias-state-error-primary,#f48771);--dvw-success:var(--dsw-alias-state-success-primary,#73c991);--dvw-warn:var(--dsw-alias-state-warn-primary,#d7ba7d);position:absolute;inset:0;z-index:100;display:grid;grid-template-columns:48px var(--dvw-side-width) minmax(0,1fr);grid-template-rows:minmax(0,1fr) auto 24px;background:var(--dvw-base);color:var(--dvw-text);pointer-events:auto;font:13px/1.4 var(--dsw-font-family,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif);overflow:hidden;color-scheme:light}
6006
6313
  body[data-ds-dark-theme] .dvw-overlay,.dvw-overlay[data-dvw-theme=dark]{color-scheme:dark}
6007
6314
  .dvw-overlay[data-dvw-theme=dark]{--dvw-base:#151517;--dvw-surface-1:#232324;--dvw-surface-2:#2c2c2e;--dvw-surface-3:#353638;--dvw-text:#f9fafb;--dvw-text-2:#cfd3d6;--dvw-text-3:#adb2b8;--dvw-border:#ffffff1f;--dvw-border-soft:#ffffff29;--dvw-hover:#ffffff14;--dvw-active:#ffffff24;--dvw-accent:#679efe;--dvw-error:#f25a5a;--dvw-success:#22c55e;--dvw-warn:#f59e0b}
6008
6315
  .dvw-overlay[data-dvw-theme=light]{--dvw-base:#fff;--dvw-surface-1:#fff;--dvw-surface-2:#fff;--dvw-surface-3:#fff;--dvw-text:#0f1115;--dvw-text-2:#61666b;--dvw-text-3:#81858c;--dvw-border:#0000001a;--dvw-border-soft:#0000001f;--dvw-hover:#2631480f;--dvw-active:#2631481a;--dvw-accent:#4176e6;--dvw-error:#ec1313;--dvw-success:#22c55e;--dvw-warn:#f59e0b;color-scheme:light}
6009
- .dvw-overlay *{box-sizing:border-box}.dvw-activity{grid-row:1/3;background:var(--dvw-base);border-right:1px solid var(--dvw-border-soft);display:flex;flex-direction:column;align-items:center}.dvw-act{width:48px;height:48px;border:0;border-left:2px solid transparent;background:transparent;color:var(--dvw-text-3);font-size:22px;cursor:pointer}.dvw-act:hover,.dvw-act[data-active=true]{color:var(--dvw-text)}.dvw-act[data-active=true]{border-left-color:var(--dvw-accent)}.dvw-act.dsh{margin-top:auto}.dvw-side{grid-column:2;grid-row:1/3;min-width:0;width:100%;background:var(--dvw-surface-1);border-right:1px solid var(--dvw-border);display:flex;flex-direction:column}.dvw-side-resizer{grid-column:2;grid-row:1/3;justify-self:end;width:7px;height:100%;z-index:4;cursor:ew-resize;transform:translateX(3px);touch-action:none}.dvw-side-resizer:hover,.dvw-side-resizer:active{background:var(--dvw-accent)}.dvw-side-head{height:36px;display:flex;align-items:center;padding:0 12px;text-transform:uppercase;font-size:11px;letter-spacing:.4px}.dvw-toolbar{margin-left:auto;display:flex;gap:2px}.dvw-icon{border:0;background:transparent;color:var(--dvw-text-2);border-radius:4px;min-width:24px;height:24px;display:inline-grid;place-items:center;cursor:pointer}.dvw-icon:hover{background:var(--dvw-hover);color:var(--dvw-text)}.dvw-icon:disabled{opacity:.45;cursor:default}.dvw-tree,.dvw-results,.dvw-git{overflow:auto;flex:1}.dvw-tree-row{display:flex;align-items:center;height:24px;padding-right:6px;cursor:pointer;white-space:nowrap}.dvw-tree-row:hover{background:var(--dvw-hover)}.dvw-tree-row[data-selected=true]{background:var(--dvw-active)}.dvw-chevron{width:16px;text-align:center;color:var(--dvw-text-3)}.dvw-file-icon{width:18px;color:var(--dvw-text-2)}.dvw-file-icon.folder{color:var(--dvw-accent)}
6316
+ .dvw-overlay *{box-sizing:border-box}.dvw-activity{grid-row:1/3;background:var(--dvw-base);border-right:1px solid var(--dvw-border-soft);display:flex;flex-direction:column;align-items:center}.dvw-act{width:48px;height:48px;border:0;border-left:2px solid transparent;background:transparent;color:var(--dvw-text-3);font-size:22px;cursor:pointer}.dvw-act:hover,.dvw-act[data-active=true]{color:var(--dvw-text)}.dvw-act[data-active=true]{border-left-color:var(--dvw-accent)}.dvw-act.dsh{margin-top:auto}.dvw-side{grid-column:2;grid-row:1/3;min-width:0;width:100%;background:var(--dvw-surface-1);border-right:1px solid var(--dvw-border);display:flex;flex-direction:column}.dvw-side-resizer{grid-column:2;grid-row:1/3;justify-self:end;width:7px;height:100%;z-index:4;cursor:ew-resize;transform:translateX(3px);touch-action:none}.dvw-side-resizer:hover,.dvw-side-resizer:active{background:var(--dvw-accent)}.dvw-side-head{height:36px;display:flex;align-items:center;padding:0 12px;text-transform:uppercase;font-size:11px;letter-spacing:.4px}.dvw-toolbar{margin-left:auto;display:flex;gap:2px}.dvw-icon{border:0;background:transparent;color:var(--dvw-text-2);border-radius:4px;min-width:24px;height:24px;display:inline-grid;place-items:center;cursor:pointer}.dvw-icon:hover{background:var(--dvw-hover);color:var(--dvw-text)}.dvw-icon:disabled{opacity:.45;cursor:default}.dvw-file-picker{display:none}.dvw-tree,.dvw-results,.dvw-git{overflow:auto;flex:1}.dvw-tree[data-drop-target=true]{outline:1px dashed var(--dvw-accent);outline-offset:-3px}.dvw-tree-row{display:flex;align-items:center;height:24px;padding-right:6px;cursor:pointer;white-space:nowrap}.dvw-tree-row:hover{background:var(--dvw-hover)}.dvw-tree-row[data-selected=true],.dvw-tree-row[data-drop-target=true]{background:var(--dvw-active)}.dvw-chevron{width:16px;text-align:center;color:var(--dvw-text-3)}.dvw-file-icon{width:18px;color:var(--dvw-text-2)}.dvw-file-icon.folder{color:var(--dvw-accent)}.dvw-tree-name{overflow:hidden;text-overflow:ellipsis}.dvw-tree-rename{min-width:0;flex:1;height:20px;border:1px solid var(--dvw-accent);background:var(--dvw-surface-1);color:var(--dvw-text);font:inherit;outline:0}.dvw-tree-status{margin-left:auto;padding-left:6px;font-size:11px;font-weight:600;color:var(--dvw-warn)}.dvw-tree-status[data-status=M]{color:var(--dvw-warn)}.dvw-tree-status[data-status=A]{color:var(--dvw-success)}.dvw-tree-status[data-status=D]{color:var(--dvw-error)}.dvw-context-menu{position:fixed;z-index:40;min-width:148px;padding:4px;border:1px solid var(--dvw-border);border-radius:5px;background:var(--dvw-surface-3);box-shadow:0 6px 20px rgba(0,0,0,.3)}.dvw-context-menu button{width:100%;height:28px;border:0;border-radius:3px;background:transparent;color:var(--dvw-text);display:flex;align-items:center;gap:8px;padding:0 8px;text-align:left;font:inherit;cursor:pointer}.dvw-context-menu button:hover{background:var(--dvw-hover)}.dvw-context-menu .dvw-context-delete{color:var(--dvw-error)}
6010
6317
  .dvw-main{grid-column:3;grid-row:1;display:flex;min-width:0;min-height:0;flex-direction:column;background:var(--dvw-surface-1)}.dvw-tabs{height:36px;background:var(--dvw-base);display:flex;overflow-x:auto}.dvw-tab{height:36px;min-width:110px;max-width:260px;border:0;border-right:1px solid var(--dvw-border-soft);background:var(--dvw-base);color:var(--dvw-text-2);padding:0 8px 0 12px;display:flex;align-items:center;gap:8px;cursor:pointer}.dvw-tab[data-active=true]{background:var(--dvw-surface-1);color:var(--dvw-text);border-top:1px solid var(--dvw-accent)}.dvw-tab-name{overflow:hidden;text-overflow:ellipsis;flex:1;text-align:left}.dvw-editor{position:relative;flex:1;min-height:0}.dvw-editor-toolbar{position:absolute;z-index:3;top:0;right:0;left:0;height:36px;display:flex;align-items:center;justify-content:flex-end;gap:4px;padding:0 10px;border-bottom:1px solid var(--dvw-border-soft);background:var(--dvw-surface-1)}.dvw-editor-body{position:absolute;inset:0;min-height:0}.dvw-editor-body[data-toolbar=true]{top:36px}.dvw-mode-button{height:27px;padding:0 10px;display:flex;align-items:center;gap:6px;border:1px solid transparent;border-radius:5px;background:transparent;color:var(--dvw-text-2);cursor:pointer}.dvw-mode-button:hover{background:var(--dvw-hover)}.dvw-mode-button[data-active=true]{border-color:var(--dvw-border);background:var(--dvw-active);color:var(--dvw-text)}.dvw-empty{height:100%;display:grid;place-items:center;color:var(--dvw-text-3);text-align:center}.dvw-empty strong{font-size:24px;color:var(--dvw-text-2)}.dvw-message{padding:12px;color:var(--dvw-warn)}
6011
6318
  .dvw-bottom{grid-column:3;grid-row:2;height:260px;min-height:120px;border-top:1px solid var(--dvw-border);background:var(--dvw-base);display:flex;flex-direction:column}.dvw-bottom-head{height:35px;display:flex;align-items:center;gap:14px;padding:0 12px}.dvw-bottom-title{border:0;border-bottom:1px solid var(--dvw-text);background:transparent;color:var(--dvw-text);height:35px}.dvw-bottom-body{flex:1;min-height:0}.dvw-status{grid-column:1/4;grid-row:3;background:var(--dvw-accent);color:#fff;display:flex;align-items:center;padding:0 8px;gap:18px;font-size:12px}.dvw-theme-control{position:relative;margin-left:auto}.dvw-theme-trigger{height:22px;min-width:112px;display:flex;align-items:center;gap:7px;padding:0 8px;border:0;border-radius:5px;background:color-mix(in srgb,#000 17%,transparent);color:#fff;cursor:pointer}.dvw-theme-trigger:hover{background:color-mix(in srgb,#000 27%,transparent)}.dvw-theme-orb{width:13px;height:13px;border:2px solid #fff;border-radius:50%;background:linear-gradient(90deg,#17181c 50%,#fff 50%)}.dvw-theme-orb.dark{background:#17181c}.dvw-theme-orb.light{background:#fff}.dvw-theme-menu{position:absolute;right:0;bottom:29px;width:224px;padding:6px;border:1px solid var(--dvw-border);border-radius:10px;background:var(--dvw-surface-3);box-shadow:0 10px 35px rgba(0,0,0,.28);color:var(--dvw-text)}.dvw-theme-menu button{width:100%;min-height:48px;border:0;border-radius:7px;background:transparent;color:inherit;display:grid;grid-template-columns:30px 1fr 20px;align-items:center;text-align:left;padding:6px;cursor:pointer}.dvw-theme-menu button:hover,.dvw-theme-menu button[aria-checked=true]{background:var(--dvw-hover)}.dvw-theme-menu strong,.dvw-theme-menu small{display:block}.dvw-theme-menu small{color:var(--dvw-text-3);margin-top:2px}.dvw-theme-swatch{width:22px;height:22px;border-radius:6px;border:1px solid var(--dvw-border);background:linear-gradient(135deg,#17181c 0 50%,#fff 50%)}.dvw-theme-swatch.dark{background:#17181c}.dvw-theme-swatch.light{background:#fff}
6012
6319
  .dvw-search-form{padding:8px;display:grid;gap:6px}.dvw-input,.dvw-textarea,.dvw-select{width:100%;border:1px solid var(--dvw-border);background:var(--dvw-surface-2);color:var(--dvw-text);padding:5px 7px;outline:none}.dvw-input:focus,.dvw-textarea:focus,.dvw-select:focus{border-color:var(--dvw-accent)}.dvw-options{display:flex;align-items:center;gap:8px;font-size:11px}.dvw-result-file{padding:5px 8px;font-weight:600}.dvw-result{padding:4px 8px 4px 20px;cursor:pointer;display:flex;gap:8px}.dvw-result:hover{background:var(--dvw-hover)}.dvw-line{color:var(--dvw-text-3);min-width:30px;text-align:right}.dvw-preview{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
6013
6320
  .dvw-git-head{padding:7px 10px;border-bottom:1px solid var(--dvw-border);display:flex;align-items:center;gap:8px}.dvw-branch{min-width:0;border:0;background:transparent;color:var(--dvw-text);font:inherit;font-weight:600;display:flex;align-items:center;gap:5px;cursor:pointer}.dvw-commit{display:grid;grid-template-columns:1fr auto;gap:6px;padding:9px}.dvw-commit .dvw-input{grid-column:1/3}.dvw-commit-button{grid-column:1/3;height:31px;border:0;border-radius:6px;background:color-mix(in srgb,var(--dvw-accent) 24%,var(--dvw-surface-2));color:var(--dvw-text);display:flex;align-items:center;justify-content:center;gap:7px;cursor:pointer}.dvw-commit-button:hover{background:color-mix(in srgb,var(--dvw-accent) 34%,var(--dvw-surface-2))}.dvw-commit-button:disabled{opacity:.45;cursor:default}.dvw-group-head{height:30px;display:flex;align-items:center;gap:5px;padding:0 6px 0 9px;border-radius:5px;font-weight:600;position:sticky;top:0;background:var(--dvw-surface-1);z-index:1}.dvw-group-head:hover{background:var(--dvw-hover)}.dvw-group-head .dvw-count{margin-left:auto;min-width:20px;height:20px;border-radius:10px;display:grid;place-items:center;background:var(--dvw-surface-3);color:var(--dvw-text-2);font-size:11px}.dvw-change{display:flex;align-items:center;min-height:28px;padding:3px 5px 3px 19px;gap:5px;cursor:pointer}.dvw-change:hover{background:var(--dvw-hover)}.dvw-change .dvw-icon{opacity:0}.dvw-change:hover .dvw-icon,.dvw-change:focus-within .dvw-icon{opacity:1}.dvw-change-path{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.dvw-badge{font-size:11px;color:var(--dvw-warn)}.dvw-graph{border-top:1px solid var(--dvw-border-soft);margin-top:8px;padding-top:3px}.dvw-graph-list{padding-bottom:8px}.dvw-graph-row{width:100%;height:38px;border:0;background:transparent;color:var(--dvw-text);display:flex;align-items:center;padding:0 7px;text-align:left;cursor:pointer;overflow:hidden}.dvw-graph-row:hover{background:var(--dvw-hover)}.dvw-graph-svg{flex:none;align-self:stretch}.dvw-graph-copy{display:flex;min-width:0;flex:1;flex-direction:column;justify-content:center}.dvw-graph-subject{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dvw-graph-meta{font-size:11px;color:var(--dvw-text-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dvw-ref{max-width:82px;margin-left:4px;padding:1px 5px;border:1px solid color-mix(in srgb,var(--dvw-accent) 50%,transparent);border-radius:9px;color:var(--dvw-accent);font-size:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dvw-error{padding:8px;color:var(--dvw-error);white-space:pre-wrap}.dvw-diff{margin:0;height:100%;overflow:auto;padding:8px 0;background:var(--dvw-surface-1);font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre}.dvw-diff-line{display:block;min-height:18px;padding:0 14px}.dvw-diff-line.add{background:color-mix(in srgb,var(--dvw-success) 15%,transparent);color:var(--dvw-success)}.dvw-diff-line.remove{background:color-mix(in srgb,var(--dvw-error) 13%,transparent);color:var(--dvw-error)}.dvw-diff-line.hunk{background:color-mix(in srgb,var(--dvw-accent) 12%,transparent);color:var(--dvw-accent)}.dvw-diff-line.meta{color:var(--dvw-text-3)}.dvw-diff-empty{padding:24px;color:var(--dvw-text-3)}
6014
6321
  .dvw-tabs{overflow:hidden}.dvw-tab-list{display:flex;min-width:0;flex:1;overflow-x:auto}.dvw-tab-actions{flex:none;width:42px;display:grid;place-items:center;padding:0 6px;background:var(--dvw-base)}.dvw-close-all{border:1px solid var(--dvw-border);background:var(--dvw-base)}.dvw-monaco-diff{position:absolute;inset:0}.dvw-diff-toolbar{justify-content:flex-end}.dvw-group-head{gap:2px;padding-left:2px}.dvw-group-toggle{min-width:0;height:30px;flex:1;border:0;border-radius:5px;background:transparent;color:var(--dvw-text);font:inherit;font-weight:600;display:flex;align-items:center;gap:5px;padding:0 7px;cursor:pointer;text-align:left}.dvw-group-toggle:hover{background:var(--dvw-hover)}.dvw-graph-entry[data-expanded=true]{background:color-mix(in srgb,var(--dvw-accent) 5%,transparent)}.dvw-commit-files{padding:1px 0 5px 26px}.dvw-commit-file{width:100%;height:27px;border:0;background:transparent;color:var(--dvw-text);display:flex;align-items:center;gap:6px;padding:0 7px;text-align:left;cursor:pointer}.dvw-commit-file:hover{background:var(--dvw-hover)}.dvw-commit-file-name{white-space:nowrap}.dvw-commit-file-dir{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dvw-text-3);font-size:11px}
6015
- .dvw-sidebar-launcher-mount{flex:none;padding:4px 8px 6px;border-bottom:1px solid var(--dvw-border-soft)}.dvw-sidebar-launcher-mount .dvw-launch{color:var(--dsw-alias-label-primary,#333)}body[data-ds-dark-theme] .dvw-sidebar-launcher-mount .dvw-launch{color:var(--dsw-alias-label-primary,#ddd)}.dvw-outline{flex:none;max-height:38%;display:flex;flex-direction:column;border-bottom:1px solid var(--dvw-border)}.dvw-outline-head{height:30px;display:flex;align-items:center;justify-content:space-between;padding:0 5px 0 10px;font-weight:600}.dvw-outline-head>span{display:flex;align-items:center;gap:6px}.dvw-outline-list{overflow:auto;padding-bottom:5px}.dvw-outline-item{display:block;width:100%;height:25px;border:0;background:transparent;color:var(--dvw-text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left;cursor:pointer}.dvw-outline-item:hover{background:var(--dvw-hover);color:var(--dvw-text)}.dvw-image-preview{position:absolute;inset:0;overflow:auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;padding:24px;background:repeating-conic-gradient(var(--dvw-surface-2) 0 25%,var(--dvw-surface-1) 0 50%) 0/20px 20px}.dvw-image-preview img{max-width:100%;max-height:calc(100% - 32px);object-fit:contain;box-shadow:0 4px 24px rgba(0,0,0,.22)}.dvw-image-preview span{max-width:100%;padding:3px 8px;border-radius:5px;background:var(--dvw-surface-1);color:var(--dvw-text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
6322
+ .dvw-outline{flex:none;max-height:38%;display:flex;flex-direction:column;border-bottom:1px solid var(--dvw-border)}.dvw-outline-head{height:30px;display:flex;align-items:center;justify-content:space-between;padding:0 5px 0 10px;font-weight:600}.dvw-outline-head>span{display:flex;align-items:center;gap:6px}.dvw-outline-list{overflow:auto;padding-bottom:5px}.dvw-outline-item{display:block;width:100%;height:25px;border:0;background:transparent;color:var(--dvw-text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left;cursor:pointer}.dvw-outline-item:hover{background:var(--dvw-hover);color:var(--dvw-text)}.dvw-image-preview{position:absolute;inset:0;overflow:auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;padding:24px;background:repeating-conic-gradient(var(--dvw-surface-2) 0 25%,var(--dvw-surface-1) 0 50%) 0/20px 20px}.dvw-image-preview img{max-width:100%;max-height:calc(100% - 32px);object-fit:contain;box-shadow:0 4px 24px rgba(0,0,0,.22)}.dvw-image-preview span{max-width:100%;padding:3px 8px;border-radius:5px;background:var(--dvw-surface-1);color:var(--dvw-text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
6016
6323
  .dvw-markdown{height:100%;overflow:auto;padding:28px clamp(24px,6vw,80px) 60px;color:var(--dvw-text);font-size:15px;line-height:1.65}.dvw-markdown>*:first-child{margin-top:0}.dvw-markdown h1,.dvw-markdown h2{padding-bottom:.3em;border-bottom:1px solid var(--dvw-border)}.dvw-markdown h1{font-size:2em}.dvw-markdown h2{font-size:1.5em}.dvw-markdown h3{font-size:1.25em}.dvw-markdown a{color:var(--dvw-accent)}.dvw-markdown code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;background:var(--dvw-surface-2);border-radius:4px;padding:.15em .35em}.dvw-markdown pre{overflow:auto;padding:14px;border:1px solid var(--dvw-border-soft);border-radius:7px;background:var(--dvw-base)}.dvw-markdown pre code{padding:0;background:transparent}.dvw-markdown blockquote{margin-left:0;padding-left:14px;border-left:3px solid var(--dvw-border);color:var(--dvw-text-2)}.dvw-markdown table{border-collapse:collapse;display:block;overflow:auto}.dvw-markdown th,.dvw-markdown td{padding:6px 12px;border:1px solid var(--dvw-border)}.dvw-markdown tr:nth-child(2n){background:var(--dvw-hover)}.dvw-markdown img{max-width:100%}.dvw-markdown hr{border:0;border-top:1px solid var(--dvw-border)}
6017
6324
  .dvw-sidebar-launcher-mount{border-bottom:1px solid var(--dsw-alias-border-l3,#e5e7eb)}.dvw-outline-toggle{margin-right:auto}.dvw-outline-toggle[data-active=true]{background:var(--dvw-active);color:var(--dvw-text)}.dvw-editor-outline{position:absolute;z-index:2;top:36px;bottom:0;left:0;width:240px;max-height:none;background:var(--dvw-surface-1);border-right:1px solid var(--dvw-border);border-bottom:0}.dvw-editor[data-outline=true] .dvw-editor-body{left:240px}.dvw-diff-preview-switch{margin-right:auto;display:flex;gap:4px}.dvw-commit-files{position:relative;padding-top:1px;padding-right:0;padding-bottom:5px}.dvw-graph-continuation{position:absolute;top:0;bottom:0;left:7px;height:100%;pointer-events:none}.dvw-commit-file-list{position:relative;min-width:0}.dvw-overlay button:focus-visible,.dvw-overlay input:focus-visible,.dvw-overlay select:focus-visible{outline:1px solid var(--dvw-accent);outline-offset:-1px}@media(max-width:800px){.dvw-overlay{grid-template-columns:44px var(--dvw-side-width) minmax(0,1fr)}.dvw-editor-outline{width:210px}.dvw-editor[data-outline=true] .dvw-editor-body{left:210px}}
6018
6325
  .dvw-overlay[data-dvw-chat=true]{grid-template-columns:48px var(--dvw-side-width) minmax(0,1fr) var(--dvw-chat-width)}
@@ -6024,12 +6331,14 @@ body[data-ds-dark-theme] .dvw-overlay,.dvw-overlay[data-dvw-theme=dark]{color-sc
6024
6331
  .dvw-chat{grid-column:4;grid-row:1/3;min-width:0;background:var(--dvw-base);border-left:1px solid var(--dvw-border);position:relative}
6025
6332
  .dvw-chat-head{height:36px;display:flex;align-items:center;justify-content:space-between;padding:0 6px 0 12px;border-bottom:1px solid var(--dvw-border);background:var(--dvw-base);font-weight:600}
6026
6333
  .dvw-chat-head>span{display:flex;align-items:center;gap:7px}.dvw-chat-body{height:calc(100% - 36px);background:var(--dvw-base)}
6027
- .dvw-dsh-mark{min-width:18px;height:18px;display:inline-grid;place-items:center;color:currentColor;font-size:0}.dvw-dsh-mark svg{display:block}
6334
+ .dvw-dsh-mark{display:block}
6028
6335
  .dvw-chat-resizer{position:fixed;top:0;right:calc(var(--dvw-chat-width) - 4px);bottom:0;width:8px;z-index:4;cursor:ew-resize;touch-action:none}
6029
6336
  .dvw-chat-resizer:hover,.dvw-chat-resizer:active{background:var(--dvw-accent)}
6030
6337
  .dvw-chat-toggle{position:absolute;z-index:5;top:6px;right:6px;border:1px solid var(--dvw-border);background:var(--dvw-base)}
6031
6338
  .dvw-overlay:not([data-dvw-chat=true]) .dvw-tabs{padding-right:36px}
6032
6339
  [data-dvw-conversation-panel]{position:fixed!important;top:36px!important;right:0!important;bottom:var(--dvw-status-height,24px)!important;left:auto!important;width:var(--dvw-chat-width)!important;height:auto!important;min-width:0!important;z-index:21!important;border-left:1px solid var(--dsw-alias-border-l2)!important;box-shadow:none!important}
6340
+ .dvw-confirm-backdrop{position:fixed;z-index:50;inset:0;display:grid;place-items:center;padding:24px;background:rgba(0,0,0,.38)}.dvw-confirm{width:min(380px,calc(100vw - 48px));border:1px solid var(--dvw-border);border-radius:7px;background:var(--dvw-surface-1);box-shadow:0 12px 36px rgba(0,0,0,.34);padding:18px}.dvw-confirm h2{margin:0 0 12px;font-size:15px}.dvw-confirm p{margin:0;color:var(--dvw-text)}.dvw-confirm-note{margin-top:8px!important;color:var(--dvw-text-3)!important;font-size:12px}.dvw-confirm footer{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}.dvw-confirm footer button{height:28px;border:1px solid var(--dvw-border);border-radius:4px;background:var(--dvw-surface-2);color:var(--dvw-text);padding:0 12px;font:inherit;cursor:pointer}.dvw-confirm footer button:hover{background:var(--dvw-hover)}.dvw-confirm footer .dvw-danger-button{border-color:color-mix(in srgb,var(--dvw-error) 65%,var(--dvw-border));background:var(--dvw-error);color:#fff}.dvw-confirm footer .dvw-danger-button:hover{background:color-mix(in srgb,var(--dvw-error) 82%,#000)}
6341
+ .dvw-tree[data-dvw-native-drop-target=true]{outline:1px dashed var(--dvw-accent);outline-offset:-3px}.dvw-tree-row[data-dvw-native-drop-target=true]{background:var(--dvw-active)}
6033
6342
  .dvw-settings-backdrop{position:absolute;inset:0;z-index:30;display:grid;place-items:center;padding:24px;background:rgba(0,0,0,.34);backdrop-filter:blur(2px)}
6034
6343
  .dvw-settings{width:min(720px,calc(100vw - 48px));max-height:min(720px,calc(100vh - 48px));display:flex;flex-direction:column;border:1px solid var(--dvw-border);border-radius:10px;background:var(--dvw-surface-1);box-shadow:0 16px 50px rgba(0,0,0,.32);overflow:hidden}
6035
6344
  .dvw-settings>header{height:44px;flex:none;display:flex;align-items:center;justify-content:space-between;padding:0 10px 0 14px;border-bottom:1px solid var(--dvw-border);font-size:14px;font-weight:600}.dvw-settings>header>span{display:flex;align-items:center;gap:8px}
@@ -6075,33 +6384,18 @@ body[data-ds-dark-theme] .dvw-overlay,.dvw-overlay[data-dvw-theme=dark]{color-sc
6075
6384
  useSessions
6076
6385
  }, sessionId ?? "no-session");
6077
6386
  };
6078
- const Footer = ({ service, wide }) => {
6079
- const marker = (0, react.useRef)(null);
6387
+ const Footer = ({ service }) => {
6080
6388
  const [target, setTarget] = (0, react.useState)();
6389
+ const [visible, setVisible] = (0, react.useState)(() => service.getSnapshot().visible);
6081
6390
  (0, react.useLayoutEffect)(() => {
6082
- let container = marker.current?.parentElement;
6083
- while (container?.previousElementSibling === null) container = container.parentElement;
6084
- const region = container?.previousElementSibling;
6085
- if (!(region instanceof HTMLElement)) {
6086
- setTarget(null);
6087
- return;
6088
- }
6089
6391
  const mount = document.createElement("div");
6090
- mount.className = "dvw-sidebar-launcher-mount";
6091
- region.prepend(mount);
6392
+ mount.className = "dvw-launcher-mount";
6393
+ document.body.append(mount);
6092
6394
  setTarget(mount);
6093
6395
  return () => mount.remove();
6094
6396
  }, []);
6095
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6096
- ref: marker,
6097
- hidden: true
6098
- }), target === void 0 ? null : target === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Launcher, {
6099
- service,
6100
- wide
6101
- }) : (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Launcher, {
6102
- service,
6103
- wide
6104
- }), target)] });
6397
+ (0, react.useEffect)(() => service.subscribe(() => setVisible(service.getSnapshot().visible)), [service]);
6398
+ return target === void 0 || visible ? null : (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Launcher, { service }), target);
6105
6399
  };
6106
6400
  const inject = ["slots"];
6107
6401
  function apply(ctx) {