@tangle-network/sandbox-ui 0.113.5 → 0.113.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1101,6 +1101,7 @@ function CreditBalance({
1101
1101
  className
1102
1102
  }) {
1103
1103
  const [topUpValue, setTopUpValue] = React4.useState("50.00");
1104
+ const topUpId = React4.useId();
1104
1105
  return /* @__PURE__ */ jsxs3(
1105
1106
  "div",
1106
1107
  {
@@ -1118,11 +1119,14 @@ function CreditBalance({
1118
1119
  /* @__PURE__ */ jsx4("p", { className: "text-sm text-muted-foreground leading-relaxed", children: description })
1119
1120
  ] }),
1120
1121
  onTopUp && /* @__PURE__ */ jsxs3("div", { className: "space-y-2.5 mt-5", children: [
1122
+ /* @__PURE__ */ jsx4("label", { htmlFor: topUpId, className: "block text-xs font-medium text-muted-foreground", children: "Top-up amount (USD)" }),
1121
1123
  /* @__PURE__ */ jsxs3("div", { className: `bg-surface-container-low border p-1 rounded-lg flex items-center ${focusFieldWithin}`, children: [
1122
1124
  /* @__PURE__ */ jsx4(
1123
1125
  "input",
1124
1126
  {
1127
+ id: topUpId,
1125
1128
  type: "text",
1129
+ inputMode: "decimal",
1126
1130
  value: `$${topUpValue}`,
1127
1131
  onChange: (e) => {
1128
1132
  const raw = e.target.value.replace(/[^0-9.]/g, "");
@@ -1936,7 +1940,7 @@ function SandboxTable({
1936
1940
  type: "button",
1937
1941
  onClick: stopRowClick,
1938
1942
  className: `p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90 ${focusRing3}`,
1939
- "aria-label": "More actions",
1943
+ "aria-label": `More actions for ${sb.name}`,
1940
1944
  title: "More actions",
1941
1945
  children: /* @__PURE__ */ jsx11(MoreVertical2, { className: "h-4 w-4" })
1942
1946
  }
@@ -1947,10 +1951,10 @@ function SandboxTable({
1947
1951
  ] }, sectionIdx)) })
1948
1952
  ] });
1949
1953
  })(),
1950
- onDelete && canAdminSandbox(sb) && /* @__PURE__ */ jsx11("button", { type: "button", onClick: (e) => {
1954
+ onDelete && canAdminSandbox(sb) && /* @__PURE__ */ jsx11("button", { type: "button", "aria-label": `Delete ${sb.name}`, onClick: (e) => {
1951
1955
  stopRowClick(e);
1952
1956
  onDelete(sb.id);
1953
- }, className: "p-2 rounded-lg hover:bg-[var(--surface-danger-bg)] text-muted-foreground hover:text-[var(--surface-danger-text)] transition-all active:scale-90", title: "Delete", children: /* @__PURE__ */ jsx11(Trash22, { className: "h-4 w-4" }) })
1957
+ }, className: `p-2 rounded-lg hover:bg-[var(--surface-danger-bg)] text-muted-foreground hover:text-[var(--surface-danger-text)] transition-all active:scale-90 ${focusRing3}`, title: "Delete", children: /* @__PURE__ */ jsx11(Trash22, { className: "h-4 w-4" }) })
1954
1958
  ] }) })
1955
1959
  ]
1956
1960
  },
@@ -3918,12 +3922,33 @@ import { jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
3918
3922
  function PortsList({ ports, onExposePort, onRemovePort, isExposing = false, className }) {
3919
3923
  const [newPort, setNewPort] = React13.useState("");
3920
3924
  const [copiedPort, setCopiedPort] = React13.useState(null);
3925
+ const [statusMessage, setStatusMessage] = React13.useState("");
3921
3926
  const copyTimerRef = React13.useRef(null);
3927
+ const pendingRemovalRef = React13.useRef(null);
3928
+ const removeButtonsRef = React13.useRef(/* @__PURE__ */ new Map());
3929
+ const portInputRef = React13.useRef(null);
3930
+ const portInputId = React13.useId();
3931
+ const portErrorId = React13.useId();
3932
+ const portNumber = Number(newPort);
3933
+ const validPort = /^\d+$/.test(newPort) && portNumber >= 1 && portNumber <= 65535;
3934
+ const duplicatePort = validPort && ports.some((port) => port.port === portNumber);
3935
+ const portError = newPort === "" ? null : !validPort ? "Enter a whole port number from 1 to 65535." : duplicatePort ? `Port ${portNumber} is already exposed.` : null;
3922
3936
  React13.useEffect(() => {
3923
3937
  return () => {
3924
3938
  if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
3925
3939
  };
3926
3940
  }, []);
3941
+ React13.useEffect(() => {
3942
+ const pending = pendingRemovalRef.current;
3943
+ if (!pending || ports.some((port) => port.port === pending.removed)) return;
3944
+ setStatusMessage(`Port ${pending.removed} removed.`);
3945
+ if (pending.restoreFocus) {
3946
+ const nextButton = pending.next == null ? null : removeButtonsRef.current.get(pending.next);
3947
+ const focusTarget = nextButton ?? removeButtonsRef.current.values().next().value ?? portInputRef.current;
3948
+ focusTarget?.focus();
3949
+ }
3950
+ pendingRemovalRef.current = null;
3951
+ }, [ports]);
3927
3952
  const handleCopy = async (url, port) => {
3928
3953
  try {
3929
3954
  await navigator.clipboard.writeText(url);
@@ -3935,87 +3960,114 @@ function PortsList({ ports, onExposePort, onRemovePort, isExposing = false, clas
3935
3960
  }
3936
3961
  };
3937
3962
  const handleExpose = () => {
3938
- const port = parseInt(newPort, 10);
3939
- if (port > 0 && port <= 65535) {
3940
- onExposePort(port);
3963
+ if (validPort && !duplicatePort && !isExposing) {
3964
+ onExposePort(portNumber);
3941
3965
  setNewPort("");
3942
3966
  }
3943
3967
  };
3944
3968
  return /* @__PURE__ */ jsxs22("div", { className: cn("space-y-4", className), children: [
3945
- ports.length > 0 ? /* @__PURE__ */ jsx25("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container overflow-hidden", children: /* @__PURE__ */ jsxs22("table", { className: "w-full text-sm", children: [
3946
- /* @__PURE__ */ jsx25("thead", { className: "bg-surface-container-high border-b border-[var(--md3-outline-variant)]", children: /* @__PURE__ */ jsxs22("tr", { children: [
3947
- /* @__PURE__ */ jsx25("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Port" }),
3948
- /* @__PURE__ */ jsx25("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Public URL" }),
3949
- /* @__PURE__ */ jsx25("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Status" }),
3950
- /* @__PURE__ */ jsx25("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-20" })
3951
- ] }) }),
3952
- /* @__PURE__ */ jsx25("tbody", { className: "divide-y divide-border", children: ports.map((p) => /* @__PURE__ */ jsxs22("tr", { children: [
3953
- /* @__PURE__ */ jsx25("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: p.port }),
3954
- /* @__PURE__ */ jsx25("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs22(
3969
+ /* @__PURE__ */ jsx25("span", { role: "status", className: "sr-only", children: statusMessage }),
3970
+ ports.length > 0 ? /* @__PURE__ */ jsxs22("div", { className: "overflow-hidden rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container text-sm", children: [
3971
+ /* @__PURE__ */ jsxs22("div", { "aria-hidden": "true", className: "hidden border-b border-[var(--md3-outline-variant)] bg-surface-container-high px-3 py-2.5 text-xs font-medium text-muted-foreground sm:grid sm:grid-cols-[4rem_minmax(0,1fr)_5.5rem_3rem] sm:gap-x-2", children: [
3972
+ /* @__PURE__ */ jsx25("span", { children: "Port" }),
3973
+ /* @__PURE__ */ jsx25("span", { children: "Public URL" }),
3974
+ /* @__PURE__ */ jsx25("span", { children: "Status" })
3975
+ ] }),
3976
+ /* @__PURE__ */ jsx25("ul", { className: "divide-y divide-border", children: ports.map((p, index) => /* @__PURE__ */ jsxs22("li", { className: "grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-2 gap-y-1 px-3 py-2 sm:grid-cols-[4rem_minmax(0,1fr)_5.5rem_3rem] sm:py-1", children: [
3977
+ /* @__PURE__ */ jsxs22("span", { className: "col-start-1 row-start-1 font-mono text-xs text-foreground", children: [
3978
+ /* @__PURE__ */ jsx25("span", { className: "sr-only", children: "Port " }),
3979
+ p.port
3980
+ ] }),
3981
+ /* @__PURE__ */ jsxs22(
3955
3982
  "button",
3956
3983
  {
3957
3984
  type: "button",
3985
+ "aria-label": copiedPort === p.port ? `Copied public URL for port ${p.port}` : `Copy public URL for port ${p.port}: ${p.url}`,
3958
3986
  onClick: () => handleCopy(p.url, p.port),
3959
- className: "flex items-center gap-2 font-mono text-xs text-primary hover:underline cursor-pointer group",
3987
+ className: `col-span-2 row-start-3 flex min-h-10 min-w-0 items-center gap-2 rounded text-left font-mono text-xs text-[var(--accent-text)] hover:underline sm:col-span-1 sm:col-start-2 sm:row-start-1 ${focusRing5}`,
3960
3988
  children: [
3961
- /* @__PURE__ */ jsx25("span", { className: "truncate max-w-[300px]", children: p.url }),
3962
- copiedPort === p.port ? /* @__PURE__ */ jsx25(Check5, { className: "h-3 w-3 text-[var(--surface-success-text)] shrink-0" }) : /* @__PURE__ */ jsx25(Copy3, { className: "h-3 w-3 opacity-0 group-hover:opacity-100 transition-opacity shrink-0" })
3989
+ /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", className: "min-w-0 flex-1 break-all sm:truncate", children: p.url }),
3990
+ copiedPort === p.port ? /* @__PURE__ */ jsx25(Check5, { className: "h-4 w-4 shrink-0 text-[var(--surface-success-text)]", "aria-hidden": "true" }) : /* @__PURE__ */ jsx25(Copy3, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })
3963
3991
  ]
3964
3992
  }
3965
- ) }),
3966
- /* @__PURE__ */ jsx25("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx25("span", { className: cn(
3967
- "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
3993
+ ),
3994
+ /* @__PURE__ */ jsxs22("span", { className: cn(
3995
+ "col-start-1 row-start-2 inline-flex items-center justify-self-start rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider sm:col-start-3 sm:row-start-1",
3968
3996
  p.status === "active" ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]"
3969
- ), children: p.status }) }),
3970
- /* @__PURE__ */ jsx25("td", { className: "px-4 py-3 text-right", children: onRemovePort && /* @__PURE__ */ jsx25(
3997
+ ), children: [
3998
+ /* @__PURE__ */ jsx25("span", { className: "sr-only", children: "Status: " }),
3999
+ p.status
4000
+ ] }),
4001
+ onRemovePort && /* @__PURE__ */ jsx25(
3971
4002
  "button",
3972
4003
  {
3973
4004
  type: "button",
3974
- onClick: () => onRemovePort(p.port),
3975
- className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
3976
- children: /* @__PURE__ */ jsx25(Trash23, { className: "h-3.5 w-3.5" })
4005
+ "aria-label": `Remove port ${p.port}`,
4006
+ ref: (button) => {
4007
+ if (button) removeButtonsRef.current.set(p.port, button);
4008
+ else removeButtonsRef.current.delete(p.port);
4009
+ },
4010
+ onClick: (event) => {
4011
+ pendingRemovalRef.current = {
4012
+ removed: p.port,
4013
+ next: ports[index + 1]?.port ?? ports[index - 1]?.port ?? null,
4014
+ restoreFocus: event.detail === 0
4015
+ };
4016
+ onRemovePort(p.port);
4017
+ },
4018
+ className: `col-start-2 row-start-1 inline-flex min-h-10 min-w-10 items-center justify-center rounded text-muted-foreground transition-colors hover:text-destructive sm:col-start-4 ${focusRing5}`,
4019
+ children: /* @__PURE__ */ jsx25(Trash23, { className: "h-4 w-4", "aria-hidden": "true" })
3977
4020
  }
3978
- ) })
4021
+ )
3979
4022
  ] }, p.port)) })
3980
- ] }) }) : /* @__PURE__ */ jsxs22("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", children: [
4023
+ ] }) : /* @__PURE__ */ jsxs22("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", children: [
3981
4024
  /* @__PURE__ */ jsx25(Globe, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
3982
4025
  /* @__PURE__ */ jsx25("p", { className: "text-sm text-muted-foreground", children: "No ports exposed yet" })
3983
4026
  ] }),
3984
- /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-3", children: [
3985
- /* @__PURE__ */ jsx25(
3986
- "input",
3987
- {
3988
- type: "number",
3989
- min: 1,
3990
- max: 65535,
3991
- placeholder: "Port (e.g. 3000)",
3992
- value: newPort,
3993
- onChange: (e) => setNewPort(e.target.value),
3994
- onKeyDown: (e) => e.key === "Enter" && handleExpose(),
3995
- className: `flex-1 rounded-lg border bg-surface-container-low px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground ${focusField}`
3996
- }
3997
- ),
4027
+ /* @__PURE__ */ jsxs22("div", { className: "flex flex-wrap items-end gap-3", children: [
4028
+ /* @__PURE__ */ jsxs22("div", { className: "min-w-0 flex-[1_1_10rem]", children: [
4029
+ /* @__PURE__ */ jsx25("label", { htmlFor: portInputId, className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Port number" }),
4030
+ /* @__PURE__ */ jsx25(
4031
+ "input",
4032
+ {
4033
+ ref: portInputRef,
4034
+ id: portInputId,
4035
+ type: "number",
4036
+ min: 1,
4037
+ max: 65535,
4038
+ step: 1,
4039
+ "aria-invalid": Boolean(portError),
4040
+ "aria-describedby": portError ? portErrorId : void 0,
4041
+ placeholder: "3000",
4042
+ value: newPort,
4043
+ onChange: (e) => setNewPort(e.target.value),
4044
+ onKeyDown: (e) => e.key === "Enter" && handleExpose(),
4045
+ className: `w-full min-w-0 rounded-lg border bg-surface-container-low px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground ${focusField}`
4046
+ }
4047
+ )
4048
+ ] }),
3998
4049
  /* @__PURE__ */ jsxs22(
3999
4050
  "button",
4000
4051
  {
4001
4052
  type: "button",
4002
4053
  onClick: handleExpose,
4003
- disabled: !newPort || isExposing,
4004
- className: `inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground shadow-sm transition-colors hover:bg-primary/90 disabled:cursor-not-allowed disabled:bg-primary/55 disabled:text-primary-foreground/90 disabled:shadow-none ${focusRing5}`,
4054
+ disabled: !validPort || duplicatePort || isExposing,
4055
+ className: `inline-flex shrink-0 items-center gap-2 rounded-lg bg-[var(--btn-primary-bg)] px-4 py-2 text-sm font-semibold text-[var(--btn-primary-text)] shadow-sm transition-colors hover:bg-[var(--btn-primary-hover)] disabled:cursor-not-allowed disabled:opacity-55 disabled:shadow-none ${focusRing5}`,
4005
4056
  children: [
4006
4057
  /* @__PURE__ */ jsx25(Plus4, { className: "h-4 w-4" }),
4007
4058
  "Expose"
4008
4059
  ]
4009
4060
  }
4010
4061
  )
4011
- ] })
4062
+ ] }),
4063
+ portError && /* @__PURE__ */ jsx25("p", { id: portErrorId, className: "text-xs text-[var(--surface-danger-text)]", children: portError })
4012
4064
  ] });
4013
4065
  }
4014
4066
 
4015
4067
  // src/dashboard/process-list.tsx
4016
4068
  import * as React14 from "react";
4017
4069
  import { Activity as Activity3, Plus as Plus5, Skull, Terminal as Terminal4 } from "lucide-react";
4018
- import { focusField as focusField2 } from "@tangle-network/ui/utils";
4070
+ import { focusField as focusField2, focusRing as focusRing6 } from "@tangle-network/ui/utils";
4019
4071
  import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
4020
4072
  function formatUptime(startedAt) {
4021
4073
  if (!startedAt) return "-";
@@ -4027,6 +4079,18 @@ function formatUptime(startedAt) {
4027
4079
  }
4028
4080
  function ProcessList({ processes, onSpawn, onKill, loading = false, className }) {
4029
4081
  const [newCommand, setNewCommand] = React14.useState("");
4082
+ const [statusMessage, setStatusMessage] = React14.useState("");
4083
+ const commandId = React14.useId();
4084
+ const commandInputRef = React14.useRef(null);
4085
+ const pendingKillRef = React14.useRef(null);
4086
+ React14.useEffect(() => {
4087
+ const pending = pendingKillRef.current;
4088
+ if (!pending || processes.some((process) => process.pid === pending.pid && process.running)) return;
4089
+ const stopped = processes.find((process) => process.pid === pending.pid);
4090
+ setStatusMessage(stopped?.exitCode == null ? `Process ${pending.pid} stopped.` : `Process ${pending.pid} exited with code ${stopped.exitCode}.`);
4091
+ if (pending.restoreFocus) commandInputRef.current?.focus();
4092
+ pendingKillRef.current = null;
4093
+ }, [processes]);
4030
4094
  const handleSpawn = () => {
4031
4095
  const cmd = newCommand.trim();
4032
4096
  if (cmd) {
@@ -4035,61 +4099,73 @@ function ProcessList({ processes, onSpawn, onKill, loading = false, className })
4035
4099
  }
4036
4100
  };
4037
4101
  return /* @__PURE__ */ jsxs23("div", { className: cn("space-y-4", className), children: [
4102
+ /* @__PURE__ */ jsx26("span", { role: "status", className: "sr-only", children: statusMessage }),
4038
4103
  loading ? /* @__PURE__ */ jsxs23("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", children: [
4039
4104
  /* @__PURE__ */ jsx26(Activity3, { className: "mx-auto h-6 w-6 text-muted-foreground animate-spin mb-2", "data-motion": "essential" }),
4040
4105
  /* @__PURE__ */ jsx26("p", { className: "text-sm text-muted-foreground", children: "Loading processes..." })
4041
- ] }) : processes.length > 0 ? /* @__PURE__ */ jsx26("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container overflow-hidden", children: /* @__PURE__ */ jsxs23("table", { className: "w-full text-sm", children: [
4042
- /* @__PURE__ */ jsx26("thead", { className: "bg-surface-container-high border-b border-[var(--md3-outline-variant)]", children: /* @__PURE__ */ jsxs23("tr", { children: [
4043
- /* @__PURE__ */ jsx26("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "PID" }),
4044
- /* @__PURE__ */ jsx26("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Command" }),
4045
- /* @__PURE__ */ jsx26("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Status" }),
4046
- /* @__PURE__ */ jsx26("th", { className: "px-4 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Uptime" }),
4047
- /* @__PURE__ */ jsx26("th", { className: "px-4 py-2.5 text-right text-xs font-medium text-muted-foreground w-20" })
4048
- ] }) }),
4049
- /* @__PURE__ */ jsx26("tbody", { className: "divide-y divide-border", children: processes.map((p) => /* @__PURE__ */ jsxs23("tr", { children: [
4050
- /* @__PURE__ */ jsx26("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: p.pid }),
4051
- /* @__PURE__ */ jsx26("td", { className: "px-4 py-3 font-mono text-xs text-foreground truncate max-w-[250px]", children: p.command }),
4052
- /* @__PURE__ */ jsx26("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx26("span", { className: cn(
4053
- "inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
4054
- p.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-surface-container-high text-muted-foreground"
4055
- ), children: p.running ? "running" : `exited (${p.exitCode ?? "?"})` }) }),
4056
- /* @__PURE__ */ jsx26("td", { className: "px-4 py-3 font-mono text-xs text-muted-foreground", children: formatUptime(p.startedAt) }),
4057
- /* @__PURE__ */ jsx26("td", { className: "px-4 py-3 text-right", children: p.running && /* @__PURE__ */ jsx26(
4106
+ ] }) : processes.length > 0 ? /* @__PURE__ */ jsx26("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container overflow-hidden", children: /* @__PURE__ */ jsx26("ul", { "aria-label": "Processes", className: "divide-y divide-border", children: processes.map((p) => {
4107
+ const age = formatUptime(p.startedAt);
4108
+ return /* @__PURE__ */ jsxs23("li", { className: "flex min-w-0 items-start gap-3 px-3 py-3", children: [
4109
+ /* @__PURE__ */ jsxs23("div", { className: "min-w-0 flex-1", children: [
4110
+ /* @__PURE__ */ jsx26("code", { className: "block break-all font-mono text-xs leading-5 text-foreground", children: p.command }),
4111
+ /* @__PURE__ */ jsxs23("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
4112
+ /* @__PURE__ */ jsxs23("span", { className: "font-mono tabular-nums", children: [
4113
+ "PID ",
4114
+ p.pid
4115
+ ] }),
4116
+ /* @__PURE__ */ jsx26("span", { className: cn(
4117
+ "inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider",
4118
+ p.running ? "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]" : "bg-surface-container-high text-muted-foreground"
4119
+ ), children: p.running ? "running" : `exited (${p.exitCode ?? "?"})` }),
4120
+ age !== "-" && /* @__PURE__ */ jsx26("span", { className: "font-mono tabular-nums", children: p.running ? `Uptime ${age}` : `Started ${age} ago` })
4121
+ ] })
4122
+ ] }),
4123
+ p.running && /* @__PURE__ */ jsx26(
4058
4124
  "button",
4059
4125
  {
4060
4126
  type: "button",
4061
- onClick: () => onKill(p.pid),
4062
- className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
4063
- title: "Kill process",
4064
- children: /* @__PURE__ */ jsx26(Skull, { className: "h-3.5 w-3.5" })
4127
+ onClick: (event) => {
4128
+ pendingKillRef.current = { pid: p.pid, restoreFocus: event.detail === 0 };
4129
+ onKill(p.pid);
4130
+ },
4131
+ className: `flex size-10 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive ${focusRing6}`,
4132
+ "aria-label": `Kill process ${p.pid}`,
4133
+ title: `Kill process ${p.pid}`,
4134
+ children: /* @__PURE__ */ jsx26(Skull, { "aria-hidden": "true", className: "h-4 w-4" })
4065
4135
  }
4066
- ) })
4067
- ] }, `${p.pid}-${p.startedAt ?? p.command}`)) })
4068
- ] }) }) : /* @__PURE__ */ jsxs23("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", children: [
4136
+ )
4137
+ ] }, `${p.pid}-${p.startedAt ?? p.command}`);
4138
+ }) }) }) : /* @__PURE__ */ jsxs23("div", { className: "rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container p-6 text-center", children: [
4069
4139
  /* @__PURE__ */ jsx26(Terminal4, { className: "mx-auto h-8 w-8 text-muted-foreground mb-2" }),
4070
4140
  /* @__PURE__ */ jsx26("p", { className: "text-sm text-muted-foreground", children: "No processes running" })
4071
4141
  ] }),
4072
- /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-3", children: [
4073
- /* @__PURE__ */ jsx26(
4074
- "input",
4075
- {
4076
- type: "text",
4077
- placeholder: "Command (e.g. node server.js)",
4078
- value: newCommand,
4079
- onChange: (e) => setNewCommand(e.target.value),
4080
- onKeyDown: (e) => e.key === "Enter" && handleSpawn(),
4081
- className: `flex-1 rounded-lg border bg-surface-container-low px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground ${focusField2}`
4082
- }
4083
- ),
4142
+ /* @__PURE__ */ jsxs23("form", { onSubmit: (event) => {
4143
+ event.preventDefault();
4144
+ handleSpawn();
4145
+ }, className: "flex min-w-0 flex-wrap items-end gap-2", children: [
4146
+ /* @__PURE__ */ jsxs23("div", { className: "min-w-0 flex-[1_1_12rem]", children: [
4147
+ /* @__PURE__ */ jsx26("label", { htmlFor: commandId, className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Command" }),
4148
+ /* @__PURE__ */ jsx26(
4149
+ "input",
4150
+ {
4151
+ ref: commandInputRef,
4152
+ id: commandId,
4153
+ type: "text",
4154
+ placeholder: "node app.js",
4155
+ value: newCommand,
4156
+ onChange: (e) => setNewCommand(e.target.value),
4157
+ className: `w-full min-w-0 rounded-lg border bg-surface-container-low px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground ${focusField2}`
4158
+ }
4159
+ )
4160
+ ] }),
4084
4161
  /* @__PURE__ */ jsxs23(
4085
4162
  "button",
4086
4163
  {
4087
- type: "button",
4088
- onClick: handleSpawn,
4164
+ type: "submit",
4089
4165
  disabled: !newCommand.trim(),
4090
- className: "inline-flex items-center gap-2 rounded-lg bg-primary/20 border border-primary/30 px-4 py-2 text-sm font-medium text-primary hover:bg-primary hover:text-primary-foreground transition-colors disabled:opacity-50",
4166
+ className: `inline-flex h-10 shrink-0 items-center gap-2 rounded-lg border border-[var(--border-accent)] bg-[var(--accent-surface-soft)] px-3 text-sm font-medium text-[var(--accent-text)] transition-colors hover:bg-[var(--btn-primary-bg)] hover:text-[var(--btn-primary-text)] disabled:opacity-50 ${focusRing6}`,
4091
4167
  children: [
4092
- /* @__PURE__ */ jsx26(Plus5, { className: "h-4 w-4" }),
4168
+ /* @__PURE__ */ jsx26(Plus5, { "aria-hidden": "true", className: "h-4 w-4" }),
4093
4169
  "Spawn"
4094
4170
  ]
4095
4171
  }
@@ -4101,7 +4177,7 @@ function ProcessList({ processes, onSpawn, onKill, loading = false, className })
4101
4177
  // src/dashboard/network-config.tsx
4102
4178
  import * as React15 from "react";
4103
4179
  import { Network as Network2, Plus as Plus6, Trash2 as Trash24, ShieldAlert } from "lucide-react";
4104
- import { focusField as focusField3 } from "@tangle-network/ui/utils";
4180
+ import { focusField as focusField3, focusRing as focusRing7 } from "@tangle-network/ui/utils";
4105
4181
  import { jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
4106
4182
  function NetworkConfig({ config, onUpdate, loading = false, className }) {
4107
4183
  const [newCidr, setNewCidr] = React15.useState("");
@@ -4171,8 +4247,9 @@ function NetworkConfig({ config, onUpdate, loading = false, className }) {
4171
4247
  "button",
4172
4248
  {
4173
4249
  type: "button",
4250
+ "aria-label": `Remove ${cidr} from allowlist`,
4174
4251
  onClick: () => handleRemoveCidr(cidr),
4175
- className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
4252
+ className: `p-2 text-muted-foreground hover:text-destructive transition-colors rounded ${focusRing7}`,
4176
4253
  children: /* @__PURE__ */ jsx27(Trash24, { className: "h-3 w-3" })
4177
4254
  }
4178
4255
  )
@@ -4182,6 +4259,7 @@ function NetworkConfig({ config, onUpdate, loading = false, className }) {
4182
4259
  "input",
4183
4260
  {
4184
4261
  type: "text",
4262
+ "aria-label": "CIDR to allow",
4185
4263
  placeholder: "CIDR (e.g. 10.0.0.0/8)",
4186
4264
  value: newCidr,
4187
4265
  onChange: (e) => setNewCidr(e.target.value),
@@ -4210,7 +4288,7 @@ function NetworkConfig({ config, onUpdate, loading = false, className }) {
4210
4288
  // src/dashboard/backend-config.tsx
4211
4289
  import * as React16 from "react";
4212
4290
  import { Bot as Bot2, Plus as Plus7, Trash2 as Trash25, Server, Wrench } from "lucide-react";
4213
- import { focusField as focusField4 } from "@tangle-network/ui/utils";
4291
+ import { focusField as focusField4, focusRing as focusRing8 } from "@tangle-network/ui/utils";
4214
4292
  import { Fragment as Fragment14, jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
4215
4293
  function BackendConfig({
4216
4294
  status,
@@ -4314,44 +4392,53 @@ function BackendConfig({
4314
4392
  "button",
4315
4393
  {
4316
4394
  type: "button",
4395
+ "aria-label": `Remove ${s.name} MCP server`,
4317
4396
  onClick: () => onRemoveMcp(s.name),
4318
- className: "p-1 text-muted-foreground hover:text-destructive transition-colors rounded",
4397
+ className: `p-2 text-muted-foreground hover:text-destructive transition-colors rounded ${focusRing8}`,
4319
4398
  children: /* @__PURE__ */ jsx28(Trash25, { className: "h-3.5 w-3.5" })
4320
4399
  }
4321
4400
  )
4322
4401
  ] })
4323
4402
  ] }, s.name)) }) : /* @__PURE__ */ jsx28("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsx28("p", { className: "text-xs text-muted-foreground", children: "No MCP servers configured" }) }),
4324
4403
  showAddMcp && /* @__PURE__ */ jsxs25("div", { className: "p-4 border-t border-[var(--md3-outline-variant)] bg-surface-container-high space-y-2", children: [
4325
- /* @__PURE__ */ jsx28(
4326
- "input",
4327
- {
4328
- type: "text",
4329
- placeholder: "Server name",
4330
- value: mcpName,
4331
- onChange: (e) => setMcpName(e.target.value),
4332
- className: `w-full rounded-lg border bg-surface-container-low px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground ${focusField4}`
4333
- }
4334
- ),
4335
- /* @__PURE__ */ jsx28(
4336
- "input",
4337
- {
4338
- type: "text",
4339
- placeholder: "Command (e.g. npx @server/mcp)",
4340
- value: mcpCommand,
4341
- onChange: (e) => setMcpCommand(e.target.value),
4342
- className: `w-full rounded-lg border bg-surface-container-low px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground ${focusField4}`
4343
- }
4344
- ),
4345
- /* @__PURE__ */ jsx28(
4346
- "input",
4347
- {
4348
- type: "text",
4349
- placeholder: "Arguments (space-separated, optional)",
4350
- value: mcpArgs,
4351
- onChange: (e) => setMcpArgs(e.target.value),
4352
- className: `w-full rounded-lg border bg-surface-container-low px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground ${focusField4}`
4353
- }
4354
- ),
4404
+ /* @__PURE__ */ jsxs25("label", { className: "block space-y-1 text-xs font-medium text-muted-foreground", children: [
4405
+ /* @__PURE__ */ jsx28("span", { children: "Server name" }),
4406
+ /* @__PURE__ */ jsx28(
4407
+ "input",
4408
+ {
4409
+ type: "text",
4410
+ value: mcpName,
4411
+ onChange: (e) => setMcpName(e.target.value),
4412
+ className: `w-full rounded-lg border bg-surface-container-low px-3 py-2 text-sm text-foreground ${focusField4}`
4413
+ }
4414
+ )
4415
+ ] }),
4416
+ /* @__PURE__ */ jsxs25("label", { className: "block space-y-1 text-xs font-medium text-muted-foreground", children: [
4417
+ /* @__PURE__ */ jsx28("span", { children: "Command" }),
4418
+ /* @__PURE__ */ jsx28(
4419
+ "input",
4420
+ {
4421
+ type: "text",
4422
+ placeholder: "npx @server/mcp",
4423
+ value: mcpCommand,
4424
+ onChange: (e) => setMcpCommand(e.target.value),
4425
+ className: `w-full rounded-lg border bg-surface-container-low px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground ${focusField4}`
4426
+ }
4427
+ )
4428
+ ] }),
4429
+ /* @__PURE__ */ jsxs25("label", { className: "block space-y-1 text-xs font-medium text-muted-foreground", children: [
4430
+ /* @__PURE__ */ jsx28("span", { children: "Arguments (optional)" }),
4431
+ /* @__PURE__ */ jsx28(
4432
+ "input",
4433
+ {
4434
+ type: "text",
4435
+ placeholder: "Space-separated arguments",
4436
+ value: mcpArgs,
4437
+ onChange: (e) => setMcpArgs(e.target.value),
4438
+ className: `w-full rounded-lg border bg-surface-container-low px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground ${focusField4}`
4439
+ }
4440
+ )
4441
+ ] }),
4355
4442
  /* @__PURE__ */ jsxs25("div", { className: "flex justify-end gap-2 pt-1", children: [
4356
4443
  /* @__PURE__ */ jsx28(
4357
4444
  "button",
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-ULPLRBMF.js";
4
4
  import {
5
5
  PortsList
6
- } from "./chunk-SX344CDB.js";
6
+ } from "./chunk-DYXXYGS4.js";
7
7
  import {
8
8
  TerminalView
9
9
  } from "./chunk-IACRDXER.js";
package/dist/dashboard.js CHANGED
@@ -60,7 +60,7 @@ import {
60
60
  chatCapableHarnesses,
61
61
  parseInsufficientBalance,
62
62
  useSidebar
63
- } from "./chunk-SX344CDB.js";
63
+ } from "./chunk-DYXXYGS4.js";
64
64
  import {
65
65
  BillingDashboard,
66
66
  InfoPanel,
package/dist/globals.css CHANGED
@@ -476,9 +476,27 @@
476
476
  .z-\[100\] {
477
477
  z-index: 100;
478
478
  }
479
+ .col-span-2 {
480
+ grid-column: span 2 / span 2;
481
+ }
479
482
  .col-span-12 {
480
483
  grid-column: span 12 / span 12;
481
484
  }
485
+ .col-start-1 {
486
+ grid-column-start: 1;
487
+ }
488
+ .col-start-2 {
489
+ grid-column-start: 2;
490
+ }
491
+ .row-start-1 {
492
+ grid-row-start: 1;
493
+ }
494
+ .row-start-2 {
495
+ grid-row-start: 2;
496
+ }
497
+ .row-start-3 {
498
+ grid-row-start: 3;
499
+ }
482
500
  .container {
483
501
  width: 100%;
484
502
  @media (width >= 40rem) {
@@ -961,6 +979,9 @@
961
979
  .min-h-8 {
962
980
  min-height: calc(var(--spacing) * 8);
963
981
  }
982
+ .min-h-10 {
983
+ min-height: calc(var(--spacing) * 10);
984
+ }
964
985
  .min-h-24 {
965
986
  min-height: calc(var(--spacing) * 24);
966
987
  }
@@ -1315,12 +1336,6 @@
1315
1336
  .max-w-\[140px\] {
1316
1337
  max-width: 140px;
1317
1338
  }
1318
- .max-w-\[250px\] {
1319
- max-width: 250px;
1320
- }
1321
- .max-w-\[300px\] {
1322
- max-width: 300px;
1323
- }
1324
1339
  .max-w-\[320px\] {
1325
1340
  max-width: 320px;
1326
1341
  }
@@ -1426,6 +1441,9 @@
1426
1441
  .min-w-4 {
1427
1442
  min-width: calc(var(--spacing) * 4);
1428
1443
  }
1444
+ .min-w-10 {
1445
+ min-width: calc(var(--spacing) * 10);
1446
+ }
1429
1447
  .min-w-40 {
1430
1448
  min-width: calc(var(--spacing) * 40);
1431
1449
  }
@@ -1465,6 +1483,12 @@
1465
1483
  .flex-1 {
1466
1484
  flex: 1;
1467
1485
  }
1486
+ .flex-\[1_1_10rem\] {
1487
+ flex: 1 1 10rem;
1488
+ }
1489
+ .flex-\[1_1_12rem\] {
1490
+ flex: 1 1 12rem;
1491
+ }
1468
1492
  .flex-\[2\] {
1469
1493
  flex: 2;
1470
1494
  }
@@ -1629,6 +1653,9 @@
1629
1653
  .grid-cols-\[auto_minmax\(0\,1fr\)\] {
1630
1654
  grid-template-columns: auto minmax(0,1fr);
1631
1655
  }
1656
+ .grid-cols-\[minmax\(0\,1fr\)_auto\] {
1657
+ grid-template-columns: minmax(0,1fr) auto;
1658
+ }
1632
1659
  .flex-col {
1633
1660
  flex-direction: column;
1634
1661
  }
@@ -1834,6 +1861,9 @@
1834
1861
  .self-start {
1835
1862
  align-self: flex-start;
1836
1863
  }
1864
+ .justify-self-start {
1865
+ justify-self: flex-start;
1866
+ }
1837
1867
  .truncate {
1838
1868
  overflow: hidden;
1839
1869
  text-overflow: ellipsis;
@@ -4127,6 +4157,9 @@
4127
4157
  .hover\:bg-\[var\(--bg-hover\,hsl\(var\(--accent\)\)\)\]:hover {
4128
4158
  background-color: var(--bg-hover,hsl(var(--accent)));
4129
4159
  }
4160
+ .hover\:bg-\[var\(--btn-primary-bg\)\]:hover {
4161
+ background-color: var(--btn-primary-bg);
4162
+ }
4130
4163
  .hover\:bg-\[var\(--btn-primary-hover\)\]:hover {
4131
4164
  background-color: var(--btn-primary-hover);
4132
4165
  }
@@ -4345,6 +4378,9 @@
4345
4378
  .hover\:text-\[var\(--accent-text\)\]:hover {
4346
4379
  color: var(--accent-text);
4347
4380
  }
4381
+ .hover\:text-\[var\(--btn-primary-text\)\]:hover {
4382
+ color: var(--btn-primary-text);
4383
+ }
4348
4384
  .hover\:text-\[var\(--status-error\,\#ff4d6d\)\]:hover {
4349
4385
  color: var(--status-error,#ff4d6d);
4350
4386
  }
@@ -4568,18 +4604,6 @@
4568
4604
  .disabled\:cursor-not-allowed:disabled {
4569
4605
  cursor: not-allowed;
4570
4606
  }
4571
- .disabled\:bg-primary\/55:disabled {
4572
- background-color: color-mix(in srgb, hsl(var(--primary)) 55%, transparent);
4573
- @supports (color: color-mix(in lab, red, red)) {
4574
- background-color: color-mix(in oklab, var(--color-primary) 55%, transparent);
4575
- }
4576
- }
4577
- .disabled\:text-primary-foreground\/90:disabled {
4578
- color: color-mix(in srgb, hsl(var(--primary-foreground)) 90%, transparent);
4579
- @supports (color: color-mix(in lab, red, red)) {
4580
- color: color-mix(in oklab, var(--color-primary-foreground) 90%, transparent);
4581
- }
4582
- }
4583
4607
  .disabled\:opacity-30:disabled {
4584
4608
  opacity: 30%;
4585
4609
  }
@@ -4589,6 +4613,9 @@
4589
4613
  .disabled\:opacity-50:disabled {
4590
4614
  opacity: 50%;
4591
4615
  }
4616
+ .disabled\:opacity-55:disabled {
4617
+ opacity: 55%;
4618
+ }
4592
4619
  .disabled\:opacity-60:disabled {
4593
4620
  opacity: 60%;
4594
4621
  }
@@ -4735,6 +4762,24 @@
4735
4762
  }
4736
4763
  }
4737
4764
  @media (width >= 40rem) {
4765
+ .sm\:col-span-1 {
4766
+ grid-column: span 1 / span 1;
4767
+ }
4768
+ .sm\:col-start-2 {
4769
+ grid-column-start: 2;
4770
+ }
4771
+ .sm\:col-start-3 {
4772
+ grid-column-start: 3;
4773
+ }
4774
+ .sm\:col-start-4 {
4775
+ grid-column-start: 4;
4776
+ }
4777
+ .sm\:row-start-1 {
4778
+ grid-row-start: 1;
4779
+ }
4780
+ .sm\:grid {
4781
+ display: grid;
4782
+ }
4738
4783
  .sm\:hidden {
4739
4784
  display: none;
4740
4785
  }
@@ -4756,6 +4801,9 @@
4756
4801
  .sm\:grid-cols-4 {
4757
4802
  grid-template-columns: repeat(4, minmax(0, 1fr));
4758
4803
  }
4804
+ .sm\:grid-cols-\[4rem_minmax\(0\,1fr\)_5\.5rem_3rem\] {
4805
+ grid-template-columns: 4rem minmax(0,1fr) 5.5rem 3rem;
4806
+ }
4759
4807
  .sm\:flex-row {
4760
4808
  flex-direction: row;
4761
4809
  }
@@ -4774,11 +4822,19 @@
4774
4822
  .sm\:gap-2 {
4775
4823
  gap: calc(var(--spacing) * 2);
4776
4824
  }
4825
+ .sm\:gap-x-2 {
4826
+ column-gap: calc(var(--spacing) * 2);
4827
+ }
4777
4828
  :where(.sm\:space-x-2 > :not(:last-child)) {
4778
4829
  --tw-space-x-reverse: 0;
4779
4830
  margin-inline-start: calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));
4780
4831
  margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
4781
4832
  }
4833
+ .sm\:truncate {
4834
+ overflow: hidden;
4835
+ text-overflow: ellipsis;
4836
+ white-space: nowrap;
4837
+ }
4782
4838
  .sm\:p-5 {
4783
4839
  padding: calc(var(--spacing) * 5);
4784
4840
  }
@@ -4791,6 +4847,9 @@
4791
4847
  .sm\:px-6 {
4792
4848
  padding-inline: calc(var(--spacing) * 6);
4793
4849
  }
4850
+ .sm\:py-1 {
4851
+ padding-block: var(--spacing);
4852
+ }
4794
4853
  .sm\:py-4 {
4795
4854
  padding-block: calc(var(--spacing) * 4);
4796
4855
  }
package/dist/index.js CHANGED
@@ -155,7 +155,7 @@ import {
155
155
  buildUnifiedPatch,
156
156
  computeDiffStats,
157
157
  resolveLanguage
158
- } from "./chunk-NNARED4T.js";
158
+ } from "./chunk-UCQNOUN3.js";
159
159
  import {
160
160
  CSV_PREVIEW_ROW_LIMIT,
161
161
  FileArtifactPane,
@@ -234,7 +234,7 @@ import {
234
234
  chatCapableHarnesses,
235
235
  parseInsufficientBalance,
236
236
  useSidebar
237
- } from "./chunk-SX344CDB.js";
237
+ } from "./chunk-DYXXYGS4.js";
238
238
  import {
239
239
  BillingDashboard,
240
240
  InfoPanel,
@@ -27,6 +27,8 @@ interface IntegrationConnector {
27
27
  interface IntegrationProvider {
28
28
  providerId: string;
29
29
  displayName?: string;
30
+ /** The hub catalog's name for the provider ("GitHub", "Google Sheets"). */
31
+ title?: string;
30
32
  description?: string;
31
33
  iconUrl?: string;
32
34
  connectors?: IntegrationConnector[];
@@ -147,10 +147,10 @@ function rankOf(provider, rank) {
147
147
  return norm ?? Number.MAX_SAFE_INTEGER;
148
148
  }
149
149
  function displayNameOf(provider) {
150
- return provider.displayName ?? provider.providerId.replace(/[-_]/g, " ");
150
+ return provider.displayName ?? provider.title ?? provider.providerId.replace(/[-_]/g, " ");
151
151
  }
152
152
  function matchesQuery(provider, q) {
153
- const hay = `${provider.displayName ?? ""} ${provider.providerId} ${provider.description ?? ""}`.toLowerCase();
153
+ const hay = `${displayNameOf(provider)} ${provider.providerId} ${provider.description ?? ""}`.toLowerCase();
154
154
  return hay.includes(q);
155
155
  }
156
156
  function IntegrationsPanel({
@@ -372,7 +372,7 @@ function IntegrationsPanel({
372
372
  {
373
373
  id: provider.providerId,
374
374
  iconUrl: provider.iconUrl,
375
- displayName: provider.displayName,
375
+ displayName: displayNameOf(provider),
376
376
  size: LOGO_SIZE,
377
377
  className: "rounded-2xl"
378
378
  }
@@ -497,7 +497,7 @@ function IntegrationsPanel({
497
497
  {
498
498
  id: provider.providerId,
499
499
  iconUrl: provider.iconUrl,
500
- displayName: provider.displayName,
500
+ displayName: displayNameOf(provider),
501
501
  size: LOGO_SIZE,
502
502
  className: "rounded-2xl"
503
503
  }
package/dist/styles.css CHANGED
@@ -476,9 +476,27 @@
476
476
  .z-\[100\] {
477
477
  z-index: 100;
478
478
  }
479
+ .col-span-2 {
480
+ grid-column: span 2 / span 2;
481
+ }
479
482
  .col-span-12 {
480
483
  grid-column: span 12 / span 12;
481
484
  }
485
+ .col-start-1 {
486
+ grid-column-start: 1;
487
+ }
488
+ .col-start-2 {
489
+ grid-column-start: 2;
490
+ }
491
+ .row-start-1 {
492
+ grid-row-start: 1;
493
+ }
494
+ .row-start-2 {
495
+ grid-row-start: 2;
496
+ }
497
+ .row-start-3 {
498
+ grid-row-start: 3;
499
+ }
482
500
  .container {
483
501
  width: 100%;
484
502
  @media (width >= 40rem) {
@@ -961,6 +979,9 @@
961
979
  .min-h-8 {
962
980
  min-height: calc(var(--spacing) * 8);
963
981
  }
982
+ .min-h-10 {
983
+ min-height: calc(var(--spacing) * 10);
984
+ }
964
985
  .min-h-24 {
965
986
  min-height: calc(var(--spacing) * 24);
966
987
  }
@@ -1315,12 +1336,6 @@
1315
1336
  .max-w-\[140px\] {
1316
1337
  max-width: 140px;
1317
1338
  }
1318
- .max-w-\[250px\] {
1319
- max-width: 250px;
1320
- }
1321
- .max-w-\[300px\] {
1322
- max-width: 300px;
1323
- }
1324
1339
  .max-w-\[320px\] {
1325
1340
  max-width: 320px;
1326
1341
  }
@@ -1426,6 +1441,9 @@
1426
1441
  .min-w-4 {
1427
1442
  min-width: calc(var(--spacing) * 4);
1428
1443
  }
1444
+ .min-w-10 {
1445
+ min-width: calc(var(--spacing) * 10);
1446
+ }
1429
1447
  .min-w-40 {
1430
1448
  min-width: calc(var(--spacing) * 40);
1431
1449
  }
@@ -1465,6 +1483,12 @@
1465
1483
  .flex-1 {
1466
1484
  flex: 1;
1467
1485
  }
1486
+ .flex-\[1_1_10rem\] {
1487
+ flex: 1 1 10rem;
1488
+ }
1489
+ .flex-\[1_1_12rem\] {
1490
+ flex: 1 1 12rem;
1491
+ }
1468
1492
  .flex-\[2\] {
1469
1493
  flex: 2;
1470
1494
  }
@@ -1629,6 +1653,9 @@
1629
1653
  .grid-cols-\[auto_minmax\(0\,1fr\)\] {
1630
1654
  grid-template-columns: auto minmax(0,1fr);
1631
1655
  }
1656
+ .grid-cols-\[minmax\(0\,1fr\)_auto\] {
1657
+ grid-template-columns: minmax(0,1fr) auto;
1658
+ }
1632
1659
  .flex-col {
1633
1660
  flex-direction: column;
1634
1661
  }
@@ -1834,6 +1861,9 @@
1834
1861
  .self-start {
1835
1862
  align-self: flex-start;
1836
1863
  }
1864
+ .justify-self-start {
1865
+ justify-self: flex-start;
1866
+ }
1837
1867
  .truncate {
1838
1868
  overflow: hidden;
1839
1869
  text-overflow: ellipsis;
@@ -4127,6 +4157,9 @@
4127
4157
  .hover\:bg-\[var\(--bg-hover\,hsl\(var\(--accent\)\)\)\]:hover {
4128
4158
  background-color: var(--bg-hover,hsl(var(--accent)));
4129
4159
  }
4160
+ .hover\:bg-\[var\(--btn-primary-bg\)\]:hover {
4161
+ background-color: var(--btn-primary-bg);
4162
+ }
4130
4163
  .hover\:bg-\[var\(--btn-primary-hover\)\]:hover {
4131
4164
  background-color: var(--btn-primary-hover);
4132
4165
  }
@@ -4345,6 +4378,9 @@
4345
4378
  .hover\:text-\[var\(--accent-text\)\]:hover {
4346
4379
  color: var(--accent-text);
4347
4380
  }
4381
+ .hover\:text-\[var\(--btn-primary-text\)\]:hover {
4382
+ color: var(--btn-primary-text);
4383
+ }
4348
4384
  .hover\:text-\[var\(--status-error\,\#ff4d6d\)\]:hover {
4349
4385
  color: var(--status-error,#ff4d6d);
4350
4386
  }
@@ -4568,18 +4604,6 @@
4568
4604
  .disabled\:cursor-not-allowed:disabled {
4569
4605
  cursor: not-allowed;
4570
4606
  }
4571
- .disabled\:bg-primary\/55:disabled {
4572
- background-color: color-mix(in srgb, hsl(var(--primary)) 55%, transparent);
4573
- @supports (color: color-mix(in lab, red, red)) {
4574
- background-color: color-mix(in oklab, var(--color-primary) 55%, transparent);
4575
- }
4576
- }
4577
- .disabled\:text-primary-foreground\/90:disabled {
4578
- color: color-mix(in srgb, hsl(var(--primary-foreground)) 90%, transparent);
4579
- @supports (color: color-mix(in lab, red, red)) {
4580
- color: color-mix(in oklab, var(--color-primary-foreground) 90%, transparent);
4581
- }
4582
- }
4583
4607
  .disabled\:opacity-30:disabled {
4584
4608
  opacity: 30%;
4585
4609
  }
@@ -4589,6 +4613,9 @@
4589
4613
  .disabled\:opacity-50:disabled {
4590
4614
  opacity: 50%;
4591
4615
  }
4616
+ .disabled\:opacity-55:disabled {
4617
+ opacity: 55%;
4618
+ }
4592
4619
  .disabled\:opacity-60:disabled {
4593
4620
  opacity: 60%;
4594
4621
  }
@@ -4735,6 +4762,24 @@
4735
4762
  }
4736
4763
  }
4737
4764
  @media (width >= 40rem) {
4765
+ .sm\:col-span-1 {
4766
+ grid-column: span 1 / span 1;
4767
+ }
4768
+ .sm\:col-start-2 {
4769
+ grid-column-start: 2;
4770
+ }
4771
+ .sm\:col-start-3 {
4772
+ grid-column-start: 3;
4773
+ }
4774
+ .sm\:col-start-4 {
4775
+ grid-column-start: 4;
4776
+ }
4777
+ .sm\:row-start-1 {
4778
+ grid-row-start: 1;
4779
+ }
4780
+ .sm\:grid {
4781
+ display: grid;
4782
+ }
4738
4783
  .sm\:hidden {
4739
4784
  display: none;
4740
4785
  }
@@ -4756,6 +4801,9 @@
4756
4801
  .sm\:grid-cols-4 {
4757
4802
  grid-template-columns: repeat(4, minmax(0, 1fr));
4758
4803
  }
4804
+ .sm\:grid-cols-\[4rem_minmax\(0\,1fr\)_5\.5rem_3rem\] {
4805
+ grid-template-columns: 4rem minmax(0,1fr) 5.5rem 3rem;
4806
+ }
4759
4807
  .sm\:flex-row {
4760
4808
  flex-direction: row;
4761
4809
  }
@@ -4774,11 +4822,19 @@
4774
4822
  .sm\:gap-2 {
4775
4823
  gap: calc(var(--spacing) * 2);
4776
4824
  }
4825
+ .sm\:gap-x-2 {
4826
+ column-gap: calc(var(--spacing) * 2);
4827
+ }
4777
4828
  :where(.sm\:space-x-2 > :not(:last-child)) {
4778
4829
  --tw-space-x-reverse: 0;
4779
4830
  margin-inline-start: calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));
4780
4831
  margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
4781
4832
  }
4833
+ .sm\:truncate {
4834
+ overflow: hidden;
4835
+ text-overflow: ellipsis;
4836
+ white-space: nowrap;
4837
+ }
4782
4838
  .sm\:p-5 {
4783
4839
  padding: calc(var(--spacing) * 5);
4784
4840
  }
@@ -4791,6 +4847,9 @@
4791
4847
  .sm\:px-6 {
4792
4848
  padding-inline: calc(var(--spacing) * 6);
4793
4849
  }
4850
+ .sm\:py-1 {
4851
+ padding-block: var(--spacing);
4852
+ }
4794
4853
  .sm\:py-4 {
4795
4854
  padding-block: calc(var(--spacing) * 4);
4796
4855
  }
package/dist/workbench.js CHANGED
@@ -14,9 +14,9 @@ import {
14
14
  buildUnifiedPatch,
15
15
  computeDiffStats,
16
16
  resolveLanguage
17
- } from "./chunk-NNARED4T.js";
17
+ } from "./chunk-UCQNOUN3.js";
18
18
  import "./chunk-ULPLRBMF.js";
19
- import "./chunk-SX344CDB.js";
19
+ import "./chunk-DYXXYGS4.js";
20
20
  import "./chunk-OMUIPJQZ.js";
21
21
  import "./chunk-IACRDXER.js";
22
22
  import "./chunk-MYZAW4RX.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.113.5",
3
+ "version": "0.113.7",
4
4
  "description": "Unified UI component library for Tangle Sandbox — primitives, chat, dashboard, terminal, editor, and workspace components",
5
5
  "repository": {
6
6
  "type": "git",