@pablopunk/webr 0.3.8 → 0.3.10

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.
@@ -1,7 +1,7 @@
1
1
  import { S as createAstro, f as renderHead, i as renderComponent, m as createRenderInstruction, p as addAttribute, s as renderSlot, u as renderTemplate } from "./server_dfdLOyV2.mjs";
2
2
  import { t as createComponent } from "./compiler_BnrQvXXD.mjs";
3
3
  import { ViewTransition, createContext, memo, useCallback, useContext, useDeferredValue, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
- import { Archive, ArchiveRestore, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Bot, Check, ChevronDown, ChevronRight, Circle, CircleAlert, CircleCheck, CircleHelp, Command, Copy, Cpu, Folder, FolderTree, GitBranch, ImagePlus, LayoutList, LoaderCircle, MessageCircleQuestion, Mic, Monitor, Moon, PanelLeft, PanelLeftClose, PanelLeftOpen, Pencil, Plus, Search, Server, Settings2, Square, SquarePen, SquareTerminal, Sun, TerminalSquare, Trash2, TriangleAlert, X } from "lucide-react";
4
+ import { Archive, ArchiveRestore, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Bot, Check, ChevronDown, ChevronRight, Circle, CircleAlert, CircleCheck, CircleHelp, Command, Copy, Cpu, FileText, Folder, FolderTree, GitBranch, ImagePlus, ImageUp, LayoutList, LoaderCircle, MessageCircleQuestion, Mic, Monitor, Moon, PanelLeft, PanelLeftClose, PanelLeftOpen, Pencil, Plus, Search, Server, Settings2, Square, SquarePen, SquareTerminal, Sun, TerminalSquare, Trash2, TriangleAlert, X } from "lucide-react";
5
5
  import { createRoot } from "react-dom/client";
6
6
  import { AlertDialog } from "@base-ui/react/alert-dialog";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -337,6 +337,17 @@ var renameProject = (project, name) => post$1("/api/projects/rename", {
337
337
  logicalId: project.logicalId ?? project.id,
338
338
  name
339
339
  });
340
+ var projectIconUrl = (project) => `/api/projects/icon?machineId=${encodeURIComponent(project.machineId ?? "local")}&logicalId=${encodeURIComponent(project.logicalId ?? project.id)}`;
341
+ async function setProjectIcon(project, png) {
342
+ if (!(await fetch(projectIconUrl(project), {
343
+ method: "PUT",
344
+ headers: { "Content-Type": "image/png" },
345
+ body: png
346
+ })).ok) throw new Error("Could not save this icon.");
347
+ }
348
+ async function resetProjectIcon(project) {
349
+ if (!(await fetch(projectIconUrl(project), { method: "DELETE" })).ok) throw new Error("Could not reset this icon.");
350
+ }
340
351
  var openThreadTerminal = (thread, afterPaneId) => post$1(`/api/threads/${encodeURIComponent(thread.id)}/panes/terminal`, {
341
352
  machineId: thread.machineId,
342
353
  afterPaneId
@@ -816,12 +827,16 @@ var attachableTypes = [
816
827
  ];
817
828
  var uploadErrors = {
818
829
  image_too_large: "This image is larger than 20 MB.",
830
+ file_too_large: "This file is larger than 5 MB.",
831
+ empty_file: "This file is empty.",
819
832
  unsupported_image: "Only PNG, JPEG, GIF and WebP images can be attached.",
820
- uploads_unsupported_target: "Images can be attached only on the Local machine for now.",
833
+ uploads_unsupported_target: "Files cannot be attached on this machine yet.",
834
+ upload_failed: "The file could not be sent to the machine.",
821
835
  machine_disconnected: "The machine is not connected."
822
836
  };
823
837
  var isAttachableImage = (file) => attachableTypes.includes(file.type);
824
838
  var imageFilesFrom = (data) => data?.files ? [...data.files].filter(isAttachableImage) : [];
839
+ var filesFrom = (data) => data?.files ? [...data.files] : [];
825
840
  var carriesFiles = (data) => !!data?.types && [...data.types].includes("Files");
826
841
  var escapePathForTerminal = (path) => path.replace(/([\s\\'"()&;$`!*?[\]{}<>|#~])/g, "\\$1");
827
842
  async function uploadImage(machineId, file) {
@@ -836,6 +851,18 @@ async function uploadImage(machineId, file) {
836
851
  if (!response.ok || !result.path) throw new Error(uploadErrors[result.error ?? ""] ?? "The image could not be attached.");
837
852
  return result.path;
838
853
  }
854
+ async function uploadFile(machineId, file) {
855
+ if (isAttachableImage(file)) return uploadImage(machineId, file);
856
+ if (file.size > 5242880) throw new Error(uploadErrors.file_too_large);
857
+ const response = await fetch(`/api/uploads?machineId=${encodeURIComponent(machineId)}&name=${encodeURIComponent(file.name)}`, {
858
+ method: "POST",
859
+ headers: { "Content-Type": "application/octet-stream" },
860
+ body: file
861
+ });
862
+ const result = await response.json().catch(() => ({}));
863
+ if (!response.ok || !result.path) throw new Error(uploadErrors[result.error ?? ""] ?? "The file could not be attached.");
864
+ return result.path;
865
+ }
839
866
  var THUMBNAIL_SIZE = 160;
840
867
  var readAsDataUrl = (file) => new Promise((done, fail) => {
841
868
  const reader = new FileReader();
@@ -854,9 +881,9 @@ async function thumbnailDataUrl(file) {
854
881
  bitmap.close();
855
882
  return canvas.toDataURL("image/jpeg", .8);
856
883
  }
857
- async function uploadImagesAsTerminalText(machineId, files) {
884
+ async function uploadFilesAsTerminalText(machineId, files) {
858
885
  const paths = [];
859
- for (const file of files) paths.push(await uploadImage(machineId, file));
886
+ for (const file of files) paths.push(await uploadFile(machineId, file));
860
887
  return paths.map(escapePathForTerminal).join(" ") + " ";
861
888
  }
862
889
  //#endregion
@@ -872,7 +899,8 @@ function uuid() {
872
899
  }
873
900
  //#endregion
874
901
  //#region src/components/usePromptImages.ts
875
- var ONLY_IMAGES$1 = "Only PNG, JPEG, GIF and WebP images can be attached.";
902
+ var NO_FILES$1 = "Nothing to attach.";
903
+ var previewOf = (file) => isAttachableImage(file) ? thumbnailDataUrl(file) : Promise.resolve("");
876
904
  var promptWithImages = (prompt, images) => [prompt.trim(), ...images.map((image) => escapePathForTerminal(image.path))].filter(Boolean).join(" ");
877
905
  function usePromptImages({ machineId }) {
878
906
  const [dragging, setDragging] = useState(false);
@@ -882,22 +910,22 @@ function usePromptImages({ machineId }) {
882
910
  const depth = useRef(0);
883
911
  const attach = async (files) => {
884
912
  if (!files.length) {
885
- setError(ONLY_IMAGES$1);
913
+ setError(NO_FILES$1);
886
914
  return;
887
915
  }
888
916
  setError("");
889
917
  setUploading((count) => count + files.length);
890
918
  for (const file of files) try {
891
- const path = await uploadImage(machineId, file);
892
- const preview = await thumbnailDataUrl(file);
919
+ const path = await uploadFile(machineId, file);
920
+ const preview = await previewOf(file);
893
921
  setAttachments((current) => [...current, {
894
922
  id: uuid(),
895
- name: file.name || "image",
923
+ name: file.name || "file",
896
924
  preview,
897
925
  path
898
926
  }]);
899
927
  } catch (cause) {
900
- setError(cause instanceof Error ? cause.message : "The image could not be attached.");
928
+ setError(cause instanceof Error ? cause.message : "The file could not be attached.");
901
929
  } finally {
902
930
  setUploading((count) => count - 1);
903
931
  }
@@ -932,7 +960,7 @@ function usePromptImages({ machineId }) {
932
960
  event.preventDefault();
933
961
  depth.current = 0;
934
962
  setDragging(false);
935
- attach(imageFilesFrom(event.dataTransfer));
963
+ attach(filesFrom(event.dataTransfer));
936
964
  },
937
965
  onPaste: (event) => {
938
966
  const images = imageFilesFrom(event.clipboardData);
@@ -1096,12 +1124,35 @@ function HarnessIcon({ harness, size = 14 }) {
1096
1124
  });
1097
1125
  }
1098
1126
  //#endregion
1127
+ //#region src/components/OsIcon.tsx
1128
+ var paths = {
1129
+ macos: "M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701",
1130
+ linux: "M12.504 0c-.155 0-.315.008-.48.021-4.226.333-3.105 4.807-3.17 6.298-.076 1.092-.3 1.953-1.05 3.02-.885 1.051-2.127 2.75-2.716 4.521-.278.832-.41 1.684-.287 2.489a.424.424 0 00-.11.135c-.26.268-.45.6-.663.839-.199.199-.485.267-.797.4-.313.136-.658.269-.864.68-.09.189-.136.394-.132.602 0 .199.027.4.055.536.058.399.116.728.04.97-.249.68-.28 1.145-.106 1.484.174.334.535.47.94.601.81.2 1.91.135 2.774.6.926.466 1.866.67 2.616.47.526-.116.97-.464 1.208-.946.587-.003 1.23-.269 2.26-.334.699-.058 1.574.267 2.577.2.025.134.063.198.114.333l.003.003c.391.778 1.113 1.132 1.884 1.071.771-.06 1.592-.536 2.257-1.306.631-.765 1.683-1.084 2.378-1.503.348-.199.629-.469.649-.853.023-.4-.2-.811-.714-1.376v-.097l-.003-.003c-.17-.2-.25-.535-.338-.926-.085-.401-.182-.786-.492-1.046h-.003c-.059-.054-.123-.067-.188-.135a.357.357 0 00-.19-.064c.431-1.278.264-2.55-.173-3.694-.533-1.41-1.465-2.638-2.175-3.483-.796-1.005-1.576-1.957-1.56-3.368.026-2.152.236-6.133-3.544-6.139zm.529 3.405h.013c.213 0 .396.062.584.198.19.135.33.332.438.533.105.259.158.459.166.724 0-.02.006-.04.006-.06v.105a.086.086 0 01-.004-.021l-.004-.024a1.807 1.807 0 01-.15.706.953.953 0 01-.213.335.71.71 0 00-.088-.042c-.104-.045-.198-.064-.284-.133a1.312 1.312 0 00-.22-.066c.05-.06.146-.133.183-.198.053-.128.082-.264.088-.402v-.02a1.21 1.21 0 00-.061-.4c-.045-.134-.101-.2-.183-.333-.084-.066-.167-.132-.267-.132h-.016c-.093 0-.176.03-.262.132a.8.8 0 00-.205.334 1.18 1.18 0 00-.09.4v.019c.002.089.008.179.02.267-.193-.067-.438-.135-.607-.202a1.635 1.635 0 01-.018-.2v-.02a1.772 1.772 0 01.15-.768c.082-.22.232-.406.43-.533a.985.985 0 01.594-.2zm-2.962.059h.036c.142 0 .27.048.399.135.146.129.264.288.344.465.09.199.14.4.153.667v.004c.007.134.006.2-.002.266v.08c-.03.007-.056.018-.083.024-.152.055-.274.135-.393.2.012-.09.013-.18.003-.267v-.015c-.012-.133-.04-.2-.082-.333a.613.613 0 00-.166-.267.248.248 0 00-.183-.064h-.021c-.071.006-.13.04-.186.132a.552.552 0 00-.12.27.944.944 0 00-.023.33v.015c.012.135.037.2.08.334.046.134.098.2.166.268.01.009.02.018.034.024-.07.057-.117.07-.176.136a.304.304 0 01-.131.068 2.62 2.62 0 01-.275-.402 1.772 1.772 0 01-.155-.667 1.759 1.759 0 01.08-.668 1.43 1.43 0 01.283-.535c.128-.133.26-.2.418-.2zm1.37 1.706c.332 0 .733.065 1.216.399.293.2.523.269 1.052.468h.003c.255.136.405.266.478.399v-.131a.571.571 0 01.016.47c-.123.31-.516.643-1.063.842v.002c-.268.135-.501.333-.775.465-.276.135-.588.292-1.012.267a1.139 1.139 0 01-.448-.067 3.566 3.566 0 01-.322-.198c-.195-.135-.363-.332-.612-.465v-.005h-.005c-.4-.246-.616-.512-.686-.71-.07-.268-.005-.47.193-.6.224-.135.38-.271.483-.336.104-.074.143-.102.176-.131h.002v-.003c.169-.202.436-.47.839-.601.139-.036.294-.065.466-.065zm2.8 2.142c.358 1.417 1.196 3.475 1.735 4.473.286.534.855 1.659 1.102 3.024.156-.005.33.018.513.064.646-1.671-.546-3.467-1.089-3.966-.22-.2-.232-.335-.123-.335.59.534 1.365 1.572 1.646 2.757.13.535.16 1.104.021 1.67.067.028.135.06.205.067 1.032.534 1.413.938 1.23 1.537v-.043c-.06-.003-.12 0-.18 0h-.016c.151-.467-.182-.825-1.065-1.224-.915-.4-1.646-.336-1.77.465-.008.043-.013.066-.018.135-.068.023-.139.053-.209.064-.43.268-.662.669-.793 1.187-.13.533-.17 1.156-.205 1.869v.003c-.02.334-.17.838-.319 1.35-1.5 1.072-3.58 1.538-5.348.334a2.645 2.645 0 00-.402-.533 1.45 1.45 0 00-.275-.333c.182 0 .338-.03.465-.067a.615.615 0 00.314-.334c.108-.267 0-.697-.345-1.163-.345-.467-.931-.995-1.788-1.521-.63-.4-.986-.87-1.15-1.396-.165-.534-.143-1.085-.015-1.645.245-1.07.873-2.11 1.274-2.763.107-.065.037.135-.408.974-.396.751-1.14 2.497-.122 3.854a8.123 8.123 0 01.647-2.876c.564-1.278 1.743-3.504 1.836-5.268.048.036.217.135.289.202.218.133.38.333.59.465.21.201.477.335.876.335.039.003.075.006.11.006.412 0 .73-.134.997-.268.29-.134.52-.334.74-.4h.005c.467-.135.835-.402 1.044-.7zm2.185 8.958c.037.6.343 1.245.882 1.377.588.134 1.434-.333 1.791-.765l.211-.01c.315-.007.577.01.847.268l.003.003c.208.199.305.53.391.876.085.4.154.78.409 1.066.486.527.645.906.636 1.14l.003-.007v.018l-.003-.012c-.015.262-.185.396-.498.595-.63.401-1.746.712-2.457 1.57-.618.737-1.37 1.14-2.036 1.191-.664.053-1.237-.2-1.574-.898l-.005-.003c-.21-.4-.12-1.025.056-1.69.176-.668.428-1.344.463-1.897.037-.714.076-1.335.195-1.814.12-.465.308-.797.641-.984l.045-.022zm-10.814.049h.01c.053 0 .105.005.157.014.376.055.706.333 1.023.752l.91 1.664.003.003c.243.533.754 1.064 1.189 1.637.434.598.77 1.131.729 1.57v.006c-.057.744-.48 1.148-1.125 1.294-.645.135-1.52.002-2.395-.464-.968-.536-2.118-.469-2.857-.602-.369-.066-.61-.2-.723-.4-.11-.2-.113-.602.123-1.23v-.004l.002-.003c.117-.334.03-.752-.027-1.118-.055-.401-.083-.71.043-.94.16-.334.396-.4.69-.533.294-.135.64-.202.915-.47h.002v-.002c.256-.268.445-.601.668-.838.19-.201.38-.336.663-.336zm7.159-9.074c-.435.201-.945.535-1.488.535-.542 0-.97-.267-1.28-.466-.154-.134-.28-.268-.373-.335-.164-.134-.144-.333-.074-.333.109.016.129.134.199.2.096.066.215.2.36.333.292.2.68.467 1.167.467.485 0 1.053-.267 1.398-.466.195-.135.445-.334.648-.467.156-.136.149-.267.279-.267.128.016.034.134-.147.332a8.097 8.097 0 01-.69.468zm-1.082-1.583V5.64c-.006-.02.013-.042.029-.05.074-.043.18-.027.26.004.063 0 .16.067.15.135-.006.049-.085.066-.135.066-.055 0-.092-.043-.141-.068-.052-.018-.146-.008-.163-.065zm-.551 0c-.02.058-.113.049-.166.066-.047.025-.086.068-.14.068-.05 0-.13-.02-.136-.068-.01-.066.088-.133.15-.133.08-.031.184-.047.259-.005.019.009.036.03.03.05v.02h.003z",
1131
+ windows: "M0,0H11.377V11.372H0ZM12.623,0H24V11.372H12.623ZM0,12.623H11.377V24H0Zm12.623,0H24V24H12.623"
1132
+ };
1133
+ var viewBoxes = {
1134
+ macos: "0 2 24 24",
1135
+ linux: "0 0 24 24",
1136
+ windows: "0 0 24 24"
1137
+ };
1138
+ function OsIcon({ os, size = 14 }) {
1139
+ return /* @__PURE__ */ jsx("svg", {
1140
+ viewBox: viewBoxes[os],
1141
+ width: size,
1142
+ height: size,
1143
+ fill: "currentColor",
1144
+ "aria-hidden": "true",
1145
+ children: /* @__PURE__ */ jsx("path", { d: paths[os] })
1146
+ });
1147
+ }
1148
+ //#endregion
1099
1149
  //#region src/components/ProjectIcon.tsx
1100
1150
  function ProjectIcon({ project }) {
1101
1151
  const image = useRef(null);
1102
1152
  const [loaded, setLoaded] = useState(false);
1103
1153
  const [failed, setFailed] = useState(false);
1104
1154
  useEffect(() => {
1155
+ setFailed(false);
1105
1156
  if (image.current?.complete) {
1106
1157
  setLoaded(image.current.naturalWidth > 0);
1107
1158
  setFailed(image.current.naturalWidth === 0);
@@ -1146,7 +1197,7 @@ function ProjectPicker({ projects, projectId, onChange, onAddProject }) {
1146
1197
  }
1147
1198
  //#endregion
1148
1199
  //#region src/components/LaunchSelectors.tsx
1149
- var machineIcon = (machine) => machine.id === "local" ? /* @__PURE__ */ jsx(Monitor, { size: 14 }) : /* @__PURE__ */ jsx(Server, { size: 14 });
1200
+ var machineIcon = (machine) => machine.os ? /* @__PURE__ */ jsx(OsIcon, { os: machine.os }) : machine.id === "local" ? /* @__PURE__ */ jsx(Monitor, { size: 14 }) : /* @__PURE__ */ jsx(Server, { size: 14 });
1150
1201
  function LaunchSelectors({ machine, machines, projects, projectId, harness, model, onMachineChange, onProjectChange, onHarnessChange, onModelChange, onAddProject }) {
1151
1202
  const models = machine.harnesses.find((choice) => choice.id === harness)?.models ?? [];
1152
1203
  return /* @__PURE__ */ jsxs("div", {
@@ -1476,6 +1527,7 @@ function NewThreadView({ projects, machines, selectedProjectId, onOpenSidebar })
1476
1527
  path: machine.projectPaths[project.id],
1477
1528
  iconUrl: machineId === "local" ? project.iconUrl : void 0
1478
1529
  }));
1530
+ const canUseWorktree = catalog.data?.isGitRepo === true;
1479
1531
  const selectedHarness = machine.harnesses.find((choice) => choice.id === harness);
1480
1532
  const canSubmit = !!(prompt.trim() && !images.uploading && model.trim() && machine.connected && machineProjects.some((project) => project.id === projectId) && selectedHarness?.launchEnabled) && !busy;
1481
1533
  const harnessIds = catalog.data?.harnesses.map((choice) => choice.id).join(",");
@@ -1554,7 +1606,7 @@ function NewThreadView({ projects, machines, selectedProjectId, onOpenSidebar })
1554
1606
  machineId,
1555
1607
  agent: harness,
1556
1608
  model: model.trim(),
1557
- worktree
1609
+ worktree: worktree && canUseWorktree
1558
1610
  });
1559
1611
  if (operation.current?.payload !== payload) operation.current = {
1560
1612
  payload,
@@ -1612,24 +1664,30 @@ function NewThreadView({ projects, machines, selectedProjectId, onOpenSidebar })
1612
1664
  children: [
1613
1665
  (images.attachments.length > 0 || images.uploading > 0) && /* @__PURE__ */ jsxs("ul", {
1614
1666
  className: "composer-images",
1615
- "aria-label": "Attached images",
1616
- children: [images.attachments.map((image) => /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("img", {
1617
- src: image.preview,
1618
- alt: image.name
1619
- }), /* @__PURE__ */ jsx(Tooltip$1, {
1620
- label: "Remove image",
1621
- children: /* @__PURE__ */ jsx("button", {
1622
- type: "button",
1623
- "aria-label": `Remove ${image.name}`,
1624
- onClick: () => images.remove(image.id),
1625
- children: /* @__PURE__ */ jsx(X, {
1626
- size: 12,
1627
- strokeWidth: 2.4
1667
+ "aria-label": "Attached files",
1668
+ children: [images.attachments.map((image) => /* @__PURE__ */ jsxs("li", {
1669
+ className: image.preview ? void 0 : "is-file",
1670
+ children: [image.preview ? /* @__PURE__ */ jsx("img", {
1671
+ src: image.preview,
1672
+ alt: image.name
1673
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(FileText, {
1674
+ size: 18,
1675
+ strokeWidth: 1.8
1676
+ }), /* @__PURE__ */ jsx("span", { children: image.name })] }), /* @__PURE__ */ jsx(Tooltip$1, {
1677
+ label: "Remove file",
1678
+ children: /* @__PURE__ */ jsx("button", {
1679
+ type: "button",
1680
+ "aria-label": `Remove ${image.name}`,
1681
+ onClick: () => images.remove(image.id),
1682
+ children: /* @__PURE__ */ jsx(X, {
1683
+ size: 12,
1684
+ strokeWidth: 2.4
1685
+ })
1628
1686
  })
1629
- })
1630
- })] }, image.id)), Array.from({ length: images.uploading }, (_, index) => /* @__PURE__ */ jsx("li", {
1687
+ })]
1688
+ }, image.id)), Array.from({ length: images.uploading }, (_, index) => /* @__PURE__ */ jsx("li", {
1631
1689
  className: "is-uploading",
1632
- "aria-label": "Uploading image"
1690
+ "aria-label": "Uploading file"
1633
1691
  }, "uploading-" + index))]
1634
1692
  }),
1635
1693
  /* @__PURE__ */ jsx("textarea", {
@@ -1663,7 +1721,7 @@ function NewThreadView({ projects, machines, selectedProjectId, onOpenSidebar })
1663
1721
  onModelChange: setModel,
1664
1722
  onAddProject: machine.connected && machine.writable ? () => setCreatingWorkspace(true) : void 0
1665
1723
  }),
1666
- /* @__PURE__ */ jsx(Tooltip$1, {
1724
+ canUseWorktree && /* @__PURE__ */ jsx(Tooltip$1, {
1667
1725
  label: worktree ? "new worktree" : "current checkout",
1668
1726
  children: /* @__PURE__ */ jsxs("label", {
1669
1727
  className: "worktree-option",
@@ -2383,6 +2441,96 @@ function ProjectNameEditor({ project, onDone, onError }) {
2383
2441
  });
2384
2442
  }
2385
2443
  //#endregion
2444
+ //#region src/components/ProjectIconDialog.tsx
2445
+ var iconSize = 128;
2446
+ async function squarePng(file) {
2447
+ const bitmap = await createImageBitmap(file);
2448
+ const canvas = document.createElement("canvas");
2449
+ canvas.width = canvas.height = iconSize;
2450
+ const scale = Math.min(iconSize / bitmap.width, iconSize / bitmap.height);
2451
+ const width = bitmap.width * scale;
2452
+ const height = bitmap.height * scale;
2453
+ canvas.getContext("2d").drawImage(bitmap, (iconSize - width) / 2, (iconSize - height) / 2, width, height);
2454
+ return new Promise((resolve, reject) => canvas.toBlob((blob) => blob ? resolve(blob) : reject(/* @__PURE__ */ new Error("Could not read this image.")), "image/png"));
2455
+ }
2456
+ function ProjectIconDialog({ project, open, onClose, onError }) {
2457
+ const picker = useRef(null);
2458
+ const [dragging, setDragging] = useState(false);
2459
+ const [busy, setBusy] = useState(false);
2460
+ const run = async (action) => {
2461
+ setBusy(true);
2462
+ try {
2463
+ await action();
2464
+ onClose();
2465
+ } catch (error) {
2466
+ onError(error);
2467
+ } finally {
2468
+ setBusy(false);
2469
+ }
2470
+ };
2471
+ const upload = (file) => {
2472
+ if (file) run(async () => setProjectIcon(project, await squarePng(file)));
2473
+ };
2474
+ return /* @__PURE__ */ jsx(Dialog.Root, {
2475
+ open,
2476
+ onOpenChange: (next) => {
2477
+ if (!next) onClose();
2478
+ },
2479
+ children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [/* @__PURE__ */ jsx(Dialog.Backdrop, { className: "dialog-backdrop" }), /* @__PURE__ */ jsxs(Dialog.Popup, {
2480
+ className: "dialog",
2481
+ children: [
2482
+ /* @__PURE__ */ jsxs(Dialog.Title, {
2483
+ className: "dialog-message",
2484
+ children: ["Change icon for ", project.name]
2485
+ }),
2486
+ /* @__PURE__ */ jsxs("button", {
2487
+ type: "button",
2488
+ className: `icon-dropzone ${dragging ? "is-dragging" : ""}`,
2489
+ disabled: busy,
2490
+ onClick: () => picker.current?.click(),
2491
+ onDragOver: (event) => {
2492
+ event.preventDefault();
2493
+ setDragging(true);
2494
+ },
2495
+ onDragLeave: () => setDragging(false),
2496
+ onDrop: (event) => {
2497
+ event.preventDefault();
2498
+ setDragging(false);
2499
+ upload(event.dataTransfer.files[0]);
2500
+ },
2501
+ children: [/* @__PURE__ */ jsx(ImageUp, {
2502
+ size: 22,
2503
+ "aria-hidden": "true"
2504
+ }), "Drop an image here, or click to choose"]
2505
+ }),
2506
+ /* @__PURE__ */ jsx("input", {
2507
+ ref: picker,
2508
+ type: "file",
2509
+ accept: "image/*",
2510
+ hidden: true,
2511
+ onChange: (event) => {
2512
+ upload(event.target.files?.[0]);
2513
+ event.target.value = "";
2514
+ }
2515
+ }),
2516
+ /* @__PURE__ */ jsxs("div", {
2517
+ className: "dialog-actions",
2518
+ children: [/* @__PURE__ */ jsx("button", {
2519
+ type: "button",
2520
+ className: "dialog-button",
2521
+ disabled: busy,
2522
+ onClick: () => void run(() => resetProjectIcon(project)),
2523
+ children: "Reset to default"
2524
+ }), /* @__PURE__ */ jsx(Dialog.Close, {
2525
+ className: "dialog-button",
2526
+ children: "Cancel"
2527
+ })]
2528
+ })
2529
+ ]
2530
+ })] })
2531
+ });
2532
+ }
2533
+ //#endregion
2386
2534
  //#region src/components/useFlipList.ts
2387
2535
  var durationMs = 220;
2388
2536
  var itemSelector = "[data-flip-id]";
@@ -2573,6 +2721,7 @@ function ThreadLink({ thread, project, machineName, current, showProject, editor
2573
2721
  }
2574
2722
  function ProjectGroupHeader({ project, closed, onToggle }) {
2575
2723
  const [renaming, setRenaming] = useState(false);
2724
+ const [changingIcon, setChangingIcon] = useState(false);
2576
2725
  const Title = renaming ? "div" : "button";
2577
2726
  const label = renaming ? /* @__PURE__ */ jsx(ProjectNameEditor, {
2578
2727
  project,
@@ -2582,7 +2731,7 @@ function ProjectGroupHeader({ project, closed, onToggle }) {
2582
2731
  className: "group-name",
2583
2732
  children: project.name
2584
2733
  });
2585
- return /* @__PURE__ */ jsxs(ContextMenu.Root, { children: [/* @__PURE__ */ jsx(ContextMenu.Trigger, {
2734
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(ContextMenu.Root, { children: [/* @__PURE__ */ jsx(ContextMenu.Trigger, {
2586
2735
  className: "group-header",
2587
2736
  children: /* @__PURE__ */ jsxs(Title, {
2588
2737
  className: "group-title",
@@ -2619,6 +2768,14 @@ function ProjectGroupHeader({ project, closed, onToggle }) {
2619
2768
  "aria-hidden": "true"
2620
2769
  }), "Rename project"]
2621
2770
  }),
2771
+ /* @__PURE__ */ jsxs(ContextMenu.Item, {
2772
+ className: "thread-menu-item",
2773
+ onClick: () => setChangingIcon(true),
2774
+ children: [/* @__PURE__ */ jsx(ImageUp, {
2775
+ size: 14,
2776
+ "aria-hidden": "true"
2777
+ }), "Change icon"]
2778
+ }),
2622
2779
  /* @__PURE__ */ jsx("div", {
2623
2780
  className: "thread-menu-path",
2624
2781
  title: project.path,
@@ -2626,7 +2783,12 @@ function ProjectGroupHeader({ project, closed, onToggle }) {
2626
2783
  })
2627
2784
  ]
2628
2785
  })
2629
- }) })] });
2786
+ }) })] }), /* @__PURE__ */ jsx(ProjectIconDialog, {
2787
+ project,
2788
+ open: changingIcon,
2789
+ onClose: () => setChangingIcon(false),
2790
+ onError: reportFailure
2791
+ })] });
2630
2792
  }
2631
2793
  function ArchivedThreads({ threads, renderThread }) {
2632
2794
  const [open, setOpen] = useState(false);
@@ -3671,28 +3833,29 @@ function bindTerminalGestures(host, screen, control, writable) {
3671
3833
  }
3672
3834
  //#endregion
3673
3835
  //#region src/components/useImageAttachments.ts
3674
- var ONLY_IMAGES = "Only PNG, JPEG, GIF and WebP images can be attached.";
3675
- var attachingLabel = (count) => count === 1 ? "Attaching image…" : `Attaching ${count} images…`;
3676
- var attachedLabel = (count) => count === 1 ? "Image attached" : `${count} images attached`;
3836
+ var overDialog = (event) => event.target instanceof Element && !!event.target.closest("[role=\"dialog\"]");
3837
+ var NO_FILES = "Nothing to attach.";
3838
+ var attachingLabel = (count) => count === 1 ? "Attaching file…" : `Attaching ${count} files…`;
3839
+ var attachedLabel = (count) => count === 1 ? "File attached" : `${count} files attached`;
3677
3840
  function useImageAttachments({ machineId, active, writable, send, show }) {
3678
3841
  const [dragging, setDragging] = useState(false);
3679
3842
  const sendRef = useRef(send);
3680
3843
  sendRef.current = send;
3681
3844
  const attach = useCallback(async (files) => {
3682
3845
  if (!files.length) {
3683
- show(ONLY_IMAGES, "error", 4e3);
3846
+ show(NO_FILES, "error", 4e3);
3684
3847
  return;
3685
3848
  }
3686
3849
  if (!writable.current) {
3687
- show("Wait until the terminal accepts input, then attach the image again.", "error", 4e3);
3850
+ show("Wait until the terminal accepts input, then attach the file again.", "error", 4e3);
3688
3851
  return;
3689
3852
  }
3690
3853
  show(attachingLabel(files.length), "info");
3691
3854
  try {
3692
- sendRef.current(await uploadImagesAsTerminalText(machineId, files));
3855
+ sendRef.current(await uploadFilesAsTerminalText(machineId, files));
3693
3856
  show(attachedLabel(files.length), "info", 1500);
3694
3857
  } catch (error) {
3695
- show(error instanceof Error ? error.message : "The image could not be attached.", "error", 5e3);
3858
+ show(error instanceof Error ? error.message : "The file could not be attached.", "error", 5e3);
3696
3859
  }
3697
3860
  }, [
3698
3861
  machineId,
@@ -3703,27 +3866,27 @@ function useImageAttachments({ machineId, active, writable, send, show }) {
3703
3866
  if (!active) return;
3704
3867
  let depth = 0;
3705
3868
  const enter = (event) => {
3706
- if (!carriesFiles(event.dataTransfer)) return;
3869
+ if (!carriesFiles(event.dataTransfer) || overDialog(event)) return;
3707
3870
  event.preventDefault();
3708
3871
  depth += 1;
3709
3872
  setDragging(true);
3710
3873
  };
3711
3874
  const over = (event) => {
3712
- if (!carriesFiles(event.dataTransfer)) return;
3875
+ if (!carriesFiles(event.dataTransfer) || overDialog(event)) return;
3713
3876
  event.preventDefault();
3714
3877
  if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
3715
3878
  };
3716
3879
  const leave = (event) => {
3717
- if (!carriesFiles(event.dataTransfer)) return;
3880
+ if (!carriesFiles(event.dataTransfer) || overDialog(event)) return;
3718
3881
  depth = Math.max(0, depth - 1);
3719
3882
  if (!depth) setDragging(false);
3720
3883
  };
3721
3884
  const drop = (event) => {
3722
- if (!carriesFiles(event.dataTransfer)) return;
3885
+ if (!carriesFiles(event.dataTransfer) || overDialog(event)) return;
3723
3886
  event.preventDefault();
3724
3887
  depth = 0;
3725
3888
  setDragging(false);
3726
- attach(imageFilesFrom(event.dataTransfer));
3889
+ attach(filesFrom(event.dataTransfer));
3727
3890
  };
3728
3891
  window.addEventListener("dragenter", enter);
3729
3892
  window.addEventListener("dragover", over);
@@ -1,6 +1,6 @@
1
1
  import { S as createAstro, V as __exportAll, i as renderComponent, u as renderTemplate } from "./server_dfdLOyV2.mjs";
2
2
  import { t as createComponent } from "./compiler_BnrQvXXD.mjs";
3
- import { n as $$Base, t as App } from "./App_DDOnT1vx.mjs";
3
+ import { n as $$Base, t as App } from "./App_BbzCRPiT.mjs";
4
4
  //#region src/pages/threads/[id].astro
5
5
  var _id__exports = /* @__PURE__ */ __exportAll({
6
6
  default: () => $$Id,
@@ -1,6 +1,6 @@
1
1
  import { S as createAstro, V as __exportAll, i as renderComponent, u as renderTemplate } from "./server_dfdLOyV2.mjs";
2
2
  import { t as createComponent } from "./compiler_BnrQvXXD.mjs";
3
- import { n as $$Base, t as App } from "./App_DDOnT1vx.mjs";
3
+ import { n as $$Base, t as App } from "./App_BbzCRPiT.mjs";
4
4
  //#region src/pages/sessions/[session]/tabs/[tab].astro
5
5
  var _tab__exports = /* @__PURE__ */ __exportAll({
6
6
  default: () => $$Tab,
@@ -1,6 +1,6 @@
1
1
  import { S as createAstro, V as __exportAll, i as renderComponent, u as renderTemplate } from "./server_dfdLOyV2.mjs";
2
2
  import { t as createComponent } from "./compiler_BnrQvXXD.mjs";
3
- import { n as $$Base, t as App } from "./App_DDOnT1vx.mjs";
3
+ import { n as $$Base, t as App } from "./App_BbzCRPiT.mjs";
4
4
  //#region src/pages/new.astro
5
5
  var new_exports = /* @__PURE__ */ __exportAll({
6
6
  default: () => $$New,
@@ -1,6 +1,6 @@
1
1
  import { S as createAstro, V as __exportAll, i as renderComponent, u as renderTemplate } from "./server_dfdLOyV2.mjs";
2
2
  import { t as createComponent } from "./compiler_BnrQvXXD.mjs";
3
- import { n as $$Base, t as App } from "./App_DDOnT1vx.mjs";
3
+ import { n as $$Base, t as App } from "./App_BbzCRPiT.mjs";
4
4
  //#region src/pages/settings.astro
5
5
  var settings_exports = /* @__PURE__ */ __exportAll({
6
6
  default: () => $$Settings,
@@ -2426,10 +2426,10 @@ var renderers = [Object.assign({
2426
2426
  //#endregion
2427
2427
  //#region \0virtual:astro:pages
2428
2428
  var _page0 = () => import("./chunks/node_C8FqXy_n.mjs").then((n) => n.t);
2429
- var _page1 = () => import("./chunks/new_CBpgizdT.mjs");
2430
- var _page2 = () => import("./chunks/_tab__1TQX6GGq.mjs");
2431
- var _page3 = () => import("./chunks/settings_BahL_Diu.mjs");
2432
- var _page4 = () => import("./chunks/_id__CRXKeG9Q.mjs");
2429
+ var _page1 = () => import("./chunks/new_n3R-7e9Z.mjs");
2430
+ var _page2 = () => import("./chunks/_tab__O2UOM0DH.mjs");
2431
+ var _page3 = () => import("./chunks/settings_CP_J5pGZ.mjs");
2432
+ var _page4 = () => import("./chunks/_id___SMAFJxg.mjs");
2433
2433
  var _page5 = () => import("./chunks/index_Bvv1gpI0.mjs");
2434
2434
  var pageMap = /* @__PURE__ */ new Map([
2435
2435
  ["node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node.js", _page0],
@@ -2441,7 +2441,7 @@ var pageMap = /* @__PURE__ */ new Map([
2441
2441
  ]);
2442
2442
  //#endregion
2443
2443
  //#region \0virtual:astro:manifest
2444
- var _manifest = deserializeManifest({"rootDir":"file:///home/runner/work/webr/webr/","cacheDir":"file:///home/runner/work/webr/webr/node_modules/.astro/","outDir":"file:///home/runner/work/webr/webr/dist/","srcDir":"file:///home/runner/work/webr/webr/src/","publicDir":"file:///home/runner/work/webr/webr/public/","buildClientDir":"file:///home/runner/work/webr/webr/dist/client/","buildServerDir":"file:///home/runner/work/webr/webr/dist/server/","adapterName":"@astrojs/node","assetsDir":"_astro","routes":[{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"type":"page","component":"_server-islands.astro","params":["name"],"segments":[[{"content":"_server-islands","dynamic":false,"spread":false}],[{"content":"name","dynamic":true,"spread":false}]],"pattern":"^\\/_server-islands\\/([^/]+?)\\/?$","prerender":false,"isIndex":false,"fallbackRoutes":[],"route":"/_server-islands/[name]","origin":"internal","distURL":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/_image","component":"node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node.js","params":[],"pathname":"/_image","pattern":"^\\/_image\\/?$","segments":[[{"content":"_image","dynamic":false,"spread":false}]],"type":"endpoint","prerender":false,"fallbackRoutes":[],"distURL":[],"isIndex":false,"origin":"internal","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.B7RjqN_I.css"}],"routeData":{"route":"/new","isIndex":false,"type":"page","pattern":"^\\/new\\/?$","segments":[[{"content":"new","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/new.astro","pathname":"/new","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.B7RjqN_I.css"}],"routeData":{"route":"/sessions/[session]/tabs/[tab]","isIndex":false,"type":"page","pattern":"^\\/sessions\\/([^/]+?)\\/tabs\\/([^/]+?)\\/?$","segments":[[{"content":"sessions","dynamic":false,"spread":false}],[{"content":"session","dynamic":true,"spread":false}],[{"content":"tabs","dynamic":false,"spread":false}],[{"content":"tab","dynamic":true,"spread":false}]],"params":["session","tab"],"component":"src/pages/sessions/[session]/tabs/[tab].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.B7RjqN_I.css"}],"routeData":{"route":"/settings","isIndex":false,"type":"page","pattern":"^\\/settings\\/?$","segments":[[{"content":"settings","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/settings.astro","pathname":"/settings","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.B7RjqN_I.css"}],"routeData":{"route":"/threads/[id]","isIndex":false,"type":"page","pattern":"^\\/threads\\/([^/]+?)\\/?$","segments":[[{"content":"threads","dynamic":false,"spread":false}],[{"content":"id","dynamic":true,"spread":false}]],"params":["id"],"component":"src/pages/threads/[id].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/","isIndex":true,"type":"page","pattern":"^\\/$","segments":[],"params":[],"component":"src/pages/index.astro","pathname":"/","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}}],"serverLike":true,"middlewareMode":"classic","base":"/","trailingSlash":"ignore","compressHTML":"jsx","componentMetadata":[["/home/runner/work/webr/webr/src/pages/new.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/sessions/[session]/tabs/[tab].astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/settings.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/threads/[id].astro",{"propagation":"none","containsHead":true}]],"renderers":[],"clientDirectives":[["idle","(()=>{var l=(n,t)=>{let i=async()=>{await(await n())()},e=typeof t.value==\"object\"?t.value:void 0,s={timeout:e==null?void 0:e.timeout};\"requestIdleCallback\"in window?window.requestIdleCallback(i,s):setTimeout(i,s.timeout||200)};(self.Astro||(self.Astro={})).idle=l;window.dispatchEvent(new Event(\"astro:idle\"));})();"],["load","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event(\"astro:load\"));})();"],["media","(()=>{var n=(a,t)=>{let i=async()=>{await(await a())()};if(t.value){let e=matchMedia(t.value);e.matches?i():e.addEventListener(\"change\",i,{once:!0})}};(self.Astro||(self.Astro={})).media=n;window.dispatchEvent(new Event(\"astro:media\"));})();"],["only","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event(\"astro:only\"));})();"],["visible","(()=>{var a=(s,i,o)=>{let r=async()=>{await(await s())()},t=typeof i.value==\"object\"?i.value:void 0,c={rootMargin:t==null?void 0:t.rootMargin},n=new IntersectionObserver(e=>{for(let l of e)if(l.isIntersecting){n.disconnect(),r();break}},c);for(let e of o.children)n.observe(e)};(self.Astro||(self.Astro={})).visible=a;window.dispatchEvent(new Event(\"astro:visible\"));})();"]],"entryModules":{"\u0000noop-middleware":"virtual_astro_middleware.mjs","\u0000virtual:astro:server-island-manifest":"chunks/_virtual_astro_server-island-manifest_C1Q2srgE.mjs","\u0000virtual:astro:session-driver":"chunks/_virtual_astro_session-driver_Dp7RXjOf.mjs","\u0000virtual:astro:actions/noop-entrypoint":"chunks/noop-entrypoint_Z3zFhrGC.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/@astrojs+react@7.0.0_@types+node@22.20.4_@types+react-dom@19.3.0_@types+react@19.3.0__@_02aa85dceaccf3d763325676dd8540a8/node_modules/@astrojs/react/dist/vnode-children.js":"chunks/vnode-children_BSi0H4PL.mjs","@astrojs/node/server.js":"entry.mjs","\u0000virtual:astro:page:src/pages/threads/[id]@_@astro":"chunks/_id__CRXKeG9Q.mjs","\u0000virtual:astro:page:src/pages/sessions/[session]/tabs/[tab]@_@astro":"chunks/_tab__1TQX6GGq.mjs","\u0000virtual:astro:page:src/pages/index@_@astro":"chunks/index_Bvv1gpI0.mjs","\u0000virtual:astro:page:src/pages/new@_@astro":"chunks/new_CBpgizdT.mjs","\u0000virtual:astro:page:node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node@_@js":"chunks/node_C8FqXy_n.mjs","\u0000virtual:astro:page:src/pages/settings@_@astro":"chunks/settings_BahL_Diu.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/services/sharp.js":"chunks/sharp_8J8zk4Wd.mjs","/home/runner/work/webr/webr/src/components/App.tsx":"_astro/App.CuhTptJe.js","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/components/ClientRouter.astro?astro&type=script&index=0&lang.ts":"_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","@astrojs/react/client.js":"_astro/client.BDkzSRUN.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-fit@0.10.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-fit/lib/addon-fit.js":"_astro/addon-fit.jgKqzwky.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-web-links@0.11.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-web-links/lib/addon-web-links.js":"_astro/addon-web-links.C9lyQRxI.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-webgl@0.18.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-webgl/lib/addon-webgl.js":"_astro/addon-webgl.Bd5ox92E.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+xterm@5.5.0/node_modules/@xterm/xterm/lib/xterm.js":"_astro/xterm.B-l5AIf6.js","astro:scripts/before-hydration.js":""},"inlinedScripts":[],"assets":["/apple-touch-icon.png","/brand-mark.png","/favicon-16.png","/favicon-32.png","/favicon.ico","/icon-192.png","/icon-512.png","/icon-maskable-512.png","/manifest.webmanifest","/_astro/App.CuhTptJe.js","/_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","/_astro/addon-fit.jgKqzwky.js","/_astro/addon-web-links.C9lyQRxI.js","/_astro/addon-webgl.Bd5ox92E.js","/_astro/client.BDkzSRUN.js","/_astro/client.Bbg9OBwT.js","/_astro/client.CLSsMr2g.js","/_astro/xterm.B-l5AIf6.js","/_astro/App.B7RjqN_I.css","/_astro/jetbrains-mono.CFJaiWaY.woff2","/_astro/symbols-nerd.CEdkKAK8.woff2"],"buildFormat":"directory","checkOrigin":true,"actionBodySizeLimit":1048576,"serverIslandBodySizeLimit":1048576,"allowedDomains":[],"key":"iVCcVi9giGk8wGpdPxt7Oy82WrH2RNoZ7NvJcqB4C/A=","sessionConfig":{"driver":"unstorage/drivers/fs-lite","options":{"base":"/home/runner/work/webr/webr/node_modules/.astro/sessions"}},"image":{},"devToolbar":{"enabled":false,"debugInfoOutput":""},"logLevel":"info","shouldInjectCspMetaTags":false});
2444
+ var _manifest = deserializeManifest({"rootDir":"file:///home/runner/work/webr/webr/","cacheDir":"file:///home/runner/work/webr/webr/node_modules/.astro/","outDir":"file:///home/runner/work/webr/webr/dist/","srcDir":"file:///home/runner/work/webr/webr/src/","publicDir":"file:///home/runner/work/webr/webr/public/","buildClientDir":"file:///home/runner/work/webr/webr/dist/client/","buildServerDir":"file:///home/runner/work/webr/webr/dist/server/","adapterName":"@astrojs/node","assetsDir":"_astro","routes":[{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"type":"page","component":"_server-islands.astro","params":["name"],"segments":[[{"content":"_server-islands","dynamic":false,"spread":false}],[{"content":"name","dynamic":true,"spread":false}]],"pattern":"^\\/_server-islands\\/([^/]+?)\\/?$","prerender":false,"isIndex":false,"fallbackRoutes":[],"route":"/_server-islands/[name]","origin":"internal","distURL":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/_image","component":"node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node.js","params":[],"pathname":"/_image","pattern":"^\\/_image\\/?$","segments":[[{"content":"_image","dynamic":false,"spread":false}]],"type":"endpoint","prerender":false,"fallbackRoutes":[],"distURL":[],"isIndex":false,"origin":"internal","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.CJakfIju.css"}],"routeData":{"route":"/new","isIndex":false,"type":"page","pattern":"^\\/new\\/?$","segments":[[{"content":"new","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/new.astro","pathname":"/new","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.CJakfIju.css"}],"routeData":{"route":"/sessions/[session]/tabs/[tab]","isIndex":false,"type":"page","pattern":"^\\/sessions\\/([^/]+?)\\/tabs\\/([^/]+?)\\/?$","segments":[[{"content":"sessions","dynamic":false,"spread":false}],[{"content":"session","dynamic":true,"spread":false}],[{"content":"tabs","dynamic":false,"spread":false}],[{"content":"tab","dynamic":true,"spread":false}]],"params":["session","tab"],"component":"src/pages/sessions/[session]/tabs/[tab].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.CJakfIju.css"}],"routeData":{"route":"/settings","isIndex":false,"type":"page","pattern":"^\\/settings\\/?$","segments":[[{"content":"settings","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/settings.astro","pathname":"/settings","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.CJakfIju.css"}],"routeData":{"route":"/threads/[id]","isIndex":false,"type":"page","pattern":"^\\/threads\\/([^/]+?)\\/?$","segments":[[{"content":"threads","dynamic":false,"spread":false}],[{"content":"id","dynamic":true,"spread":false}]],"params":["id"],"component":"src/pages/threads/[id].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/","isIndex":true,"type":"page","pattern":"^\\/$","segments":[],"params":[],"component":"src/pages/index.astro","pathname":"/","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}}],"serverLike":true,"middlewareMode":"classic","base":"/","trailingSlash":"ignore","compressHTML":"jsx","componentMetadata":[["/home/runner/work/webr/webr/src/pages/new.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/sessions/[session]/tabs/[tab].astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/settings.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/threads/[id].astro",{"propagation":"none","containsHead":true}]],"renderers":[],"clientDirectives":[["idle","(()=>{var l=(n,t)=>{let i=async()=>{await(await n())()},e=typeof t.value==\"object\"?t.value:void 0,s={timeout:e==null?void 0:e.timeout};\"requestIdleCallback\"in window?window.requestIdleCallback(i,s):setTimeout(i,s.timeout||200)};(self.Astro||(self.Astro={})).idle=l;window.dispatchEvent(new Event(\"astro:idle\"));})();"],["load","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event(\"astro:load\"));})();"],["media","(()=>{var n=(a,t)=>{let i=async()=>{await(await a())()};if(t.value){let e=matchMedia(t.value);e.matches?i():e.addEventListener(\"change\",i,{once:!0})}};(self.Astro||(self.Astro={})).media=n;window.dispatchEvent(new Event(\"astro:media\"));})();"],["only","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event(\"astro:only\"));})();"],["visible","(()=>{var a=(s,i,o)=>{let r=async()=>{await(await s())()},t=typeof i.value==\"object\"?i.value:void 0,c={rootMargin:t==null?void 0:t.rootMargin},n=new IntersectionObserver(e=>{for(let l of e)if(l.isIntersecting){n.disconnect(),r();break}},c);for(let e of o.children)n.observe(e)};(self.Astro||(self.Astro={})).visible=a;window.dispatchEvent(new Event(\"astro:visible\"));})();"]],"entryModules":{"\u0000noop-middleware":"virtual_astro_middleware.mjs","\u0000virtual:astro:server-island-manifest":"chunks/_virtual_astro_server-island-manifest_C1Q2srgE.mjs","\u0000virtual:astro:session-driver":"chunks/_virtual_astro_session-driver_Dp7RXjOf.mjs","\u0000virtual:astro:actions/noop-entrypoint":"chunks/noop-entrypoint_Z3zFhrGC.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/@astrojs+react@7.0.0_@types+node@22.20.4_@types+react-dom@19.3.0_@types+react@19.3.0__@_02aa85dceaccf3d763325676dd8540a8/node_modules/@astrojs/react/dist/vnode-children.js":"chunks/vnode-children_BSi0H4PL.mjs","@astrojs/node/server.js":"entry.mjs","\u0000virtual:astro:page:src/pages/threads/[id]@_@astro":"chunks/_id___SMAFJxg.mjs","\u0000virtual:astro:page:src/pages/sessions/[session]/tabs/[tab]@_@astro":"chunks/_tab__O2UOM0DH.mjs","\u0000virtual:astro:page:src/pages/index@_@astro":"chunks/index_Bvv1gpI0.mjs","\u0000virtual:astro:page:src/pages/new@_@astro":"chunks/new_n3R-7e9Z.mjs","\u0000virtual:astro:page:node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node@_@js":"chunks/node_C8FqXy_n.mjs","\u0000virtual:astro:page:src/pages/settings@_@astro":"chunks/settings_CP_J5pGZ.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/services/sharp.js":"chunks/sharp_8J8zk4Wd.mjs","/home/runner/work/webr/webr/src/components/App.tsx":"_astro/App.bmHm9djw.js","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/components/ClientRouter.astro?astro&type=script&index=0&lang.ts":"_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","@astrojs/react/client.js":"_astro/client.BDkzSRUN.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-fit@0.10.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-fit/lib/addon-fit.js":"_astro/addon-fit.jgKqzwky.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-web-links@0.11.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-web-links/lib/addon-web-links.js":"_astro/addon-web-links.C9lyQRxI.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-webgl@0.18.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-webgl/lib/addon-webgl.js":"_astro/addon-webgl.Bd5ox92E.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+xterm@5.5.0/node_modules/@xterm/xterm/lib/xterm.js":"_astro/xterm.B-l5AIf6.js","astro:scripts/before-hydration.js":""},"inlinedScripts":[],"assets":["/apple-touch-icon.png","/brand-mark.png","/favicon-16.png","/favicon-32.png","/favicon.ico","/icon-192.png","/icon-512.png","/icon-maskable-512.png","/manifest.webmanifest","/_astro/App.bmHm9djw.js","/_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","/_astro/addon-fit.jgKqzwky.js","/_astro/addon-web-links.C9lyQRxI.js","/_astro/addon-webgl.Bd5ox92E.js","/_astro/client.BDkzSRUN.js","/_astro/client.Bbg9OBwT.js","/_astro/client.CLSsMr2g.js","/_astro/xterm.B-l5AIf6.js","/_astro/App.CJakfIju.css","/_astro/jetbrains-mono.CFJaiWaY.woff2","/_astro/symbols-nerd.CEdkKAK8.woff2"],"buildFormat":"directory","checkOrigin":true,"actionBodySizeLimit":1048576,"serverIslandBodySizeLimit":1048576,"allowedDomains":[],"key":"wMBFmhe+pn2DnaCSXSWVokwTbr5rGkrH+IRu6kzYs+A=","sessionConfig":{"driver":"unstorage/drivers/fs-lite","options":{"base":"/home/runner/work/webr/webr/node_modules/.astro/sessions"}},"image":{},"devToolbar":{"enabled":false,"debugInfoOutput":""},"logLevel":"info","shouldInjectCspMetaTags":false});
2445
2445
  var manifestRoutes = _manifest.routes;
2446
2446
  var manifest = Object.assign(_manifest, {
2447
2447
  renderers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pablopunk/webr",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "Control your Herdr agents from anywhere.",
5
5
  "keywords": [
6
6
  "herdr",
@@ -1,4 +1,5 @@
1
1
  export type MachineHarness = { id: string; name: string; models: string[]; customModels?: boolean; launchEnabled?: boolean; reason?: string };
2
+ export type MachineOs = 'macos' | 'linux' | 'windows';
2
3
  export type Machine = {
3
4
  id: string;
4
5
  name: string;
@@ -7,6 +8,8 @@ export type Machine = {
7
8
  projectPaths: Record<string, string>;
8
9
  harnesses: MachineHarness[];
9
10
  modelsPending?: boolean;
11
+ isGitRepo?: boolean;
12
+ os?: MachineOs;
10
13
  error?: string;
11
14
  writable?: boolean;
12
15
  configVersion?: number;
@@ -14,7 +17,7 @@ export type Machine = {
14
17
 
15
18
  export const machines: Machine[] = [
16
19
  {
17
- id: 'local', name: 'Local', connected: true, session: 'default',
20
+ id: 'local', name: 'Local', os: 'macos', connected: true, session: 'default',
18
21
  projectPaths: { herdr: '~/src/herdr', maze: '~/src/maze/monorepo', spotifin: '~/src/spotifin' },
19
22
  harnesses: [
20
23
  { id: 'claude', name: 'Claude Code', models: ['Default', 'sonnet', 'opus'] },
@@ -24,7 +27,7 @@ export const machines: Machine[] = [
24
27
  ],
25
28
  },
26
29
  {
27
- id: 'demo-build', name: 'Build machine (demo)', connected: true, session: 'agents',
30
+ id: 'demo-build', name: 'Build machine (demo)', os: 'linux', connected: true, session: 'agents',
28
31
  projectPaths: { herdr: '/srv/herdr', spotifin: '/srv/spotifin' },
29
32
  harnesses: [
30
33
  { id: 'opencode', name: 'OpenCode', models: ['Default', 'demo/remote-model'] },