@dong-victor/dsh-better-sidebar-starter 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -161,7 +161,7 @@ window.__ModuleLoader__.load({
161
161
  }
162
162
  /** The config tree component. */
163
163
  function ConfigTree(props) {
164
- const { configs, instances, selectedInstanceId, selectedConfigId, onSelectConfig, onSelectInstance, onStartConfig, onStopInstance, onEditConfig, onDuplicateConfig } = props;
164
+ const { configs, instances, selectedInstanceId, selectedConfigId, onSelectConfig, onSelectInstance, onStartConfig, onStopInstance, onEditConfig, onDuplicateConfig, onDeleteConfig } = props;
165
165
  const [collapsed, setCollapsed] = (0, react.useState)(/* @__PURE__ */ new Set());
166
166
  const toggleGroup = (type) => {
167
167
  setCollapsed((prev) => {
@@ -228,7 +228,14 @@ window.__ModuleLoader__.load({
228
228
  e.stopPropagation();
229
229
  onDuplicateConfig(config.id);
230
230
  }
231
- }, CopyIcon({ size: 12 }))))];
231
+ }, CopyIcon({ size: 12 })), (0, react.createElement)("button", {
232
+ className: "sts-icon-btn trash",
233
+ title: "删除配置",
234
+ onClick: (e) => {
235
+ e.stopPropagation();
236
+ onDeleteConfig(config.id);
237
+ }
238
+ }, TrashIcon({ size: 12 }))))];
232
239
  for (const inst of configInstances) {
233
240
  const instSelected = selectedInstanceId === inst.id;
234
241
  rows.push((0, react.createElement)("div", {
@@ -2851,7 +2858,10 @@ window.__ModuleLoader__.load({
2851
2858
  }
2852
2859
  } catch {}
2853
2860
  }, [sessionQuery, fetchConfigs]);
2854
- (0, react.useCallback)(async (configId) => {
2861
+ /** Delete a config. */
2862
+ const handleDeleteConfig = (0, react.useCallback)(async (configId) => {
2863
+ const target = configs.find((c) => c.id === configId);
2864
+ if (!globalThis.confirm(`确定删除配置「${target?.name ?? configId}」?运行中的实例将被一并停止。`)) return;
2855
2865
  for (const inst of instances) if (inst.configId === configId && inst.status === "running") await handleStop(inst.id);
2856
2866
  try {
2857
2867
  if ((await fetch(`${API_BASE}/configs?${sessionQuery()}&id=${encodeURIComponent(configId)}`, { method: "DELETE" })).ok) {
@@ -2861,6 +2871,7 @@ window.__ModuleLoader__.load({
2861
2871
  }
2862
2872
  } catch {}
2863
2873
  }, [
2874
+ configs,
2864
2875
  instances,
2865
2876
  handleStop,
2866
2877
  sessionQuery,
@@ -2929,7 +2940,10 @@ window.__ModuleLoader__.load({
2929
2940
  handleStop(id);
2930
2941
  },
2931
2942
  onEditConfig: handleEditConfig,
2932
- onDuplicateConfig: handleDuplicateConfig
2943
+ onDuplicateConfig: handleDuplicateConfig,
2944
+ onDeleteConfig: (id) => {
2945
+ handleDeleteConfig(id);
2946
+ }
2933
2947
  })), (0, react.createElement)("div", {
2934
2948
  className: `sts-split-gutter${dragging ? " dragging" : ""}${isHorizontal ? "" : " sts-split-gutter-v"}`,
2935
2949
  onMouseDown: handleMouseDown
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["createElement","TYPE_LABELS","useState","useMemo","createElement","API_BASE","createElement","useState","useRef","useCallback","API_BASE","createElement","useState","useRef","useCallback","fsTree","useState","useRef","createElement","createElement","useState","createElement","useState","useRef","useCallback","createElement","useState","createElement","createElement"],"sources":["../src/client/icons.tsx","../src/client/ConfigTree.tsx","../src/client/ansi.ts","../src/client/logHighlight.ts","../src/client/conversationDraft.ts","../src/client/utils.ts","../src/client/LogView.tsx","../src/client/LogHistoryView.tsx","../src/client/DirChooser.tsx","../src/client/PathPicker.tsx","../src/client/ConfigEditorModal.tsx","../src/client/styles.ts","../src/client/ServicesTab.tsx","../src/client/SettingsPanel.tsx","../src/client/index.tsx"],"sourcesContent":["/**\r\n * SVG icons for the starter plugin. Each icon is a React component that\r\n * takes a size prop and renders an inline SVG. Monochrome, currentColor fill.\r\n * @module dsh-better-sidebar-starter/client/icons\r\n */\r\n\r\nimport { createElement, type ReactNode } from 'react'\r\n\r\ninterface IconProps {\r\n size: number\r\n}\r\n\r\nfunction svg(size: number, pathChildren: string): ReactNode {\r\n const svgHtml = `<svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">${pathChildren}</svg>`\r\n return createElement('span', {\r\n style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: size, height: size },\r\n dangerouslySetInnerHTML: { __html: svgHtml },\r\n })\r\n}\r\n\r\n/** 🚀 Rocket — the tab icon. */\r\nexport function RocketIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M9.19 8.35c.34-.05.67-.1 1-.13 1.2-1.49 2.85-2.49 4.85-3.02 2-.53 4.16-.42 6.5.33.75 2.34.86 4.5.33 6.5-.53 2-1.53 3.65-3.02 4.85-.04.33-.08.66-.13 1-.13.87-.32 1.71-.57 2.5-.42 1.3-1.02 2.5-1.78 3.6-.42.6-1.1.96-1.86.96-.72 0-1.4-.34-1.84-.93l-2.06-2.76c-.2-.27-.5-.43-.82-.43H7.68c-.32 0-.62-.16-.82-.43l-2.06-2.76C4.36 19.34 4 18.66 4 17.94c0-.76.36-1.44.96-1.86 1.1-.76 2.3-1.36 3.6-1.78.79-.25 1.63-.44 2.5-.57l.13-1c.05-.34.1-.67.13-1z\" fill=\"currentColor\" opacity=\"0.85\"/><circle cx=\"15.5\" cy=\"8.5\" r=\"1.5\" fill=\"currentColor\" opacity=\"0.6\"/><path d=\"M5 14c-1 0-2 .5-2 2 0 1 .5 2 .5 2s1-.5 1.5-.5 1.5.5 1.5.5 1-.5 1-2-.5-2-2-2z\" fill=\"currentColor\" opacity=\"0.5\"/>`)\r\n}\r\n\r\n/** ▶ Play — start button. */\r\nexport function PlayIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M8 5v14l11-7z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ⏹ Stop — stop button. */\r\nexport function StopIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"1\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 🔄 Restart — restart button. */\r\nexport function RestartIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 5V2L7 7l5 5V8c2.76 0 5 2.24 5 5s-2.24 5-5 5-5-2.24-5-5H5c0 3.87 3.13 7 7 7s7-3.13 7-7-3.13-7-7-7z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ⚙ Gear — edit button. */\r\nexport function GearIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3l-1.97-.39c-.11-.37-.26-.72-.44-1.06l1.14-1.64c.26-.37.22-.88-.09-1.2l-1.28-1.28c-.32-.31-.83-.35-1.2-.09l-1.64 1.14c-.34-.18-.69-.33-1.06-.44L14.94 4c-.1-.42-.47-.72-.9-.72h-1.8c-.43 0-.8.3-.9.72l-.39 1.97c-.37.11-.72.26-1.06.44L7.25 5.27c-.37-.26-.88-.22-1.2.09L4.77 6.64c-.31.32-.35.83-.09 1.2l1.14 1.64c-.18.34-.33.69-.44 1.06L3.4 11c-.42.1-.72.47-.72.9v1.8c0 .43.3.8.72.9l1.97.39c.11.37.26.72.44 1.06l-1.14 1.64c-.26.37-.22.88.09 1.2l1.28 1.28c.32.31.83.35 1.2.09l1.64-1.14c.34.18.69.33 1.06.44l.39 1.97c.1.42.47.72.9.72h1.8c.43 0 .8-.3.9-.72l.39-1.97c.37-.11.72-.26 1.06-.44l1.64 1.14c.37.26.88.22 1.2-.09l1.28-1.28c.31-.32.35-.83.09-1.2l-1.14-1.64c.18-.34.33-.69.44-1.06L20.6 14c.42-.1.72-.47.72-.9v-1.8c0-.43-.3-.8-.72-.9z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 🗑 Trash — delete button. */\r\nexport function TrashIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ➕ Plus — add new config. */\r\nexport function PlusIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ✕ Close — modal cancel. */\r\nexport function CloseIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ◀ Back — history file list. */\r\nexport function BackIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 📄 File — history log entry. */\r\nexport function FileIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M6 2c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z\" fill=\"currentColor\" opacity=\"0.9\"/>`)\r\n}\r\n\r\n/** ⟳ Refresh — reload file list. */\r\nexport function RefreshIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M17.65 6.35A7.95 7.95 0 0 0 12 4a8 8 0 1 0 7.73 10h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ▾ Chevron down — tree expand. */\r\nexport function ChevronDownIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M7 10l5 5 5-5z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ▸ Chevron right — tree collapse. */\r\nexport function ChevronRightIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M10 7l5 5-5 5z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 🟢 Dot — running status. */\r\nexport function RunningDotIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<circle cx=\"12\" cy=\"12\" r=\"5\" fill=\"#4e9a06\"/>`)\r\n}\r\n\r\n/** ✅ Check — completed status. */\r\nexport function CheckIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\" fill=\"#4e9a06\"/>`)\r\n}\r\n\r\n/** 🔴 Error dot — failed status. */\r\nexport function ErrorDotIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<circle cx=\"12\" cy=\"12\" r=\"5\" fill=\"#cc0000\"/>`)\r\n}\r\n\r\n/** 📋 Log — download log. */\r\nexport function DownloadIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 📦 npm package icon. */\r\nexport function NpmIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M7 17V8h4v6h2V8h4v9h-4v-3h-2v3H7z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ☕ springboot icon. */\r\nexport function SpringIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 2C7.5 2 4 5.5 4 10c0 3 2 5.5 4.5 7 .5-2 1.5-3.5 3-4.5 1.5 1 2.5 2.5 3 4.5 2.5-1.5 4.5-4 4.5-7 0-4.5-3.5-8-7-8z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M8 10c1-1 2.5-1.5 4-1.5s3 .5 4 1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>`)\r\n}\r\n\r\n/** 🐍 python icon. */\r\nexport function PythonIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 4c-2 0-4 1-4 3v2h4v1H6c-1 0-2 1-2 3s1 3 2 3h2v-2c0-2 2-3 4-3h2c1 0 2-1 2-2V7c0-2-2-3-4-3z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"10.5\" cy=\"6.5\" r=\"0.8\" fill=\"currentColor\"/><path d=\"M12 20c2 0 4-1 4-3v-2h-4v-1h4c1 0 2-1 2-3s-1-3-2-3h-2v2c0 2-2 3-4 3h-2c-1 0-2 1-2 2v3c0 2 2 3 4 3z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"13.5\" cy=\"17.5\" r=\"0.8\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ⚡ custom icon. */\r\nexport function CustomIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M11 21h-1l1-7H7.5c-.6 0-.8-.4-.5-.9 1.5-2.6 3.7-6.2 6.5-10.1h1l-1 7h3.5c.6 0 .8.4.5.9C14.5 14.7 12.3 18.1 11 21z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 📁 Folder — directory tree icon. */\r\nexport function FolderIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z\" fill=\"currentColor\" opacity=\"0.7\"/>`)\r\n}\r\n\r\n/** 📋 Clipboard — bulk paste env. */\r\nexport function ClipboardIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 📄 Copy — duplicate a run config. */\r\nexport function CopyIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M21 8c0-1.1-.9-2-2-2h-9c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h9c1.1 0 2-.9 2-2V8z\" fill=\"currentColor\"/><path d=\"M3 16V4c0-1.1.9-2 2-2h10v2H5v12H3z\" fill=\"currentColor\" opacity=\"0.6\"/>`)\r\n}\r\n\r\n/** ✈️ Send — send selected text to the conversation composer. */\r\nexport function SendToChatIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** Get the icon for a config type. */\r\nexport function typeIcon(type: string, size: number): ReactNode {\r\n switch (type) {\r\n case 'npm': return NpmIcon({ size })\r\n case 'springboot': return SpringIcon({ size })\r\n case 'python': return PythonIcon({ size })\r\n default: return CustomIcon({ size })\r\n }\r\n}\r\n","/**\r\n * Config tree: the left pane showing all run configurations grouped by type,\r\n * with running instances as child nodes.\r\n * @module dsh-better-sidebar-starter/client/ConfigTree\r\n */\r\n\r\nimport { createElement, useState, useMemo, type ReactNode } from 'react'\r\nimport type { RunConfig, RunInstance } from './types.ts'\r\nimport {\r\n PlayIcon, StopIcon, GearIcon, CopyIcon,\r\n ChevronDownIcon, ChevronRightIcon,\r\n RunningDotIcon, CheckIcon, ErrorDotIcon,\r\n typeIcon,\r\n} from './icons.tsx'\r\n\r\n/** Props for the config tree. */\r\nexport interface ConfigTreeProps {\r\n configs: RunConfig[]\r\n instances: RunInstance[]\r\n selectedInstanceId: string | null\r\n selectedConfigId: string | null\r\n onSelectConfig: (configId: string) => void\r\n onSelectInstance: (instanceId: string) => void\r\n onStartConfig: (configId: string) => void\r\n onStopInstance: (instanceId: string) => void\r\n onEditConfig: (configId: string) => void\r\n /** Duplicate a config: opens the editor prefilled with all its info under a new name. */\r\n onDuplicateConfig: (configId: string) => void\r\n}\r\n\r\n/** Type group order. */\r\nconst TYPE_ORDER: string[] = ['npm', 'springboot', 'python', 'custom']\r\nconst TYPE_LABELS: Record<string, string> = {\r\n npm: 'npm', springboot: 'Spring Boot', python: 'Python', custom: 'Custom',\r\n}\r\n\r\n/** Status icon for an instance. */\r\nfunction statusIcon(status: string, size: number): ReactNode {\r\n switch (status) {\r\n case 'running': return RunningDotIcon({ size })\r\n case 'exited': return CheckIcon({ size })\r\n case 'killed':\r\n case 'error': return ErrorDotIcon({ size })\r\n default: return null\r\n }\r\n}\r\n\r\n/** The config tree component. */\r\nexport function ConfigTree(props: ConfigTreeProps): ReactNode {\r\n const { configs, instances, selectedInstanceId, selectedConfigId,\r\n onSelectConfig, onSelectInstance, onStartConfig, onStopInstance, onEditConfig, onDuplicateConfig } = props\r\n\r\n const [collapsed, setCollapsed] = useState<Set<string>>(new Set())\r\n\r\n const toggleGroup = (type: string): void => {\r\n setCollapsed((prev) => {\r\n const next = new Set(prev)\r\n if (next.has(type)) next.delete(type)\r\n else next.add(type)\r\n return next\r\n })\r\n }\r\n\r\n // Group configs by type, ordered by TYPE_ORDER, sorted by lastRunAt desc.\r\n const groups = useMemo(() => {\r\n const map = new Map<string, RunConfig[]>()\r\n for (const config of configs) {\r\n const arr = map.get(config.type) ?? []\r\n arr.push(config)\r\n map.set(config.type, arr)\r\n }\r\n for (const arr of map.values()) {\r\n arr.sort((a, b) => (b.lastRunAt ?? 0) - (a.lastRunAt ?? 0))\r\n }\r\n return TYPE_ORDER\r\n .filter((type) => map.has(type) && map.get(type)!.length > 0)\r\n .map((type) => ({ type, items: map.get(type)! }))\r\n }, [configs])\r\n\r\n if (configs.length === 0) {\r\n return createElement('div', { className: 'sts-empty' },\r\n '暂无运行配置',\r\n createElement('br'),\r\n '点击右上角 + 新建配置',\r\n )\r\n }\r\n\r\n return createElement('div', { className: 'sts-tree' },\r\n ...groups.map((group) => {\r\n const isCollapsed = collapsed.has(group.type)\r\n const label = TYPE_LABELS[group.type] ?? group.type\r\n const runningCount = instances.filter(\r\n (inst) => inst.configType === group.type && inst.status === 'running',\r\n ).length\r\n\r\n return createElement('div', { key: group.type },\r\n // Group header\r\n createElement('div', {\r\n className: 'sts-group-header',\r\n onClick: () => toggleGroup(group.type),\r\n },\r\n isCollapsed\r\n ? createElement(ChevronRightIcon, { size: 12 })\r\n : createElement(ChevronDownIcon, { size: 12 }),\r\n typeIcon(group.type, 14),\r\n label,\r\n createElement('span', { className: 'sts-group-count' },\r\n ` (${group.items.length}${runningCount > 0 ? `, ${runningCount} 运行中` : ''})`,\r\n ),\r\n ),\r\n // Group children\r\n createElement('div', {\r\n className: `sts-group-children${isCollapsed ? ' collapsed' : ''}`,\r\n },\r\n ...group.items.flatMap((config) => {\r\n const configInstances = instances.filter((inst) => inst.configId === config.id)\r\n const isRunning = configInstances.some((inst) => inst.status === 'running')\r\n const isSelected = selectedConfigId === config.id && selectedInstanceId === null\r\n\r\n const rows: ReactNode[] = [\r\n // Config row\r\n createElement('div', {\r\n key: config.id,\r\n className: `sts-config-row${isSelected ? ' selected' : ''}${isRunning ? ' running' : ''}`,\r\n onClick: () => onSelectConfig(config.id),\r\n },\r\n isRunning\r\n ? RunningDotIcon({ size: 12 })\r\n : typeIcon(config.type, 12),\r\n createElement('div', { className: 'sts-config-name' }, config.name),\r\n createElement('div', { className: 'sts-config-cmd' }, config.command),\r\n createElement('div', { className: 'sts-config-actions' },\r\n isRunning\r\n ? createElement('button', {\r\n className: 'sts-icon-btn stop',\r\n title: '停止全部',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n for (const inst of configInstances) {\r\n if (inst.status === 'running') onStopInstance(inst.id)\r\n }\r\n },\r\n }, StopIcon({ size: 14 }))\r\n : createElement('button', {\r\n className: 'sts-icon-btn play',\r\n title: '启动',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onStartConfig(config.id)\r\n },\r\n }, PlayIcon({ size: 14 })),\r\n createElement('button', {\r\n className: 'sts-icon-btn gear',\r\n title: '编辑',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onEditConfig(config.id)\r\n },\r\n }, GearIcon({ size: 12 })),\r\n createElement('button', {\r\n className: 'sts-icon-btn dup',\r\n title: '复制(生成副本配置)',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onDuplicateConfig(config.id)\r\n },\r\n }, CopyIcon({ size: 12 })),\r\n ),\r\n ),\r\n ]\r\n\r\n // Instance rows (children)\r\n for (const inst of configInstances) {\r\n const instSelected = selectedInstanceId === inst.id\r\n rows.push(\r\n createElement('div', {\r\n key: inst.id,\r\n className: `sts-instance-row${instSelected ? ' selected' : ''}`,\r\n onClick: () => onSelectInstance(inst.id),\r\n },\r\n statusIcon(inst.status, 10),\r\n `#${inst.id.slice(0, 8)}`,\r\n createElement('span', { className: 'sts-instance-pid' },\r\n inst.status === 'running' ? ` pid:${inst.pid}` : ` ${inst.status}`,\r\n ),\r\n inst.status === 'running'\r\n ? createElement('button', {\r\n className: 'sts-icon-btn stop',\r\n title: '停止',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onStopInstance(inst.id)\r\n },\r\n }, StopIcon({ size: 12 }))\r\n : null,\r\n ),\r\n )\r\n }\r\n\r\n return rows\r\n }),\r\n ),\r\n )\r\n }),\r\n )\r\n}\r\n","/**\n * ANSI escape code parser: converts raw terminal output with ANSI SGR\n * sequences into an array of styled text segments for React rendering.\n * Covers the common color codes (30-37, 40-47, 90-97, 100-107, bold, dim,\n * italic, underline, reset). Non-SGR escape sequences are stripped.\n * @module dsh-better-sidebar-starter/client/ansi\n */\n\n/** One styled text segment. */\nexport interface AnsiSegment {\n text: string\n bold: boolean\n dim: boolean\n italic: boolean\n underline: boolean\n fg: number | null\n bg: number | null\n}\n\ninterface ParseState {\n bold: boolean\n dim: boolean\n italic: boolean\n underline: boolean\n fg: number | null\n bg: number | null\n}\n\nfunction cloneState(s: ParseState): AnsiSegment {\n return {\n text: '',\n bold: s.bold,\n dim: s.dim,\n italic: s.italic,\n underline: s.underline,\n fg: s.fg,\n bg: s.bg,\n }\n}\n\n/** Apply one SGR parameter to the parse state. */\nfunction applySgr(state: ParseState, code: number): void {\n if (code === 0) {\n state.bold = false; state.dim = false; state.italic = false\n state.underline = false; state.fg = null; state.bg = null\n } else if (code === 1) {\n state.bold = true\n } else if (code === 2) {\n state.dim = true\n } else if (code === 3) {\n state.italic = true\n } else if (code === 4) {\n state.underline = true\n } else if (code === 22) {\n state.bold = false; state.dim = false\n } else if (code === 23) {\n state.italic = false\n } else if (code === 24) {\n state.underline = false\n } else if (code >= 30 && code <= 37) {\n state.fg = code - 30\n } else if (code === 39) {\n state.fg = null\n } else if (code >= 40 && code <= 47) {\n state.bg = code - 40\n } else if (code === 49) {\n state.bg = null\n } else if (code >= 90 && code <= 97) {\n state.fg = code - 90 + 8\n } else if (code >= 100 && code <= 107) {\n state.bg = code - 100 + 8\n }\n}\n\n/**\n * Parse a string with ANSI escape codes into styled segments.\n * Returns an array of AnsiSegment objects ready for React rendering.\n */\nexport function parseAnsi(input: string): AnsiSegment[] {\n const state: ParseState = {\n bold: false, dim: false, italic: false, underline: false, fg: null, bg: null,\n }\n const segments: AnsiSegment[] = []\n let current = cloneState(state)\n\n // Regex: match CSI sequences (ESC [ ... letter) and non-CSI text.\n const regex = /\\x1b\\[([0-9;]*)m|\\x1b\\][^\\x07]*\\x07|\\x1b\\[[0-9;]*[A-Za-z]/g\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(input)) !== null) {\n // Text before this escape sequence.\n if (match.index > lastIndex) {\n current.text += input.slice(lastIndex, match.index)\n }\n // Process the escape sequence.\n if (match[1] !== undefined) {\n // SGR sequence: ESC [ ... m\n if (current.text !== '') {\n segments.push(current)\n }\n const params = match[1] === '' ? '0' : match[1]\n for (const codeStr of params.split(';')) {\n const code = codeStr === '' ? 0 : Number(codeStr)\n if (!Number.isNaN(code)) applySgr(state, code)\n }\n current = cloneState(state)\n }\n // Non-SGR sequences (OSC, other CSI) are simply stripped.\n lastIndex = regex.lastIndex\n }\n\n // Remaining text after the last escape.\n if (lastIndex < input.length) {\n current.text += input.slice(lastIndex)\n }\n if (current.text !== '') {\n segments.push(current)\n }\n\n return segments\n}\n\n/** Map an ANSI color index (0-15) to a CSS color string. */\nconst ANSI_COLORS: readonly string[] = [\n // 0-7: standard colors\n '#000000', '#cc0000', '#4e9a06', '#c4a000', '#3465a4', '#75507b', '#06989a', '#d3d7cf',\n // 8-15: bright colors\n '#555753', '#ef2929', '#8ae234', '#fce94f', '#729fcf', '#ad7fa8', '#34e2e2', '#eeeeec',\n]\n\n/** Resolve a segment's foreground color to a CSS string, or null for default. */\nexport function segmentFgColor(seg: AnsiSegment): string | null {\n if (seg.fg === null) return null\n return ANSI_COLORS[seg.fg] ?? null\n}\n\n/** Resolve a segment's background color to a CSS string, or null for default. */\nexport function segmentBgColor(seg: AnsiSegment): string | null {\n if (seg.bg === null) return null\n return ANSI_COLORS[seg.bg] ?? null\n}\n","/**\n * Log syntax highlighting: tokenizes plain log text (Java / Spring Boot\n * style, but harmless for npm/python output) into colored segments —\n * timestamps, log levels, thread names, fully-qualified class names,\n * exception markers and stack frames. Applied AFTER ANSI parsing, only to\n * text the program did not color itself.\n * @module dsh-better-sidebar-starter/client/logHighlight\n */\n\n/** One highlighted span. */\nexport interface HighlightSegment {\n text: string\n color?: string\n bold?: boolean\n /** Source-code navigation target (makes the span clickable). */\n link?: LogCodeLink\n}\n\n/** A clickable source reference parsed out of the log line. */\nexport interface LogCodeLink {\n kind: 'class' | 'frame' | 'location'\n /** Fully-qualified class name (dots), e.g. com.example.service.UserService. */\n className?: string\n /** Source file base name, e.g. UserService.java. */\n file?: string\n /** Method name from a stack frame, e.g. getUser ('' for constructors). */\n method?: string\n /** 1-based line (from a stack frame / (File.java:45) token). */\n line?: number\n}\n\n// Palette chosen for dark backgrounds (mirrors typical IDE console themes).\nconst C = {\n ts: '#8f8f98', // timestamp — muted grey\n thread: '#b48ead', // [thread-name] — purple\n levelInfo: '#6ab04c', // INFO — green\n levelDebug: '#7a9ce8',// DEBUG — blue\n levelWarn: '#e2b93d', // WARN — yellow\n levelError: '#f0625f',// ERROR/FATAL — red\n clazz: '#61afef', // com.example.Foo — light blue\n exception: '#ff5f56', // *Exception / Caused by: — red\n frame: '#c678dd', // at com.foo.Bar(...) — violet\n location: '#56b6c2', // (Foo.java:123) — cyan\n}\n\n/** Log level → color. */\nfunction levelColor(level: string): string {\n switch (level) {\n case 'DEBUG':\n case 'TRACE': return C.levelDebug\n case 'INFO': return C.levelInfo\n case 'WARN': return C.levelWarn\n case 'ERROR':\n case 'FATAL':\n case 'OFF': return C.levelError\n default: return C.levelInfo\n }\n}\n\n/** One-pass tokenizer: timestamps, levels, threads, exceptions, stacks, classes. */\nconst TOKEN_RE = new RegExp([\n // 2026-08-20T17:44:42.123+08:00 | 2026-08-20 17:44:42,123\n '(?<ts>\\\\d{4}-\\\\d{2}-\\\\d{2}[T ]\\\\d{2}:\\\\d{2}:\\\\d{2}[.,]\\\\d{1,6}(?:Z|[+-]\\\\d{2}:?\\\\d{2})?)',\n // DEBUG / INFO / WARN / ERROR / ...\n '|(?<level>\\\\b(?:DEBUG|INFO|WARN|ERROR|TRACE|FATAL|OFF)\\\\b)',\n // [http-nio-8080-exec-1]\n '|(?<thread>\\\\[[^\\\\]\\\\r\\\\n]+\\\\])',\n // Caused by: / SQLException / NullPointerException\n '|(?<exception>\\\\bCaused by:|[A-Z]\\\\w*(?:Exception|Error)(?:\\\\b|:\\\\s))',\n // at com.foo.Bar.method(Foo.java:12) — whole stack frame\n '|(?<frame>\\\\s+at\\\\s+(?<target>[\\\\w$./<>]+)(?:\\\\((?<loc>[^)]*)\\\\))?)',\n // (Foo.java:123:45)\n '|(?<location>\\\\((?<lfile>[\\\\w$]+\\\\.(?:java|kt|groovy|scala)):(?<lline>\\\\d+)(?::\\\\d+)?\\\\))',\n // pkg.Cls.method(...) — a method CALL in the message text\n '|(?<call>(?<callCls>[A-Za-z_$][\\\\w$]*(?:\\\\.[A-Za-z_$][\\\\w$]*)+)\\\\.(?<callMethod>[A-Za-z_$][\\\\w$]*)\\\\s*\\\\()',\n // com.example.SomeClass (FQCN)\n '|(?<clazz>[A-Za-z_$][\\\\w$]*(?:\\\\.[A-Za-z_$][\\\\w$]*)+\\\\b)',\n].join(''), 'g')\n\n/** The last segment must look like a class name (starts uppercase); field\n * chains such as System.out are skipped. */\nfunction looksLikeClassName(fqcn: string): boolean {\n const last = fqcn.split('.').pop() ?? ''\n return last !== '' && /^[A-Z]/.test(last)\n}\n\n/** Split a stack-frame target `pkg.Cls.method` into class + method. */\nfunction parseFrameTarget(target: string): { className: string; method: string } {\n const dot = target.lastIndexOf('.')\n if (dot > 0 && dot < target.length - 1) {\n return { className: target.slice(0, dot), method: target.slice(dot + 1) }\n }\n return { className: target, method: '' }\n}\n\n/** Parse a `File.java:45` (or `File.java:45:12`) location string. */\nfunction parseFrameLoc(loc: string | undefined): { file?: string; line?: number } {\n if (loc === undefined || loc.trim() === '') return {}\n const parts = loc.trim().split(':')\n if (parts.length < 2) return {}\n const line = Number(parts[1])\n return { file: parts[0], line: Number.isInteger(line) && line > 0 ? line : undefined }\n}\n\n/**\n * Tokenize a single log line into highlighted segments.\n * Returns one segment per token; the rest stays plain.\n */\nexport function highlightLogText(text: string): HighlightSegment[] {\n const out: HighlightSegment[] = []\n let last = 0\n TOKEN_RE.lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = TOKEN_RE.exec(text)) !== null) {\n if (m.index > last) out.push({ text: text.slice(last, m.index) })\n const g = m.groups ?? {}\n if (g.ts !== undefined) {\n out.push({ text: m[0], color: C.ts })\n } else if (g.level !== undefined) {\n out.push({ text: m[0], color: levelColor(g.level), bold: true })\n } else if (g.thread !== undefined) {\n out.push({ text: m[0], color: C.thread })\n } else if (g.exception !== undefined) {\n out.push({ text: m[0], color: C.exception, bold: true })\n } else if (g.frame !== undefined) {\n const { className, method } = parseFrameTarget(g.target ?? '')\n const { file, line } = parseFrameLoc(g.loc)\n out.push({\n text: m[0],\n color: C.frame,\n link: { kind: 'frame', className, method, file, line },\n })\n } else if (g.location !== undefined) {\n out.push({\n text: m[0],\n color: C.location,\n link: {\n kind: 'location',\n file: g.lfile,\n line: Number(g.lline),\n },\n })\n } else if (g.call !== undefined) {\n const className = g.callCls ?? ''\n out.push({\n text: m[0],\n color: C.clazz,\n link: looksLikeClassName(className)\n ? { kind: 'class', className, method: g.callMethod ?? '' }\n : undefined,\n })\n } else if (g.clazz !== undefined) {\n const className = g.clazz\n out.push({\n text: m[0],\n color: C.clazz,\n link: looksLikeClassName(className) ? { kind: 'class', className } : undefined,\n })\n } else {\n out.push({ text: m[0] })\n }\n last = m.index + m[0].length\n }\n if (last < text.length) out.push({ text: text.slice(last) })\n return out\n}","/**\n * Send text into the DSH conversation composer draft. Mirrors the internal\n * `appendToDraft` of dsh-better-sidebar (not exported from that package):\n * the shared path behind the explorer's @-reference button / viewer\n * selection popup. The conversation service is resolved lazily through\n * `ctx.get`; a missing service, session scope, or activity scope degrades\n * to a logged no-op — never a crash.\n * @module dsh-better-sidebar-starter/client/conversationDraft\n */\n\nimport type { Context } from 'cordis'\n\n/** Minimal structural face of the composer input handle we touch. */\ninterface DraftInputLike {\n state: { getSnapshot(): { draft?: string } }\n setDraft(draft: string): void\n}\n\n/** Minimal structural face of the ui-conversation service (composer). */\ninterface ConversationLike {\n input: { for(actx: unknown): DraftInputLike | undefined }\n}\n\n/** Structural face of the client runtime sessions service. */\ninterface SessionsLike {\n scope(sessionId: string): unknown\n}\n\n/**\n * Append `text` to the session's composer draft (space-separated, like the\n * @-mentions). Returns false — and warns — when the conversation service\n * or the session scope is unavailable.\n */\nexport function appendToDraft(ctx: Context, sessionId: string, text: string): boolean {\n try {\n const sessions = (ctx as { sessions?: SessionsLike }).sessions\n const actx = sessions?.scope(sessionId)\n if (actx === undefined) return false\n const getService = (ctx as unknown as { get: (name: string) => unknown }).get.bind(ctx)\n const conversation = getService('conversation') as ConversationLike | undefined\n if (conversation === undefined || conversation === null) return false\n const input = conversation.input.for(actx)\n if (input === undefined) return false\n const draft = input.state.getSnapshot().draft ?? ''\n input.setDraft(draft.trim() === '' ? text : `${draft} ${text}`)\n return true\n } catch (error) {\n console.warn('[dsh-better-sidebar-starter] draft insert failed:', error)\n return false\n }\n}","/**\n * Shared utility functions for the starter plugin.\n * @module dsh-better-sidebar-starter/client/utils\n */\n\nlet toastEl: HTMLDivElement | null = null\nlet toastTimer: ReturnType<typeof setTimeout> | null = null\n\n/** Show a small floating toast notification. */\nexport function showToast(message: string, success: boolean): void {\n // Dismiss previous toast.\n if (toastTimer !== null) {\n clearTimeout(toastTimer)\n toastTimer = null\n }\n if (toastEl !== null) {\n document.body.removeChild(toastEl)\n toastEl = null\n }\n\n const el = document.createElement('div')\n el.textContent = message\n el.style.cssText = `\n position: fixed;\n top: 16px;\n left: 50%;\n transform: translateX(-50%);\n padding: 8px 16px;\n background: ${success ? 'rgba(78,154,6,0.9)' : 'rgba(235,87,87,0.9)'};\n color: #fff;\n font-size: 12px;\n border-radius: 6px;\n z-index: 999999;\n box-shadow: 0 2px 12px rgba(0,0,0,0.4);\n pointer-events: none;\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\n `\n document.body.appendChild(el)\n toastEl = el\n toastTimer = setTimeout(() => {\n if (toastEl !== null) {\n document.body.removeChild(toastEl)\n toastEl = null\n }\n toastTimer = null\n }, 2000)\n}\n\n/** Copy text to clipboard with fallback for sandboxed iframes. Shows toast feedback. */\nexport function copyToClipboard(text: string): boolean {\n try {\n // Try modern clipboard API.\n if (navigator.clipboard && (window as any).clipboardEvent !== undefined) {\n void navigator.clipboard.writeText(text)\n showToast('已复制到剪贴板', true)\n return true\n }\n } catch { /* fallback below */ }\n\n // Fallback: create textarea, select, execCommand.\n try {\n const ta = document.createElement('textarea')\n ta.value = text\n ta.style.position = 'fixed'\n ta.style.left = '-9999px'\n ta.style.top = '0'\n ta.style.opacity = '0'\n ta.style.width = '1px'\n ta.style.height = '1px'\n document.body.appendChild(ta)\n ta.focus()\n ta.select()\n const ok = document.execCommand('copy')\n document.body.removeChild(ta)\n if (ok) {\n showToast('已复制到剪贴板', true)\n return true\n }\n } catch { /* last resort below */ }\n\n // Last resort.\n try {\n void navigator.clipboard?.writeText(text)\n showToast('已复制到剪贴板', true)\n return true\n } catch {\n showToast('复制失败', false)\n return false\n }\n}","/**\r\n * Log view: the right pane showing real-time log output from a running\r\n * instance. Connects to the WebSocket endpoint, parses ANSI colors, and\r\n * renders log lines with auto-scroll.\r\n * @module dsh-better-sidebar-starter/client/LogView\r\n */\r\n\r\nimport { createElement, useState, useEffect, useRef, useCallback, type ReactNode } from 'react'\r\nimport type { Context } from 'cordis'\r\nimport type { LogEntry, RunInstance } from './types.ts'\r\nimport type { WsMessage } from './types.ts'\r\nimport { parseAnsi, segmentFgColor, segmentBgColor, type AnsiSegment } from './ansi.ts'\r\nimport { highlightLogText, type LogCodeLink } from './logHighlight.ts'\r\nimport { StopIcon, RestartIcon, TrashIcon, SendToChatIcon } from './icons.tsx'\r\nimport { appendToDraft } from './conversationDraft.ts'\r\nimport { showToast } from './utils.ts'\r\n\r\n/** API base. */\r\nconst API_BASE = '/api/dsh-better-sidebar-starter'\r\n\r\n/** Structural face of ctx.betterSidebar (openFile only). */\r\ninterface BetterSidebarLike {\r\n openFile(scope: { sessionId: string; cwd?: string }, path: string, title?: string): void\r\n}\r\n\r\n/** Props for the log view. */\r\nexport interface LogViewProps {\r\n instance: RunInstance\r\n /** The sidebar tab's cordis context (used to reach the conversation service). */\r\n ctx: Context\r\n /** The session to send selected text into. */\r\n sessionId: string\r\n /** Session working directory (for locating source files / openFile scope). */\r\n cwd?: string\r\n onStop: () => void\r\n onRestart: () => void\r\n}\r\n\r\n/** One rendered log line. */\r\nexport interface LogLine {\r\n entries: LogEntry[]\r\n key: number\r\n}\r\n\r\n/** Structural face of ctx.betterSidebar (openFile only). */\r\ninterface BetterSidebarLike {\r\n openFile(scope: { sessionId: string; cwd?: string }, path: string, title?: string): void\r\n}\r\n\r\n/** Open the source behind a log token via the locate-source API. Shared by the\r\n * live LogView and the persisted LogHistoryView. `root` is the locating root\r\n * (live instance cwd, or the session workspace for history logs). */\r\nexport async function handleLogCodeClick(\r\n ctx: Context,\r\n sessionId: string,\r\n cwd: string | undefined,\r\n root: string | undefined,\r\n link: LogCodeLink,\r\n): Promise<void> {\r\n const payload: Record<string, unknown> = {}\r\n if (root !== undefined && root !== '') payload.root = root\r\n if (link.kind === 'class' || link.kind === 'frame') payload.className = link.className\r\n if (link.file !== undefined) payload.file = link.file\r\n if (link.method !== undefined && link.method !== '<init>' && link.method !== '<clinit>') payload.method = link.method\r\n if (link.line !== undefined) payload.line = link.line\r\n try {\r\n // The host resolves the session cwd from the query — sessionId is required.\r\n const params = new URLSearchParams({ sessionId })\r\n if (cwd !== undefined && cwd !== '') params.set('cwd', cwd)\r\n const resp = await fetch(`${API_BASE}/locate-source?${params.toString()}`, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json' },\r\n body: JSON.stringify(payload),\r\n })\r\n if (!resp.ok) {\r\n // Surface the host's error message (e.g. missing sessionId) instead\r\n // of a generic failure.\r\n let reason = '定位源码失败'\r\n try {\r\n const err = await resp.json() as { error?: string }\r\n if (typeof err.error === 'string' && err.error !== '') reason = err.error\r\n } catch { /* non-JSON error body — keep generic message */ }\r\n showToast(reason, false)\r\n return\r\n }\r\n const data = await resp.json() as { ok: boolean; path?: string; line?: number; decompiled?: boolean; reason?: string }\r\n if (data.ok !== true || typeof data.path !== 'string') {\r\n showToast(data.reason ?? '未找到源文件', false)\r\n return\r\n }\r\n const base = data.path.split(/[\\\\/]/).pop() ?? data.path\r\n const line = typeof data.line === 'number' ? data.line : undefined\r\n const decompiled = data.decompiled === true\r\n const badge = decompiled ? ' [反编译]' : ''\r\n const title = line !== undefined ? `${base} : ${line}${badge}` : `${base}${badge}`\r\n const betterSidebar = (ctx as { betterSidebar?: BetterSidebarLike }).betterSidebar\r\n // Open in the session's sidebar editor.\r\n if (betterSidebar !== undefined) {\r\n betterSidebar.openFile({ sessionId, cwd }, data.path, title)\r\n }\r\n // Copy \"File.java:line\" so the user can jump in an unsupported editor.\r\n try {\r\n void navigator.clipboard?.writeText(`${base}${line !== undefined ? `:${line}` : ''}`)\r\n } catch { /* clipboard unavailable — ignore */ }\r\n showToast(\r\n line !== undefined\r\n ? `已打开 ${base},定位到第 ${line} 行(已复制 ${base}:${line})${decompiled ? '(反编译产物)' : ''}`\r\n : decompiled\r\n ? `已打开反编译产物 ${base}`\r\n : `已打开 ${base}`,\r\n true,\r\n )\r\n } catch {\r\n showToast('定位源码失败', false)\r\n }\r\n}\r\n\r\n/** Render ANSI-colored text segments, applying Java-log syntax highlighting\r\n * to text the program did not color itself (ANSI-colored spans keep the\r\n * program's own colors). Tokens with a source link become clickable. */\r\n/** Split plain text into plain/url parts. Trailing punctuation that is\r\n * almost certainly sentence text (.,;:)])。,) stays outside the link. */\r\nconst URL_RE = /https?:\\/\\/[^\\s\"'<>()\\[\\]{}]+/g\r\n\r\nfunction splitUrlParts(text: string): Array<{ text: string; url?: string }> {\r\n const out: Array<{ text: string; url?: string }> = []\r\n let last = 0\r\n for (const m of text.matchAll(URL_RE)) {\r\n const idx = m.index ?? 0\r\n if (idx > last) out.push({ text: text.slice(last, idx) })\r\n let url = m[0]\r\n let trail = ''\r\n while (url.length > 0 && /[.,;:)\\]}》」),。;:]$/.test(url)) {\r\n trail = url.slice(-1) + trail\r\n url = url.slice(0, -1)\r\n }\r\n if (url !== '') out.push({ text: url, url })\r\n if (trail !== '') out.push({ text: trail })\r\n last = idx + m[0].length\r\n }\r\n if (last < text.length) out.push({ text: text.slice(last) })\r\n return out\r\n}\r\n\r\nexport function renderAnsiLine(text: string, keyBase: string, onLinkClick: (link: LogCodeLink) => void): ReactNode {\r\n const segments = parseAnsi(text)\r\n const nodes: ReactNode[] = []\r\n if (segments.length === 0) return nodes\r\n let hlCounter = 0\r\n\r\n // Stitch the whole line into one plain string FIRST: Vite et al. split\r\n // their URLs across color runs (\"http://ip:\" + BOLD \"5173\" + \"/\"), so\r\n // per-segment detection loses the port. URLs are matched on the full\r\n // line, then mapped back onto the styled segments they overlap.\r\n let fullText = ''\r\n const ranges: Array<{ seg: AnsiSegment; start: number; end: number }> = []\r\n for (const seg of segments) {\r\n ranges.push({ seg, start: fullText.length, end: fullText.length + seg.text.length })\r\n fullText += seg.text\r\n }\r\n\r\n const parts = splitUrlParts(fullText)\r\n let offset = 0\r\n for (const part of parts) {\r\n const pStart = offset\r\n const pEnd = offset + part.text.length\r\n offset = pEnd\r\n for (const r of ranges) {\r\n const s = Math.max(r.start, pStart)\r\n const e = Math.min(r.end, pEnd)\r\n if (s >= e) continue\r\n const slice = fullText.slice(s, e)\r\n const seg = r.seg\r\n // Style carried from the ANSI segment (bold/dim/italic/underline).\r\n const style: Record<string, string> = {}\r\n if (seg.bold) style.fontWeight = 'bold'\r\n if (seg.dim) style.opacity = '0.6'\r\n if (seg.italic) style.fontStyle = 'italic'\r\n if (seg.underline) style.textDecoration = 'underline'\r\n const fg = segmentFgColor(seg)\r\n const bg = segmentBgColor(seg)\r\n if (fg !== null) style.color = fg\r\n if (bg !== null) style.backgroundColor = bg\r\n\r\n if (part.url !== undefined) {\r\n const url = part.url\r\n nodes.push(createElement('span', {\r\n key: `${keyBase}-u${hlCounter++}`,\r\n className: 'sts-log-url',\r\n style: { ...style, textDecoration: 'underline', cursor: 'pointer' },\r\n title: `在浏览器打开 ${url}`,\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n try { window.open(url, '_blank', 'noopener') } catch { /* popup blocked */ }\r\n },\r\n }, slice))\r\n } else if (fg !== null || bg !== null) {\r\n // Program-colored text: keep the ANSI colors as-is.\r\n nodes.push(createElement('span', { key: `${keyBase}-c${hlCounter++}`, style }, slice))\r\n } else {\r\n // Uncolored text runs through the log syntax highlighter.\r\n const hl = highlightLogText(slice)\r\n for (const h of hl) {\r\n const hStyle: Record<string, string> = { ...style }\r\n if (h.color !== undefined) hStyle.color = h.color\r\n if (h.bold === true) hStyle.fontWeight = 'bold'\r\n nodes.push(createElement('span', {\r\n key: `${keyBase}-hl${hlCounter++}`,\r\n className: h.link !== undefined ? 'sts-log-link' : undefined,\r\n style: hStyle,\r\n onClick: h.link !== undefined\r\n ? (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onLinkClick(h.link!)\r\n }\r\n : undefined,\r\n }, h.text))\r\n }\r\n }\r\n }\r\n }\r\n return nodes\r\n}\r\n\r\n/** The log view component. */\r\nexport function LogView({ instance, ctx, sessionId, cwd, onStop, onRestart }: LogViewProps): ReactNode {\r\n const [logLines, setLogLines] = useState<LogLine[]>([])\r\n const [autoScroll, setAutoScroll] = useState(true)\r\n const [exitInfo, setExitInfo] = useState<{ status: string; exitCode: number | null } | null>(null)\r\n const [selLength, setSelLength] = useState(0)\r\n const contentRef = useRef<HTMLDivElement>(null)\r\n const wsRef = useRef<WebSocket | null>(null)\r\n const lineKeyCounter = useRef(0)\r\n\r\n // Track the length of the current text selection inside this panel.\r\n useEffect(() => {\r\n const updateSel = (): void => {\r\n const sel = window.getSelection()\r\n const len = sel !== null && !sel.isCollapsed ? sel.toString().trim().length : 0\r\n setSelLength(len)\r\n }\r\n document.addEventListener('selectionchange', updateSel)\r\n return () => document.removeEventListener('selectionchange', updateSel)\r\n }, [])\r\n\r\n /** Send the currently selected log text into the conversation composer. */\r\n const handleSendSelection = (): void => {\r\n const sel = window.getSelection()\r\n const text = sel !== null && !sel.isCollapsed ? sel.toString().trim() : ''\r\n if (text === '') {\r\n showToast('请先在日志中选中文本', false)\r\n return\r\n }\r\n if (appendToDraft(ctx, sessionId, text)) {\r\n showToast(`已将选中文本(${text.length} 字符)发送到对话`, true)\r\n } else {\r\n showToast('发送失败:对话服务不可用', false)\r\n }\r\n }\r\n\r\n // Buffer accumulation: accumulate text entries into complete lines.\r\n const lineBuffer = useRef<{ current: LogEntry[] }>({ current: [] })\r\n\r\n /** Flush the buffer into a new log line. */\r\n const flushLine = useCallback((): void => {\r\n const entries = lineBuffer.current.current\r\n if (entries.length === 0) return\r\n lineBuffer.current.current = []\r\n setLogLines((prev) => {\r\n const key = lineKeyCounter.current++\r\n // Cap at 5000 lines to avoid memory issues.\r\n const next = [...prev, { entries, key }]\r\n if (next.length > 5000) next.splice(0, next.length - 5000)\r\n return next\r\n })\r\n }, [])\r\n\r\n /** Add an entry to the current line buffer, splitting on newlines. */\r\n const addEntry = useCallback((entry: LogEntry): void => {\r\n const parts = entry.text.split('\\n')\r\n for (let i = 0; i < parts.length; i++) {\r\n if (i > 0) {\r\n // Newline boundary: flush the current line.\r\n flushLine()\r\n }\r\n if (parts[i] !== '') {\r\n lineBuffer.current.current.push({\r\n stream: entry.stream,\r\n text: parts[i],\r\n ts: entry.ts,\r\n })\r\n }\r\n }\r\n // If the entry ends with \\n, flush.\r\n if (parts.length > 1 && parts[parts.length - 1] === '') {\r\n flushLine()\r\n }\r\n }, [flushLine])\r\n\r\n // Connect WebSocket.\r\n useEffect(() => {\r\n const url = `/api/dsh-better-sidebar-starter/logs?instanceId=${encodeURIComponent(instance.id)}`\r\n const ws = new WebSocket(url)\r\n wsRef.current = ws\r\n\r\n ws.onmessage = (event: MessageEvent): void => {\r\n try {\r\n const msg = JSON.parse(event.data as string) as WsMessage\r\n if (msg.type === 'history') {\r\n setLogLines([])\r\n lineBuffer.current.current = []\r\n for (const entry of msg.entries) {\r\n addEntry(entry)\r\n }\r\n flushLine()\r\n } else if (msg.type === 'log') {\r\n addEntry(msg.entry)\r\n } else if (msg.type === 'status') {\r\n if (msg.status !== 'running') {\r\n flushLine()\r\n setExitInfo({ status: msg.status, exitCode: msg.exitCode })\r\n }\r\n }\r\n } catch { /* ignore parse errors */ }\r\n }\r\n\r\n return () => {\r\n ws.close()\r\n wsRef.current = null\r\n }\r\n }, [instance.id, addEntry, flushLine])\r\n\r\n // Auto-scroll to bottom when new lines arrive.\r\n useEffect(() => {\r\n if (autoScroll && contentRef.current !== null) {\r\n contentRef.current.scrollTop = contentRef.current.scrollHeight\r\n }\r\n }, [logLines, autoScroll])\r\n\r\n const handleClear = (): void => {\r\n setLogLines([])\r\n lineBuffer.current.current = []\r\n }\r\n\r\n return createElement('div', { style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 } },\r\n // Toolbar\r\n createElement('div', { className: 'sts-log-toolbar' },\r\n instance.configName,\r\n createElement('span', { style: { opacity: 0.5, marginLeft: '4px' } }, `#${instance.id.slice(0, 8)}`),\r\n createElement('div', { className: 'sts-log-toolbar-spacer' }),\r\n instance.status === 'running'\r\n ? createElement('button', {\r\n className: 'sts-icon-btn stop',\r\n title: '停止',\r\n onClick: onStop,\r\n }, StopIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '停止'))\r\n : null,\r\n createElement('button', {\r\n className: 'sts-icon-btn',\r\n title: '重启',\r\n onClick: onRestart,\r\n }, RestartIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '重启')),\r\n createElement('button', {\r\n className: 'sts-icon-btn',\r\n title: '清空',\r\n onClick: handleClear,\r\n }, TrashIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '清空')),\r\n createElement('button', {\r\n className: 'sts-icon-btn send',\r\n title: selLength > 0 ? `发送选中的 ${selLength} 字符到对话` : '发送选中的文本到对话',\r\n onClick: handleSendSelection,\r\n }, SendToChatIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, selLength > 0 ? `发送选中(${selLength})` : '发送选中')),\r\n createElement('label', {\r\n className: 'sts-log-toolbar-label',\r\n onClick: () => setAutoScroll((v) => !v),\r\n },\r\n autoScroll ? '☑' : '☐',\r\n ' 自动滚动',\r\n ),\r\n ),\r\n // Log content\r\n createElement('div', { ref: contentRef, className: 'sts-log-content' },\r\n ...logLines.map((line) => {\r\n const keyStr = `line-${line.key}`\r\n return createElement('div', { key: keyStr, className: 'sts-log-line' },\r\n ...line.entries.map((entry, i) => {\r\n const segKey = `${keyStr}-e${i}`\r\n return createElement('span', {\r\n key: segKey,\r\n className: entry.stream === 'stderr' ? 'sts-log-stderr' : '',\r\n }, renderAnsiLine(entry.text, segKey, (link) => { void handleLogCodeClick(ctx, sessionId, cwd, instance.cwd, link) }))\r\n }),\r\n )\r\n }),\r\n // Exit info\r\n exitInfo !== null\r\n ? createElement('div', {\r\n key: 'exit-info',\r\n style: {\r\n color: exitInfo.status === 'exited' && exitInfo.exitCode === 0 ? '#4e9a06' : '#cc0000',\r\n fontWeight: 'bold',\r\n marginTop: '4px',\r\n },\r\n },\r\n `\\n进程${exitInfo.status === 'exited' ? '退出' : exitInfo.status === 'killed' ? '被终止' : '出错'}`,\r\n exitInfo.exitCode !== null ? `,退出码: ${exitInfo.exitCode}` : '',\r\n )\r\n : null,\r\n ),\r\n )\r\n}\r\n","/**\n * Log history view: read-only viewer for persisted run logs\n * (`GET /logs-history`). Two states: the file list under the workspace's\n * `.dsh/logs` directory, and the rendered content of one log file. Reuses\n * the LogView line renderer (ANSI + Java-log highlighting + code links),\n * so previous runs — including failed spawns — stay inspectable forever.\n * @module dsh-better-sidebar-starter/client/LogHistoryView\n */\n\nimport { createElement, useState, useRef, useCallback, type ReactNode } from 'react'\nimport type { Context } from 'cordis'\nimport type { LogEntry } from './types.ts'\nimport { renderAnsiLine, handleLogCodeClick, type LogLine } from './LogView.tsx'\nimport { BackIcon, CloseIcon, FileIcon, TrashIcon, RefreshIcon } from './icons.tsx'\nimport { showToast } from './utils.ts'\n\n/** API base. */\nconst API_BASE = '/api/dsh-better-sidebar-starter'\n\n/** One persisted log file (from the host list). */\ninterface LogFileInfo {\n file: string\n name: string\n mtime: number\n size: number\n}\n\n/** Render one history log line (stderr red, meta dim). */\nfunction renderLine(line: LogLine, onLinkClick: (link: Parameters<typeof handleLogCodeClick>[4]) => void): ReactNode {\n return createElement('div', { key: `line-${line.key}`, className: 'sts-log-line' },\n ...line.entries.map((entry, i) => {\n const segKey = `line-${line.key}-e${i}`\n const cls = entry.stream === 'stderr'\n ? 'sts-log-stderr'\n : entry.stream === 'meta'\n ? 'sts-log-meta'\n : ''\n return createElement('span', { key: segKey, className: cls },\n renderAnsiLine(entry.text, segKey, onLinkClick))\n }),\n )\n}\n\n/** Props. */\nexport interface LogHistoryViewProps {\n ctx: Context\n sessionId: string\n /** Session working directory (for host cwd resolution + openFile scope). */\n cwd?: string\n /** Close the whole history browser (back to the services view). */\n onClose: () => void\n /** Optional run config name: when set, only that config's log files are\n * shown (used when clicking a config in the tree). */\n filter?: string\n}\n\n/** Split a raw log entry into RenderLine-ready single-line entries. */\nfunction splitEntry(entry: LogEntry, keyBase: number, out: LogLine[]): void {\n const parts = entry.text.split('\\n')\n let current: LogEntry[] = []\n const pushLine = (): void => {\n if (current.length > 0) {\n out.push({ entries: current, key: keyBase++ })\n current = []\n }\n }\n for (let i = 0; i < parts.length; i++) {\n if (i > 0) pushLine()\n if (parts[i] !== '') current.push({ stream: entry.stream, text: parts[i], ts: entry.ts })\n }\n if (parts.length > 1 && parts[parts.length - 1] === '') pushLine()\n}\n\n/** The log history viewer. */\nexport function LogHistoryView({ ctx, sessionId, cwd, onClose, filter }: LogHistoryViewProps): ReactNode {\n const [files, setFiles] = useState<LogFileInfo[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [activeFile, setActiveFile] = useState<LogFileInfo | null>(null)\n const [lines, setLines] = useState<LogLine[]>([])\n const [loadedName, setLoadedName] = useState('')\n const lineKeyCounter = useRef(0)\n\n const loadList = useCallback(async (): Promise<void> => {\n setLoading(true)\n try {\n const params = new URLSearchParams({ sessionId })\n if (cwd !== undefined && cwd !== '') params.set('cwd', cwd)\n const resp = await fetch(`${API_BASE}/logs-history?${params.toString()}`)\n if (!resp.ok) {\n let reason = filter !== undefined ? '加载历史日志失败' : '加载历史日志失败'\n try {\n const err = await resp.json() as { error?: string }\n if (typeof err.error === 'string' && err.error !== '') reason = err.error\n } catch { /* keep generic */ }\n showToast(reason, false)\n setFiles([])\n return\n }\n const data = await resp.json() as { logs?: LogFileInfo[] }\n const all = data.logs ?? []\n // File names are `<safeName>_<stamp>.log`; configs never contain the\n // stamp, so a prefix match on the name string is unambiguous.\n const shown = filter !== undefined ? all.filter((f) => f.name.startsWith(filter)) : all\n setFiles(shown)\n } catch {\n showToast('加载历史日志失败', false)\n setFiles([])\n } finally {\n setLoading(false)\n }\n }, [sessionId, cwd, filter])\n\n // Initial load of the file list.\n const [initialized, setInitialized] = useState(false)\n if (!initialized) {\n setInitialized(true)\n void loadList()\n }\n\n /** Open one log file and render its content. */\n const openFile = useCallback(async (info: LogFileInfo): Promise<void> => {\n setLoading(true)\n try {\n const params = new URLSearchParams({ sessionId, file: info.file })\n if (cwd !== undefined && cwd !== '') params.set('cwd', cwd)\n const resp = await fetch(`${API_BASE}/logs-history?${params.toString()}`)\n if (!resp.ok) {\n let reason = '读取日志失败'\n try {\n const err = await resp.json() as { error?: string }\n if (typeof err.error === 'string' && err.error !== '') reason = err.error\n } catch { /* keep generic */ }\n showToast(reason, false)\n return\n }\n const data = await resp.json() as { logs?: LogEntry[]; file?: string }\n const out: LogLine[] = []\n for (const entry of data.logs ?? []) {\n splitEntry(entry, lineKeyCounter.current, out)\n lineKeyCounter.current += 200000\n }\n setActiveFile(info)\n setLoadedName(info.name)\n setLines(out)\n } catch {\n showToast('读取日志失败', false)\n } finally {\n setLoading(false)\n }\n }, [sessionId, cwd])\n\n // ---- list view ----\n if (activeFile === null) {\n const title = filter !== undefined ? `${filter} · 历史日志` : '历史日志'\n return createElement('div', { style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 } },\n createElement('div', { className: 'sts-log-toolbar' },\n title,\n createElement('div', { className: 'sts-log-toolbar-spacer' }),\n createElement('button', {\n className: 'sts-icon-btn',\n title: '刷新',\n onClick: () => { void loadList() },\n }, RefreshIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '刷新')),\n createElement('button', {\n className: 'sts-icon-btn',\n title: filter !== undefined ? '返回服务视图' : '关闭历史日志',\n onClick: onClose,\n }, CloseIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, filter !== undefined ? '返回' : '关闭')),\n ),\n createElement('div', {\n style: { overflow: 'auto', flex: 1, minHeight: 0, padding: '4px 0' },\n },\n loading && files === null\n ? createElement('div', { className: 'sts-empty' }, '加载中…')\n : files !== null && files.length === 0\n ? createElement('div', { className: 'sts-empty' },\n filter !== undefined\n ? `「${filter}」暂无历史日志。每次启动都会在 <工作空间>/logs/ 下永久保留一份日志,即使启动失败或 dsh 重启也能查看。`\n : '暂无历史日志(工作空间 logs/ 为空)。每次启动服务都会在 <工作空间>/logs/ 下永久保留一份日志,即使启动失败或 dsh 重启也能查看。')\n : createElement('div', {},\n (files ?? []).map((f) =>\n createElement('button', {\n key: f.file,\n className: 'sts-history-item',\n title: f.file,\n onClick: () => { void openFile(f) },\n },\n createElement(FileIcon, { size: 13 }),\n createElement('span', { style: { flex: 1, textAlign: 'left', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, f.name),\n createElement('span', { className: 'sts-history-item-meta' },\n `${new Date(f.mtime).toLocaleString()} ${(f.size / 1024).toFixed(1)} KB`),\n ),\n ),\n ),\n ),\n )\n }\n\n // ---- file view ----\n return createElement('div', { style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 } },\n createElement('div', { className: 'sts-log-toolbar' },\n createElement('button', {\n className: 'sts-icon-btn',\n title: '返回文件列表',\n onClick: () => { setActiveFile(null); setLines([]) },\n }, BackIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '返回')),\n loadedName,\n createElement('span', { style: { opacity: 0.5, marginLeft: '4px' } }, '(历史日志)'),\n createElement('div', { className: 'sts-log-toolbar-spacer' }),\n createElement('button', {\n className: 'sts-icon-btn',\n title: '清空视图',\n onClick: () => setLines([]),\n }, TrashIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '清空')),\n createElement('button', {\n className: 'sts-icon-btn',\n title: '关闭历史日志',\n onClick: onClose,\n }, CloseIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '关闭')),\n ),\n createElement('div', { className: 'sts-log-content' },\n loading && lines.length === 0\n ? createElement('div', { className: 'sts-empty' }, '读取中…')\n : lines.length === 0\n ? createElement('div', { className: 'sts-empty' }, '日志文件为空。')\n : lines.map((line) => renderLine(line, (link) => { void handleLogCodeClick(ctx, sessionId, cwd, cwd, link) })),\n lines.length === 0\n ? null\n : createElement('div', { key: 'history-end', style: { opacity: 0.5, marginTop: '6px' } },\n `—— 历史日志已加载完毕(${lines.length} 行),此为只读视图 ——`),\n ),\n )\n}","/**\n * Shared directory chooser: a filesystem folder tree (backed by the sidebar\n * fs.tree API, which can list any absolute path) plus a browse button that\n * opens a modal popup. Browsing starts at the user's home directory and can\n * navigate up to any drive/root, so runtime tool paths (JDK/Maven/...) that\n * live outside the workspace can be selected.\n * @module dsh-better-sidebar-starter/client/DirChooser\n */\n\nimport { createElement, useState, useEffect, useRef, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { FolderIcon, ChevronRightIcon, ChevronDownIcon, CloseIcon, CheckIcon } from './icons.tsx'\n\n/** One directory entry from the sidebar fs.tree API. */\nexport interface FsEntry {\n name: string\n path: string\n isDir: boolean\n hidden: boolean\n isSymlink: boolean\n broken: boolean\n}\n\n/** Call the sidebar fs.tree API directly via fetch. Returns directories only. */\nexport async function fsTree(scope: SessionScope, path: string): Promise<FsEntry[]> {\n const payload: Record<string, string> = { sessionId: scope.sessionId, path }\n if (scope.cwd !== undefined && scope.cwd !== '') payload.cwd = scope.cwd\n const resp = await fetch('/sidebar/api/fs.tree', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!resp.ok) throw new Error(`fs.tree ${path}: ${resp.status}`)\n const parsed = await resp.json() as { ok?: boolean; value?: { entries: FsEntry[] }; error?: { message: string } }\n if (!parsed.ok || parsed.value === undefined) {\n throw new Error(`fs.tree ${path}: ${parsed.error?.message ?? 'unknown error'}`)\n }\n return parsed.value.entries.filter((e) => e.isDir && !e.hidden)\n}\n\n/** Fetch the user's home directory from the host. Falls back to cwd. */\nasync function fetchHomeDefault(scope: SessionScope): Promise<string> {\n try {\n const r = await fetch('/api/dsh-better-sidebar-starter/detect-env', { method: 'GET' })\n if (r.ok) {\n const data = await r.json() as { home?: string }\n if (data.home && data.home !== '') return data.home\n }\n } catch { /* ignore */ }\n return scope.cwd ?? '.'\n}\n\n/** Normalize a path, returning its parent or null if at the root. */\nfunction parentOf(p: string): string | null {\n const trimmed = p.replace(/[\\\\/]+$/, '')\n const lastSep = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\\\'))\n if (lastSep <= 0) return null\n return trimmed.substring(0, lastSep)\n}\n\n/** The folder tree panel: shows the current dir's subdirectories with up-nav. */\nexport function DirTree(props: {\n scope: SessionScope\n selectedPath: string\n onPick: (path: string) => void\n}): ReactNode {\n const { scope, selectedPath, onPick } = props\n const [cur, setCur] = useState<string>('.') // current directory being browsed\n const [entries, setEntries] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [expanded, setExpanded] = useState<Set<string>>(new Set())\n\n // Stash scope in a ref so the init effect only runs once regardless of the\n // parent re-rendering with a fresh scope object each frame.\n const scopeRef = useRef(scope)\n const initDoneRef = useRef(false)\n // Remember the initial selected value so the popup opens at, or as close to\n // as possible, the already-configured path.\n const initialValueRef = useRef(selectedPath)\n\n // Initialize browsing root: prefer an existing selected path, else home.\n useEffect(() => {\n if (initDoneRef.current) return\n initDoneRef.current = true\n let cancelled = false\n const s = scopeRef.current\n const startPath = initialValueRef.current?.trim() && initialValueRef.current !== '.' && initialValueRef.current !== './'\n ? initialValueRef.current.trim()\n : null\n if (startPath !== null) {\n setCur(startPath)\n loadDir(s, startPath)\n // If the start path points at a file dir inside a parent, still fine.\n } else {\n fetchHomeDefault(s).then((home) => {\n if (cancelled) return\n setCur(home)\n loadDir(s, home)\n })\n }\n return () => { cancelled = true }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const loadDir = (s: SessionScope, path: string): void => {\n setLoading(true)\n setError(null)\n fsTree(s, path)\n .then((dirs) => {\n setEntries(dirs)\n setLoading(false)\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : String(err))\n setLoading(false)\n })\n }\n\n const goUp = (): void => {\n const parent = parentOf(cur)\n if (parent !== null) {\n setCur(parent)\n loadDir(scopeRef.current, parent)\n }\n }\n\n const navigateInto = (path: string): void => {\n setCur(path)\n loadDir(scopeRef.current, path)\n }\n\n const toggleExpand = (path: string): void => {\n setExpanded((prev) => {\n const next = new Set(prev)\n if (next.has(path)) next.delete(path)\n else next.add(path)\n return next\n })\n }\n\n const isSelected = (p: string): boolean => selectedPath !== '' && selectedPath !== '.' && selectedPath === p\n\n return createElement('div', { className: 'sts-dirtree' },\n // Navigation bar: current path + up button\n createElement('div', { className: 'sts-dirtree-nav' },\n createElement('button', {\n className: 'sts-dirtree-up',\n onClick: goUp,\n disabled: parentOf(cur) === null,\n title: '\\u4e0a\\u7ea7\\u76ee\\u5f55',\n }, '\\u2191'),\n createElement('button', {\n className: 'sts-dirtree-home',\n onClick: () => loadDir(scopeRef.current, cur), // refresh current dir\n title: '\\u5237\\u65b0',\n }, '\\u21bb'),\n createElement('span', { className: 'sts-dirtree-cur', title: cur }, cur || '(.)'),\n ),\n // Current directory selection row + subdir tree\n createElement('div', {\n className: `sts-tree-row${isSelected(cur) ? ' selected' : ''}`,\n style: { paddingLeft: '8px' },\n onClick: () => onPick(cur),\n },\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, cur || '(.)'),\n createElement('span', { className: 'sts-tree-select-hint' }, '\\u25cf \\u9009\\u62e9\\u6b64\\u76ee\\u5f55'),\n ),\n loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: '16px' } }, '\\u52a0\\u8f7d\\u4e2d...')\n : error !== null\n ? createElement('div', { className: 'sts-tree-error', style: { paddingLeft: '16px' } }, error)\n : entries !== null && entries.length > 0\n ? createElement('div', { className: 'sts-dirtree-sub' },\n ...entries.map((entry) => createElement(DirRow, {\n key: entry.path,\n scope,\n entry,\n depth: 1,\n selectedPath,\n expanded,\n onToggleExpand: toggleExpand,\n onPick,\n onNavigate: navigateInto,\n })),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: '16px' } }, '\\u65e0\\u5b50\\u76ee\\u5f55'),\n )\n}\n\n/** One directory row with recursive expansion + click into. */\nfunction DirRow(props: {\n scope: SessionScope\n entry: FsEntry\n depth: number\n selectedPath: string\n expanded: Set<string>\n onToggleExpand: (p: string) => void\n onPick: (p: string) => void\n onNavigate: (p: string) => void\n}): ReactNode {\n const { scope, entry, depth, selectedPath, expanded, onToggleExpand, onPick, onNavigate } = props\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const isExpanded = expanded.has(entry.path)\n const isSelected = selectedPath === entry.path\n\n const handleClick = (): void => {\n // Clicking a row selects it AND expands to reveal children (unless already\n // expanded, in which case a second click collapses).\n onPick(entry.path)\n if (isExpanded) {\n onToggleExpand(entry.path)\n return\n }\n onToggleExpand(entry.path)\n if (children === null) {\n setLoading(true)\n fsTree(scope, entry.path)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch(() => { setChildren([]); setLoading(false) })\n }\n }\n\n return createElement('div', null,\n createElement('div', {\n className: `sts-tree-row${isSelected ? ' selected' : ''}`,\n style: { paddingLeft: `${depth * 16 + 8}px` },\n onClick: handleClick,\n },\n createElement('span', { className: `sts-tree-chevron${isExpanded ? ' expanded' : ''}` },\n createElement(isExpanded ? ChevronDownIcon : ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, entry.name),\n ),\n isExpanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u52a0\\u8f7d\\u4e2d...')\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(DirRow, {\n key: child.path,\n scope,\n entry: child,\n depth: depth + 1,\n selectedPath,\n expanded,\n onToggleExpand: onToggleExpand,\n onPick,\n onNavigate: props.onNavigate,\n }),\n ),\n )\n : null\n : null,\n )\n}\n\n/** Props for the browse button + modal chooser. */\nexport interface DirChooserProps {\n scope: SessionScope\n value: string\n onChange: (v: string) => void\n /** Button/input presentation. */\n compact?: boolean\n /** Placeholder for the value input. */\n placeholder?: string\n}\n\n/** A read-only path input + browse button that opens a modal folder tree. */\nexport function DirChooser(props: DirChooserProps): ReactNode {\n const { scope, value, onChange, compact, placeholder } = props\n const [open, setOpen] = useState(false)\n\n return createElement('div', { className: 'sts-settings-path-row' },\n createElement('span', { className: 'sts-settings-path-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('input', {\n className: compact ? 'sts-modal-input mono' : 'sts-settings-input',\n value: value,\n readOnly: true,\n placeholder: placeholder ?? '',\n title: value,\n onClick: () => setOpen(true),\n }),\n createElement('button', {\n className: 'sts-settings-browse',\n onClick: () => setOpen(true),\n title: '\\u9009\\u62e9\\u6587\\u4ef6\\u5939',\n }, '\\u6d4f\\u89c8'),\n open\n ? createElement(DirChooserModal, {\n scope,\n value,\n onConfirm: (p) => { onChange(p); setOpen(false) },\n onCancel: () => setOpen(false),\n })\n : null,\n )\n}\n\n/** Modal popup hosting the folder tree with confirm/cancel. */\nfunction DirChooserModal(props: {\n scope: SessionScope\n value: string\n onConfirm: (path: string) => void\n onCancel: () => void\n}): ReactNode {\n const { scope, value, onConfirm, onCancel } = props\n const [sel, setSel] = useState(value)\n\n return createElement('div', {\n className: 'sts-modal-overlay',\n onClick: (e: MouseEvent) => { if (e.target === e.currentTarget) onCancel() },\n },\n createElement('div', { className: 'sts-dir-modal' },\n createElement('div', { className: 'sts-modal-header' },\n '\\u9009\\u62e9\\u6587\\u4ef6\\u5939',\n createElement('button', { className: 'sts-icon-btn', onClick: onCancel },\n createElement(CloseIcon, { size: 16 }),\n ),\n ),\n createElement('div', { className: 'sts-dir-modal-tree' },\n createElement(DirTree, { scope, selectedPath: sel, onPick: setSel }),\n ),\n createElement('div', { className: 'sts-modal-footer' },\n createElement('button', { className: 'sts-modal-btn', onClick: onCancel }, '\\u53d6\\u6d88'),\n createElement('button', {\n className: 'sts-modal-btn primary',\n onClick: () => onConfirm(sel),\n style: {\n background: '#1a1a1e',\n border: '1px solid #3a3a3e',\n color: '#fff',\n borderRadius: '6px',\n padding: '8px 18px',\n cursor: 'pointer',\n fontSize: '13px',\n },\n },\n createElement(CheckIcon, { size: 14 }), ' \\u786e\\u5b9a'),\n ),\n ),\n )\n}\n","/**\n * Path picker: a read-only input + browse button that opens a folder tree\n * modal (DirChooser). Shared by the settings panel and the config editor.\n * @module dsh-better-sidebar-starter/client/PathPicker\n */\n\nimport { createElement, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { DirChooser } from './DirChooser.tsx'\n\nexport type PathKind = 'java' | 'node' | 'python' | 'mvn'\n\n/** Props for the path picker. */\nexport interface PathPickerProps {\n scope: SessionScope\n kind: PathKind\n value: string\n onChange: (v: string) => void\n /** Compact mode (no label/desc wrapper), used inside the modal. */\n compact?: boolean\n desc?: string\n}\n\n/** A folder-browse input with a hosted modal tree chooser. */\nexport function PathPicker(props: PathPickerProps): ReactNode {\n const { scope, value, onChange, compact, desc } = props\n return createElement(DirChooser, {\n scope,\n value,\n onChange,\n compact,\n placeholder: desc,\n })\n}\n","/**\n * Config editor modal dialog: create or edit a run configuration.\n * Lets the user set name, type, command, cwd (tree picker), jvmArgs, args,\n * and environment variables.\n * @module dsh-better-sidebar-starter/client/ConfigEditorModal\n */\n\nimport { createElement, useState, useEffect, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { CloseIcon, PlusIcon, TrashIcon, ChevronRightIcon, FolderIcon, ClipboardIcon } from './icons.tsx'\nimport { PathPicker } from './PathPicker.tsx'\n\n/** One directory entry from the sidebar fs.tree API. */\ninterface FsEntry {\n name: string\n path: string\n isDir: boolean\n hidden: boolean\n isSymlink: boolean\n broken: boolean\n}\n\n/** Call the sidebar fs.tree API directly via fetch. Returns directories only.\n * If path is omitted, the server uses the session cwd (root listing).\n * The sidebar API wraps responses in { ok: true, value: ... } envelope. */\nasync function fsTree(scope: SessionScope, path?: string): Promise<FsEntry[]> {\n const payload: Record<string, string> = { sessionId: scope.sessionId }\n if (scope.cwd !== undefined && scope.cwd !== '') payload.cwd = scope.cwd\n if (path !== undefined) payload.path = path\n const resp = await fetch('/sidebar/api/fs.tree', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!resp.ok) throw new Error(`fs.tree ${path ?? '(root)'}: ${resp.status}`)\n const parsed = await resp.json() as { ok?: boolean; value?: { entries: FsEntry[] }; error?: { message: string } }\n if (!parsed.ok || parsed.value === undefined) {\n throw new Error(`fs.tree ${path ?? '(root)'}: ${parsed.error?.message ?? 'unknown error'}`)\n }\n return parsed.value.entries.filter((e) => e.isDir && !e.hidden)\n}\n\n/** Recursive tree node: loads its own children on expand. */\nfunction TreeNode(props: {\n scope: SessionScope\n entry: FsEntry\n depth: number\n selectedPath: string\n onSelect: (path: string) => void\n}): ReactNode {\n const { scope, entry, depth, selectedPath, onSelect } = props\n const [expanded, setExpanded] = useState(false)\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n\n const handleToggle = (): void => {\n if (!expanded && children === null) {\n setLoading(true)\n fsTree(scope, entry.path)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch(() => { setChildren([]); setLoading(false) })\n }\n setExpanded(!expanded)\n }\n\n const isSelected = selectedPath === entry.path\n\n return createElement('div', null,\n createElement('div', {\n className: `sts-tree-row${isSelected ? ' selected' : ''}`,\n style: { paddingLeft: `${depth * 16 + 8}px` },\n onClick: () => { onSelect(entry.path); handleToggle() },\n },\n createElement('span', { className: `sts-tree-chevron${expanded ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, entry.name),\n ),\n expanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u52a0\\u8f7d\\u4e2d...')\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(TreeNode, {\n key: child.path,\n scope,\n entry: child,\n depth: depth + 1,\n selectedPath,\n onSelect,\n }),\n ),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u65e0\\u5b50\\u76ee\\u5f55')\n : null,\n )\n}\n\n/** Directory tree picker: shows a collapsible tree rooted at session cwd. */\nfunction DirTreePicker(props: { scope: SessionScope; value: string; onSelect: (path: string) => void }): ReactNode {\n const { scope, value, onSelect } = props\n const [expanded, setExpanded] = useState(false)\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const handleToggle = (): void => {\n if (!expanded && children === null) {\n setLoading(true)\n setError(null)\n fsTree(scope)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch((err) => { setError(err instanceof Error ? err.message : String(err)); setLoading(false) })\n }\n setExpanded(!expanded)\n }\n\n const rootSelected = value === '.' || value === '' || value === './'\n\n return createElement('div', { className: 'sts-tree-picker' },\n createElement('div', {\n className: `sts-tree-row${rootSelected ? ' selected' : ''}`,\n style: { paddingLeft: '8px' },\n onClick: () => { onSelect('.'); handleToggle() },\n },\n createElement('span', { className: `sts-tree-chevron${expanded ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, '(\\u5de5\\u4f5c\\u7a7a\\u95f4\\u6839\\u76ee\\u5f55)'),\n ),\n expanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: '24px' } }, '\\u52a0\\u8f7d\\u4e2d...')\n : error !== null\n ? createElement('div', { className: 'sts-tree-error', style: { paddingLeft: '24px' } }, error)\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(TreeNode, {\n key: child.path,\n scope,\n entry: child,\n depth: 1,\n selectedPath: value,\n onSelect,\n }),\n ),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: '24px' } }, '\\u65e0\\u5b50\\u76ee\\u5f55')\n : null,\n )\n}\n\n/** One env var row. */\ninterface EnvRow {\n key: string\n value: string\n}\n\n/** Props for the modal. */\nexport interface ConfigEditorModalProps {\n /** Existing config to edit, or null for a new config. */\n config: {\n id?: string\n name: string\n type: string\n command: string\n cwd: string\n env: Record<string, string>\n jvmArgs?: string\n args?: string\n runtime?: { java?: string; node?: string; python?: string; mvn?: string }\n } | null\n /** Session scope for fsTree calls. */\n scope: SessionScope\n /** Called when the user saves. */\n onSave: (config: {\n id?: string\n name: string\n type: string\n command: string\n cwd: string\n env: Record<string, string>\n jvmArgs: string\n args: string\n runtime: Record<string, string>\n }) => void\n /** Called when the user cancels. */\n onCancel: () => void\n}\n\n/** Type \\u2192 default command mapping. */\nconst TYPE_DEFAULTS: Record<string, string> = {\n npm: 'npm run dev',\n springboot: 'mvn spring-boot:run',\n python: 'python main.py',\n custom: '',\n}\n\nconst TYPE_LABELS: Record<string, string> = {\n npm: 'npm',\n springboot: 'Spring Boot',\n python: 'Python',\n custom: 'Custom',\n}\n\n/** The modal dialog component. */\nexport function ConfigEditorModal({ config, scope, onSave, onCancel }: ConfigEditorModalProps): ReactNode {\n const [name, setName] = useState(config?.name ?? '')\n const [type, setType] = useState(config?.type ?? 'custom')\n const [command, setCommand] = useState(config?.command ?? '')\n const [cwd, setCwd] = useState(config?.cwd ?? '.')\n const [jvmArgs, setJvmArgs] = useState(config?.jvmArgs ?? '')\n const [args, setArgs] = useState(config?.args ?? '')\n const [envRows, setEnvRows] = useState<EnvRow[]>(\n config\n ? Object.entries(config.env).map(([key, value]) => ({ key, value }))\n : [],\n )\n const [showTree, setShowTree] = useState(false)\n const [showRuntime, setShowRuntime] = useState(false)\n const [rtJava, setRtJava] = useState(config?.runtime?.java ?? '')\n const [rtNode, setRtNode] = useState(config?.runtime?.node ?? '')\n const [rtPython, setRtPython] = useState(config?.runtime?.python ?? '')\n const [rtMvn, setRtMvn] = useState(config?.runtime?.mvn ?? '')\n\n // When type changes and command is empty (new config), auto-fill template.\n useEffect(() => {\n if (command === '' && TYPE_DEFAULTS[type] !== '') {\n setCommand(TYPE_DEFAULTS[type])\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [type])\n\n const handleSave = (): void => {\n const env: Record<string, string> = {}\n for (const row of envRows) {\n if (row.key.trim() !== '') {\n env[row.key.trim()] = row.value\n }\n }\n const runtime: Record<string, string> = {}\n if (rtJava.trim() !== '') runtime.java = rtJava.trim()\n if (rtNode.trim() !== '') runtime.node = rtNode.trim()\n if (rtPython.trim() !== '') runtime.python = rtPython.trim()\n if (rtMvn.trim() !== '') runtime.mvn = rtMvn.trim()\n onSave({\n id: config?.id,\n name: name.trim(),\n type,\n command: command.trim(),\n cwd: cwd.trim() === '' ? '.' : cwd.trim(),\n env,\n jvmArgs: jvmArgs.trim(),\n args: args.trim(),\n runtime,\n })\n }\n\n const updateEnvRow = (idx: number, field: 'key' | 'value', val: string): void => {\n setEnvRows((prev) => prev.map((row, i) => i === idx ? { ...row, [field]: val } : row))\n }\n const addEnvRow = (): void => setEnvRows((prev) => [...prev, { key: '', value: '' }])\n const removeEnvRow = (idx: number): void => setEnvRows((prev) => prev.filter((_, i) => i !== idx))\n\n // Bulk paste state.\n const [showEnvPaste, setShowEnvPaste] = useState(false)\n const [envPasteText, setEnvPasteText] = useState('')\n\n /** Parse \"K=V;K=V,...\" (or newline/comma separated) into env rows, merging\n * into the existing rows (later values override earlier for duplicate keys). */\n const applyEnvPaste = (): void => {\n const existing = new Map(envRows.filter((r) => r.key.trim() !== '').map((r) => [r.key.trim(), r.value]))\n const text = envPasteText.trim()\n if (text === '') return\n // Split on semicolons, commas, or newlines.\n const pairs = text.split(/[;,\\n]+/)\n for (const pair of pairs) {\n const p = pair.trim()\n if (p === '') continue\n const eq = p.indexOf('=')\n if (eq <= 0) continue // ignore malformed entries\n const key = p.substring(0, eq).trim()\n const value = p.substring(eq + 1).trim()\n if (key !== '') existing.set(key, value)\n }\n setEnvRows(Array.from(existing, ([key, value]) => ({ key, value })))\n setEnvPasteText('')\n setShowEnvPaste(false)\n }\n\n const isSpringboot = type === 'springboot'\n\n return createElement('div', {\n className: 'sts-modal-overlay',\n onClick: (e: MouseEvent) => { if (e.target === e.currentTarget) onCancel() },\n },\n createElement('div', { className: 'sts-modal' },\n // Header\n createElement('div', { className: 'sts-modal-header' },\n config?.id ? '\\u7f16\\u8f91\\u8fd0\\u884c\\u914d\\u7f6e' : '\\u65b0\\u5efa\\u8fd0\\u884c\\u914d\\u7f6e',\n createElement('button', {\n className: 'sts-icon-btn',\n onClick: onCancel,\n }, createElement(CloseIcon, { size: 16 })),\n ),\n // Body\n createElement('div', { className: 'sts-modal-body' },\n // Name\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u540d\\u79f0'),\n createElement('input', {\n className: 'sts-modal-input',\n value: name,\n onChange: (e: { target: { value: string } }) => setName(e.target.value),\n placeholder: '\\u914d\\u7f6e\\u540d\\u79f0',\n }),\n ),\n // Type\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u7c7b\\u578b'),\n createElement('select', {\n className: 'sts-modal-select',\n value: type,\n onChange: (e: { target: { value: string } }) => setType(e.target.value),\n },\n ...Object.entries(TYPE_LABELS).map(([val, label]) =>\n createElement('option', { key: val, value: val }, label),\n ),\n ),\n ),\n // Command\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u547d\\u4ee4'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: command,\n onChange: (e: { target: { value: string } }) => setCommand(e.target.value),\n placeholder: '\\u8981\\u6267\\u884c\\u7684\\u547d\\u4ee4',\n }),\n ),\n // Working directory (tree picker)\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u5de5\\u4f5c\\u76ee\\u5f55'),\n createElement('div', { className: 'sts-cwd-display', onClick: () => setShowTree(!showTree) },\n createElement('span', { className: 'sts-cwd-icon' }, createElement(FolderIcon, { size: 14 })),\n createElement('span', { className: 'sts-cwd-value' }, cwd),\n ),\n showTree\n ? createElement('div', { className: 'sts-tree-picker-container' },\n createElement(DirTreePicker, { scope, value: cwd, onSelect: (p) => setCwd(p) }),\n )\n : null,\n ),\n // JVM args (springboot only)\n isSpringboot\n ? createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u865a\\u62df\\u673a\\u53c2\\u6570 (JVM Args)'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: jvmArgs,\n onChange: (e: { target: { value: string } }) => setJvmArgs(e.target.value),\n placeholder: '\\u4f8b: -Xmx512m -Dspring.profiles.active=dev',\n }),\n )\n : null,\n // Program args (all types)\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u7a0b\\u5e8f\\u5b9e\\u53c2'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: args,\n onChange: (e: { target: { value: string } }) => setArgs(e.target.value),\n placeholder: '\\u4f8b: --server.port=8080 --debug',\n }),\n ),\n // Runtime environment (collapsible advanced section)\n createElement('div', { className: 'sts-modal-field' },\n createElement('div', {\n className: `sts-collapse-header${showRuntime ? ' open' : ''}`,\n onClick: () => setShowRuntime(!showRuntime),\n },\n createElement('span', { className: `sts-collapse-chevron${showRuntime ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', null, '\\u8fd0\\u884c\\u65f6\\u73af\\u5883 (\\u9ad8\\u7ea7)'),\n createElement('span', { className: 'sts-collapse-hint' }, '\\u9ed8\\u8ba4\\u7528\\u5168\\u5c40\\u914d\\u7f6e'),\n ),\n showRuntime\n ? createElement('div', { className: 'sts-collapse-body' },\n createElement('div', { className: 'sts-modal-label' }, '\\u7559\\u7a7a\\u5219\\u7528\\u5168\\u5c40/\\u7cfb\\u7edf\\u914d\\u7f6e'),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Java \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'java',\n value: rtJava,\n onChange: setRtJava,\n compact: true,\n desc: '\\u9009\\u62e9 JDK \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Node.js \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'node',\n value: rtNode,\n onChange: setRtNode,\n compact: true,\n desc: '\\u9009\\u62e9 Node \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Python \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'python',\n value: rtPython,\n onChange: setRtPython,\n compact: true,\n desc: '\\u9009\\u62e9 Python \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Maven \\u8def\\u5f84 (MVN)'),\n createElement(PathPicker, {\n scope,\n kind: 'mvn',\n value: rtMvn,\n onChange: setRtMvn,\n compact: true,\n desc: '\\u9009\\u62e9 Maven \\u76ee\\u5f55',\n }),\n ),\n )\n : null,\n ),\n // Environment variables\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u73af\\u5883\\u53d8\\u91cf'),\n ...envRows.map((row, idx) =>\n createElement('div', { key: idx, className: 'sts-env-row' },\n createElement('input', {\n className: 'sts-modal-input sts-env-key',\n value: row.key,\n onChange: (e: { target: { value: string } }) => updateEnvRow(idx, 'key', e.target.value),\n placeholder: '\\u53d8\\u91cf\\u540d (\\u5982 JAVA_HOME)',\n }),\n createElement('span', { className: 'sts-env-sep' }, '='),\n createElement('input', {\n className: 'sts-modal-input sts-env-val',\n value: row.value,\n onChange: (e: { target: { value: string } }) => updateEnvRow(idx, 'value', e.target.value),\n placeholder: '\\u53d8\\u91cf\\u503c',\n }),\n createElement('button', {\n className: 'sts-icon-btn trash',\n onClick: () => removeEnvRow(idx),\n }, createElement(TrashIcon, { size: 14 })),\n ),\n ),\n createElement('div', { className: 'sts-env-actions' },\n createElement('button', {\n className: 'sts-env-add', onClick: addEnvRow,\n },\n createElement(PlusIcon, { size: 12 }), ' \\u6dfb\\u52a0\\u73af\\u5883\\u53d8\\u91cf',\n ),\n createElement('button', {\n className: 'sts-env-add', onClick: () => setShowEnvPaste(!showEnvPaste),\n },\n createElement(ClipboardIcon, { size: 12 }), showEnvPaste ? ' \\u53d6\\u6d88' : ' \\u6279\\u91cf\\u7c98\\u8d34',\n ),\n ),\n showEnvPaste\n ? createElement('div', { className: 'sts-env-paste' },\n createElement('textarea', {\n className: 'sts-env-paste-input',\n value: envPasteText,\n onChange: (e: { target: { value: string } }) => setEnvPasteText(e.target.value),\n placeholder: '\\u7c98\\u8d34 KEY=VALUE;KEY=VALUE;\\u6216\\u4ee5\\u6362\\u884c/\\u9017\\u53f7\\u5206\\u9694\\uff0c\\u5982\\uff1a\\nDB_DATABASE=cassiopeia_ap;DB_HOST=10.18.18.18;DB_PORT=3306;MQ_HOST=10.18.18.18;MQ_PASSWORD=admin123;MQ_PORT=5672;MQ_USERNAME=admin;MQ_VIRTUAL_HOST=/;REDIS_HOST=10.18.18.17;REDIS_PASSWORD=Qaz!wsx2;REDIS_PORT=6379',\n rows: 4,\n }),\n createElement('div', { className: 'sts-env-paste-footer' },\n createElement('button', {\n className: 'sts-env-paste-apply', onClick: applyEnvPaste,\n },\n '\\u786e\\u5b9a\\u5e76\\u751f\\u6210\\u73af\\u5883\\u53d8\\u91cf'),\n createElement('span', { className: 'sts-env-paste-hint' }, '\\u91cd\\u590d\\u5c06\\u8986\\u76d6'),\n ),\n )\n : null,\n ),\n ),\n // Footer\n createElement('div', { className: 'sts-modal-footer' },\n createElement('button', { className: 'sts-modal-btn', onClick: onCancel }, '\\u53d6\\u6d88'),\n createElement('button', {\n className: 'sts-modal-btn primary',\n onClick: handleSave,\n disabled: name.trim() === '',\n style: {\n background: '#1a1a1e',\n border: '1px solid #3a3a3e',\n color: '#fff',\n borderRadius: '6px',\n padding: '8px 18px',\n cursor: name.trim() === '' ? 'not-allowed' : 'pointer',\n fontSize: '13px',\n opacity: name.trim() === '' ? 0.5 : 1,\n },\n }, '\\u4fdd\\u5b58'),\n ),\n ),\n )\n}\n","/**\r\n * CSS styles for the starter plugin panel. Uses .sts- prefixed class names\r\n * (scoped enough to avoid collisions with the host shell).\r\n * Uses --dsw-alias-* CSS variables from the DSH design system to follow the\r\n * active theme automatically (dark/light).\r\n * @module dsh-better-sidebar-starter/client/styles\r\n */\r\n\r\nexport const STYLES_CSS = `\r\n/* ===== Root container ===== */\r\n.sts-root {\r\n display: flex;\r\n flex-direction: column;\r\n height: 100%;\r\n min-height: 0;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-bg-base, #1a1a1e);\r\n}\r\n\r\n/* ===== Toolbar ===== */\r\n.sts-toolbar {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 0 12px;\r\n height: 40px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n flex-shrink: 0;\r\n}\r\n.sts-toolbar-title {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n font: var(--dsw-font-m-14, 500 14px/20px sans-serif);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-toolbar-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 4px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 12px;\r\n transition: background 0.12s, border-color 0.12s;\r\n}\r\n.sts-toolbar-btn:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-toolbar-btn.active {\r\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.18));\r\n border-color: var(--dsw-alias-brand, #007acc);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n\r\n/* ===== Log history viewer ===== */\r\n.sts-history-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n width: calc(100% - 8px);\r\n margin: 2px 4px;\r\n padding: 6px 8px;\r\n border: 1px solid transparent;\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 12px;\r\n text-align: left;\r\n transition: background 0.12s, border-color 0.12s;\r\n}\r\n.sts-history-item:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n border-color: var(--dsw-alias-border-l2, #2a2a2e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-history-item-meta {\r\n flex-shrink: 0;\r\n font-size: 11px;\r\n opacity: 0.55;\r\n}\r\n.sts-log-meta {\r\n opacity: 0.72;\r\n font-style: italic;\r\n}\r\n\r\n/* ===== Split pane (horizontal: default for right panel) ===== */\r\n.sts-split {\r\n display: flex;\r\n flex: 1;\r\n min-height: 0;\r\n}\r\n.sts-split-first {\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n border-right: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n flex-shrink: 0;\r\n}\r\n.sts-split-gutter {\r\n width: 1px;\r\n background: var(--dsw-alias-border-l2, #2a2a2e);\r\n cursor: col-resize;\r\n flex-shrink: 0;\r\n transition: width 0.12s, background 0.12s;\r\n}\r\n.sts-split-gutter:hover,\r\n.sts-split-gutter.dragging {\r\n width: 3px;\r\n background: var(--dsw-alias-interactive-bg-hover-accent, #007acc);\r\n}\r\n.sts-split-second {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0;\r\n min-height: 0;\r\n}\r\n\r\n/* ===== Split pane (vertical: for bottom panel) ===== */\r\n.sts-split-vertical {\r\n flex-direction: column;\r\n}\r\n.sts-split-first-v {\r\n border-right: none;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n width: 100% !important;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n flex-shrink: 0;\r\n}\r\n.sts-split-gutter-v {\r\n width: auto;\r\n height: 1px;\r\n cursor: row-resize;\r\n transition: height 0.12s, background 0.12s;\r\n}\r\n.sts-split-gutter-v:hover,\r\n.sts-split-gutter-v.dragging {\r\n width: auto;\r\n height: 3px;\r\n background: var(--dsw-alias-interactive-bg-hover-accent, #007acc);\r\n}\r\n.sts-split-second-v {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0;\r\n min-height: 0;\r\n}\r\n\r\n/* ===== Config tree ===== */\r\n.sts-tree {\r\n padding: 4px 4px 8px;\r\n user-select: none;\r\n}\r\n.sts-group-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 6px 8px;\r\n font: var(--dsw-font-xxxs-11, 500 11px/16px sans-serif);\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n cursor: pointer;\r\n border-radius: 6px;\r\n}\r\n.sts-group-header:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-group-count {\r\n color: rgba(255,255,255,0.4);\r\n font-weight: 400;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n.sts-group-children {\r\n overflow: hidden;\r\n}\r\n.sts-group-children.collapsed {\r\n display: none;\r\n}\r\n\r\n/* ===== Config row ===== */\r\n.sts-config-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 5px 8px 5px 24px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n border-radius: 6px;\r\n margin: 1px 2px;\r\n transition: background 0.1s;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-config-row:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-config-row.selected {\r\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\r\n}\r\n.sts-config-name {\r\n flex: 1;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n font-weight: 400;\r\n}\r\n.sts-config-cmd {\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n max-width: 100px;\r\n}\r\n.sts-config-actions {\r\n display: flex;\r\n align-items: center;\r\n gap: 1px;\r\n flex-shrink: 0;\r\n}\r\n.sts-icon-btn {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 22px;\r\n height: 22px;\r\n border: none;\r\n border-radius: 4px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n opacity: 0;\r\n transition: opacity 0.1s, background 0.1s;\r\n}\r\n.sts-config-row:hover .sts-icon-btn {\r\n opacity: 0.7;\r\n}\r\n.sts-icon-btn:hover {\r\n opacity: 1 !important;\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.08));\r\n}\r\n.sts-icon-btn.play { color: #6ab04c; }\r\n.sts-icon-btn.stop { color: #eb5757; }\r\n.sts-icon-btn.gear { color: var(--dsw-alias-label-secondary, #b0b0b0); }\r\n.sts-icon-btn.trash { color: #eb5757; }\r\n.sts-icon-btn.dup { color: var(--dsw-alias-label-secondary, #b0b0b0); }\r\n.sts-icon-btn.send { color: #4a90d9; }\r\n/* Toolbar buttons (log view) are icon-only; the label expands on hover. */\r\n.sts-log-toolbar .sts-icon-btn {\r\n width: 26px;\r\n height: 26px;\r\n padding: 0 6px;\r\n gap: 0;\r\n font-size: 11px;\r\n opacity: 1;\r\n}\r\n.sts-log-toolbar .sts-icon-btn .sts-icon-btn-label {\r\n display: inline-block;\r\n max-width: 0;\r\n overflow: hidden;\r\n white-space: nowrap;\r\n opacity: 0;\r\n transition: max-width 0.18s ease, opacity 0.18s ease, margin-left 0.18s ease;\r\n}\r\n.sts-log-toolbar .sts-icon-btn:hover .sts-icon-btn-label {\r\n max-width: 120px;\r\n margin-left: 4px;\r\n opacity: 1;\r\n}\r\n\r\n/* ===== Instance row ===== */\r\n.sts-instance-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 3px 8px 3px 40px;\r\n font-size: 11px;\r\n cursor: pointer;\r\n border-radius: 6px;\r\n margin: 1px 2px;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n transition: background 0.1s;\r\n}\r\n.sts-instance-row:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-instance-row.selected {\r\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-instance-pid {\r\n color: rgba(255,255,255,0.4);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n/* ===== Empty states ===== */\r\n.sts-empty {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n height: 100%;\r\n color: rgba(255,255,255,0.4);\r\n font-size: 12px;\r\n padding: 24px;\r\n text-align: center;\r\n gap: 8px;\r\n line-height: 20px;\r\n}\r\n\r\n/* ===== Log view ===== */\r\n.sts-log-toolbar {\r\n display: flex;\r\n align-items: center;\r\n gap: 2px;\r\n padding: 0 8px;\r\n height: 36px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n flex-shrink: 0;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n font-size: 12px;\r\n}\r\n.sts-log-toolbar-spacer {\r\n flex: 1;\r\n}\r\n.sts-log-toolbar-label {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n font-size: 11px;\r\n cursor: pointer;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n padding: 2px 6px;\r\n border-radius: 4px;\r\n}\r\n.sts-log-toolbar-label:hover {\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-log-content {\r\n flex: 1;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n padding: 8px 12px;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n font-size: 12px;\r\n line-height: 1.6;\r\n white-space: pre-wrap;\r\n word-break: break-all;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-bg-base, #1a1a1e);\r\n}\r\n.sts-log-line {\r\n min-height: 1em;\r\n}\r\n/* Clickable source tokens (classes / stack frames) inside the log. */\r\n.sts-log-link {\r\n cursor: pointer;\r\n border-bottom: 1px dashed rgba(97, 175, 239, 0.4);\r\n transition: background 0.1s ease;\r\n}\r\n.sts-log-link:hover {\r\n background: rgba(97, 175, 239, 0.14);\r\n border-bottom-color: #61afef;\r\n}\r\n/* Clickable http(s) URLs inside the log (open in a new browser tab). */\r\n.sts-log-url {\r\n color: #61afef;\r\n}\r\n.sts-log-url:hover {\r\n background: rgba(97, 175, 239, 0.18);\r\n}\r\n\r\n/* ===== Config detail ===== */\r\n.sts-detail {\r\n padding: 20px;\r\n overflow-y: auto;\r\n flex: 1;\r\n}\r\n.sts-detail-card {\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n background: var(--dsw-alias-bg-layer-2, #202024);\r\n border-radius: 12px;\r\n padding: 20px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n}\r\n.sts-detail-title {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n font-size: 16px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n padding-bottom: 12px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-detail-row {\r\n display: flex;\r\n gap: 12px;\r\n font-size: 13px;\r\n align-items: baseline;\r\n}\r\n.sts-detail-label {\r\n width: 72px;\r\n flex-shrink: 0;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n font-size: 11px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.3px;\r\n}\r\n.sts-detail-value {\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n word-break: break-all;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-detail-actions {\r\n display: flex;\r\n gap: 8px;\r\n justify-content: flex-end;\r\n margin-top: 12px;\r\n padding-top: 16px;\r\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-detail-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 6px 14px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 12px;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-detail-btn:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-detail-btn.danger {\r\n color: #eb5757;\r\n border-color: rgba(235,87,87,0.4);\r\n}\r\n.sts-detail-btn.danger:hover {\r\n background: rgba(235,87,87,0.1);\r\n}\r\n\r\n/* ===== Modal ===== */\r\n.sts-modal-overlay {\r\n position: fixed;\r\n top: 0; left: 0; right: 0; bottom: 0;\r\n background: rgba(0,0,0,0.45);\r\n backdrop-filter: blur(2px);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 10000;\r\n}\r\n.sts-modal {\r\n background: var(--dsw-alias-bg-layer-3, #242428);\r\n border: 1px solid var(--dsw-alias-border-l3, #3a3a3e);\r\n border-radius: 12px;\r\n width: 480px;\r\n max-width: 90vw;\r\n max-height: 80vh;\r\n overflow-y: auto;\r\n display: flex;\r\n flex-direction: column;\r\n box-shadow: 0 8px 32px rgba(0,0,0,0.3);\r\n}\r\n.sts-modal-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 14px 20px;\r\n font-size: 15px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-modal-body {\r\n padding: 20px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n}\r\n.sts-modal-field {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n.sts-modal-label {\r\n font-size: 11px;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n text-transform: uppercase;\r\n letter-spacing: 0.3px;\r\n}\r\n.sts-modal-input,\r\n.sts-modal-select {\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n font-size: 13px;\r\n font-family: inherit;\r\n outline: none;\r\n transition: border-color 0.12s, background 0.12s;\r\n}\r\n.sts-modal-input:focus,\r\n.sts-modal-select:focus {\r\n border-color: var(--dsw-alias-button-primary-fill, #007acc);\r\n background: var(--dsw-alias-bg-base, #1a1a1e);\r\n}\r\n.sts-modal-input.mono {\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n}\r\n.sts-env-row {\r\n display: flex;\r\n gap: 6px;\r\n align-items: center;\r\n}\r\n.sts-env-key {\r\n flex: 1;\r\n}\r\n.sts-env-val {\r\n flex: 2;\r\n}\r\n.sts-env-sep {\r\n color: var(--dsw-alias-label-dimmed, #555);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n}\r\n.sts-env-add {\r\n font-size: 12px;\r\n padding: 6px 10px;\r\n border: 1px dashed var(--dsw-alias-border-l3, #3a3a3e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n cursor: pointer;\r\n align-self: flex-start;\r\n transition: color 0.12s, border-color 0.12s;\r\n}\r\n.sts-env-add:hover {\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n border-color: var(--dsw-alias-button-primary-fill, #007acc);\r\n}\r\n.sts-modal-footer {\r\n display: flex;\r\n justify-content: flex-end;\r\n gap: 8px;\r\n padding: 14px 20px;\r\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-modal-btn {\r\n padding: 8px 18px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 13px;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-modal-btn:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n\r\n/* ===== Directory tree picker ===== */\r\n.sts-cwd-display {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n cursor: pointer;\r\n transition: border-color 0.12s;\r\n font-size: 13px;\r\n}\r\n.sts-cwd-display:hover {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-cwd-icon {\r\n display: flex;\r\n align-items: center;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n}\r\n.sts-cwd-value {\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n.sts-tree-picker-container {\r\n margin-top: 4px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n max-height: 240px;\r\n overflow-y: auto;\r\n padding: 4px;\r\n}\r\n.sts-tree-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 4px 8px;\r\n cursor: pointer;\r\n border-radius: 4px;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n transition: background 0.08s;\r\n white-space: nowrap;\r\n}\r\n.sts-tree-row:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-tree-row.selected {\r\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-tree-chevron {\r\n display: flex;\r\n align-items: center;\r\n transition: transform 0.1s;\r\n flex-shrink: 0;\r\n}\r\n.sts-tree-chevron.expanded {\r\n transform: rotate(90deg);\r\n}\r\n.sts-tree-icon {\r\n display: flex;\r\n align-items: center;\r\n flex-shrink: 0;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n}\r\n.sts-tree-label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n}\r\n.sts-tree-loading,\r\n.sts-tree-error,\r\n.sts-tree-empty {\r\n padding: 8px 12px;\r\n font-size: 12px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-tree-error {\r\n color: #eb5757;\r\n}\r\n\r\n/* ===== Settings panel ===== */\r\n.sts-settings-panel {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n padding: 8px;\r\n min-width: 320px;\r\n}\r\n.sts-settings-row {\r\n padding: 4px 0;\r\n}\r\n.sts-settings-switch-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-settings-switch {\r\n width: 16px;\r\n height: 16px;\r\n cursor: pointer;\r\n}\r\n.sts-settings-sep {\r\n margin: 8px 0 4px;\r\n padding: 0 4px;\r\n font-size: 10px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-settings-path {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n padding: 4px 0;\r\n}\r\n.sts-settings-path-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n}\r\n.sts-settings-label {\r\n font-size: 12px;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n}\r\n.sts-settings-badge {\r\n display: flex;\r\n align-items: center;\r\n gap: 2px;\r\n font-size: 10px;\r\n padding: 1px 6px;\r\n border-radius: 10px;\r\n}\r\n.sts-settings-badge.ok {\r\n color: #4e9a06;\r\n background: rgba(78,154,6,0.12);\r\n}\r\n.sts-settings-badge.err {\r\n color: #eb5757;\r\n background: rgba(235,87,87,0.12);\r\n}\r\n.sts-settings-path-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n}\r\n.sts-settings-path-icon {\r\n display: flex;\r\n align-items: center;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n flex-shrink: 0;\r\n}\r\n.sts-settings-input {\r\n flex: 1;\r\n padding: 6px 8px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 4px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n font-size: 12px;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n outline: none;\r\n min-width: 0;\r\n}\r\n.sts-settings-input:focus {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-settings-input::placeholder {\r\n color: rgba(255,255,255,0.3);\r\n}\r\n.sts-settings-browse {\r\n padding: 6px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 4px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 12px;\r\n flex-shrink: 0;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-settings-browse:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-settings-msg {\r\n font-size: 10px;\r\n padding: 0 4px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-settings-msg.valid {\r\n color: #4e9a06;\r\n}\r\n.sts-settings-msg.invalid {\r\n color: #eb5757;\r\n}\r\n\r\n/* ===== Collapsible advanced section ===== */\r\n.sts-collapse-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n transition: border-color 0.12s;\r\n user-select: none;\r\n}\r\n.sts-collapse-header:hover {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-collapse-chevron {\r\n display: flex;\r\n align-items: center;\r\n transition: transform 0.12s;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n}\r\n.sts-collapse-chevron.expanded {\r\n transform: rotate(90deg);\r\n}\r\n.sts-collapse-hint {\r\n margin-left: auto;\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-collapse-body {\r\n margin-top: 8px;\r\n padding: 12px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n display: flex;\r\n flex-direction: column;\r\n gap: 10px;\r\n}\r\n\r\n/* ===== Settings picker rows & dir chooser modal ===== */\r\n.sts-settings-picker-row {\r\n margin: 6px 0;\r\n}\r\n.sts-settings-picker-row .sts-settings-label {\r\n display: block;\r\n margin-bottom: 4px;\r\n font-size: 11px;\r\n}\r\n.sts-dir-modal {\r\n background: var(--dsw-alias-bg-layer-3, #242428);\r\n border: 1px solid var(--dsw-alias-border-l3, #3a3a3e);\r\n border-radius: 12px;\r\n width: 420px;\r\n max-width: 90vw;\r\n max-height: 70vh;\r\n display: flex;\r\n flex-direction: column;\r\n box-shadow: 0 8px 32px rgba(0,0,0,0.3);\r\n}\r\n.sts-dir-modal-tree {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 8px;\r\n min-height: 160px;\r\n}\r\n.sts-dirtree {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 2px;\r\n}\r\n.sts-dirtree-nav {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 4px 4px 8px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n margin-bottom: 4px;\r\n}\r\n.sts-dirtree-up,\r\n.sts-dirtree-home {\r\n width: 24px;\r\n height: 24px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 4px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 13px;\r\n flex-shrink: 0;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-dirtree-up:hover,\r\n.sts-dirtree-home:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-dirtree-up:disabled {\r\n opacity: 0.4;\r\n cursor: default;\r\n}\r\n.sts-dirtree-cur {\r\n flex: 1;\r\n font-size: 11px;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n direction: rtl;\r\n text-align: left;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n}\r\n.sts-tree-select-hint {\r\n margin-left: auto;\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-dirtree-sub {\r\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n margin-top: 2px;\r\n padding-top: 2px;\r\n}\r\n\r\n/* ===== Env bulk paste ===== */\r\n.sts-env-actions {\r\n display: flex;\r\n gap: 6px;\r\n align-items: center;\r\n margin-top: 2px;\r\n}\r\n.sts-env-paste {\r\n margin-top: 8px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n.sts-env-paste-input {\r\n width: 100%;\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n font-size: 12px;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n outline: none;\r\n resize: vertical;\r\n min-height: 80px;\r\n box-sizing: border-box;\r\n}\r\n.sts-env-paste-input:focus {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-env-paste-input::placeholder {\r\n color: rgba(255,255,255,0.3);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n}\r\n.sts-env-paste-footer {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n}\r\n.sts-env-paste-apply {\r\n padding: 6px 14px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n cursor: pointer;\r\n font-size: 12px;\r\n transition: background 0.12s;\r\n}\r\n.sts-env-paste-apply:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n}\r\n.sts-env-paste-hint {\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n`\r\n","/**\n * Services tab: the main sidebar panel component. Left 1/3 config tree,\n * right 2/3 log view or config detail. Manages config CRUD via the host\n * REST API and tracks running instances.\n * @module dsh-better-sidebar-starter/client/ServicesTab\n */\n\nimport { createElement, useState, useEffect, useRef, useCallback, type ReactNode } from 'react'\nimport type { TabComponentProps } from 'dsh-better-sidebar/client/service'\nimport type { RunConfig, RunInstance } from './types.ts'\nimport { ConfigTree } from './ConfigTree.tsx'\nimport { LogView } from './LogView.tsx'\nimport { LogHistoryView } from './LogHistoryView.tsx'\nimport { ConfigEditorModal, type ConfigEditorModalProps } from './ConfigEditorModal.tsx'\nimport { RocketIcon, PlusIcon } from './icons.tsx'\nimport { STYLES_CSS } from './styles.ts'\n\n/** API base. */\nconst API_BASE = '/api/dsh-better-sidebar-starter'\n\n/** Instance poll interval (ms). */\nconst POLL_INTERVAL = 3000\n\n/** Inject styles once. */\nlet STYLES_INJECTED = false\nfunction injectStyles(): void {\n if (STYLES_INJECTED) return\n STYLES_INJECTED = true\n const tag = document.createElement('style')\n tag.id = 'dsh-better-sidebar-starter-styles'\n tag.setAttribute('data-plugin', '@dong-victor/dsh-better-sidebar-starter')\n tag.textContent = STYLES_CSS\n document.head.appendChild(tag)\n}\n\n/** The main services tab component. */\nexport function ServicesTab(props: TabComponentProps): ReactNode {\n injectStyles()\n\n const { scope, store } = props\n const [configs, setConfigs] = useState<RunConfig[]>([])\n const [instances, setInstances] = useState<RunInstance[]>([])\n const [selectedConfigId, setSelectedConfigId] = useState<string | null>(null)\n const [selectedInstanceId, setSelectedInstanceId] = useState<string | null>(null)\n const [editingConfig, setEditingConfig] = useState<ConfigEditorModalProps['config'] | null>(null)\n const [showModal, setShowModal] = useState(false)\n const [splitRatio, setSplitRatio] = useState(0.33)\n const [dragging, setDragging] = useState(false)\n const [isHorizontal, setIsHorizontal] = useState(true) // true = left/right split (right panel), false = top/bottom (bottom panel)\n\n const containerRef = useRef<HTMLDivElement>(null)\n\n // Detect panel orientation via ResizeObserver (aspect ratio).\n useEffect(() => {\n const el = containerRef.current\n if (el === null) return\n const check = (): void => {\n const rect = el.getBoundingClientRect()\n // If wider than tall → horizontal split (left/right). If taller → vertical (top/bottom).\n setIsHorizontal(rect.width > rect.height * 1.2)\n }\n check()\n const ro = new ResizeObserver(check)\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n // Read split ratio from plugin settings on mount.\n useEffect(() => {\n const blob = store.getPrefs()?.pluginSettings?.['dsh-better-sidebar-starter:services'] ?? {}\n if (typeof blob.splitRatio === 'number') {\n setSplitRatio(Math.max(0.2, Math.min(0.5, blob.splitRatio)))\n }\n }, [store])\n\n /** Persist split ratio to plugin settings. */\n const persistSplitRatio = useCallback((ratio: number): void => {\n const service = (store as any).service\n if (service !== undefined && typeof service.updateTab === 'function') {\n // Use pluginSettings persistence if available.\n }\n }, [store])\n\n /** Build a query string for the session scope. */\n const sessionQuery = useCallback((): string => {\n const params = new URLSearchParams({ sessionId: scope.sessionId })\n if (scope.cwd !== undefined) params.set('cwd', scope.cwd)\n return params.toString()\n }, [scope])\n\n /** Fetch configs from the host. */\n const fetchConfigs = useCallback(async (): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}`)\n if (resp.ok) {\n const data = await resp.json() as { configs: RunConfig[] }\n setConfigs(data.configs)\n }\n } catch { /* network error — ignore */ }\n }, [sessionQuery])\n\n /** Fetch instances from the host. */\n const fetchInstances = useCallback(async (): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/instances?${sessionQuery()}`)\n if (resp.ok) {\n const data = await resp.json() as { instances: RunInstance[] }\n setInstances(data.instances)\n }\n } catch { /* network error — ignore */ }\n }, [sessionQuery])\n\n // Initial load.\n useEffect(() => {\n void fetchConfigs()\n void fetchInstances()\n }, [fetchConfigs, fetchInstances])\n\n // Poll instances.\n useEffect(() => {\n const timer = setInterval(() => void fetchInstances(), POLL_INTERVAL)\n return () => clearInterval(timer)\n }, [fetchInstances])\n\n /** Start a config. */\n const handleStart = useCallback(async (configId: string): Promise<void> => {\n try {\n // Read global env paths from plugin settings.\n const blob = store.getPrefs()?.pluginSettings?.['dsh-better-sidebar-starter:services'] ?? {}\n const globalEnv: Record<string, string> = {}\n if (typeof blob.javaHome === 'string' && blob.javaHome.trim() !== '') globalEnv.JAVA_HOME = blob.javaHome.trim()\n if (typeof blob.nodePath === 'string' && blob.nodePath.trim() !== '') globalEnv.NODE_PATH = blob.nodePath.trim()\n if (typeof blob.pythonPath === 'string' && blob.pythonPath.trim() !== '') globalEnv.PYTHON_PATH = blob.pythonPath.trim()\n if (typeof blob.mvnPath === 'string' && blob.mvnPath.trim() !== '') globalEnv.MVN_PATH = blob.mvnPath.trim()\n\n const resp = await fetch(`${API_BASE}/run?${sessionQuery()}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ configId, globalEnv }),\n })\n if (resp.ok) {\n const data = await resp.json() as { instance: RunInstance }\n setSelectedInstanceId(data.instance.id)\n setSelectedConfigId(null)\n void fetchInstances()\n void fetchConfigs() // lastRunAt update\n }\n } catch { /* ignore */ }\n }, [sessionQuery, fetchInstances, fetchConfigs, store])\n\n /** Stop an instance. */\n const handleStop = useCallback(async (instanceId: string): Promise<void> => {\n try {\n await fetch(`${API_BASE}/stop`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ instanceId }),\n })\n void fetchInstances()\n } catch { /* ignore */ }\n }, [fetchInstances])\n\n /** Restart = stop + start the same config. */\n const handleRestart = useCallback(async (instanceId: string): Promise<void> => {\n const inst = instances.find((i) => i.id === instanceId)\n if (inst === undefined) return\n await handleStop(instanceId)\n // Small delay to let the process die.\n await new Promise((r) => setTimeout(r, 200))\n await handleStart(inst.configId)\n }, [instances, handleStop, handleStart])\n\n /** Open the new config modal. */\n const handleNewConfig = (): void => {\n setEditingConfig(null)\n setShowModal(true)\n }\n\n /** Open the edit config modal. */\n const handleEditConfig = (configId: string): void => {\n const config = configs.find((c) => c.id === configId)\n if (config === undefined) return\n setEditingConfig({\n id: config.id,\n name: config.name,\n type: config.type,\n command: config.command,\n cwd: config.cwd,\n env: config.env,\n jvmArgs: config.jvmArgs,\n args: config.args,\n runtime: config.runtime,\n })\n setShowModal(true)\n }\n\n /**\n * Duplicate a config: open the editor prefilled with ALL of the original\n * config's info under an auto-generated \"xxx-副本\" name (deduped against\n * existing names). Saving it (id omitted) creates a brand-new config.\n */\n const handleDuplicateConfig = (configId: string): void => {\n const config = configs.find((c) => c.id === configId)\n if (config === undefined) return\n // Derive a unique copy name: strip a trailing \"-副本\", then append\n // \"-副本\" (or \"-副本2\", \"-副本3\", …) while avoiding collisions.\n const stem = config.name.endsWith('-副本') ? config.name.slice(0, -3) : config.name\n const taken = new Set(configs.map((c) => c.name))\n let dupName = `${stem}-副本`\n let suffix = 2\n while (taken.has(dupName)) {\n dupName = `${stem}-副本${suffix}`\n suffix++\n }\n setEditingConfig({\n // No id → saving creates a new config instead of updating the source.\n name: dupName,\n type: config.type,\n command: config.command,\n cwd: config.cwd,\n env: config.env,\n jvmArgs: config.jvmArgs,\n args: config.args,\n runtime: config.runtime,\n })\n setShowModal(true)\n }\n\n /** Save a config (create or update). */\n const handleSaveConfig = useCallback(async (config: ConfigEditorModalProps['config']): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ config }),\n })\n if (resp.ok) {\n setShowModal(false)\n setEditingConfig(null)\n void fetchConfigs()\n }\n } catch { /* ignore */ }\n }, [sessionQuery, fetchConfigs])\n\n /** Delete a config. */\n const handleDeleteConfig = useCallback(async (configId: string): Promise<void> => {\n // Stop any running instances first.\n for (const inst of instances) {\n if (inst.configId === configId && inst.status === 'running') {\n await handleStop(inst.id)\n }\n }\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}&id=${encodeURIComponent(configId)}`, {\n method: 'DELETE',\n })\n if (resp.ok) {\n if (selectedConfigId === configId) setSelectedConfigId(null)\n void fetchConfigs()\n void fetchInstances()\n }\n } catch { /* ignore */ }\n }, [instances, handleStop, sessionQuery, fetchConfigs, fetchInstances, selectedConfigId])\n\n /** Select a config (deselect any instance). */\n const handleSelectConfig = (configId: string): void => {\n setSelectedConfigId(configId)\n setSelectedInstanceId(null)\n }\n\n /** Select an instance. */\n const handleSelectInstance = (instanceId: string): void => {\n setSelectedInstanceId(instanceId)\n setSelectedConfigId(null)\n }\n\n // -- Split pane drag handling --\n const handleMouseDown = (e: MouseEvent): void => {\n e.preventDefault()\n setDragging(true)\n }\n\n useEffect(() => {\n if (!dragging) return\n const handleMove = (e: MouseEvent): void => {\n if (containerRef.current === null) return\n const rect = containerRef.current.getBoundingClientRect()\n const ratio = isHorizontal\n ? (e.clientX - rect.left) / rect.width\n : (e.clientY - rect.top) / rect.height\n const clamped = Math.max(0.2, Math.min(0.5, ratio))\n setSplitRatio(clamped)\n persistSplitRatio(clamped)\n }\n const handleUp = (): void => setDragging(false)\n document.addEventListener('mousemove', handleMove)\n document.addEventListener('mouseup', handleUp)\n return () => {\n document.removeEventListener('mousemove', handleMove)\n document.removeEventListener('mouseup', handleUp)\n }\n }, [dragging, isHorizontal, persistSplitRatio])\n\n // Resolve what to show in the right pane.\n const selectedInstance = selectedInstanceId !== null\n ? instances.find((i) => i.id === selectedInstanceId) ?? null\n : null\n const selectedConfig = selectedConfigId !== null\n ? configs.find((c) => c.id === selectedConfigId) ?? null\n : null\n\n return createElement('div', { ref: containerRef, className: 'sts-root' },\n // Toolbar\n createElement('div', { className: 'sts-toolbar' },\n createElement('div', { className: 'sts-toolbar-title' },\n createElement(RocketIcon, { size: 16 }),\n '服务',\n ),\n createElement('button', {\n className: 'sts-toolbar-btn',\n onClick: handleNewConfig,\n },\n createElement(PlusIcon, { size: 13 }), '新建配置'),\n ),\n // Split pane (horizontal or vertical based on panel position)\n createElement('div', { className: `sts-split${isHorizontal ? '' : ' sts-split-vertical'}` },\n // First pane: config tree\n createElement('div', {\n className: `sts-split-first${isHorizontal ? '' : ' sts-split-first-v'}`,\n style: isHorizontal\n ? { width: `${splitRatio * 100}%` }\n : { height: `${splitRatio * 100}%` },\n },\n configs.length === 0\n ? createElement('div', { className: 'sts-empty' }, '暂无运行配置,点击右上角 + 新建')\n : createElement(ConfigTree, {\n configs,\n instances,\n selectedInstanceId,\n selectedConfigId,\n onSelectConfig: handleSelectConfig,\n onSelectInstance: handleSelectInstance,\n onStartConfig: (id: string) => { void handleStart(id) },\n onStopInstance: (id: string) => { void handleStop(id) },\n onEditConfig: handleEditConfig,\n onDuplicateConfig: handleDuplicateConfig,\n }),\n ),\n // Gutter\n createElement('div', {\n className: `sts-split-gutter${dragging ? ' dragging' : ''}${isHorizontal ? '' : ' sts-split-gutter-v'}`,\n onMouseDown: handleMouseDown,\n }),\n // Second pane: log view or config history or empty\n createElement('div', { className: `sts-split-second${isHorizontal ? '' : ' sts-split-second-v'}` },\n selectedInstance !== null\n ? createElement(LogView, {\n instance: selectedInstance,\n ctx: props.ctx,\n sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? '',\n cwd: scope.cwd,\n onStop: () => { void handleStop(selectedInstance.id) },\n onRestart: () => { void handleRestart(selectedInstance.id) },\n })\n : selectedConfig !== null\n // Clicking a config shows its persisted run logs directly\n // (no config-detail pane).\n ? createElement(LogHistoryView, {\n ctx: props.ctx,\n sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? '',\n cwd: scope.cwd,\n filter: selectedConfig.name,\n onClose: () => { setSelectedConfigId(null) },\n })\n : createElement('div', { className: 'sts-empty' }, '选择左侧配置查看其历史日志,或选择运行中的服务查看实时日志'),\n ),\n ),\n // Modal\n showModal\n ? createElement(ConfigEditorModal, {\n config: editingConfig,\n scope: { sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? '', cwd: scope.cwd },\n onSave: (c) => { void handleSaveConfig(c) },\n onCancel: () => { setShowModal(false); setEditingConfig(null) },\n })\n : null,\n )\n}\n","/**\n * Custom settings panel for the \"服务\" tab: renders switch toggles plus\n * validated directory pickers for Java/Node/Python/Maven paths.\n * @module dsh-better-sidebar-starter/client/SettingsPanel\n */\n\nimport { createElement, useState, useEffect, type ReactNode } from 'react'\nimport type { SidebarSettingsRenderProps, SessionScope } from 'dsh-better-sidebar/client/service'\nimport { PathPicker } from './PathPicker.tsx'\n\n/** The custom settings panel rendered in the gear popup. */\nexport function SettingsPanel(props: SidebarSettingsRenderProps): ReactNode {\n const { store, pluginSettings, updatePluginSetting } = props\n const scope: SessionScope = { sessionId: store.getSnapshot().sessionId ?? '' }\n\n const get = (key: string): string =>\n typeof pluginSettings[key] === 'string' ? pluginSettings[key] as string : ''\n const getBool = (key: string): boolean =>\n pluginSettings[key] === true\n\n const [autoSelect, setAutoSelect] = useState(getBool('autoSelectOnStart'))\n const [autoScroll, setAutoScroll] = useState(getBool('autoScroll'))\n const [javaHome, setJavaHome] = useState(get('javaHome'))\n const [nodePath, setNodePath] = useState(get('nodePath'))\n const [pythonPath, setPythonPath] = useState(get('pythonPath'))\n const [mvnPath, setMvnPath] = useState(get('mvnPath'))\n\n // Auto-detect environment paths on first open if not yet configured.\n useEffect(() => {\n const needDetect = get('javaHome') === '' && get('nodePath') === '' && get('pythonPath') === '' && get('mvnPath') === ''\n if (!needDetect) return\n let cancelled = false\n fetch('/api/dsh-better-sidebar-starter/detect-env')\n .then((r) => r.ok ? r.json() as Promise<{ javaHome?: string; nodePath?: string; pythonPath?: string; mvnPath?: string }> : null)\n .then((detected) => {\n if (cancelled || detected === null) return\n if (detected.javaHome !== undefined && detected.javaHome !== '') {\n setJavaHome(detected.javaHome)\n updatePluginSetting('javaHome', detected.javaHome)\n }\n if (detected.nodePath !== undefined && detected.nodePath !== '') {\n setNodePath(detected.nodePath)\n updatePluginSetting('nodePath', detected.nodePath)\n }\n if (detected.pythonPath !== undefined && detected.pythonPath !== '') {\n setPythonPath(detected.pythonPath)\n updatePluginSetting('pythonPath', detected.pythonPath)\n }\n if (detected.mvnPath !== undefined && detected.mvnPath !== '') {\n setMvnPath(detected.mvnPath)\n updatePluginSetting('mvnPath', detected.mvnPath)\n }\n })\n .catch(() => { /* ignore */ })\n return () => { cancelled = true }\n }, [])\n\n const handleSwitch = (key: string, setter: (v: boolean) => void) => (v: boolean): void => {\n setter(v)\n updatePluginSetting(key, v)\n }\n const handlePath = (key: string, setter: (v: string) => void) => (v: string): void => {\n setter(v)\n updatePluginSetting(key, v)\n }\n\n return createElement('div', { className: 'sts-settings-panel' },\n // Switches\n createElement('div', { className: 'sts-settings-row' },\n createElement('label', { className: 'sts-settings-switch-row' },\n createElement('input', {\n type: 'checkbox',\n className: 'sts-settings-switch',\n checked: autoSelect,\n onChange: (e: { target: { checked: boolean } }) => handleSwitch('autoSelectOnStart', setAutoSelect)(e.target.checked),\n }),\n createElement('span', null, '\\u542f\\u52a8\\u540e\\u81ea\\u52a8\\u9009\\u4e2d\\u65e5\\u5fd7'),\n ),\n ),\n createElement('div', { className: 'sts-settings-row' },\n createElement('label', { className: 'sts-settings-switch-row' },\n createElement('input', {\n type: 'checkbox',\n className: 'sts-settings-switch',\n checked: autoScroll,\n onChange: (e: { target: { checked: boolean } }) => handleSwitch('autoScroll', setAutoScroll)(e.target.checked),\n }),\n createElement('span', null, '\\u81ea\\u52a8\\u6eda\\u52a8\\u5230\\u6700\\u65b0\\u65e5\\u5fd7'),\n ),\n ),\n // Separator\n createElement('div', { className: 'sts-settings-sep' }, '\\u8fd0\\u884c\\u73af\\u5883\\u8def\\u5f84'),\n // Path pickers (each wrapped with a label row)\n createElement(PathPickerRow, {\n label: 'Java \\u4e3b\\u76ee\\u5f55 (JAVA_HOME)',\n scope,\n kind: 'java',\n value: javaHome,\n onChange: handlePath('javaHome', setJavaHome),\n desc: 'Spring Boot \\u7528\\u7684 JDK\\uff0c\\u7559\\u7a7a\\u5219\\u81ea\\u52a8\\u67e5\\u627e',\n }),\n createElement(PathPickerRow, {\n label: 'Node.js \\u8def\\u5f84',\n scope,\n kind: 'node',\n value: nodePath,\n onChange: handlePath('nodePath', setNodePath),\n desc: 'npm \\u7528\\u7684 Node\\uff0c\\u7559\\u7a7a\\u5219\\u7528\\u7cfb\\u7edf\\u9ed8\\u8ba4',\n }),\n createElement(PathPickerRow, {\n label: 'Python \\u8def\\u5f84',\n scope,\n kind: 'python',\n value: pythonPath,\n onChange: handlePath('pythonPath', setPythonPath),\n desc: 'Python \\u89e3\\u91ca\\u5668\\uff0c\\u7559\\u7a7a\\u5219\\u7528\\u7cfb\\u7edf\\u9ed8\\u8ba4',\n }),\n createElement(PathPickerRow, {\n label: 'Maven \\u8def\\u5f84 (MVN)',\n scope,\n kind: 'mvn',\n value: mvnPath,\n onChange: handlePath('mvnPath', setMvnPath),\n desc: 'springboot \\u7528\\u7684 Maven\\uff0c\\u7559\\u7a7a\\u5219\\u81ea\\u52a8\\u67e5\\u627e',\n }),\n )\n}\n\n/** A labeled path picker row for the settings panel. */\nfunction PathPickerRow(props: {\n label: string\n scope: SessionScope\n kind: 'java' | 'node' | 'python' | 'mvn'\n value: string\n onChange: (v: string) => void\n desc: string\n}): ReactNode {\n return createElement('div', { className: 'sts-settings-picker-row' },\n createElement('label', { className: 'sts-settings-label' }, props.label),\n createElement(PathPicker, {\n scope: props.scope,\n kind: props.kind,\n value: props.value,\n onChange: props.onChange,\n desc: props.desc,\n }),\n )\n}\n","/**\n * Client half of the dsh-better-sidebar-starter plugin: registers the\n * \"服务\" (Services) sidebar tab through the `ctx.betterSidebar` service.\n * The tab renders a split-pane panel: left config tree + right log/detail.\n * The host half (same package) owns the session-scoped config/process routes.\n * @module dsh-better-sidebar-starter/client\n */\n\nimport { createElement, type ReactNode } from 'react'\nimport type { Context } from 'cordis'\n// Triggers the ctx.betterSidebar type augmentation (erased at build).\nimport type {} from 'dsh-better-sidebar'\nimport { ServicesTab } from './ServicesTab.tsx'\nimport { SettingsPanel } from './SettingsPanel.tsx'\nimport { RocketIcon } from './icons.tsx'\n\n/** Services required before mounting: the betterSidebar registry (provided\n * by dsh-better-sidebar's own client half) and the session store. */\nexport const inject = ['betterSidebar', 'sessions']\n\n/** Plugin body. */\nexport function apply(ctx: Context): void {\n // The \"服务\" sidebar tab.\n ctx.effect(() =>\n ctx.betterSidebar.registerTab({\n id: 'dsh-better-sidebar-starter:services',\n title: () => '服务',\n icon: (size: number): ReactNode => createElement(RocketIcon, { size }),\n order: 50,\n single: true,\n settings: {\n pluginToggles: [\n { key: 'autoSelectOnStart', type: 'switch', title: '启动后自动选中日志' },\n { key: 'autoScroll', type: 'switch', title: '自动滚动到最新日志' },\n { key: 'javaHome', type: 'text', title: 'Java 主目录 (JAVA_HOME)', desc: 'Spring Boot 用的 JDK 路径,留空则自动查找' },\n { key: 'nodePath', type: 'text', title: 'Node.js 路径', desc: 'npm 命令用的 Node 路径,留空则用系统默认' },\n { key: 'pythonPath', type: 'text', title: 'Python 路径', desc: 'Python 命令用的解释器路径,留空则用系统默认' },\n ],\n render: (props): ReactNode => createElement(SettingsPanel, props),\n },\n component: (props): ReactNode => createElement(ServicesTab, props),\n }),\n 'dsh-better-sidebar-starter: services tab',\n )\n}\n"],"mappings":";;;;;;;;;;;;;EAYA,SAAS,IAAI,MAAc,cAAiC;GAC1D,MAAM,UAAU,eAAe,KAAK,YAAY,KAAK,uEAAuE,aAAa;GACzI,QAAA,GAAOA,MAAAA,cAAAA,CAAc,QAAQ;IAC3B,OAAO;KAAE,SAAS;KAAe,YAAY;KAAU,gBAAgB;KAAU,OAAO;KAAM,QAAQ;IAAK;IAC3G,yBAAyB,EAAE,QAAQ,QAAQ;GAC7C,CAAC;EACH;;EAGA,SAAgB,WAAW,EAAE,QAA8B;GACzD,OAAO,IAAI,MAAM,0qBAA0qB;EAC7rB;;EAGA,SAAgB,SAAS,EAAE,QAA8B;GACvD,OAAO,IAAI,MAAM,+CAA+C;EAClE;;EAGA,SAAgB,SAAS,EAAE,QAA8B;GACvD,OAAO,IAAI,MAAM,uEAAuE;EAC1F;;EAGA,SAAgB,YAAY,EAAE,QAA8B;GAC1D,OAAO,IAAI,MAAM,uIAAuI;EAC1J;;EAGA,SAAgB,SAAS,EAAE,QAA8B;GACvD,OAAO,IAAI,MAAM,6zBAA6zB;EACh1B;;EAGA,SAAgB,UAAU,EAAE,QAA8B;GACxD,OAAO,IAAI,MAAM,+GAA+G;EAClI;;EAGA,SAAgB,SAAS,EAAE,QAA8B;GACvD,OAAO,IAAI,MAAM,qEAAqE;EACxF;;EAGA,SAAgB,UAAU,EAAE,QAA8B;GACxD,OAAO,IAAI,MAAM,uIAAuI;EAC1J;;EAGA,SAAgB,SAAS,EAAE,QAA8B;GACvD,OAAO,IAAI,MAAM,8FAA8F;EACjH;;EAGA,SAAgB,SAAS,EAAE,QAA8B;GACvD,OAAO,IAAI,MAAM,oIAAoI;EACvJ;;EAGA,SAAgB,YAAY,EAAE,QAA8B;GAC1D,OAAO,IAAI,MAAM,wJAAwJ;EAC3K;;EAGA,SAAgB,gBAAgB,EAAE,QAA8B;GAC9D,OAAO,IAAI,MAAM,gDAAgD;EACnE;;EAGA,SAAgB,iBAAiB,EAAE,QAA8B;GAC/D,OAAO,IAAI,MAAM,gDAAgD;EACnE;;EAGA,SAAgB,eAAe,EAAE,QAA8B;GAC7D,OAAO,IAAI,MAAM,gDAAgD;EACnE;;EAGA,SAAgB,UAAU,EAAE,QAA8B;GACxD,OAAO,IAAI,MAAM,8EAA8E;EACjG;;EAGA,SAAgB,aAAa,EAAE,QAA8B;GAC3D,OAAO,IAAI,MAAM,gDAAgD;EACnE;;EAQA,SAAgB,QAAQ,EAAE,QAA8B;GACtD,OAAO,IAAI,MAAM,yKAAyK;EAC5L;;EAGA,SAAgB,WAAW,EAAE,QAA8B;GACzD,OAAO,IAAI,MAAM,+SAA+S;EAClU;;EAGA,SAAgB,WAAW,EAAE,QAA8B;GACzD,OAAO,IAAI,MAAM,obAAob;EACvc;;EAGA,SAAgB,WAAW,EAAE,QAA8B;GACzD,OAAO,IAAI,MAAM,kJAAkJ;EACrK;;EAGA,SAAgB,WAAW,EAAE,QAA8B;GACzD,OAAO,IAAI,MAAM,oIAAoI;EACvJ;;EAGA,SAAgB,cAAc,EAAE,QAA8B;GAC5D,OAAO,IAAI,MAAM,gQAAgQ;EACnR;;EAGA,SAAgB,SAAS,EAAE,QAA8B;GACvD,OAAO,IAAI,MAAM,8LAA8L;EACjN;;EAGA,SAAgB,eAAe,EAAE,QAA8B;GAC7D,OAAO,IAAI,MAAM,uEAAuE;EAC1F;;EAGA,SAAgB,SAAS,MAAc,MAAyB;GAC9D,QAAQ,MAAR;IACE,KAAK,OAAO,OAAO,QAAQ,EAAE,KAAK,CAAC;IACnC,KAAK,cAAc,OAAO,WAAW,EAAE,KAAK,CAAC;IAC7C,KAAK,UAAU,OAAO,WAAW,EAAE,KAAK,CAAC;IACzC,SAAS,OAAO,WAAW,EAAE,KAAK,CAAC;GACrC;EACF;;;;;;;;;EC1HA,MAAM,aAAuB;GAAC;GAAO;GAAc;GAAU;EAAQ;EACrE,MAAMC,gBAAsC;GAC1C,KAAK;GAAO,YAAY;GAAe,QAAQ;GAAU,QAAQ;EACnE;;EAGA,SAAS,WAAW,QAAgB,MAAyB;GAC3D,QAAQ,QAAR;IACE,KAAK,WAAW,OAAO,eAAe,EAAE,KAAK,CAAC;IAC9C,KAAK,UAAU,OAAO,UAAU,EAAE,KAAK,CAAC;IACxC,KAAK;IACL,KAAK,SAAS,OAAO,aAAa,EAAE,KAAK,CAAC;IAC1C,SAAS,OAAO;GAClB;EACF;;EAGA,SAAgB,WAAW,OAAmC;GAC5D,MAAM,EAAE,SAAS,WAAW,oBAAoB,kBAC9C,gBAAgB,kBAAkB,eAAe,gBAAgB,cAAc,sBAAsB;GAEvG,MAAM,CAAC,WAAW,iBAAA,GAAgBC,MAAAA,SAAAA,iBAAsB,IAAI,IAAI,CAAC;GAEjE,MAAM,eAAe,SAAuB;IAC1C,cAAc,SAAS;KACrB,MAAM,OAAO,IAAI,IAAI,IAAI;KACzB,IAAI,KAAK,IAAI,IAAI,GAAG,KAAK,OAAO,IAAI;UAC/B,KAAK,IAAI,IAAI;KAClB,OAAO;IACT,CAAC;GACH;GAGA,MAAM,UAAA,GAASC,MAAAA,QAAAA,OAAc;IAC3B,MAAM,sBAAM,IAAI,IAAyB;IACzC,KAAK,MAAM,UAAU,SAAS;KAC5B,MAAM,MAAM,IAAI,IAAI,OAAO,IAAI,KAAK,CAAC;KACrC,IAAI,KAAK,MAAM;KACf,IAAI,IAAI,OAAO,MAAM,GAAG;IAC1B;IACA,KAAK,MAAM,OAAO,IAAI,OAAO,GAC3B,IAAI,MAAM,GAAG,OAAO,EAAE,aAAa,MAAM,EAAE,aAAa,EAAE;IAE5D,OAAO,WACJ,QAAQ,SAAS,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,CAAE,SAAS,CAAC,CAAC,CAC5D,KAAK,UAAU;KAAE;KAAM,OAAO,IAAI,IAAI,IAAI;IAAG,EAAE;GACpD,GAAG,CAAC,OAAO,CAAC;GAEZ,IAAI,QAAQ,WAAW,GACrB,QAAA,GAAOC,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,YAAY,GACnD,WAAA,GACAA,MAAAA,cAAAA,CAAc,IAAI,GAClB,cACF;GAGF,QAAA,GAAOA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,WAAW,GAClD,GAAG,OAAO,KAAK,UAAU;IACvB,MAAM,cAAc,UAAU,IAAI,MAAM,IAAI;IAC5C,MAAM,QAAQH,cAAY,MAAM,SAAS,MAAM;IAC/C,MAAM,eAAe,UAAU,QAC5B,SAAS,KAAK,eAAe,MAAM,QAAQ,KAAK,WAAW,SAC9D,CAAC,CAAC;IAEF,QAAA,GAAOG,MAAAA,cAAAA,CAAc,OAAO,EAAE,KAAK,MAAM,KAAK,IAAA,GAE5CA,MAAAA,cAAAA,CAAc,OAAO;KACnB,WAAW;KACX,eAAe,YAAY,MAAM,IAAI;IACvC,GACE,eAAA,GACIA,MAAAA,cAAAA,CAAc,kBAAkB,EAAE,MAAM,GAAG,CAAC,KAAA,GAC5CA,MAAAA,cAAAA,CAAc,iBAAiB,EAAE,MAAM,GAAG,CAAC,GAC/C,SAAS,MAAM,MAAM,EAAE,GACvB,QAAA,GACAA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,kBAAkB,GACnD,KAAK,MAAM,MAAM,SAAS,eAAe,IAAI,KAAK,aAAa,QAAQ,GAAG,EAC5E,CACF,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO,EACnB,WAAW,qBAAqB,cAAc,eAAe,KAC/D,GACE,GAAG,MAAM,MAAM,SAAS,WAAW;KACjC,MAAM,kBAAkB,UAAU,QAAQ,SAAS,KAAK,aAAa,OAAO,EAAE;KAC9E,MAAM,YAAY,gBAAgB,MAAM,SAAS,KAAK,WAAW,SAAS;KAC1E,MAAM,aAAa,qBAAqB,OAAO,MAAM,uBAAuB;KAE5E,MAAM,OAAoB,EAAA,GAExBA,MAAAA,cAAAA,CAAc,OAAO;MACnB,KAAK,OAAO;MACZ,WAAW,iBAAiB,aAAa,cAAc,KAAK,YAAY,aAAa;MACrF,eAAe,eAAe,OAAO,EAAE;KACzC,GACE,YACI,eAAe,EAAE,MAAM,GAAG,CAAC,IAC3B,SAAS,OAAO,MAAM,EAAE,IAAA,GAC5BA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,GAAG,OAAO,IAAI,IAAA,GAClEA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,iBAAiB,GAAG,OAAO,OAAO,IAAA,GACpEA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,qBAAqB,GACrD,aAAA,GACIA,MAAAA,cAAAA,CAAc,UAAU;MACtB,WAAW;MACX,OAAO;MACP,UAAU,MAAkB;OAC1B,EAAE,gBAAgB;OAClB,KAAK,MAAM,QAAQ,iBACjB,IAAI,KAAK,WAAW,WAAW,eAAe,KAAK,EAAE;MAEzD;KACF,GAAG,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,KAAA,GACzBA,MAAAA,cAAAA,CAAc,UAAU;MACtB,WAAW;MACX,OAAO;MACP,UAAU,MAAkB;OAC1B,EAAE,gBAAgB;OAClB,cAAc,OAAO,EAAE;MACzB;KACF,GAAG,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,IAAA,GAC7BA,MAAAA,cAAAA,CAAc,UAAU;MACtB,WAAW;MACX,OAAO;MACP,UAAU,MAAkB;OAC1B,EAAE,gBAAgB;OAClB,aAAa,OAAO,EAAE;MACxB;KACF,GAAG,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,IAAA,GACzBA,MAAAA,cAAAA,CAAc,UAAU;MACtB,WAAW;MACX,OAAO;MACP,UAAU,MAAkB;OAC1B,EAAE,gBAAgB;OAClB,kBAAkB,OAAO,EAAE;MAC7B;KACF,GAAG,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,CAC3B,CACF,CACF;KAGA,KAAK,MAAM,QAAQ,iBAAiB;MAClC,MAAM,eAAe,uBAAuB,KAAK;MACjD,KAAK,MAAA,GACHA,MAAAA,cAAAA,CAAc,OAAO;OACnB,KAAK,KAAK;OACV,WAAW,mBAAmB,eAAe,cAAc;OAC3D,eAAe,iBAAiB,KAAK,EAAE;MACzC,GACE,WAAW,KAAK,QAAQ,EAAE,GAC1B,IAAI,KAAK,GAAG,MAAM,GAAG,CAAC,MAAA,GACtBA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,mBAAmB,GACpD,KAAK,WAAW,YAAY,QAAQ,KAAK,QAAQ,IAAI,KAAK,QAC5D,GACA,KAAK,WAAW,aAAA,GACZA,MAAAA,cAAAA,CAAc,UAAU;OACtB,WAAW;OACX,OAAO;OACP,UAAU,MAAkB;QAC1B,EAAE,gBAAgB;QAClB,eAAe,KAAK,EAAE;OACxB;MACF,GAAG,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,IACzB,IACN,CACF;KACF;KAEA,OAAO;IACT,CAAC,CACH,CACF;GACF,CAAC,CACH;EACF;;;ECjLA,SAAS,WAAW,GAA4B;GAC9C,OAAO;IACL,MAAM;IACN,MAAM,EAAE;IACR,KAAK,EAAE;IACP,QAAQ,EAAE;IACV,WAAW,EAAE;IACb,IAAI,EAAE;IACN,IAAI,EAAE;GACR;EACF;;EAGA,SAAS,SAAS,OAAmB,MAAoB;GACvD,IAAI,SAAS,GAAG;IACd,MAAM,OAAO;IAAO,MAAM,MAAM;IAAO,MAAM,SAAS;IACtD,MAAM,YAAY;IAAO,MAAM,KAAK;IAAM,MAAM,KAAK;GACvD,OAAO,IAAI,SAAS,GAClB,MAAM,OAAO;QACR,IAAI,SAAS,GAClB,MAAM,MAAM;QACP,IAAI,SAAS,GAClB,MAAM,SAAS;QACV,IAAI,SAAS,GAClB,MAAM,YAAY;QACb,IAAI,SAAS,IAAI;IACtB,MAAM,OAAO;IAAO,MAAM,MAAM;GAClC,OAAO,IAAI,SAAS,IAClB,MAAM,SAAS;QACV,IAAI,SAAS,IAClB,MAAM,YAAY;QACb,IAAI,QAAQ,MAAM,QAAQ,IAC/B,MAAM,KAAK,OAAO;QACb,IAAI,SAAS,IAClB,MAAM,KAAK;QACN,IAAI,QAAQ,MAAM,QAAQ,IAC/B,MAAM,KAAK,OAAO;QACb,IAAI,SAAS,IAClB,MAAM,KAAK;QACN,IAAI,QAAQ,MAAM,QAAQ,IAC/B,MAAM,KAAK,OAAO,KAAK;QAClB,IAAI,QAAQ,OAAO,QAAQ,KAChC,MAAM,KAAK,OAAO,MAAM;EAE5B;;;;;EAMA,SAAgB,UAAU,OAA8B;GACtD,MAAM,QAAoB;IACxB,MAAM;IAAO,KAAK;IAAO,QAAQ;IAAO,WAAW;IAAO,IAAI;IAAM,IAAI;GAC1E;GACA,MAAM,WAA0B,CAAC;GACjC,IAAI,UAAU,WAAW,KAAK;GAG9B,MAAM,QAAQ;GACd,IAAI,YAAY;GAChB,IAAI;GAEJ,QAAQ,QAAQ,MAAM,KAAK,KAAK,OAAO,MAAM;IAE3C,IAAI,MAAM,QAAQ,WAChB,QAAQ,QAAQ,MAAM,MAAM,WAAW,MAAM,KAAK;IAGpD,IAAI,MAAM,OAAO,KAAA,GAAW;KAE1B,IAAI,QAAQ,SAAS,IACnB,SAAS,KAAK,OAAO;KAEvB,MAAM,SAAS,MAAM,OAAO,KAAK,MAAM,MAAM;KAC7C,KAAK,MAAM,WAAW,OAAO,MAAM,GAAG,GAAG;MACvC,MAAM,OAAO,YAAY,KAAK,IAAI,OAAO,OAAO;MAChD,IAAI,CAAC,OAAO,MAAM,IAAI,GAAG,SAAS,OAAO,IAAI;KAC/C;KACA,UAAU,WAAW,KAAK;IAC5B;IAEA,YAAY,MAAM;GACpB;GAGA,IAAI,YAAY,MAAM,QACpB,QAAQ,QAAQ,MAAM,MAAM,SAAS;GAEvC,IAAI,QAAQ,SAAS,IACnB,SAAS,KAAK,OAAO;GAGvB,OAAO;EACT;;EAGA,MAAM,cAAiC;GAErC;GAAW;GAAW;GAAW;GAAW;GAAW;GAAW;GAAW;GAE7E;GAAW;GAAW;GAAW;GAAW;GAAW;GAAW;GAAW;EAC/E;;EAGA,SAAgB,eAAe,KAAiC;GAC9D,IAAI,IAAI,OAAO,MAAM,OAAO;GAC5B,OAAO,YAAY,IAAI,OAAO;EAChC;;EAGA,SAAgB,eAAe,KAAiC;GAC9D,IAAI,IAAI,OAAO,MAAM,OAAO;GAC5B,OAAO,YAAY,IAAI,OAAO;EAChC;;;EC7GA,MAAM,IAAI;GACR,IAAI;GACJ,QAAQ;GACR,WAAW;GACX,YAAY;GACZ,WAAW;GACX,YAAY;GACZ,OAAO;GACP,WAAW;GACX,OAAO;GACP,UAAU;EACZ;;EAGA,SAAS,WAAW,OAAuB;GACzC,QAAQ,OAAR;IACE,KAAK;IACL,KAAK,SAAS,OAAO,EAAE;IACvB,KAAK,QAAQ,OAAO,EAAE;IACtB,KAAK,QAAQ,OAAO,EAAE;IACtB,KAAK;IACL,KAAK;IACL,KAAK,OAAO,OAAO,EAAE;IACrB,SAAS,OAAO,EAAE;GACpB;EACF;;EAGA,MAAM,WAAW,IAAI,OAAO;GAE1B;GAEA;GAEA;GAEA;GAEA;GAEA;GAEA;GAEA;EACF,CAAC,CAAC,KAAK,EAAE,GAAG,GAAG;;;EAIf,SAAS,mBAAmB,MAAuB;GACjD,MAAM,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;GACtC,OAAO,SAAS,MAAM,SAAS,KAAK,IAAI;EAC1C;;EAGA,SAAS,iBAAiB,QAAuD;GAC/E,MAAM,MAAM,OAAO,YAAY,GAAG;GAClC,IAAI,MAAM,KAAK,MAAM,OAAO,SAAS,GACnC,OAAO;IAAE,WAAW,OAAO,MAAM,GAAG,GAAG;IAAG,QAAQ,OAAO,MAAM,MAAM,CAAC;GAAE;GAE1E,OAAO;IAAE,WAAW;IAAQ,QAAQ;GAAG;EACzC;;EAGA,SAAS,cAAc,KAA2D;GAChF,IAAI,QAAQ,KAAA,KAAa,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC;GACpD,MAAM,QAAQ,IAAI,KAAK,CAAC,CAAC,MAAM,GAAG;GAClC,IAAI,MAAM,SAAS,GAAG,OAAO,CAAC;GAC9B,MAAM,OAAO,OAAO,MAAM,EAAE;GAC5B,OAAO;IAAE,MAAM,MAAM;IAAI,MAAM,OAAO,UAAU,IAAI,KAAK,OAAO,IAAI,OAAO,KAAA;GAAU;EACvF;;;;;EAMA,SAAgB,iBAAiB,MAAkC;GACjE,MAAM,MAA0B,CAAC;GACjC,IAAI,OAAO;GACX,SAAS,YAAY;GACrB,IAAI;GACJ,QAAQ,IAAI,SAAS,KAAK,IAAI,OAAO,MAAM;IACzC,IAAI,EAAE,QAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,KAAK,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC;IAChE,MAAM,IAAI,EAAE,UAAU,CAAC;IACvB,IAAI,EAAE,OAAO,KAAA,GACX,IAAI,KAAK;KAAE,MAAM,EAAE;KAAI,OAAO,EAAE;IAAG,CAAC;SAC/B,IAAI,EAAE,UAAU,KAAA,GACrB,IAAI,KAAK;KAAE,MAAM,EAAE;KAAI,OAAO,WAAW,EAAE,KAAK;KAAG,MAAM;IAAK,CAAC;SAC1D,IAAI,EAAE,WAAW,KAAA,GACtB,IAAI,KAAK;KAAE,MAAM,EAAE;KAAI,OAAO,EAAE;IAAO,CAAC;SACnC,IAAI,EAAE,cAAc,KAAA,GACzB,IAAI,KAAK;KAAE,MAAM,EAAE;KAAI,OAAO,EAAE;KAAW,MAAM;IAAK,CAAC;SAClD,IAAI,EAAE,UAAU,KAAA,GAAW;KAChC,MAAM,EAAE,WAAW,WAAW,iBAAiB,EAAE,UAAU,EAAE;KAC7D,MAAM,EAAE,MAAM,SAAS,cAAc,EAAE,GAAG;KAC1C,IAAI,KAAK;MACP,MAAM,EAAE;MACR,OAAO,EAAE;MACT,MAAM;OAAE,MAAM;OAAS;OAAW;OAAQ;OAAM;MAAK;KACvD,CAAC;IACH,OAAO,IAAI,EAAE,aAAa,KAAA,GACxB,IAAI,KAAK;KACP,MAAM,EAAE;KACR,OAAO,EAAE;KACT,MAAM;MACJ,MAAM;MACN,MAAM,EAAE;MACR,MAAM,OAAO,EAAE,KAAK;KACtB;IACF,CAAC;SACI,IAAI,EAAE,SAAS,KAAA,GAAW;KAC/B,MAAM,YAAY,EAAE,WAAW;KAC/B,IAAI,KAAK;MACP,MAAM,EAAE;MACR,OAAO,EAAE;MACT,MAAM,mBAAmB,SAAS,IAC9B;OAAE,MAAM;OAAS;OAAW,QAAQ,EAAE,cAAc;MAAG,IACvD,KAAA;KACN,CAAC;IACH,OAAO,IAAI,EAAE,UAAU,KAAA,GAAW;KAChC,MAAM,YAAY,EAAE;KACpB,IAAI,KAAK;MACP,MAAM,EAAE;MACR,OAAO,EAAE;MACT,MAAM,mBAAmB,SAAS,IAAI;OAAE,MAAM;OAAS;MAAU,IAAI,KAAA;KACvE,CAAC;IACH,OACE,IAAI,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC;IAEzB,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC;GACxB;GACA,IAAI,OAAO,KAAK,QAAQ,IAAI,KAAK,EAAE,MAAM,KAAK,MAAM,IAAI,EAAE,CAAC;GAC3D,OAAO;EACT;;;;;;;;ECpIA,SAAgB,cAAc,KAAc,WAAmB,MAAuB;GACpF,IAAI;IAEF,MAAM,OADY,IAAoC,UAC/B,MAAM,SAAS;IACtC,IAAI,SAAS,KAAA,GAAW,OAAO;IAE/B,MAAM,eADc,IAAsD,IAAI,KAAK,GACrD,CAAC,CAAC,cAAc;IAC9C,IAAI,iBAAiB,KAAA,KAAa,iBAAiB,MAAM,OAAO;IAChE,MAAM,QAAQ,aAAa,MAAM,IAAI,IAAI;IACzC,IAAI,UAAU,KAAA,GAAW,OAAO;IAChC,MAAM,QAAQ,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS;IACjD,MAAM,SAAS,MAAM,KAAK,MAAM,KAAK,OAAO,GAAG,MAAM,GAAG,MAAM;IAC9D,OAAO;GACT,SAAS,OAAO;IACd,QAAQ,KAAK,qDAAqD,KAAK;IACvE,OAAO;GACT;EACF;;;;;;;EC7CA,IAAI,UAAiC;EACrC,IAAI,aAAmD;;EAGvD,SAAgB,UAAU,SAAiB,SAAwB;GAEjE,IAAI,eAAe,MAAM;IACvB,aAAa,UAAU;IACvB,aAAa;GACf;GACA,IAAI,YAAY,MAAM;IACpB,SAAS,KAAK,YAAY,OAAO;IACjC,UAAU;GACZ;GAEA,MAAM,KAAK,SAAS,cAAc,KAAK;GACvC,GAAG,cAAc;GACjB,GAAG,MAAM,UAAU;;;;;;kBAMH,UAAU,uBAAuB,sBAAsB;;;;;;;;;GASvE,SAAS,KAAK,YAAY,EAAE;GAC5B,UAAU;GACV,aAAa,iBAAiB;IAC5B,IAAI,YAAY,MAAM;KACpB,SAAS,KAAK,YAAY,OAAO;KACjC,UAAU;IACZ;IACA,aAAa;GACf,GAAG,GAAI;EACT;;;;;;;;;;EC5BA,MAAMC,aAAW;;;;EAkCjB,eAAsB,mBACpB,KACA,WACA,KACA,MACA,MACe;GACf,MAAM,UAAmC,CAAC;GAC1C,IAAI,SAAS,KAAA,KAAa,SAAS,IAAI,QAAQ,OAAO;GACtD,IAAI,KAAK,SAAS,WAAW,KAAK,SAAS,SAAS,QAAQ,YAAY,KAAK;GAC7E,IAAI,KAAK,SAAS,KAAA,GAAW,QAAQ,OAAO,KAAK;GACjD,IAAI,KAAK,WAAW,KAAA,KAAa,KAAK,WAAW,YAAY,KAAK,WAAW,YAAY,QAAQ,SAAS,KAAK;GAC/G,IAAI,KAAK,SAAS,KAAA,GAAW,QAAQ,OAAO,KAAK;GACjD,IAAI;IAEF,MAAM,SAAS,IAAI,gBAAgB,EAAE,UAAU,CAAC;IAChD,IAAI,QAAQ,KAAA,KAAa,QAAQ,IAAI,OAAO,IAAI,OAAO,GAAG;IAC1D,MAAM,OAAO,MAAM,MAAM,GAAGA,WAAS,iBAAiB,OAAO,SAAS,KAAK;KACzE,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;KAC9C,MAAM,KAAK,UAAU,OAAO;IAC9B,CAAC;IACD,IAAI,CAAC,KAAK,IAAI;KAGZ,IAAI,SAAS;KACb,IAAI;MACF,MAAM,MAAM,MAAM,KAAK,KAAK;MAC5B,IAAI,OAAO,IAAI,UAAU,YAAY,IAAI,UAAU,IAAI,SAAS,IAAI;KACtE,QAAQ,CAAmD;KAC3D,UAAU,QAAQ,KAAK;KACvB;IACF;IACA,MAAM,OAAO,MAAM,KAAK,KAAK;IAC7B,IAAI,KAAK,OAAO,QAAQ,OAAO,KAAK,SAAS,UAAU;KACrD,UAAU,KAAK,UAAU,UAAU,KAAK;KACxC;IACF;IACA,MAAM,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC,CAAC,IAAI,KAAK,KAAK;IACpD,MAAM,OAAO,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,KAAA;IACzD,MAAM,aAAa,KAAK,eAAe;IACvC,MAAM,QAAQ,aAAa,WAAW;IACtC,MAAM,QAAQ,SAAS,KAAA,IAAY,GAAG,KAAK,KAAK,OAAO,UAAU,GAAG,OAAO;IAC3E,MAAM,gBAAiB,IAA8C;IAErE,IAAI,kBAAkB,KAAA,GACpB,cAAc,SAAS;KAAE;KAAW;IAAI,GAAG,KAAK,MAAM,KAAK;IAG7D,IAAI;KACF,UAAe,WAAW,UAAU,GAAG,OAAO,SAAS,KAAA,IAAY,IAAI,SAAS,IAAI;IACtF,QAAQ,CAAuC;IAC/C,UACE,SAAS,KAAA,IACL,OAAO,KAAK,QAAQ,KAAK,SAAS,KAAK,GAAG,KAAK,GAAG,aAAa,YAAY,OAC3E,aACE,YAAY,SACZ,OAAO,QACb,IACF;GACF,QAAQ;IACN,UAAU,UAAU,KAAK;GAC3B;EACF;;;;;;EAOA,MAAM,SAAS;EAEf,SAAS,cAAc,MAAqD;GAC1E,MAAM,MAA6C,CAAC;GACpD,IAAI,OAAO;GACX,KAAK,MAAM,KAAK,KAAK,SAAS,MAAM,GAAG;IACrC,MAAM,MAAM,EAAE,SAAS;IACvB,IAAI,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC;IACxD,IAAI,MAAM,EAAE;IACZ,IAAI,QAAQ;IACZ,OAAO,IAAI,SAAS,KAAK,qBAAqB,KAAK,GAAG,GAAG;KACvD,QAAQ,IAAI,MAAM,EAAE,IAAI;KACxB,MAAM,IAAI,MAAM,GAAG,EAAE;IACvB;IACA,IAAI,QAAQ,IAAI,IAAI,KAAK;KAAE,MAAM;KAAK;IAAI,CAAC;IAC3C,IAAI,UAAU,IAAI,IAAI,KAAK,EAAE,MAAM,MAAM,CAAC;IAC1C,OAAO,MAAM,EAAE,EAAE,CAAC;GACpB;GACA,IAAI,OAAO,KAAK,QAAQ,IAAI,KAAK,EAAE,MAAM,KAAK,MAAM,IAAI,EAAE,CAAC;GAC3D,OAAO;EACT;EAEA,SAAgB,eAAe,MAAc,SAAiB,aAAqD;GACjH,MAAM,WAAW,UAAU,IAAI;GAC/B,MAAM,QAAqB,CAAC;GAC5B,IAAI,SAAS,WAAW,GAAG,OAAO;GAClC,IAAI,YAAY;GAMhB,IAAI,WAAW;GACf,MAAM,SAAkE,CAAC;GACzE,KAAK,MAAM,OAAO,UAAU;IAC1B,OAAO,KAAK;KAAE;KAAK,OAAO,SAAS;KAAQ,KAAK,SAAS,SAAS,IAAI,KAAK;IAAO,CAAC;IACnF,YAAY,IAAI;GAClB;GAEA,MAAM,QAAQ,cAAc,QAAQ;GACpC,IAAI,SAAS;GACb,KAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,SAAS;IACf,MAAM,OAAO,SAAS,KAAK,KAAK;IAChC,SAAS;IACT,KAAK,MAAM,KAAK,QAAQ;KACtB,MAAM,IAAI,KAAK,IAAI,EAAE,OAAO,MAAM;KAClC,MAAM,IAAI,KAAK,IAAI,EAAE,KAAK,IAAI;KAC9B,IAAI,KAAK,GAAG;KACZ,MAAM,QAAQ,SAAS,MAAM,GAAG,CAAC;KACjC,MAAM,MAAM,EAAE;KAEd,MAAM,QAAgC,CAAC;KACvC,IAAI,IAAI,MAAM,MAAM,aAAa;KACjC,IAAI,IAAI,KAAK,MAAM,UAAU;KAC7B,IAAI,IAAI,QAAQ,MAAM,YAAY;KAClC,IAAI,IAAI,WAAW,MAAM,iBAAiB;KAC1C,MAAM,KAAK,eAAe,GAAG;KAC7B,MAAM,KAAK,eAAe,GAAG;KAC7B,IAAI,OAAO,MAAM,MAAM,QAAQ;KAC/B,IAAI,OAAO,MAAM,MAAM,kBAAkB;KAEzC,IAAI,KAAK,QAAQ,KAAA,GAAW;MAC1B,MAAM,MAAM,KAAK;MACjB,MAAM,MAAA,GAAKC,MAAAA,cAAAA,CAAc,QAAQ;OAC/B,KAAK,GAAG,QAAQ,IAAI;OACpB,WAAW;OACX,OAAO;QAAE,GAAG;QAAO,gBAAgB;QAAa,QAAQ;OAAU;OAClE,OAAO,UAAU;OACjB,UAAU,MAAkB;QAC1B,EAAE,gBAAgB;QAClB,IAAI;SAAE,OAAO,KAAK,KAAK,UAAU,UAAU;QAAE,QAAQ,CAAsB;OAC7E;MACF,GAAG,KAAK,CAAC;KACX,OAAO,IAAI,OAAO,QAAQ,OAAO,MAE/B,MAAM,MAAA,GAAKA,MAAAA,cAAAA,CAAc,QAAQ;MAAE,KAAK,GAAG,QAAQ,IAAI;MAAe;KAAM,GAAG,KAAK,CAAC;UAChF;MAEL,MAAM,KAAK,iBAAiB,KAAK;MACjC,KAAK,MAAM,KAAK,IAAI;OAClB,MAAM,SAAiC,EAAE,GAAG,MAAM;OAClD,IAAI,EAAE,UAAU,KAAA,GAAW,OAAO,QAAQ,EAAE;OAC5C,IAAI,EAAE,SAAS,MAAM,OAAO,aAAa;OACzC,MAAM,MAAA,GAAKA,MAAAA,cAAAA,CAAc,QAAQ;QAC/B,KAAK,GAAG,QAAQ,KAAK;QACrB,WAAW,EAAE,SAAS,KAAA,IAAY,iBAAiB,KAAA;QACnD,OAAO;QACP,SAAS,EAAE,SAAS,KAAA,KACf,MAAkB;SACjB,EAAE,gBAAgB;SAClB,YAAY,EAAE,IAAK;QACrB,IACA,KAAA;OACN,GAAG,EAAE,IAAI,CAAC;MACZ;KACF;IACF;GACF;GACA,OAAO;EACT;;EAGA,SAAgB,QAAQ,EAAE,UAAU,KAAK,WAAW,KAAK,QAAQ,aAAsC;GACrG,MAAM,CAAC,UAAU,gBAAA,GAAeC,MAAAA,SAAAA,CAAoB,CAAC,CAAC;GACtD,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAS,IAAI;GACjD,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAA6D,IAAI;GACjG,MAAM,CAAC,WAAW,iBAAA,GAAgBA,MAAAA,SAAAA,CAAS,CAAC;GAC5C,MAAM,cAAA,GAAaC,MAAAA,OAAAA,CAAuB,IAAI;GAC9C,MAAM,SAAA,GAAQA,MAAAA,OAAAA,CAAyB,IAAI;GAC3C,MAAM,kBAAA,GAAiBA,MAAAA,OAAAA,CAAO,CAAC;GAG/B,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,kBAAwB;KAC5B,MAAM,MAAM,OAAO,aAAa;KAChC,MAAM,MAAM,QAAQ,QAAQ,CAAC,IAAI,cAAc,IAAI,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS;KAC9E,aAAa,GAAG;IAClB;IACA,SAAS,iBAAiB,mBAAmB,SAAS;IACtD,aAAa,SAAS,oBAAoB,mBAAmB,SAAS;GACxE,GAAG,CAAC,CAAC;;GAGL,MAAM,4BAAkC;IACtC,MAAM,MAAM,OAAO,aAAa;IAChC,MAAM,OAAO,QAAQ,QAAQ,CAAC,IAAI,cAAc,IAAI,SAAS,CAAC,CAAC,KAAK,IAAI;IACxE,IAAI,SAAS,IAAI;KACf,UAAU,cAAc,KAAK;KAC7B;IACF;IACA,IAAI,cAAc,KAAK,WAAW,IAAI,GACpC,UAAU,UAAU,KAAK,OAAO,YAAY,IAAI;SAEhD,UAAU,gBAAgB,KAAK;GAEnC;GAGA,MAAM,cAAA,GAAaA,MAAAA,OAAAA,CAAgC,EAAE,SAAS,CAAC,EAAE,CAAC;;GAGlE,MAAM,aAAA,GAAYC,MAAAA,YAAAA,OAAwB;IACxC,MAAM,UAAU,WAAW,QAAQ;IACnC,IAAI,QAAQ,WAAW,GAAG;IAC1B,WAAW,QAAQ,UAAU,CAAC;IAC9B,aAAa,SAAS;KACpB,MAAM,MAAM,eAAe;KAE3B,MAAM,OAAO,CAAC,GAAG,MAAM;MAAE;MAAS;KAAI,CAAC;KACvC,IAAI,KAAK,SAAS,KAAM,KAAK,OAAO,GAAG,KAAK,SAAS,GAAI;KACzD,OAAO;IACT,CAAC;GACH,GAAG,CAAC,CAAC;;GAGL,MAAM,YAAA,GAAWA,MAAAA,YAAAA,EAAa,UAA0B;IACtD,MAAM,QAAQ,MAAM,KAAK,MAAM,IAAI;IACnC,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;KACrC,IAAI,IAAI,GAEN,UAAU;KAEZ,IAAI,MAAM,OAAO,IACf,WAAW,QAAQ,QAAQ,KAAK;MAC9B,QAAQ,MAAM;MACd,MAAM,MAAM;MACZ,IAAI,MAAM;KACZ,CAAC;IAEL;IAEA,IAAI,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,OAAO,IAClD,UAAU;GAEd,GAAG,CAAC,SAAS,CAAC;GAGd,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,MAAM,mDAAmD,mBAAmB,SAAS,EAAE;IAC7F,MAAM,KAAK,IAAI,UAAU,GAAG;IAC5B,MAAM,UAAU;IAEhB,GAAG,aAAa,UAA8B;KAC5C,IAAI;MACF,MAAM,MAAM,KAAK,MAAM,MAAM,IAAc;MAC3C,IAAI,IAAI,SAAS,WAAW;OAC1B,YAAY,CAAC,CAAC;OACd,WAAW,QAAQ,UAAU,CAAC;OAC9B,KAAK,MAAM,SAAS,IAAI,SACtB,SAAS,KAAK;OAEhB,UAAU;MACZ,OAAO,IAAI,IAAI,SAAS,OACtB,SAAS,IAAI,KAAK;WACb,IAAI,IAAI,SAAS,UAClB;WAAA,IAAI,WAAW,WAAW;QAC5B,UAAU;QACV,YAAY;SAAE,QAAQ,IAAI;SAAQ,UAAU,IAAI;QAAS,CAAC;OAC5D;;KAEJ,QAAQ,CAA4B;IACtC;IAEA,aAAa;KACX,GAAG,MAAM;KACT,MAAM,UAAU;IAClB;GACF,GAAG;IAAC,SAAS;IAAI;IAAU;GAAS,CAAC;GAGrC,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,cAAc,WAAW,YAAY,MACvC,WAAW,QAAQ,YAAY,WAAW,QAAQ;GAEtD,GAAG,CAAC,UAAU,UAAU,CAAC;GAEzB,MAAM,oBAA0B;IAC9B,YAAY,CAAC,CAAC;IACd,WAAW,QAAQ,UAAU,CAAC;GAChC;GAEA,QAAA,GAAOH,MAAAA,cAAAA,CAAc,OAAO,EAAE,OAAO;IAAE,SAAS;IAAQ,eAAe;IAAU,QAAQ;IAAQ,WAAW;GAAE,EAAE,IAAA,GAE9GA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,GAClD,SAAS,aAAA,GACTA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,OAAO;IAAE,SAAS;IAAK,YAAY;GAAM,EAAE,GAAG,IAAI,SAAS,GAAG,MAAM,GAAG,CAAC,GAAG,IAAA,GACnGA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,yBAAyB,CAAC,GAC5D,SAAS,WAAW,aAAA,GAChBA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,OAAO;IACP,SAAS;GACX,GAAG,SAAS,EAAE,MAAM,GAAG,CAAC,IAAA,GAAGA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,qBAAqB,GAAG,IAAI,CAAC,IAC3F,OAAA,GACJA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,OAAO;IACP,SAAS;GACX,GAAG,YAAY,EAAE,MAAM,GAAG,CAAC,IAAA,GAAGA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,qBAAqB,GAAG,IAAI,CAAC,IAAA,GAC9FA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,OAAO;IACP,SAAS;GACX,GAAG,UAAU,EAAE,MAAM,GAAG,CAAC,IAAA,GAAGA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,qBAAqB,GAAG,IAAI,CAAC,IAAA,GAC5FA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,OAAO,YAAY,IAAI,SAAS,UAAU,UAAU;IACpD,SAAS;GACX,GAAG,eAAe,EAAE,MAAM,GAAG,CAAC,IAAA,GAAGA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,qBAAqB,GAAG,YAAY,IAAI,QAAQ,UAAU,KAAK,MAAM,CAAC,IAAA,GAC1IA,MAAAA,cAAAA,CAAc,SAAS;IACrB,WAAW;IACX,eAAe,eAAe,MAAM,CAAC,CAAC;GACxC,GACE,aAAa,MAAM,KACnB,OACF,CACF,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO;IAAE,KAAK;IAAY,WAAW;GAAkB,GACnE,GAAG,SAAS,KAAK,SAAS;IACxB,MAAM,SAAS,QAAQ,KAAK;IAC5B,QAAA,GAAOA,MAAAA,cAAAA,CAAc,OAAO;KAAE,KAAK;KAAQ,WAAW;IAAe,GACnE,GAAG,KAAK,QAAQ,KAAK,OAAO,MAAM;KAChC,MAAM,SAAS,GAAG,OAAO,IAAI;KAC7B,QAAA,GAAOA,MAAAA,cAAAA,CAAc,QAAQ;MAC3B,KAAK;MACL,WAAW,MAAM,WAAW,WAAW,mBAAmB;KAC5D,GAAG,eAAe,MAAM,MAAM,SAAS,SAAS;MAAE,mBAAwB,KAAK,WAAW,KAAK,SAAS,KAAK,IAAI;KAAE,CAAC,CAAC;IACvH,CAAC,CACH;GACF,CAAC,GAED,aAAa,QAAA,GACTA,MAAAA,cAAAA,CAAc,OAAO;IACnB,KAAK;IACL,OAAO;KACL,OAAO,SAAS,WAAW,YAAY,SAAS,aAAa,IAAI,YAAY;KAC7E,YAAY;KACZ,WAAW;IACb;GACF,GACE,OAAO,SAAS,WAAW,WAAW,OAAO,SAAS,WAAW,WAAW,QAAQ,QACpF,SAAS,aAAa,OAAO,SAAS,SAAS,aAAa,EAC9D,IACA,IACN,CACF;EACF;;;;;;;;;;;;ECzYA,MAAMI,aAAW;;EAWjB,SAAS,WAAW,MAAe,aAAkF;GACnH,QAAA,GAAOC,MAAAA,cAAAA,CAAc,OAAO;IAAE,KAAK,QAAQ,KAAK;IAAO,WAAW;GAAe,GAC/E,GAAG,KAAK,QAAQ,KAAK,OAAO,MAAM;IAChC,MAAM,SAAS,QAAQ,KAAK,IAAI,IAAI;IACpC,MAAM,MAAM,MAAM,WAAW,WACzB,mBACA,MAAM,WAAW,SACf,iBACA;IACN,QAAA,GAAOA,MAAAA,cAAAA,CAAc,QAAQ;KAAE,KAAK;KAAQ,WAAW;IAAI,GACzD,eAAe,MAAM,MAAM,QAAQ,WAAW,CAAC;GACnD,CAAC,CACH;EACF;;EAgBA,SAAS,WAAW,OAAiB,SAAiB,KAAsB;GAC1E,MAAM,QAAQ,MAAM,KAAK,MAAM,IAAI;GACnC,IAAI,UAAsB,CAAC;GAC3B,MAAM,iBAAuB;IAC3B,IAAI,QAAQ,SAAS,GAAG;KACtB,IAAI,KAAK;MAAE,SAAS;MAAS,KAAK;KAAU,CAAC;KAC7C,UAAU,CAAC;IACb;GACF;GACA,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACrC,IAAI,IAAI,GAAG,SAAS;IACpB,IAAI,MAAM,OAAO,IAAI,QAAQ,KAAK;KAAE,QAAQ,MAAM;KAAQ,MAAM,MAAM;KAAI,IAAI,MAAM;IAAG,CAAC;GAC1F;GACA,IAAI,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,OAAO,IAAI,SAAS;EACnE;;EAGA,SAAgB,eAAe,EAAE,KAAK,WAAW,KAAK,SAAS,UAA0C;GACvG,MAAM,CAAC,OAAO,aAAA,GAAYC,MAAAA,SAAAA,CAA+B,IAAI;GAC7D,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,KAAK;GAC5C,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAA6B,IAAI;GACrE,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAoB,CAAC,CAAC;GAChD,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAS,EAAE;GAC/C,MAAM,kBAAA,GAAiBC,MAAAA,OAAAA,CAAO,CAAC;GAE/B,MAAM,YAAA,GAAWC,MAAAA,YAAAA,CAAY,YAA2B;IACtD,WAAW,IAAI;IACf,IAAI;KACF,MAAM,SAAS,IAAI,gBAAgB,EAAE,UAAU,CAAC;KAChD,IAAI,QAAQ,KAAA,KAAa,QAAQ,IAAI,OAAO,IAAI,OAAO,GAAG;KAC1D,MAAM,OAAO,MAAM,MAAM,GAAGJ,WAAS,gBAAgB,OAAO,SAAS,GAAG;KACxE,IAAI,CAAC,KAAK,IAAI;MACZ,IAAI,SAAS,WAAW,KAAA,IAAY,aAAa;MACjD,IAAI;OACF,MAAM,MAAM,MAAM,KAAK,KAAK;OAC5B,IAAI,OAAO,IAAI,UAAU,YAAY,IAAI,UAAU,IAAI,SAAS,IAAI;MACtE,QAAQ,CAAqB;MAC7B,UAAU,QAAQ,KAAK;MACvB,SAAS,CAAC,CAAC;MACX;KACF;KAEA,MAAM,OAAM,MADO,KAAK,KAAK,EAAA,CACZ,QAAQ,CAAC;KAG1B,MAAM,QAAQ,WAAW,KAAA,IAAY,IAAI,QAAQ,MAAM,EAAE,KAAK,WAAW,MAAM,CAAC,IAAI;KACpF,SAAS,KAAK;IAChB,QAAQ;KACN,UAAU,YAAY,KAAK;KAC3B,SAAS,CAAC,CAAC;IACb,UAAU;KACR,WAAW,KAAK;IAClB;GACF,GAAG;IAAC;IAAW;IAAK;GAAM,CAAC;GAG3B,MAAM,CAAC,aAAa,mBAAA,GAAkBE,MAAAA,SAAAA,CAAS,KAAK;GACpD,IAAI,CAAC,aAAa;IAChB,eAAe,IAAI;IACnB,SAAc;GAChB;;GAGA,MAAM,YAAA,GAAWE,MAAAA,YAAAA,CAAY,OAAO,SAAqC;IACvE,WAAW,IAAI;IACf,IAAI;KACF,MAAM,SAAS,IAAI,gBAAgB;MAAE;MAAW,MAAM,KAAK;KAAK,CAAC;KACjE,IAAI,QAAQ,KAAA,KAAa,QAAQ,IAAI,OAAO,IAAI,OAAO,GAAG;KAC1D,MAAM,OAAO,MAAM,MAAM,GAAGJ,WAAS,gBAAgB,OAAO,SAAS,GAAG;KACxE,IAAI,CAAC,KAAK,IAAI;MACZ,IAAI,SAAS;MACb,IAAI;OACF,MAAM,MAAM,MAAM,KAAK,KAAK;OAC5B,IAAI,OAAO,IAAI,UAAU,YAAY,IAAI,UAAU,IAAI,SAAS,IAAI;MACtE,QAAQ,CAAqB;MAC7B,UAAU,QAAQ,KAAK;MACvB;KACF;KACA,MAAM,OAAO,MAAM,KAAK,KAAK;KAC7B,MAAM,MAAiB,CAAC;KACxB,KAAK,MAAM,SAAS,KAAK,QAAQ,CAAC,GAAG;MACnC,WAAW,OAAO,eAAe,SAAS,GAAG;MAC7C,eAAe,WAAW;KAC5B;KACA,cAAc,IAAI;KAClB,cAAc,KAAK,IAAI;KACvB,SAAS,GAAG;IACd,QAAQ;KACN,UAAU,UAAU,KAAK;IAC3B,UAAU;KACR,WAAW,KAAK;IAClB;GACF,GAAG,CAAC,WAAW,GAAG,CAAC;GAGnB,IAAI,eAAe,MAAM;IACvB,MAAM,QAAQ,WAAW,KAAA,IAAY,GAAG,OAAO,WAAW;IAC1D,QAAA,GAAOC,MAAAA,cAAAA,CAAc,OAAO,EAAE,OAAO;KAAE,SAAS;KAAQ,eAAe;KAAU,QAAQ;KAAQ,WAAW;IAAE,EAAE,IAAA,GAC9GA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,GAClD,QAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,yBAAyB,CAAC,IAAA,GAC5DA,MAAAA,cAAAA,CAAc,UAAU;KACtB,WAAW;KACX,OAAO;KACP,eAAe;MAAE,SAAc;KAAE;IACnC,GAAG,YAAY,EAAE,MAAM,GAAG,CAAC,IAAA,GAAGA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,qBAAqB,GAAG,IAAI,CAAC,IAAA,GAC9FA,MAAAA,cAAAA,CAAc,UAAU;KACtB,WAAW;KACX,OAAO,WAAW,KAAA,IAAY,WAAW;KACzC,SAAS;IACX,GAAG,UAAU,EAAE,MAAM,GAAG,CAAC,IAAA,GAAGA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,qBAAqB,GAAG,WAAW,KAAA,IAAY,OAAO,IAAI,CAAC,CAC5H,IAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO,EACnB,OAAO;KAAE,UAAU;KAAQ,MAAM;KAAG,WAAW;KAAG,SAAS;IAAQ,EACrE,GACE,WAAW,UAAU,QAAA,GACjBA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,YAAY,GAAG,MAAM,IACvD,UAAU,QAAQ,MAAM,WAAW,KAAA,GACjCA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,YAAY,GAC5C,WAAW,KAAA,IACP,IAAI,OAAO,8DACX,4EAA4E,KAAA,GAClFA,MAAAA,cAAAA,CAAc,OAAO,CAAC,IACnB,SAAS,CAAC,EAAA,CAAG,KAAK,OAAA,GACjBA,MAAAA,cAAAA,CAAc,UAAU;KACtB,KAAK,EAAE;KACP,WAAW;KACX,OAAO,EAAE;KACT,eAAe;MAAE,SAAc,CAAC;KAAE;IACpC,IAAA,GACEA,MAAAA,cAAAA,CAAc,UAAU,EAAE,MAAM,GAAG,CAAC,IAAA,GACpCA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,OAAO;KAAE,MAAM;KAAG,WAAW;KAAQ,UAAU;KAAU,cAAc;KAAY,YAAY;IAAS,EAAE,GAAG,EAAE,IAAI,IAAA,GAC3IA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,wBAAwB,GACzD,GAAG,IAAI,KAAK,EAAE,KAAK,CAAC,CAAC,eAAe,EAAE,KAAK,EAAE,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,IAAI,CAC7E,CACF,CACF,CACR,CACF;GACF;GAGA,QAAA,GAAOA,MAAAA,cAAAA,CAAc,OAAO,EAAE,OAAO;IAAE,SAAS;IAAQ,eAAe;IAAU,QAAQ;IAAQ,WAAW;GAAE,EAAE,IAAA,GAC9GA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,OAAO;IACP,eAAe;KAAE,cAAc,IAAI;KAAG,SAAS,CAAC,CAAC;IAAE;GACrD,GAAG,SAAS,EAAE,MAAM,GAAG,CAAC,IAAA,GAAGA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,qBAAqB,GAAG,IAAI,CAAC,GAC3F,aAAA,GACAA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,OAAO;IAAE,SAAS;IAAK,YAAY;GAAM,EAAE,GAAG,QAAQ,IAAA,GAC9EA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,yBAAyB,CAAC,IAAA,GAC5DA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,OAAO;IACP,eAAe,SAAS,CAAC,CAAC;GAC5B,GAAG,UAAU,EAAE,MAAM,GAAG,CAAC,IAAA,GAAGA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,qBAAqB,GAAG,IAAI,CAAC,IAAA,GAC5FA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,OAAO;IACP,SAAS;GACX,GAAG,UAAU,EAAE,MAAM,GAAG,CAAC,IAAA,GAAGA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,qBAAqB,GAAG,IAAI,CAAC,CAC9F,IAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,GAClD,WAAW,MAAM,WAAW,KAAA,GACxBA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,YAAY,GAAG,MAAM,IACvD,MAAM,WAAW,KAAA,GACfA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,YAAY,GAAG,SAAS,IAC1D,MAAM,KAAK,SAAS,WAAW,OAAO,SAAS;IAAE,mBAAwB,KAAK,WAAW,KAAK,KAAK,IAAI;GAAE,CAAC,CAAC,GACjH,MAAM,WAAW,IACb,QAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO;IAAE,KAAK;IAAe,OAAO;KAAE,SAAS;KAAK,WAAW;IAAM;GAAE,GACnF,gBAAgB,MAAM,OAAO,cAAc,CACnD,CACF;EACF;;;;;;;;;;;;EChNA,eAAsBI,SAAO,OAAqB,MAAkC;GAClF,MAAM,UAAkC;IAAE,WAAW,MAAM;IAAW;GAAK;GAC3E,IAAI,MAAM,QAAQ,KAAA,KAAa,MAAM,QAAQ,IAAI,QAAQ,MAAM,MAAM;GACrE,MAAM,OAAO,MAAM,MAAM,wBAAwB;IAC/C,QAAQ;IACR,SAAS,EAAE,gBAAgB,mBAAmB;IAC9C,MAAM,KAAK,UAAU,OAAO;GAC9B,CAAC;GACD,IAAI,CAAC,KAAK,IAAI,MAAM,IAAI,MAAM,WAAW,KAAK,IAAI,KAAK,QAAQ;GAC/D,MAAM,SAAS,MAAM,KAAK,KAAK;GAC/B,IAAI,CAAC,OAAO,MAAM,OAAO,UAAU,KAAA,GACjC,MAAM,IAAI,MAAM,WAAW,KAAK,IAAI,OAAO,OAAO,WAAW,iBAAiB;GAEhF,OAAO,OAAO,MAAM,QAAQ,QAAQ,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;EAChE;;EAGA,eAAe,iBAAiB,OAAsC;GACpE,IAAI;IACF,MAAM,IAAI,MAAM,MAAM,8CAA8C,EAAE,QAAQ,MAAM,CAAC;IACrF,IAAI,EAAE,IAAI;KACR,MAAM,OAAO,MAAM,EAAE,KAAK;KAC1B,IAAI,KAAK,QAAQ,KAAK,SAAS,IAAI,OAAO,KAAK;IACjD;GACF,QAAQ,CAAe;GACvB,OAAO,MAAM,OAAO;EACtB;;EAGA,SAAS,SAAS,GAA0B;GAC1C,MAAM,UAAU,EAAE,QAAQ,WAAW,EAAE;GACvC,MAAM,UAAU,KAAK,IAAI,QAAQ,YAAY,GAAG,GAAG,QAAQ,YAAY,IAAI,CAAC;GAC5E,IAAI,WAAW,GAAG,OAAO;GACzB,OAAO,QAAQ,UAAU,GAAG,OAAO;EACrC;;EAGA,SAAgB,QAAQ,OAIV;GACZ,MAAM,EAAE,OAAO,cAAc,WAAW;GACxC,MAAM,CAAC,KAAK,WAAA,GAAUC,MAAAA,SAAAA,CAAiB,GAAG;GAC1C,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAA2B,IAAI;GAC7D,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,KAAK;GAC5C,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAwB,IAAI;GACtD,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,iBAAsB,IAAI,IAAI,CAAC;GAI/D,MAAM,YAAA,GAAWC,MAAAA,OAAAA,CAAO,KAAK;GAC7B,MAAM,eAAA,GAAcA,MAAAA,OAAAA,CAAO,KAAK;GAGhC,MAAM,mBAAA,GAAkBA,MAAAA,OAAAA,CAAO,YAAY;GAG3C,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,YAAY,SAAS;IACzB,YAAY,UAAU;IACtB,IAAI,YAAY;IAChB,MAAM,IAAI,SAAS;IACnB,MAAM,YAAY,gBAAgB,SAAS,KAAK,KAAK,gBAAgB,YAAY,OAAO,gBAAgB,YAAY,OAChH,gBAAgB,QAAQ,KAAK,IAC7B;IACJ,IAAI,cAAc,MAAM;KACtB,OAAO,SAAS;KAChB,QAAQ,GAAG,SAAS;IAEtB,OACE,iBAAiB,CAAC,CAAC,CAAC,MAAM,SAAS;KACjC,IAAI,WAAW;KACf,OAAO,IAAI;KACX,QAAQ,GAAG,IAAI;IACjB,CAAC;IAEH,aAAa;KAAE,YAAY;IAAK;GAElC,GAAG,CAAC,CAAC;GAEL,MAAM,WAAW,GAAiB,SAAuB;IACvD,WAAW,IAAI;IACf,SAAS,IAAI;IACb,SAAO,GAAG,IAAI,CAAC,CACZ,MAAM,SAAS;KACd,WAAW,IAAI;KACf,WAAW,KAAK;IAClB,CAAC,CAAC,CACD,OAAO,QAAQ;KACd,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;KACzD,WAAW,KAAK;IAClB,CAAC;GACL;GAEA,MAAM,aAAmB;IACvB,MAAM,SAAS,SAAS,GAAG;IAC3B,IAAI,WAAW,MAAM;KACnB,OAAO,MAAM;KACb,QAAQ,SAAS,SAAS,MAAM;IAClC;GACF;GAEA,MAAM,gBAAgB,SAAuB;IAC3C,OAAO,IAAI;IACX,QAAQ,SAAS,SAAS,IAAI;GAChC;GAEA,MAAM,gBAAgB,SAAuB;IAC3C,aAAa,SAAS;KACpB,MAAM,OAAO,IAAI,IAAI,IAAI;KACzB,IAAI,KAAK,IAAI,IAAI,GAAG,KAAK,OAAO,IAAI;UAC/B,KAAK,IAAI,IAAI;KAClB,OAAO;IACT,CAAC;GACH;GAEA,MAAM,cAAc,MAAuB,iBAAiB,MAAM,iBAAiB,OAAO,iBAAiB;GAE3G,QAAA,GAAOC,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,cAAc,IAAA,GAErDA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,SAAS;IACT,UAAU,SAAS,GAAG,MAAM;IAC5B,OAAO;GACT,GAAG,GAAQ,IAAA,GACXA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,eAAe,QAAQ,SAAS,SAAS,GAAG;IAC5C,OAAO;GACT,GAAG,GAAQ,IAAA,GACXA,MAAAA,cAAAA,CAAc,QAAQ;IAAE,WAAW;IAAmB,OAAO;GAAI,GAAG,OAAO,KAAK,CAClF,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO;IACnB,WAAW,eAAe,WAAW,GAAG,IAAI,cAAc;IAC1D,OAAO,EAAE,aAAa,MAAM;IAC5B,eAAe,OAAO,GAAG;GAC3B,IAAA,GACEA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,gBAAgB,IAAA,GACjDA,MAAAA,cAAAA,CAAc,YAAY,EAAE,MAAM,GAAG,CAAC,CACxC,IAAA,GACAA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,iBAAiB,GAAG,OAAO,KAAK,IAAA,GACnEA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,uBAAuB,GAAG,SAAuC,CACtG,GACA,WAAA,GACIA,MAAAA,cAAAA,CAAc,OAAO;IAAE,WAAW;IAAoB,OAAO,EAAE,aAAa,OAAO;GAAE,GAAG,QAAuB,IAC/G,UAAU,QAAA,GACRA,MAAAA,cAAAA,CAAc,OAAO;IAAE,WAAW;IAAkB,OAAO,EAAE,aAAa,OAAO;GAAE,GAAG,KAAK,IAC3F,YAAY,QAAQ,QAAQ,SAAS,KAAA,GACnCA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,GAClD,GAAG,QAAQ,KAAK,WAAA,GAAUA,MAAAA,cAAAA,CAAc,QAAQ;IAC9C,KAAK,MAAM;IACX;IACA;IACA,OAAO;IACP;IACA;IACA,gBAAgB;IAChB;IACA,YAAY;GACd,CAAC,CAAC,CACJ,KAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO;IAAE,WAAW;IAAkB,OAAO,EAAE,aAAa,OAAO;GAAE,GAAG,MAA0B,CAC1H;EACF;;EAGA,SAAS,OAAO,OASF;GACZ,MAAM,EAAE,OAAO,OAAO,OAAO,cAAc,UAAU,gBAAgB,QAAQ,eAAe;GAC5F,MAAM,CAAC,UAAU,gBAAA,GAAeF,MAAAA,SAAAA,CAA2B,IAAI;GAC/D,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,KAAK;GAC5C,MAAM,aAAa,SAAS,IAAI,MAAM,IAAI;GAC1C,MAAM,aAAa,iBAAiB,MAAM;GAE1C,MAAM,oBAA0B;IAG9B,OAAO,MAAM,IAAI;IACjB,IAAI,YAAY;KACd,eAAe,MAAM,IAAI;KACzB;IACF;IACA,eAAe,MAAM,IAAI;IACzB,IAAI,aAAa,MAAM;KACrB,WAAW,IAAI;KACf,SAAO,OAAO,MAAM,IAAI,CAAC,CACtB,MAAM,SAAS;MAAE,YAAY,IAAI;MAAG,WAAW,KAAK;KAAE,CAAC,CAAC,CACxD,YAAY;MAAE,YAAY,CAAC,CAAC;MAAG,WAAW,KAAK;KAAE,CAAC;IACvD;GACF;GAEA,QAAA,GAAOE,MAAAA,cAAAA,CAAc,OAAO,OAAA,GAC1BA,MAAAA,cAAAA,CAAc,OAAO;IACnB,WAAW,eAAe,aAAa,cAAc;IACrD,OAAO,EAAE,aAAa,GAAG,QAAQ,KAAK,EAAE,IAAI;IAC5C,SAAS;GACX,IAAA,GACEA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,mBAAmB,aAAa,cAAc,KAAK,IAAA,GACpFA,MAAAA,cAAAA,CAAc,aAAa,kBAAkB,kBAAkB,EAAE,MAAM,GAAG,CAAC,CAC7E,IAAA,GACAA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,gBAAgB,IAAA,GACjDA,MAAAA,cAAAA,CAAc,YAAY,EAAE,MAAM,GAAG,CAAC,CACxC,IAAA,GACAA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,iBAAiB,GAAG,MAAM,IAAI,CACnE,GACA,aACI,WAAA,GACEA,MAAAA,cAAAA,CAAc,OAAO;IAAE,WAAW;IAAoB,OAAO,EAAE,aAAa,IAAI,QAAQ,KAAK,KAAK,EAAE,IAAI;GAAE,GAAG,QAAuB,IACpI,aAAa,QAAQ,SAAS,SAAS,KAAA,GACrCA,MAAAA,cAAAA,CAAc,OAAO,MACnB,GAAG,SAAS,KAAK,WAAA,GACfA,MAAAA,cAAAA,CAAc,QAAQ;IACpB,KAAK,MAAM;IACX;IACA,OAAO;IACP,OAAO,QAAQ;IACf;IACA;IACgB;IAChB;IACA,YAAY,MAAM;GACpB,CAAC,CACH,CACF,IACA,OACJ,IACN;EACF;;EAcA,SAAgB,WAAW,OAAmC;GAC5D,MAAM,EAAE,OAAO,OAAO,UAAU,SAAS,gBAAgB;GACzD,MAAM,CAAC,MAAM,YAAA,GAAWF,MAAAA,SAAAA,CAAS,KAAK;GAEtC,QAAA,GAAOE,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,wBAAwB,IAAA,GAC/DA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,yBAAyB,IAAA,GAC1DA,MAAAA,cAAAA,CAAc,YAAY,EAAE,MAAM,GAAG,CAAC,CACxC,IAAA,GACAA,MAAAA,cAAAA,CAAc,SAAS;IACrB,WAAW,UAAU,yBAAyB;IACvC;IACP,UAAU;IACV,aAAa,eAAe;IAC5B,OAAO;IACP,eAAe,QAAQ,IAAI;GAC7B,CAAC,IAAA,GACDA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,eAAe,QAAQ,IAAI;IAC3B,OAAO;GACT,GAAG,IAAc,GACjB,QAAA,GACIA,MAAAA,cAAAA,CAAc,iBAAiB;IAC7B;IACA;IACA,YAAY,MAAM;KAAE,SAAS,CAAC;KAAG,QAAQ,KAAK;IAAE;IAChD,gBAAgB,QAAQ,KAAK;GAC/B,CAAC,IACD,IACN;EACF;;EAGA,SAAS,gBAAgB,OAKX;GACZ,MAAM,EAAE,OAAO,OAAO,WAAW,aAAa;GAC9C,MAAM,CAAC,KAAK,WAAA,GAAUF,MAAAA,SAAAA,CAAS,KAAK;GAEpC,QAAA,GAAOE,MAAAA,cAAAA,CAAc,OAAO;IAC1B,WAAW;IACX,UAAU,MAAkB;KAAE,IAAI,EAAE,WAAW,EAAE,eAAe,SAAS;IAAE;GAC7E,IAAA,GACEA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,gBAAgB,IAAA,GAChDA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,mBAAmB,GACnD,UAAA,GACAA,MAAAA,cAAAA,CAAc,UAAU;IAAE,WAAW;IAAgB,SAAS;GAAS,IAAA,GACrEA,MAAAA,cAAAA,CAAc,WAAW,EAAE,MAAM,GAAG,CAAC,CACvC,CACF,IAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,qBAAqB,IAAA,GACrDA,MAAAA,cAAAA,CAAc,SAAS;IAAE;IAAO,cAAc;IAAK,QAAQ;GAAO,CAAC,CACrE,IAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,mBAAmB,IAAA,GACnDA,MAAAA,cAAAA,CAAc,UAAU;IAAE,WAAW;IAAiB,SAAS;GAAS,GAAG,IAAc,IAAA,GACzFA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,eAAe,UAAU,GAAG;IAC5B,OAAO;KACL,YAAY;KACZ,QAAQ;KACR,OAAO;KACP,cAAc;KACd,SAAS;KACT,QAAQ;KACR,UAAU;IACZ;GACF,IAAA,GACEA,MAAAA,cAAAA,CAAc,WAAW,EAAE,MAAM,GAAG,CAAC,GAAG,KAAe,CAC3D,CACF,CACF;EACF;;;;;;;;;ECxUA,SAAgB,WAAW,OAAmC;GAC5D,MAAM,EAAE,OAAO,OAAO,UAAU,SAAS,SAAS;GAClD,QAAA,GAAOC,MAAAA,cAAAA,CAAc,YAAY;IAC/B;IACA;IACA;IACA;IACA,aAAa;GACf,CAAC;EACH;;;;;;;;;;;;ECRA,eAAe,OAAO,OAAqB,MAAmC;GAC5E,MAAM,UAAkC,EAAE,WAAW,MAAM,UAAU;GACrE,IAAI,MAAM,QAAQ,KAAA,KAAa,MAAM,QAAQ,IAAI,QAAQ,MAAM,MAAM;GACrE,IAAI,SAAS,KAAA,GAAW,QAAQ,OAAO;GACvC,MAAM,OAAO,MAAM,MAAM,wBAAwB;IAC/C,QAAQ;IACR,SAAS,EAAE,gBAAgB,mBAAmB;IAC9C,MAAM,KAAK,UAAU,OAAO;GAC9B,CAAC;GACD,IAAI,CAAC,KAAK,IAAI,MAAM,IAAI,MAAM,WAAW,QAAQ,SAAS,IAAI,KAAK,QAAQ;GAC3E,MAAM,SAAS,MAAM,KAAK,KAAK;GAC/B,IAAI,CAAC,OAAO,MAAM,OAAO,UAAU,KAAA,GACjC,MAAM,IAAI,MAAM,WAAW,QAAQ,SAAS,IAAI,OAAO,OAAO,WAAW,iBAAiB;GAE5F,OAAO,OAAO,MAAM,QAAQ,QAAQ,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;EAChE;;EAGA,SAAS,SAAS,OAMJ;GACZ,MAAM,EAAE,OAAO,OAAO,OAAO,cAAc,aAAa;GACxD,MAAM,CAAC,UAAU,gBAAA,GAAeC,MAAAA,SAAAA,CAAS,KAAK;GAC9C,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAA2B,IAAI;GAC/D,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,KAAK;GAE5C,MAAM,qBAA2B;IAC/B,IAAI,CAAC,YAAY,aAAa,MAAM;KAClC,WAAW,IAAI;KACf,OAAO,OAAO,MAAM,IAAI,CAAC,CACtB,MAAM,SAAS;MAAE,YAAY,IAAI;MAAG,WAAW,KAAK;KAAE,CAAC,CAAC,CACxD,YAAY;MAAE,YAAY,CAAC,CAAC;MAAG,WAAW,KAAK;KAAE,CAAC;IACvD;IACA,YAAY,CAAC,QAAQ;GACvB;GAEA,MAAM,aAAa,iBAAiB,MAAM;GAE1C,QAAA,GAAOC,MAAAA,cAAAA,CAAc,OAAO,OAAA,GAC1BA,MAAAA,cAAAA,CAAc,OAAO;IACnB,WAAW,eAAe,aAAa,cAAc;IACrD,OAAO,EAAE,aAAa,GAAG,QAAQ,KAAK,EAAE,IAAI;IAC5C,eAAe;KAAE,SAAS,MAAM,IAAI;KAAG,aAAa;IAAE;GACxD,IAAA,GACEA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,mBAAmB,WAAW,cAAc,KAAK,IAAA,GAClFA,MAAAA,cAAAA,CAAc,kBAAkB,EAAE,MAAM,GAAG,CAAC,CAC9C,IAAA,GACAA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,gBAAgB,IAAA,GACjDA,MAAAA,cAAAA,CAAc,YAAY,EAAE,MAAM,GAAG,CAAC,CACxC,IAAA,GACAA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,iBAAiB,GAAG,MAAM,IAAI,CACnE,GACA,WACI,WAAA,GACEA,MAAAA,cAAAA,CAAc,OAAO;IAAE,WAAW;IAAoB,OAAO,EAAE,aAAa,IAAI,QAAQ,KAAK,KAAK,EAAE,IAAI;GAAE,GAAG,QAAuB,IACpI,aAAa,QAAQ,SAAS,SAAS,KAAA,GACrCA,MAAAA,cAAAA,CAAc,OAAO,MACnB,GAAG,SAAS,KAAK,WAAA,GACfA,MAAAA,cAAAA,CAAc,UAAU;IACtB,KAAK,MAAM;IACX;IACA,OAAO;IACP,OAAO,QAAQ;IACf;IACA;GACF,CAAC,CACH,CACF,KAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO;IAAE,WAAW;IAAkB,OAAO,EAAE,aAAa,IAAI,QAAQ,KAAK,KAAK,EAAE,IAAI;GAAE,GAAG,MAA0B,IACzI,IACN;EACF;;EAGA,SAAS,cAAc,OAA4F;GACjH,MAAM,EAAE,OAAO,OAAO,aAAa;GACnC,MAAM,CAAC,UAAU,gBAAA,GAAeD,MAAAA,SAAAA,CAAS,KAAK;GAC9C,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAA2B,IAAI;GAC/D,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,KAAK;GAC5C,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAwB,IAAI;GAEtD,MAAM,qBAA2B;IAC/B,IAAI,CAAC,YAAY,aAAa,MAAM;KAClC,WAAW,IAAI;KACf,SAAS,IAAI;KACb,OAAO,KAAK,CAAC,CACV,MAAM,SAAS;MAAE,YAAY,IAAI;MAAG,WAAW,KAAK;KAAE,CAAC,CAAC,CACxD,OAAO,QAAQ;MAAE,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;MAAG,WAAW,KAAK;KAAE,CAAC;IACrG;IACA,YAAY,CAAC,QAAQ;GACvB;GAIA,QAAA,GAAOC,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GACzDA,MAAAA,cAAAA,CAAc,OAAO;IACnB,WAAW,eAJM,UAAU,OAAO,UAAU,MAAM,UAAU,OAInB,cAAc;IACvD,OAAO,EAAE,aAAa,MAAM;IAC5B,eAAe;KAAE,SAAS,GAAG;KAAG,aAAa;IAAE;GACjD,IAAA,GACEA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,mBAAmB,WAAW,cAAc,KAAK,IAAA,GAClFA,MAAAA,cAAAA,CAAc,kBAAkB,EAAE,MAAM,GAAG,CAAC,CAC9C,IAAA,GACAA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,gBAAgB,IAAA,GACjDA,MAAAA,cAAAA,CAAc,YAAY,EAAE,MAAM,GAAG,CAAC,CACxC,IAAA,GACAA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,iBAAiB,GAAG,WAA8C,CACvG,GACA,WACI,WAAA,GACEA,MAAAA,cAAAA,CAAc,OAAO;IAAE,WAAW;IAAoB,OAAO,EAAE,aAAa,OAAO;GAAE,GAAG,QAAuB,IAC/G,UAAU,QAAA,GACRA,MAAAA,cAAAA,CAAc,OAAO;IAAE,WAAW;IAAkB,OAAO,EAAE,aAAa,OAAO;GAAE,GAAG,KAAK,IAC3F,aAAa,QAAQ,SAAS,SAAS,KAAA,GACrCA,MAAAA,cAAAA,CAAc,OAAO,MACnB,GAAG,SAAS,KAAK,WAAA,GACfA,MAAAA,cAAAA,CAAc,UAAU;IACtB,KAAK,MAAM;IACX;IACA,OAAO;IACP,OAAO;IACP,cAAc;IACd;GACF,CAAC,CACH,CACF,KAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO;IAAE,WAAW;IAAkB,OAAO,EAAE,aAAa,OAAO;GAAE,GAAG,MAA0B,IACtH,IACN;EACF;;EAyCA,MAAM,gBAAwC;GAC5C,KAAK;GACL,YAAY;GACZ,QAAQ;GACR,QAAQ;EACV;EAEA,MAAM,cAAsC;GAC1C,KAAK;GACL,YAAY;GACZ,QAAQ;GACR,QAAQ;EACV;;EAGA,SAAgB,kBAAkB,EAAE,QAAQ,OAAO,QAAQ,YAA+C;GACxG,MAAM,CAAC,MAAM,YAAA,GAAWD,MAAAA,SAAAA,CAAS,QAAQ,QAAQ,EAAE;GACnD,MAAM,CAAC,MAAM,YAAA,GAAWA,MAAAA,SAAAA,CAAS,QAAQ,QAAQ,QAAQ;GACzD,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,QAAQ,WAAW,EAAE;GAC5D,MAAM,CAAC,KAAK,WAAA,GAAUA,MAAAA,SAAAA,CAAS,QAAQ,OAAO,GAAG;GACjD,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,QAAQ,WAAW,EAAE;GAC5D,MAAM,CAAC,MAAM,YAAA,GAAWA,MAAAA,SAAAA,CAAS,QAAQ,QAAQ,EAAE;GACnD,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAC5B,SACI,OAAO,QAAQ,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,YAAY;IAAE;IAAK;GAAM,EAAE,IACjE,CAAC,CACP;GACA,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAS,KAAK;GAC9C,MAAM,CAAC,aAAa,mBAAA,GAAkBA,MAAAA,SAAAA,CAAS,KAAK;GACpD,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAS,QAAQ,SAAS,QAAQ,EAAE;GAChE,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAS,QAAQ,SAAS,QAAQ,EAAE;GAChE,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAS,QAAQ,SAAS,UAAU,EAAE;GACtE,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAS,QAAQ,SAAS,OAAO,EAAE;GAG7D,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,YAAY,MAAM,cAAc,UAAU,IAC5C,WAAW,cAAc,KAAK;GAGlC,GAAG,CAAC,IAAI,CAAC;GAET,MAAM,mBAAyB;IAC7B,MAAM,MAA8B,CAAC;IACrC,KAAK,MAAM,OAAO,SAChB,IAAI,IAAI,IAAI,KAAK,MAAM,IACrB,IAAI,IAAI,IAAI,KAAK,KAAK,IAAI;IAG9B,MAAM,UAAkC,CAAC;IACzC,IAAI,OAAO,KAAK,MAAM,IAAI,QAAQ,OAAO,OAAO,KAAK;IACrD,IAAI,OAAO,KAAK,MAAM,IAAI,QAAQ,OAAO,OAAO,KAAK;IACrD,IAAI,SAAS,KAAK,MAAM,IAAI,QAAQ,SAAS,SAAS,KAAK;IAC3D,IAAI,MAAM,KAAK,MAAM,IAAI,QAAQ,MAAM,MAAM,KAAK;IAClD,OAAO;KACL,IAAI,QAAQ;KACZ,MAAM,KAAK,KAAK;KAChB;KACA,SAAS,QAAQ,KAAK;KACtB,KAAK,IAAI,KAAK,MAAM,KAAK,MAAM,IAAI,KAAK;KACxC;KACA,SAAS,QAAQ,KAAK;KACtB,MAAM,KAAK,KAAK;KAChB;IACF,CAAC;GACH;GAEA,MAAM,gBAAgB,KAAa,OAAwB,QAAsB;IAC/E,YAAY,SAAS,KAAK,KAAK,KAAK,MAAM,MAAM,MAAM;KAAE,GAAG;MAAM,QAAQ;IAAI,IAAI,GAAG,CAAC;GACvF;GACA,MAAM,kBAAwB,YAAY,SAAS,CAAC,GAAG,MAAM;IAAE,KAAK;IAAI,OAAO;GAAG,CAAC,CAAC;GACpF,MAAM,gBAAgB,QAAsB,YAAY,SAAS,KAAK,QAAQ,GAAG,MAAM,MAAM,GAAG,CAAC;GAGjG,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAS,KAAK;GACtD,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAS,EAAE;;;GAInD,MAAM,sBAA4B;IAChC,MAAM,WAAW,IAAI,IAAI,QAAQ,QAAQ,MAAM,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,EAAE,IAAI,KAAK,GAAG,EAAE,KAAK,CAAC,CAAC;IACvG,MAAM,OAAO,aAAa,KAAK;IAC/B,IAAI,SAAS,IAAI;IAEjB,MAAM,QAAQ,KAAK,MAAM,SAAS;IAClC,KAAK,MAAM,QAAQ,OAAO;KACxB,MAAM,IAAI,KAAK,KAAK;KACpB,IAAI,MAAM,IAAI;KACd,MAAM,KAAK,EAAE,QAAQ,GAAG;KACxB,IAAI,MAAM,GAAG;KACb,MAAM,MAAM,EAAE,UAAU,GAAG,EAAE,CAAC,CAAC,KAAK;KACpC,MAAM,QAAQ,EAAE,UAAU,KAAK,CAAC,CAAC,CAAC,KAAK;KACvC,IAAI,QAAQ,IAAI,SAAS,IAAI,KAAK,KAAK;IACzC;IACA,WAAW,MAAM,KAAK,WAAW,CAAC,KAAK,YAAY;KAAE;KAAK;IAAM,EAAE,CAAC;IACnE,gBAAgB,EAAE;IAClB,gBAAgB,KAAK;GACvB;GAEA,MAAM,eAAe,SAAS;GAE9B,QAAA,GAAOC,MAAAA,cAAAA,CAAc,OAAO;IAC1B,WAAW;IACX,UAAU,MAAkB;KAAE,IAAI,EAAE,WAAW,EAAE,eAAe,SAAS;IAAE;GAC7E,IAAA,GACEA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,YAAY,IAAA,GAE5CA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,mBAAmB,GACnD,QAAQ,KAAK,WAAyC,WAAA,GACtDA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,SAAS;GACX,IAAA,GAAGA,MAAAA,cAAAA,CAAc,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC,CAC3C,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,iBAAiB,IAAA,GAEjDA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,kBAAkB,GAAG,IAAc,IAAA,GACvEA,MAAAA,cAAAA,CAAc,SAAS;IACrB,WAAW;IACX,OAAO;IACP,WAAW,MAAqC,QAAQ,EAAE,OAAO,KAAK;IACtE,aAAa;GACf,CAAC,CACH,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,kBAAkB,GAAG,IAAc,IAAA,GACvEA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,OAAO;IACP,WAAW,MAAqC,QAAQ,EAAE,OAAO,KAAK;GACxE,GACE,GAAG,OAAO,QAAQ,WAAW,CAAC,CAAC,KAAK,CAAC,KAAK,YAAA,GACxCA,MAAAA,cAAAA,CAAc,UAAU;IAAE,KAAK;IAAK,OAAO;GAAI,GAAG,KAAK,CACzD,CACF,CACF,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,kBAAkB,GAAG,IAAc,IAAA,GACvEA,MAAAA,cAAAA,CAAc,SAAS;IACrB,WAAW;IACX,OAAO;IACP,WAAW,MAAqC,WAAW,EAAE,OAAO,KAAK;IACzE,aAAa;GACf,CAAC,CACH,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,kBAAkB,GAAG,MAA0B,IAAA,GACnFA,MAAAA,cAAAA,CAAc,OAAO;IAAE,WAAW;IAAmB,eAAe,YAAY,CAAC,QAAQ;GAAE,IAAA,GACzFA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,eAAe,IAAA,GAAGA,MAAAA,cAAAA,CAAc,YAAY,EAAE,MAAM,GAAG,CAAC,CAAC,IAAA,GAC5FA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,gBAAgB,GAAG,GAAG,CAC3D,GACA,YAAA,GACIA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,4BAA4B,IAAA,GAC5DA,MAAAA,cAAAA,CAAc,eAAe;IAAE;IAAO,OAAO;IAAK,WAAW,MAAM,OAAO,CAAC;GAAE,CAAC,CAChF,IACA,IACN,GAEA,gBAAA,GACIA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,kBAAkB,GAAG,kBAA2C,IAAA,GACpGA,MAAAA,cAAAA,CAAc,SAAS;IACrB,WAAW;IACX,OAAO;IACP,WAAW,MAAqC,WAAW,EAAE,OAAO,KAAK;IACzE,aAAa;GACf,CAAC,CACH,IACA,OAAA,GAEJA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,kBAAkB,GAAG,MAA0B,IAAA,GACnFA,MAAAA,cAAAA,CAAc,SAAS;IACrB,WAAW;IACX,OAAO;IACP,WAAW,MAAqC,QAAQ,EAAE,OAAO,KAAK;IACtE,aAAa;GACf,CAAC,CACH,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,OAAO;IACnB,WAAW,sBAAsB,cAAc,UAAU;IACzD,eAAe,eAAe,CAAC,WAAW;GAC5C,IAAA,GACEA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,uBAAuB,cAAc,cAAc,KAAK,IAAA,GACzFA,MAAAA,cAAAA,CAAc,kBAAkB,EAAE,MAAM,GAAG,CAAC,CAC9C,IAAA,GACAA,MAAAA,cAAAA,CAAc,QAAQ,MAAM,YAA+C,IAAA,GAC3EA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,oBAAoB,GAAG,SAA4C,CACxG,GACA,eAAA,GACIA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,oBAAoB,IAAA,GACpDA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,GAAG,aAA+D,IAAA,GACtHA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,kBAAkB,GAAG,SAAmB,IAAA,GAC5EA,MAAAA,cAAAA,CAAc,YAAY;IACxB;IACA,MAAM;IACN,OAAO;IACP,UAAU;IACV,SAAS;IACT,MAAM;GACR,CAAC,CACH,IAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,kBAAkB,GAAG,YAAsB,IAAA,GAC/EA,MAAAA,cAAAA,CAAc,YAAY;IACxB;IACA,MAAM;IACN,OAAO;IACP,UAAU;IACV,SAAS;IACT,MAAM;GACR,CAAC,CACH,IAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,kBAAkB,GAAG,WAAqB,IAAA,GAC9EA,MAAAA,cAAAA,CAAc,YAAY;IACxB;IACA,MAAM;IACN,OAAO;IACP,UAAU;IACV,SAAS;IACT,MAAM;GACR,CAAC,CACH,IAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,kBAAkB,GAAG,gBAA0B,IAAA,GACnFA,MAAAA,cAAAA,CAAc,YAAY;IACxB;IACA,MAAM;IACN,OAAO;IACP,UAAU;IACV,SAAS;IACT,MAAM;GACR,CAAC,CACH,CACF,IACA,IACN,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,kBAAkB,GAAG,MAA0B,GACnF,GAAG,QAAQ,KAAK,KAAK,SAAA,GACnBA,MAAAA,cAAAA,CAAc,OAAO;IAAE,KAAK;IAAK,WAAW;GAAc,IAAA,GACxDA,MAAAA,cAAAA,CAAc,SAAS;IACrB,WAAW;IACX,OAAO,IAAI;IACX,WAAW,MAAqC,aAAa,KAAK,OAAO,EAAE,OAAO,KAAK;IACvF,aAAa;GACf,CAAC,IAAA,GACDA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,cAAc,GAAG,GAAG,IAAA,GACvDA,MAAAA,cAAAA,CAAc,SAAS;IACrB,WAAW;IACX,OAAO,IAAI;IACX,WAAW,MAAqC,aAAa,KAAK,SAAS,EAAE,OAAO,KAAK;IACzF,aAAa;GACf,CAAC,IAAA,GACDA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,eAAe,aAAa,GAAG;GACjC,IAAA,GAAGA,MAAAA,cAAAA,CAAc,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC,CAC3C,CACF,IAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,kBAAkB,IAAA,GAClDA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IAAe,SAAS;GACrC,IAAA,GACEA,MAAAA,cAAAA,CAAc,UAAU,EAAE,MAAM,GAAG,CAAC,GAAG,SACzC,IAAA,GACAA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IAAe,eAAe,gBAAgB,CAAC,YAAY;GACxE,IAAA,GACEA,MAAAA,cAAAA,CAAc,eAAe,EAAE,MAAM,GAAG,CAAC,GAAG,eAAe,QAAkB,OAC/E,CACF,GACA,gBAAA,GACIA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,gBAAgB,IAAA,GAChDA,MAAAA,cAAAA,CAAc,YAAY;IACxB,WAAW;IACX,OAAO;IACP,WAAW,MAAqC,gBAAgB,EAAE,OAAO,KAAK;IAC9E,aAAa;IACb,MAAM;GACR,CAAC,IAAA,GACDA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,uBAAuB,IAAA,GACvDA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IAAuB,SAAS;GAC7C,GACE,WAAwD,IAAA,GAC1DA,MAAAA,cAAAA,CAAc,QAAQ,EAAE,WAAW,qBAAqB,GAAG,OAAgC,CAC7F,CACF,IACA,IACN,CACF,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,mBAAmB,IAAA,GACnDA,MAAAA,cAAAA,CAAc,UAAU;IAAE,WAAW;IAAiB,SAAS;GAAS,GAAG,IAAc,IAAA,GACzFA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,SAAS;IACT,UAAU,KAAK,KAAK,MAAM;IAC1B,OAAO;KACL,YAAY;KACZ,QAAQ;KACR,OAAO;KACP,cAAc;KACd,SAAS;KACT,QAAQ,KAAK,KAAK,MAAM,KAAK,gBAAgB;KAC7C,UAAU;KACV,SAAS,KAAK,KAAK,MAAM,KAAK,KAAM;IACtC;GACF,GAAG,IAAc,CACnB,CACF,CACF;EACF;;;;;;;;;;EClgBA,MAAa,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECU1B,MAAM,WAAW;;EAGjB,MAAM,gBAAgB;;EAGtB,IAAI,kBAAkB;EACtB,SAAS,eAAqB;GAC5B,IAAI,iBAAiB;GACrB,kBAAkB;GAClB,MAAM,MAAM,SAAS,cAAc,OAAO;GAC1C,IAAI,KAAK;GACT,IAAI,aAAa,eAAe,yCAAyC;GACzE,IAAI,cAAc;GAClB,SAAS,KAAK,YAAY,GAAG;EAC/B;;EAGA,SAAgB,YAAY,OAAqC;GAC/D,aAAa;GAEb,MAAM,EAAE,OAAO,UAAU;GACzB,MAAM,CAAC,SAAS,eAAA,GAAcC,MAAAA,SAAAA,CAAsB,CAAC,CAAC;GACtD,MAAM,CAAC,WAAW,iBAAA,GAAgBA,MAAAA,SAAAA,CAAwB,CAAC,CAAC;GAC5D,MAAM,CAAC,kBAAkB,wBAAA,GAAuBA,MAAAA,SAAAA,CAAwB,IAAI;GAC5E,MAAM,CAAC,oBAAoB,0BAAA,GAAyBA,MAAAA,SAAAA,CAAwB,IAAI;GAChF,MAAM,CAAC,eAAe,qBAAA,GAAoBA,MAAAA,SAAAA,CAAkD,IAAI;GAChG,MAAM,CAAC,WAAW,iBAAA,GAAgBA,MAAAA,SAAAA,CAAS,KAAK;GAChD,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAS,GAAI;GACjD,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAS,KAAK;GAC9C,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAS,IAAI;GAErD,MAAM,gBAAA,GAAeC,MAAAA,OAAAA,CAAuB,IAAI;GAGhD,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,KAAK,aAAa;IACxB,IAAI,OAAO,MAAM;IACjB,MAAM,cAAoB;KACxB,MAAM,OAAO,GAAG,sBAAsB;KAEtC,gBAAgB,KAAK,QAAQ,KAAK,SAAS,GAAG;IAChD;IACA,MAAM;IACN,MAAM,KAAK,IAAI,eAAe,KAAK;IACnC,GAAG,QAAQ,EAAE;IACb,aAAa,GAAG,WAAW;GAC7B,GAAG,CAAC,CAAC;GAGL,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,OAAO,MAAM,SAAS,CAAC,EAAE,iBAAiB,0CAA0C,CAAC;IAC3F,IAAI,OAAO,KAAK,eAAe,UAC7B,cAAc,KAAK,IAAI,IAAK,KAAK,IAAI,IAAK,KAAK,UAAU,CAAC,CAAC;GAE/D,GAAG,CAAC,KAAK,CAAC;;GAGV,MAAM,qBAAA,GAAoBC,MAAAA,YAAAA,EAAa,UAAwB;IAC7D,MAAM,UAAW,MAAc;IAC/B,IAAI,YAAY,KAAA,KAAa,OAAO,QAAQ,cAAc,YAAY,CAEtE;GACF,GAAG,CAAC,KAAK,CAAC;;GAGV,MAAM,gBAAA,GAAeA,MAAAA,YAAAA,OAA0B;IAC7C,MAAM,SAAS,IAAI,gBAAgB,EAAE,WAAW,MAAM,UAAU,CAAC;IACjE,IAAI,MAAM,QAAQ,KAAA,GAAW,OAAO,IAAI,OAAO,MAAM,GAAG;IACxD,OAAO,OAAO,SAAS;GACzB,GAAG,CAAC,KAAK,CAAC;;GAGV,MAAM,gBAAA,GAAeA,MAAAA,YAAAA,CAAY,YAA2B;IAC1D,IAAI;KACF,MAAM,OAAO,MAAM,MAAM,GAAG,SAAS,WAAW,aAAa,GAAG;KAChE,IAAI,KAAK,IAAI;MACX,MAAM,OAAO,MAAM,KAAK,KAAK;MAC7B,WAAW,KAAK,OAAO;KACzB;IACF,QAAQ,CAA+B;GACzC,GAAG,CAAC,YAAY,CAAC;;GAGjB,MAAM,kBAAA,GAAiBA,MAAAA,YAAAA,CAAY,YAA2B;IAC5D,IAAI;KACF,MAAM,OAAO,MAAM,MAAM,GAAG,SAAS,aAAa,aAAa,GAAG;KAClE,IAAI,KAAK,IAAI;MACX,MAAM,OAAO,MAAM,KAAK,KAAK;MAC7B,aAAa,KAAK,SAAS;KAC7B;IACF,QAAQ,CAA+B;GACzC,GAAG,CAAC,YAAY,CAAC;GAGjB,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,aAAkB;IAClB,eAAoB;GACtB,GAAG,CAAC,cAAc,cAAc,CAAC;GAGjC,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,QAAQ,kBAAkB,KAAK,eAAe,GAAG,aAAa;IACpE,aAAa,cAAc,KAAK;GAClC,GAAG,CAAC,cAAc,CAAC;;GAGnB,MAAM,eAAA,GAAcA,MAAAA,YAAAA,CAAY,OAAO,aAAoC;IACzE,IAAI;KAEF,MAAM,OAAO,MAAM,SAAS,CAAC,EAAE,iBAAiB,0CAA0C,CAAC;KAC3F,MAAM,YAAoC,CAAC;KAC3C,IAAI,OAAO,KAAK,aAAa,YAAY,KAAK,SAAS,KAAK,MAAM,IAAI,UAAU,YAAY,KAAK,SAAS,KAAK;KAC/G,IAAI,OAAO,KAAK,aAAa,YAAY,KAAK,SAAS,KAAK,MAAM,IAAI,UAAU,YAAY,KAAK,SAAS,KAAK;KAC/G,IAAI,OAAO,KAAK,eAAe,YAAY,KAAK,WAAW,KAAK,MAAM,IAAI,UAAU,cAAc,KAAK,WAAW,KAAK;KACvH,IAAI,OAAO,KAAK,YAAY,YAAY,KAAK,QAAQ,KAAK,MAAM,IAAI,UAAU,WAAW,KAAK,QAAQ,KAAK;KAE3G,MAAM,OAAO,MAAM,MAAM,GAAG,SAAS,OAAO,aAAa,KAAK;MAC5D,QAAQ;MACR,SAAS,EAAE,gBAAgB,mBAAmB;MAC9C,MAAM,KAAK,UAAU;OAAE;OAAU;MAAU,CAAC;KAC9C,CAAC;KACD,IAAI,KAAK,IAAI;MACX,MAAM,OAAO,MAAM,KAAK,KAAK;MAC7B,sBAAsB,KAAK,SAAS,EAAE;MACtC,oBAAoB,IAAI;MACxB,eAAoB;MACpB,aAAkB;KACpB;IACF,QAAQ,CAAe;GACzB,GAAG;IAAC;IAAc;IAAgB;IAAc;GAAK,CAAC;;GAGtD,MAAM,cAAA,GAAaA,MAAAA,YAAAA,CAAY,OAAO,eAAsC;IAC1E,IAAI;KACF,MAAM,MAAM,GAAG,SAAS,QAAQ;MAC9B,QAAQ;MACR,SAAS,EAAE,gBAAgB,mBAAmB;MAC9C,MAAM,KAAK,UAAU,EAAE,WAAW,CAAC;KACrC,CAAC;KACD,eAAoB;IACtB,QAAQ,CAAe;GACzB,GAAG,CAAC,cAAc,CAAC;;GAGnB,MAAM,iBAAA,GAAgBA,MAAAA,YAAAA,CAAY,OAAO,eAAsC;IAC7E,MAAM,OAAO,UAAU,MAAM,MAAM,EAAE,OAAO,UAAU;IACtD,IAAI,SAAS,KAAA,GAAW;IACxB,MAAM,WAAW,UAAU;IAE3B,MAAM,IAAI,SAAS,MAAM,WAAW,GAAG,GAAG,CAAC;IAC3C,MAAM,YAAY,KAAK,QAAQ;GACjC,GAAG;IAAC;IAAW;IAAY;GAAW,CAAC;;GAGvC,MAAM,wBAA8B;IAClC,iBAAiB,IAAI;IACrB,aAAa,IAAI;GACnB;;GAGA,MAAM,oBAAoB,aAA2B;IACnD,MAAM,SAAS,QAAQ,MAAM,MAAM,EAAE,OAAO,QAAQ;IACpD,IAAI,WAAW,KAAA,GAAW;IAC1B,iBAAiB;KACf,IAAI,OAAO;KACX,MAAM,OAAO;KACb,MAAM,OAAO;KACb,SAAS,OAAO;KAChB,KAAK,OAAO;KACZ,KAAK,OAAO;KACZ,SAAS,OAAO;KAChB,MAAM,OAAO;KACb,SAAS,OAAO;IAClB,CAAC;IACD,aAAa,IAAI;GACnB;;;;;;GAOA,MAAM,yBAAyB,aAA2B;IACxD,MAAM,SAAS,QAAQ,MAAM,MAAM,EAAE,OAAO,QAAQ;IACpD,IAAI,WAAW,KAAA,GAAW;IAG1B,MAAM,OAAO,OAAO,KAAK,SAAS,KAAK,IAAI,OAAO,KAAK,MAAM,GAAG,EAAE,IAAI,OAAO;IAC7E,MAAM,QAAQ,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,IAAI,CAAC;IAChD,IAAI,UAAU,GAAG,KAAK;IACtB,IAAI,SAAS;IACb,OAAO,MAAM,IAAI,OAAO,GAAG;KACzB,UAAU,GAAG,KAAK,KAAK;KACvB;IACF;IACA,iBAAiB;KAEf,MAAM;KACN,MAAM,OAAO;KACb,SAAS,OAAO;KAChB,KAAK,OAAO;KACZ,KAAK,OAAO;KACZ,SAAS,OAAO;KAChB,MAAM,OAAO;KACb,SAAS,OAAO;IAClB,CAAC;IACD,aAAa,IAAI;GACnB;;GAGA,MAAM,oBAAA,GAAmBA,MAAAA,YAAAA,CAAY,OAAO,WAA4D;IACtG,IAAI;KAMF,KAAI,MALe,MAAM,GAAG,SAAS,WAAW,aAAa,KAAK;MAChE,QAAQ;MACR,SAAS,EAAE,gBAAgB,mBAAmB;MAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,CAAC;KACjC,CAAC,EAAA,CACQ,IAAI;MACX,aAAa,KAAK;MAClB,iBAAiB,IAAI;MACrB,aAAkB;KACpB;IACF,QAAQ,CAAe;GACzB,GAAG,CAAC,cAAc,YAAY,CAAC;GAGJ,CAAA,GAAA,MAAA,YAAA,CAAY,OAAO,aAAoC;IAEhF,KAAK,MAAM,QAAQ,WACjB,IAAI,KAAK,aAAa,YAAY,KAAK,WAAW,WAChD,MAAM,WAAW,KAAK,EAAE;IAG5B,IAAI;KAIF,KAAI,MAHe,MAAM,GAAG,SAAS,WAAW,aAAa,EAAE,MAAM,mBAAmB,QAAQ,KAAK,EACnG,QAAQ,SACV,CAAC,EAAA,CACQ,IAAI;MACX,IAAI,qBAAqB,UAAU,oBAAoB,IAAI;MAC3D,aAAkB;MAClB,eAAoB;KACtB;IACF,QAAQ,CAAe;GACzB,GAAG;IAAC;IAAW;IAAY;IAAc;IAAc;IAAgB;GAAgB,CAAC;;GAGxF,MAAM,sBAAsB,aAA2B;IACrD,oBAAoB,QAAQ;IAC5B,sBAAsB,IAAI;GAC5B;;GAGA,MAAM,wBAAwB,eAA6B;IACzD,sBAAsB,UAAU;IAChC,oBAAoB,IAAI;GAC1B;GAGA,MAAM,mBAAmB,MAAwB;IAC/C,EAAE,eAAe;IACjB,YAAY,IAAI;GAClB;GAEA,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,CAAC,UAAU;IACf,MAAM,cAAc,MAAwB;KAC1C,IAAI,aAAa,YAAY,MAAM;KACnC,MAAM,OAAO,aAAa,QAAQ,sBAAsB;KACxD,MAAM,QAAQ,gBACT,EAAE,UAAU,KAAK,QAAQ,KAAK,SAC9B,EAAE,UAAU,KAAK,OAAO,KAAK;KAClC,MAAM,UAAU,KAAK,IAAI,IAAK,KAAK,IAAI,IAAK,KAAK,CAAC;KAClD,cAAc,OAAO;KACrB,kBAAkB,OAAO;IAC3B;IACA,MAAM,iBAAuB,YAAY,KAAK;IAC9C,SAAS,iBAAiB,aAAa,UAAU;IACjD,SAAS,iBAAiB,WAAW,QAAQ;IAC7C,aAAa;KACX,SAAS,oBAAoB,aAAa,UAAU;KACpD,SAAS,oBAAoB,WAAW,QAAQ;IAClD;GACF,GAAG;IAAC;IAAU;IAAc;GAAiB,CAAC;GAG9C,MAAM,mBAAmB,uBAAuB,OAC5C,UAAU,MAAM,MAAM,EAAE,OAAO,kBAAkB,KAAK,OACtD;GACJ,MAAM,iBAAiB,qBAAqB,OACxC,QAAQ,MAAM,MAAM,EAAE,OAAO,gBAAgB,KAAK,OAClD;GAEJ,QAAA,GAAOC,MAAAA,cAAAA,CAAc,OAAO;IAAE,KAAK;IAAc,WAAW;GAAW,IAAA,GAErEA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,cAAc,IAAA,GAC9CA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,oBAAoB,IAAA,GACpDA,MAAAA,cAAAA,CAAc,YAAY,EAAE,MAAM,GAAG,CAAC,GACtC,IACF,IAAA,GACAA,MAAAA,cAAAA,CAAc,UAAU;IACtB,WAAW;IACX,SAAS;GACX,IAAA,GACEA,MAAAA,cAAAA,CAAc,UAAU,EAAE,MAAM,GAAG,CAAC,GAAG,MAAM,CACjD,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,YAAY,eAAe,KAAK,wBAAwB,IAAA,GAExFA,MAAAA,cAAAA,CAAc,OAAO;IACnB,WAAW,kBAAkB,eAAe,KAAK;IACjD,OAAO,eACH,EAAE,OAAO,GAAG,aAAa,IAAI,GAAG,IAChC,EAAE,QAAQ,GAAG,aAAa,IAAI,GAAG;GACvC,GACE,QAAQ,WAAW,KAAA,GACfA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,YAAY,GAAG,mBAAmB,KAAA,GACpEA,MAAAA,cAAAA,CAAc,YAAY;IACxB;IACA;IACA;IACA;IACA,gBAAgB;IAChB,kBAAkB;IAClB,gBAAgB,OAAe;KAAE,YAAiB,EAAE;IAAE;IACtD,iBAAiB,OAAe;KAAE,WAAgB,EAAE;IAAE;IACtD,cAAc;IACd,mBAAmB;GACrB,CAAC,CACP,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO;IACnB,WAAW,mBAAmB,WAAW,cAAc,KAAK,eAAe,KAAK;IAChF,aAAa;GACf,CAAC,IAAA,GAEDA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,mBAAmB,eAAe,KAAK,wBAAwB,GAC/F,qBAAqB,QAAA,GACjBA,MAAAA,cAAAA,CAAc,SAAS;IACrB,UAAU;IACV,KAAK,MAAM;IACX,WAAW,MAAM,aAAa,MAAM,YAAY,CAAC,CAAC,aAAa;IAC/D,KAAK,MAAM;IACX,cAAc;KAAE,WAAgB,iBAAiB,EAAE;IAAE;IACrD,iBAAiB;KAAE,cAAmB,iBAAiB,EAAE;IAAE;GAC7D,CAAC,IACD,mBAAmB,QAAA,GAGfA,MAAAA,cAAAA,CAAc,gBAAgB;IAC5B,KAAK,MAAM;IACX,WAAW,MAAM,aAAa,MAAM,YAAY,CAAC,CAAC,aAAa;IAC/D,KAAK,MAAM;IACX,QAAQ,eAAe;IACvB,eAAe;KAAE,oBAAoB,IAAI;IAAE;GAC7C,CAAC,KAAA,GACDA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,YAAY,GAAG,+BAA+B,CAC1F,CACF,GAEA,aAAA,GACIA,MAAAA,cAAAA,CAAc,mBAAmB;IAC/B,QAAQ;IACR,OAAO;KAAE,WAAW,MAAM,aAAa,MAAM,YAAY,CAAC,CAAC,aAAa;KAAI,KAAK,MAAM;IAAI;IAC3F,SAAS,MAAM;KAAE,iBAAsB,CAAC;IAAE;IAC1C,gBAAgB;KAAE,aAAa,KAAK;KAAG,iBAAiB,IAAI;IAAE;GAChE,CAAC,IACD,IACN;EACF;;;;;;;;;ECxXA,SAAgB,cAAc,OAA8C;GAC1E,MAAM,EAAE,OAAO,gBAAgB,wBAAwB;GACvD,MAAM,QAAsB,EAAE,WAAW,MAAM,YAAY,CAAC,CAAC,aAAa,GAAG;GAE7E,MAAM,OAAO,QACX,OAAO,eAAe,SAAS,WAAW,eAAe,OAAiB;GAC5E,MAAM,WAAW,QACf,eAAe,SAAS;GAE1B,MAAM,CAAC,YAAY,kBAAA,GAAiBC,MAAAA,SAAAA,CAAS,QAAQ,mBAAmB,CAAC;GACzE,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAS,QAAQ,YAAY,CAAC;GAClE,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAS,IAAI,UAAU,CAAC;GACxD,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAS,IAAI,UAAU,CAAC;GACxD,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAS,IAAI,YAAY,CAAC;GAC9D,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,IAAI,SAAS,CAAC;GAGrD,CAAA,GAAA,MAAA,UAAA,OAAgB;IAEd,IAAI,EADe,IAAI,UAAU,MAAM,MAAM,IAAI,UAAU,MAAM,MAAM,IAAI,YAAY,MAAM,MAAM,IAAI,SAAS,MAAM,KACrG;IACjB,IAAI,YAAY;IAChB,MAAM,4CAA4C,CAAC,CAChD,MAAM,MAAM,EAAE,KAAK,EAAE,KAAK,IAAgG,IAAI,CAAC,CAC/H,MAAM,aAAa;KAClB,IAAI,aAAa,aAAa,MAAM;KACpC,IAAI,SAAS,aAAa,KAAA,KAAa,SAAS,aAAa,IAAI;MAC/D,YAAY,SAAS,QAAQ;MAC7B,oBAAoB,YAAY,SAAS,QAAQ;KACnD;KACA,IAAI,SAAS,aAAa,KAAA,KAAa,SAAS,aAAa,IAAI;MAC/D,YAAY,SAAS,QAAQ;MAC7B,oBAAoB,YAAY,SAAS,QAAQ;KACnD;KACA,IAAI,SAAS,eAAe,KAAA,KAAa,SAAS,eAAe,IAAI;MACnE,cAAc,SAAS,UAAU;MACjC,oBAAoB,cAAc,SAAS,UAAU;KACvD;KACA,IAAI,SAAS,YAAY,KAAA,KAAa,SAAS,YAAY,IAAI;MAC7D,WAAW,SAAS,OAAO;MAC3B,oBAAoB,WAAW,SAAS,OAAO;KACjD;IACF,CAAC,CAAC,CACD,YAAY,CAAe,CAAC;IAC/B,aAAa;KAAE,YAAY;IAAK;GAClC,GAAG,CAAC,CAAC;GAEL,MAAM,gBAAgB,KAAa,YAAkC,MAAqB;IACxF,OAAO,CAAC;IACR,oBAAoB,KAAK,CAAC;GAC5B;GACA,MAAM,cAAc,KAAa,YAAiC,MAAoB;IACpF,OAAO,CAAC;IACR,oBAAoB,KAAK,CAAC;GAC5B;GAEA,QAAA,GAAOC,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,qBAAqB,IAAA,GAE5DA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,mBAAmB,IAAA,GACnDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,0BAA0B,IAAA,GAC5DA,MAAAA,cAAAA,CAAc,SAAS;IACrB,MAAM;IACN,WAAW;IACX,SAAS;IACT,WAAW,MAAwC,aAAa,qBAAqB,aAAa,CAAC,CAAC,EAAE,OAAO,OAAO;GACtH,CAAC,IAAA,GACDA,MAAAA,cAAAA,CAAc,QAAQ,MAAM,WAAwD,CACtF,CACF,IAAA,GACAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,mBAAmB,IAAA,GACnDA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,0BAA0B,IAAA,GAC5DA,MAAAA,cAAAA,CAAc,SAAS;IACrB,MAAM;IACN,WAAW;IACX,SAAS;IACT,WAAW,MAAwC,aAAa,cAAc,aAAa,CAAC,CAAC,EAAE,OAAO,OAAO;GAC/G,CAAC,IAAA,GACDA,MAAAA,cAAAA,CAAc,QAAQ,MAAM,WAAwD,CACtF,CACF,IAAA,GAEAA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,mBAAmB,GAAG,QAAsC,IAAA,GAE9FA,MAAAA,cAAAA,CAAc,eAAe;IAC3B,OAAO;IACP;IACA,MAAM;IACN,OAAO;IACP,UAAU,WAAW,YAAY,WAAW;IAC5C,MAAM;GACR,CAAC,IAAA,GACDA,MAAAA,cAAAA,CAAc,eAAe;IAC3B,OAAO;IACP;IACA,MAAM;IACN,OAAO;IACP,UAAU,WAAW,YAAY,WAAW;IAC5C,MAAM;GACR,CAAC,IAAA,GACDA,MAAAA,cAAAA,CAAc,eAAe;IAC3B,OAAO;IACP;IACA,MAAM;IACN,OAAO;IACP,UAAU,WAAW,cAAc,aAAa;IAChD,MAAM;GACR,CAAC,IAAA,GACDA,MAAAA,cAAAA,CAAc,eAAe;IAC3B,OAAO;IACP;IACA,MAAM;IACN,OAAO;IACP,UAAU,WAAW,WAAW,UAAU;IAC1C,MAAM;GACR,CAAC,CACH;EACF;;EAGA,SAAS,cAAc,OAOT;GACZ,QAAA,GAAOA,MAAAA,cAAAA,CAAc,OAAO,EAAE,WAAW,0BAA0B,IAAA,GACjEA,MAAAA,cAAAA,CAAc,SAAS,EAAE,WAAW,qBAAqB,GAAG,MAAM,KAAK,IAAA,GACvEA,MAAAA,cAAAA,CAAc,YAAY;IACxB,OAAO,MAAM;IACb,MAAM,MAAM;IACZ,OAAO,MAAM;IACb,UAAU,MAAM;IAChB,MAAM,MAAM;GACd,CAAC,CACH;EACF;;;;;;;;;;;;ECjIA,MAAa,SAAS,CAAC,iBAAiB,UAAU;;EAGlD,SAAgB,MAAM,KAAoB;GAExC,IAAI,aACF,IAAI,cAAc,YAAY;IAC5B,IAAI;IACJ,aAAa;IACb,OAAO,UAAA,GAA4BC,MAAAA,cAAAA,CAAc,YAAY,EAAE,KAAK,CAAC;IACrE,OAAO;IACP,QAAQ;IACR,UAAU;KACR,eAAe;MACb;OAAE,KAAK;OAAqB,MAAM;OAAU,OAAO;MAAY;MAC/D;OAAE,KAAK;OAAc,MAAM;OAAU,OAAO;MAAY;MACxD;OAAE,KAAK;OAAY,MAAM;OAAQ,OAAO;OAAwB,MAAM;MAAgC;MACtG;OAAE,KAAK;OAAY,MAAM;OAAQ,OAAO;OAAc,MAAM;MAA4B;MACxF;OAAE,KAAK;OAAc,MAAM;OAAQ,OAAO;OAAa,MAAM;MAA4B;KAC3F;KACA,SAAS,WAAA,GAAqBA,MAAAA,cAAAA,CAAc,eAAe,KAAK;IAClE;IACA,YAAY,WAAA,GAAqBA,MAAAA,cAAAA,CAAc,aAAa,KAAK;GACnE,CAAC,GACD,0CACF;EACF"}
1
+ {"version":3,"file":"client.js","names":["createElement","TYPE_LABELS","useState","useMemo","createElement","API_BASE","createElement","useState","useRef","useCallback","API_BASE","createElement","useState","useRef","useCallback","fsTree","useState","useRef","createElement","createElement","useState","createElement","useState","useRef","useCallback","createElement","useState","createElement","createElement"],"sources":["../src/client/icons.tsx","../src/client/ConfigTree.tsx","../src/client/ansi.ts","../src/client/logHighlight.ts","../src/client/conversationDraft.ts","../src/client/utils.ts","../src/client/LogView.tsx","../src/client/LogHistoryView.tsx","../src/client/DirChooser.tsx","../src/client/PathPicker.tsx","../src/client/ConfigEditorModal.tsx","../src/client/styles.ts","../src/client/ServicesTab.tsx","../src/client/SettingsPanel.tsx","../src/client/index.tsx"],"sourcesContent":["/**\r\n * SVG icons for the starter plugin. Each icon is a React component that\r\n * takes a size prop and renders an inline SVG. Monochrome, currentColor fill.\r\n * @module dsh-better-sidebar-starter/client/icons\r\n */\r\n\r\nimport { createElement, type ReactNode } from 'react'\r\n\r\ninterface IconProps {\r\n size: number\r\n}\r\n\r\nfunction svg(size: number, pathChildren: string): ReactNode {\r\n const svgHtml = `<svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">${pathChildren}</svg>`\r\n return createElement('span', {\r\n style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: size, height: size },\r\n dangerouslySetInnerHTML: { __html: svgHtml },\r\n })\r\n}\r\n\r\n/** 🚀 Rocket — the tab icon. */\r\nexport function RocketIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M9.19 8.35c.34-.05.67-.1 1-.13 1.2-1.49 2.85-2.49 4.85-3.02 2-.53 4.16-.42 6.5.33.75 2.34.86 4.5.33 6.5-.53 2-1.53 3.65-3.02 4.85-.04.33-.08.66-.13 1-.13.87-.32 1.71-.57 2.5-.42 1.3-1.02 2.5-1.78 3.6-.42.6-1.1.96-1.86.96-.72 0-1.4-.34-1.84-.93l-2.06-2.76c-.2-.27-.5-.43-.82-.43H7.68c-.32 0-.62-.16-.82-.43l-2.06-2.76C4.36 19.34 4 18.66 4 17.94c0-.76.36-1.44.96-1.86 1.1-.76 2.3-1.36 3.6-1.78.79-.25 1.63-.44 2.5-.57l.13-1c.05-.34.1-.67.13-1z\" fill=\"currentColor\" opacity=\"0.85\"/><circle cx=\"15.5\" cy=\"8.5\" r=\"1.5\" fill=\"currentColor\" opacity=\"0.6\"/><path d=\"M5 14c-1 0-2 .5-2 2 0 1 .5 2 .5 2s1-.5 1.5-.5 1.5.5 1.5.5 1-.5 1-2-.5-2-2-2z\" fill=\"currentColor\" opacity=\"0.5\"/>`)\r\n}\r\n\r\n/** ▶ Play — start button. */\r\nexport function PlayIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M8 5v14l11-7z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ⏹ Stop — stop button. */\r\nexport function StopIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"1\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 🔄 Restart — restart button. */\r\nexport function RestartIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 5V2L7 7l5 5V8c2.76 0 5 2.24 5 5s-2.24 5-5 5-5-2.24-5-5H5c0 3.87 3.13 7 7 7s7-3.13 7-7-3.13-7-7-7z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ⚙ Gear — edit button. */\r\nexport function GearIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3l-1.97-.39c-.11-.37-.26-.72-.44-1.06l1.14-1.64c.26-.37.22-.88-.09-1.2l-1.28-1.28c-.32-.31-.83-.35-1.2-.09l-1.64 1.14c-.34-.18-.69-.33-1.06-.44L14.94 4c-.1-.42-.47-.72-.9-.72h-1.8c-.43 0-.8.3-.9.72l-.39 1.97c-.37.11-.72.26-1.06.44L7.25 5.27c-.37-.26-.88-.22-1.2.09L4.77 6.64c-.31.32-.35.83-.09 1.2l1.14 1.64c-.18.34-.33.69-.44 1.06L3.4 11c-.42.1-.72.47-.72.9v1.8c0 .43.3.8.72.9l1.97.39c.11.37.26.72.44 1.06l-1.14 1.64c-.26.37-.22.88.09 1.2l1.28 1.28c.32.31.83.35 1.2.09l1.64-1.14c.34.18.69.33 1.06.44l.39 1.97c.1.42.47.72.9.72h1.8c.43 0 .8-.3.9-.72l.39-1.97c.37-.11.72-.26 1.06-.44l1.64 1.14c.37.26.88.22 1.2-.09l1.28-1.28c.31-.32.35-.83.09-1.2l-1.14-1.64c.18-.34.33-.69.44-1.06L20.6 14c.42-.1.72-.47.72-.9v-1.8c0-.43-.3-.8-.72-.9z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 🗑 Trash — delete button. */\r\nexport function TrashIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ➕ Plus — add new config. */\r\nexport function PlusIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ✕ Close — modal cancel. */\r\nexport function CloseIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ◀ Back — history file list. */\r\nexport function BackIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 📄 File — history log entry. */\r\nexport function FileIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M6 2c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z\" fill=\"currentColor\" opacity=\"0.9\"/>`)\r\n}\r\n\r\n/** ⟳ Refresh — reload file list. */\r\nexport function RefreshIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M17.65 6.35A7.95 7.95 0 0 0 12 4a8 8 0 1 0 7.73 10h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ▾ Chevron down — tree expand. */\r\nexport function ChevronDownIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M7 10l5 5 5-5z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ▸ Chevron right — tree collapse. */\r\nexport function ChevronRightIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M10 7l5 5-5 5z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 🟢 Dot — running status. */\r\nexport function RunningDotIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<circle cx=\"12\" cy=\"12\" r=\"5\" fill=\"#4e9a06\"/>`)\r\n}\r\n\r\n/** ✅ Check — completed status. */\r\nexport function CheckIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\" fill=\"#4e9a06\"/>`)\r\n}\r\n\r\n/** 🔴 Error dot — failed status. */\r\nexport function ErrorDotIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<circle cx=\"12\" cy=\"12\" r=\"5\" fill=\"#cc0000\"/>`)\r\n}\r\n\r\n/** 📋 Log — download log. */\r\nexport function DownloadIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 📦 npm package icon. */\r\nexport function NpmIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M7 17V8h4v6h2V8h4v9h-4v-3h-2v3H7z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ☕ springboot icon. */\r\nexport function SpringIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 2C7.5 2 4 5.5 4 10c0 3 2 5.5 4.5 7 .5-2 1.5-3.5 3-4.5 1.5 1 2.5 2.5 3 4.5 2.5-1.5 4.5-4 4.5-7 0-4.5-3.5-8-7-8z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M8 10c1-1 2.5-1.5 4-1.5s3 .5 4 1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>`)\r\n}\r\n\r\n/** 🐍 python icon. */\r\nexport function PythonIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M12 4c-2 0-4 1-4 3v2h4v1H6c-1 0-2 1-2 3s1 3 2 3h2v-2c0-2 2-3 4-3h2c1 0 2-1 2-2V7c0-2-2-3-4-3z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"10.5\" cy=\"6.5\" r=\"0.8\" fill=\"currentColor\"/><path d=\"M12 20c2 0 4-1 4-3v-2h-4v-1h4c1 0 2-1 2-3s-1-3-2-3h-2v2c0 2-2 3-4 3h-2c-1 0-2 1-2 2v3c0 2 2 3 4 3z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"13.5\" cy=\"17.5\" r=\"0.8\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** ⚡ custom icon. */\r\nexport function CustomIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M11 21h-1l1-7H7.5c-.6 0-.8-.4-.5-.9 1.5-2.6 3.7-6.2 6.5-10.1h1l-1 7h3.5c.6 0 .8.4.5.9C14.5 14.7 12.3 18.1 11 21z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 📁 Folder — directory tree icon. */\r\nexport function FolderIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z\" fill=\"currentColor\" opacity=\"0.7\"/>`)\r\n}\r\n\r\n/** 📋 Clipboard — bulk paste env. */\r\nexport function ClipboardIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** 📄 Copy — duplicate a run config. */\r\nexport function CopyIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M21 8c0-1.1-.9-2-2-2h-9c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h9c1.1 0 2-.9 2-2V8z\" fill=\"currentColor\"/><path d=\"M3 16V4c0-1.1.9-2 2-2h10v2H5v12H3z\" fill=\"currentColor\" opacity=\"0.6\"/>`)\r\n}\r\n\r\n/** ✈️ Send — send selected text to the conversation composer. */\r\nexport function SendToChatIcon({ size }: IconProps): ReactNode {\r\n return svg(size, `<path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" fill=\"currentColor\"/>`)\r\n}\r\n\r\n/** Get the icon for a config type. */\r\nexport function typeIcon(type: string, size: number): ReactNode {\r\n switch (type) {\r\n case 'npm': return NpmIcon({ size })\r\n case 'springboot': return SpringIcon({ size })\r\n case 'python': return PythonIcon({ size })\r\n default: return CustomIcon({ size })\r\n }\r\n}\r\n","/**\r\n * Config tree: the left pane showing all run configurations grouped by type,\r\n * with running instances as child nodes.\r\n * @module dsh-better-sidebar-starter/client/ConfigTree\r\n */\r\n\r\nimport { createElement, useState, useMemo, type ReactNode } from 'react'\r\nimport type { RunConfig, RunInstance } from './types.ts'\r\nimport {\r\n PlayIcon, StopIcon, GearIcon, CopyIcon,\r\n ChevronDownIcon, ChevronRightIcon,\r\n RunningDotIcon, CheckIcon, ErrorDotIcon, TrashIcon,\r\n typeIcon,\r\n} from './icons.tsx'\r\n\r\n/** Props for the config tree. */\r\nexport interface ConfigTreeProps {\r\n configs: RunConfig[]\r\n instances: RunInstance[]\r\n selectedInstanceId: string | null\r\n selectedConfigId: string | null\r\n onSelectConfig: (configId: string) => void\r\n onSelectInstance: (instanceId: string) => void\r\n onStartConfig: (configId: string) => void\r\n onStopInstance: (instanceId: string) => void\r\n onEditConfig: (configId: string) => void\r\n /** Duplicate a config: opens the editor prefilled with all its info under a new name. */\r\n onDuplicateConfig: (configId: string) => void\r\n /** Delete a config (stops its running instances; asks for confirmation in the parent). */\r\n onDeleteConfig: (configId: string) => void\r\n}\r\n\r\n/** Type group order. */\r\nconst TYPE_ORDER: string[] = ['npm', 'springboot', 'python', 'custom']\r\nconst TYPE_LABELS: Record<string, string> = {\r\n npm: 'npm', springboot: 'Spring Boot', python: 'Python', custom: 'Custom',\r\n}\r\n\r\n/** Status icon for an instance. */\r\nfunction statusIcon(status: string, size: number): ReactNode {\r\n switch (status) {\r\n case 'running': return RunningDotIcon({ size })\r\n case 'exited': return CheckIcon({ size })\r\n case 'killed':\r\n case 'error': return ErrorDotIcon({ size })\r\n default: return null\r\n }\r\n}\r\n\r\n/** The config tree component. */\r\nexport function ConfigTree(props: ConfigTreeProps): ReactNode {\r\n const { configs, instances, selectedInstanceId, selectedConfigId,\r\n onSelectConfig, onSelectInstance, onStartConfig, onStopInstance, onEditConfig, onDuplicateConfig,\r\n onDeleteConfig } = props\r\n\r\n const [collapsed, setCollapsed] = useState<Set<string>>(new Set())\r\n\r\n const toggleGroup = (type: string): void => {\r\n setCollapsed((prev) => {\r\n const next = new Set(prev)\r\n if (next.has(type)) next.delete(type)\r\n else next.add(type)\r\n return next\r\n })\r\n }\r\n\r\n // Group configs by type, ordered by TYPE_ORDER, sorted by lastRunAt desc.\r\n const groups = useMemo(() => {\r\n const map = new Map<string, RunConfig[]>()\r\n for (const config of configs) {\r\n const arr = map.get(config.type) ?? []\r\n arr.push(config)\r\n map.set(config.type, arr)\r\n }\r\n for (const arr of map.values()) {\r\n arr.sort((a, b) => (b.lastRunAt ?? 0) - (a.lastRunAt ?? 0))\r\n }\r\n return TYPE_ORDER\r\n .filter((type) => map.has(type) && map.get(type)!.length > 0)\r\n .map((type) => ({ type, items: map.get(type)! }))\r\n }, [configs])\r\n\r\n if (configs.length === 0) {\r\n return createElement('div', { className: 'sts-empty' },\r\n '暂无运行配置',\r\n createElement('br'),\r\n '点击右上角 + 新建配置',\r\n )\r\n }\r\n\r\n return createElement('div', { className: 'sts-tree' },\r\n ...groups.map((group) => {\r\n const isCollapsed = collapsed.has(group.type)\r\n const label = TYPE_LABELS[group.type] ?? group.type\r\n const runningCount = instances.filter(\r\n (inst) => inst.configType === group.type && inst.status === 'running',\r\n ).length\r\n\r\n return createElement('div', { key: group.type },\r\n // Group header\r\n createElement('div', {\r\n className: 'sts-group-header',\r\n onClick: () => toggleGroup(group.type),\r\n },\r\n isCollapsed\r\n ? createElement(ChevronRightIcon, { size: 12 })\r\n : createElement(ChevronDownIcon, { size: 12 }),\r\n typeIcon(group.type, 14),\r\n label,\r\n createElement('span', { className: 'sts-group-count' },\r\n ` (${group.items.length}${runningCount > 0 ? `, ${runningCount} 运行中` : ''})`,\r\n ),\r\n ),\r\n // Group children\r\n createElement('div', {\r\n className: `sts-group-children${isCollapsed ? ' collapsed' : ''}`,\r\n },\r\n ...group.items.flatMap((config) => {\r\n const configInstances = instances.filter((inst) => inst.configId === config.id)\r\n const isRunning = configInstances.some((inst) => inst.status === 'running')\r\n const isSelected = selectedConfigId === config.id && selectedInstanceId === null\r\n\r\n const rows: ReactNode[] = [\r\n // Config row\r\n createElement('div', {\r\n key: config.id,\r\n className: `sts-config-row${isSelected ? ' selected' : ''}${isRunning ? ' running' : ''}`,\r\n onClick: () => onSelectConfig(config.id),\r\n },\r\n isRunning\r\n ? RunningDotIcon({ size: 12 })\r\n : typeIcon(config.type, 12),\r\n createElement('div', { className: 'sts-config-name' }, config.name),\r\n createElement('div', { className: 'sts-config-cmd' }, config.command),\r\n createElement('div', { className: 'sts-config-actions' },\r\n isRunning\r\n ? createElement('button', {\r\n className: 'sts-icon-btn stop',\r\n title: '停止全部',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n for (const inst of configInstances) {\r\n if (inst.status === 'running') onStopInstance(inst.id)\r\n }\r\n },\r\n }, StopIcon({ size: 14 }))\r\n : createElement('button', {\r\n className: 'sts-icon-btn play',\r\n title: '启动',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onStartConfig(config.id)\r\n },\r\n }, PlayIcon({ size: 14 })),\r\n createElement('button', {\r\n className: 'sts-icon-btn gear',\r\n title: '编辑',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onEditConfig(config.id)\r\n },\r\n }, GearIcon({ size: 12 })),\r\n createElement('button', {\r\n className: 'sts-icon-btn dup',\r\n title: '复制(生成副本配置)',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onDuplicateConfig(config.id)\r\n },\r\n }, CopyIcon({ size: 12 })),\r\n createElement('button', {\r\n className: 'sts-icon-btn trash',\r\n title: '删除配置',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onDeleteConfig(config.id)\r\n },\r\n }, TrashIcon({ size: 12 })),\r\n ),\r\n ),\r\n ]\r\n\r\n // Instance rows (children)\r\n for (const inst of configInstances) {\r\n const instSelected = selectedInstanceId === inst.id\r\n rows.push(\r\n createElement('div', {\r\n key: inst.id,\r\n className: `sts-instance-row${instSelected ? ' selected' : ''}`,\r\n onClick: () => onSelectInstance(inst.id),\r\n },\r\n statusIcon(inst.status, 10),\r\n `#${inst.id.slice(0, 8)}`,\r\n createElement('span', { className: 'sts-instance-pid' },\r\n inst.status === 'running' ? ` pid:${inst.pid}` : ` ${inst.status}`,\r\n ),\r\n inst.status === 'running'\r\n ? createElement('button', {\r\n className: 'sts-icon-btn stop',\r\n title: '停止',\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onStopInstance(inst.id)\r\n },\r\n }, StopIcon({ size: 12 }))\r\n : null,\r\n ),\r\n )\r\n }\r\n\r\n return rows\r\n }),\r\n ),\r\n )\r\n }),\r\n )\r\n}\r\n","/**\n * ANSI escape code parser: converts raw terminal output with ANSI SGR\n * sequences into an array of styled text segments for React rendering.\n * Covers the common color codes (30-37, 40-47, 90-97, 100-107, bold, dim,\n * italic, underline, reset). Non-SGR escape sequences are stripped.\n * @module dsh-better-sidebar-starter/client/ansi\n */\n\n/** One styled text segment. */\nexport interface AnsiSegment {\n text: string\n bold: boolean\n dim: boolean\n italic: boolean\n underline: boolean\n fg: number | null\n bg: number | null\n}\n\ninterface ParseState {\n bold: boolean\n dim: boolean\n italic: boolean\n underline: boolean\n fg: number | null\n bg: number | null\n}\n\nfunction cloneState(s: ParseState): AnsiSegment {\n return {\n text: '',\n bold: s.bold,\n dim: s.dim,\n italic: s.italic,\n underline: s.underline,\n fg: s.fg,\n bg: s.bg,\n }\n}\n\n/** Apply one SGR parameter to the parse state. */\nfunction applySgr(state: ParseState, code: number): void {\n if (code === 0) {\n state.bold = false; state.dim = false; state.italic = false\n state.underline = false; state.fg = null; state.bg = null\n } else if (code === 1) {\n state.bold = true\n } else if (code === 2) {\n state.dim = true\n } else if (code === 3) {\n state.italic = true\n } else if (code === 4) {\n state.underline = true\n } else if (code === 22) {\n state.bold = false; state.dim = false\n } else if (code === 23) {\n state.italic = false\n } else if (code === 24) {\n state.underline = false\n } else if (code >= 30 && code <= 37) {\n state.fg = code - 30\n } else if (code === 39) {\n state.fg = null\n } else if (code >= 40 && code <= 47) {\n state.bg = code - 40\n } else if (code === 49) {\n state.bg = null\n } else if (code >= 90 && code <= 97) {\n state.fg = code - 90 + 8\n } else if (code >= 100 && code <= 107) {\n state.bg = code - 100 + 8\n }\n}\n\n/**\n * Parse a string with ANSI escape codes into styled segments.\n * Returns an array of AnsiSegment objects ready for React rendering.\n */\nexport function parseAnsi(input: string): AnsiSegment[] {\n const state: ParseState = {\n bold: false, dim: false, italic: false, underline: false, fg: null, bg: null,\n }\n const segments: AnsiSegment[] = []\n let current = cloneState(state)\n\n // Regex: match CSI sequences (ESC [ ... letter) and non-CSI text.\n const regex = /\\x1b\\[([0-9;]*)m|\\x1b\\][^\\x07]*\\x07|\\x1b\\[[0-9;]*[A-Za-z]/g\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(input)) !== null) {\n // Text before this escape sequence.\n if (match.index > lastIndex) {\n current.text += input.slice(lastIndex, match.index)\n }\n // Process the escape sequence.\n if (match[1] !== undefined) {\n // SGR sequence: ESC [ ... m\n if (current.text !== '') {\n segments.push(current)\n }\n const params = match[1] === '' ? '0' : match[1]\n for (const codeStr of params.split(';')) {\n const code = codeStr === '' ? 0 : Number(codeStr)\n if (!Number.isNaN(code)) applySgr(state, code)\n }\n current = cloneState(state)\n }\n // Non-SGR sequences (OSC, other CSI) are simply stripped.\n lastIndex = regex.lastIndex\n }\n\n // Remaining text after the last escape.\n if (lastIndex < input.length) {\n current.text += input.slice(lastIndex)\n }\n if (current.text !== '') {\n segments.push(current)\n }\n\n return segments\n}\n\n/** Map an ANSI color index (0-15) to a CSS color string. */\nconst ANSI_COLORS: readonly string[] = [\n // 0-7: standard colors\n '#000000', '#cc0000', '#4e9a06', '#c4a000', '#3465a4', '#75507b', '#06989a', '#d3d7cf',\n // 8-15: bright colors\n '#555753', '#ef2929', '#8ae234', '#fce94f', '#729fcf', '#ad7fa8', '#34e2e2', '#eeeeec',\n]\n\n/** Resolve a segment's foreground color to a CSS string, or null for default. */\nexport function segmentFgColor(seg: AnsiSegment): string | null {\n if (seg.fg === null) return null\n return ANSI_COLORS[seg.fg] ?? null\n}\n\n/** Resolve a segment's background color to a CSS string, or null for default. */\nexport function segmentBgColor(seg: AnsiSegment): string | null {\n if (seg.bg === null) return null\n return ANSI_COLORS[seg.bg] ?? null\n}\n","/**\n * Log syntax highlighting: tokenizes plain log text (Java / Spring Boot\n * style, but harmless for npm/python output) into colored segments —\n * timestamps, log levels, thread names, fully-qualified class names,\n * exception markers and stack frames. Applied AFTER ANSI parsing, only to\n * text the program did not color itself.\n * @module dsh-better-sidebar-starter/client/logHighlight\n */\n\n/** One highlighted span. */\nexport interface HighlightSegment {\n text: string\n color?: string\n bold?: boolean\n /** Source-code navigation target (makes the span clickable). */\n link?: LogCodeLink\n}\n\n/** A clickable source reference parsed out of the log line. */\nexport interface LogCodeLink {\n kind: 'class' | 'frame' | 'location'\n /** Fully-qualified class name (dots), e.g. com.example.service.UserService. */\n className?: string\n /** Source file base name, e.g. UserService.java. */\n file?: string\n /** Method name from a stack frame, e.g. getUser ('' for constructors). */\n method?: string\n /** 1-based line (from a stack frame / (File.java:45) token). */\n line?: number\n}\n\n// Palette chosen for dark backgrounds (mirrors typical IDE console themes).\nconst C = {\n ts: '#8f8f98', // timestamp — muted grey\n thread: '#b48ead', // [thread-name] — purple\n levelInfo: '#6ab04c', // INFO — green\n levelDebug: '#7a9ce8',// DEBUG — blue\n levelWarn: '#e2b93d', // WARN — yellow\n levelError: '#f0625f',// ERROR/FATAL — red\n clazz: '#61afef', // com.example.Foo — light blue\n exception: '#ff5f56', // *Exception / Caused by: — red\n frame: '#c678dd', // at com.foo.Bar(...) — violet\n location: '#56b6c2', // (Foo.java:123) — cyan\n}\n\n/** Log level → color. */\nfunction levelColor(level: string): string {\n switch (level) {\n case 'DEBUG':\n case 'TRACE': return C.levelDebug\n case 'INFO': return C.levelInfo\n case 'WARN': return C.levelWarn\n case 'ERROR':\n case 'FATAL':\n case 'OFF': return C.levelError\n default: return C.levelInfo\n }\n}\n\n/** One-pass tokenizer: timestamps, levels, threads, exceptions, stacks, classes. */\nconst TOKEN_RE = new RegExp([\n // 2026-08-20T17:44:42.123+08:00 | 2026-08-20 17:44:42,123\n '(?<ts>\\\\d{4}-\\\\d{2}-\\\\d{2}[T ]\\\\d{2}:\\\\d{2}:\\\\d{2}[.,]\\\\d{1,6}(?:Z|[+-]\\\\d{2}:?\\\\d{2})?)',\n // DEBUG / INFO / WARN / ERROR / ...\n '|(?<level>\\\\b(?:DEBUG|INFO|WARN|ERROR|TRACE|FATAL|OFF)\\\\b)',\n // [http-nio-8080-exec-1]\n '|(?<thread>\\\\[[^\\\\]\\\\r\\\\n]+\\\\])',\n // Caused by: / SQLException / NullPointerException\n '|(?<exception>\\\\bCaused by:|[A-Z]\\\\w*(?:Exception|Error)(?:\\\\b|:\\\\s))',\n // at com.foo.Bar.method(Foo.java:12) — whole stack frame\n '|(?<frame>\\\\s+at\\\\s+(?<target>[\\\\w$./<>]+)(?:\\\\((?<loc>[^)]*)\\\\))?)',\n // (Foo.java:123:45)\n '|(?<location>\\\\((?<lfile>[\\\\w$]+\\\\.(?:java|kt|groovy|scala)):(?<lline>\\\\d+)(?::\\\\d+)?\\\\))',\n // pkg.Cls.method(...) — a method CALL in the message text\n '|(?<call>(?<callCls>[A-Za-z_$][\\\\w$]*(?:\\\\.[A-Za-z_$][\\\\w$]*)+)\\\\.(?<callMethod>[A-Za-z_$][\\\\w$]*)\\\\s*\\\\()',\n // com.example.SomeClass (FQCN)\n '|(?<clazz>[A-Za-z_$][\\\\w$]*(?:\\\\.[A-Za-z_$][\\\\w$]*)+\\\\b)',\n].join(''), 'g')\n\n/** The last segment must look like a class name (starts uppercase); field\n * chains such as System.out are skipped. */\nfunction looksLikeClassName(fqcn: string): boolean {\n const last = fqcn.split('.').pop() ?? ''\n return last !== '' && /^[A-Z]/.test(last)\n}\n\n/** Split a stack-frame target `pkg.Cls.method` into class + method. */\nfunction parseFrameTarget(target: string): { className: string; method: string } {\n const dot = target.lastIndexOf('.')\n if (dot > 0 && dot < target.length - 1) {\n return { className: target.slice(0, dot), method: target.slice(dot + 1) }\n }\n return { className: target, method: '' }\n}\n\n/** Parse a `File.java:45` (or `File.java:45:12`) location string. */\nfunction parseFrameLoc(loc: string | undefined): { file?: string; line?: number } {\n if (loc === undefined || loc.trim() === '') return {}\n const parts = loc.trim().split(':')\n if (parts.length < 2) return {}\n const line = Number(parts[1])\n return { file: parts[0], line: Number.isInteger(line) && line > 0 ? line : undefined }\n}\n\n/**\n * Tokenize a single log line into highlighted segments.\n * Returns one segment per token; the rest stays plain.\n */\nexport function highlightLogText(text: string): HighlightSegment[] {\n const out: HighlightSegment[] = []\n let last = 0\n TOKEN_RE.lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = TOKEN_RE.exec(text)) !== null) {\n if (m.index > last) out.push({ text: text.slice(last, m.index) })\n const g = m.groups ?? {}\n if (g.ts !== undefined) {\n out.push({ text: m[0], color: C.ts })\n } else if (g.level !== undefined) {\n out.push({ text: m[0], color: levelColor(g.level), bold: true })\n } else if (g.thread !== undefined) {\n out.push({ text: m[0], color: C.thread })\n } else if (g.exception !== undefined) {\n out.push({ text: m[0], color: C.exception, bold: true })\n } else if (g.frame !== undefined) {\n const { className, method } = parseFrameTarget(g.target ?? '')\n const { file, line } = parseFrameLoc(g.loc)\n out.push({\n text: m[0],\n color: C.frame,\n link: { kind: 'frame', className, method, file, line },\n })\n } else if (g.location !== undefined) {\n out.push({\n text: m[0],\n color: C.location,\n link: {\n kind: 'location',\n file: g.lfile,\n line: Number(g.lline),\n },\n })\n } else if (g.call !== undefined) {\n const className = g.callCls ?? ''\n out.push({\n text: m[0],\n color: C.clazz,\n link: looksLikeClassName(className)\n ? { kind: 'class', className, method: g.callMethod ?? '' }\n : undefined,\n })\n } else if (g.clazz !== undefined) {\n const className = g.clazz\n out.push({\n text: m[0],\n color: C.clazz,\n link: looksLikeClassName(className) ? { kind: 'class', className } : undefined,\n })\n } else {\n out.push({ text: m[0] })\n }\n last = m.index + m[0].length\n }\n if (last < text.length) out.push({ text: text.slice(last) })\n return out\n}","/**\n * Send text into the DSH conversation composer draft. Mirrors the internal\n * `appendToDraft` of dsh-better-sidebar (not exported from that package):\n * the shared path behind the explorer's @-reference button / viewer\n * selection popup. The conversation service is resolved lazily through\n * `ctx.get`; a missing service, session scope, or activity scope degrades\n * to a logged no-op — never a crash.\n * @module dsh-better-sidebar-starter/client/conversationDraft\n */\n\nimport type { Context } from 'cordis'\n\n/** Minimal structural face of the composer input handle we touch. */\ninterface DraftInputLike {\n state: { getSnapshot(): { draft?: string } }\n setDraft(draft: string): void\n}\n\n/** Minimal structural face of the ui-conversation service (composer). */\ninterface ConversationLike {\n input: { for(actx: unknown): DraftInputLike | undefined }\n}\n\n/** Structural face of the client runtime sessions service. */\ninterface SessionsLike {\n scope(sessionId: string): unknown\n}\n\n/**\n * Append `text` to the session's composer draft (space-separated, like the\n * @-mentions). Returns false — and warns — when the conversation service\n * or the session scope is unavailable.\n */\nexport function appendToDraft(ctx: Context, sessionId: string, text: string): boolean {\n try {\n const sessions = (ctx as { sessions?: SessionsLike }).sessions\n const actx = sessions?.scope(sessionId)\n if (actx === undefined) return false\n const getService = (ctx as unknown as { get: (name: string) => unknown }).get.bind(ctx)\n const conversation = getService('conversation') as ConversationLike | undefined\n if (conversation === undefined || conversation === null) return false\n const input = conversation.input.for(actx)\n if (input === undefined) return false\n const draft = input.state.getSnapshot().draft ?? ''\n input.setDraft(draft.trim() === '' ? text : `${draft} ${text}`)\n return true\n } catch (error) {\n console.warn('[dsh-better-sidebar-starter] draft insert failed:', error)\n return false\n }\n}","/**\n * Shared utility functions for the starter plugin.\n * @module dsh-better-sidebar-starter/client/utils\n */\n\nlet toastEl: HTMLDivElement | null = null\nlet toastTimer: ReturnType<typeof setTimeout> | null = null\n\n/** Show a small floating toast notification. */\nexport function showToast(message: string, success: boolean): void {\n // Dismiss previous toast.\n if (toastTimer !== null) {\n clearTimeout(toastTimer)\n toastTimer = null\n }\n if (toastEl !== null) {\n document.body.removeChild(toastEl)\n toastEl = null\n }\n\n const el = document.createElement('div')\n el.textContent = message\n el.style.cssText = `\n position: fixed;\n top: 16px;\n left: 50%;\n transform: translateX(-50%);\n padding: 8px 16px;\n background: ${success ? 'rgba(78,154,6,0.9)' : 'rgba(235,87,87,0.9)'};\n color: #fff;\n font-size: 12px;\n border-radius: 6px;\n z-index: 999999;\n box-shadow: 0 2px 12px rgba(0,0,0,0.4);\n pointer-events: none;\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\n `\n document.body.appendChild(el)\n toastEl = el\n toastTimer = setTimeout(() => {\n if (toastEl !== null) {\n document.body.removeChild(toastEl)\n toastEl = null\n }\n toastTimer = null\n }, 2000)\n}\n\n/** Copy text to clipboard with fallback for sandboxed iframes. Shows toast feedback. */\nexport function copyToClipboard(text: string): boolean {\n try {\n // Try modern clipboard API.\n if (navigator.clipboard && (window as any).clipboardEvent !== undefined) {\n void navigator.clipboard.writeText(text)\n showToast('已复制到剪贴板', true)\n return true\n }\n } catch { /* fallback below */ }\n\n // Fallback: create textarea, select, execCommand.\n try {\n const ta = document.createElement('textarea')\n ta.value = text\n ta.style.position = 'fixed'\n ta.style.left = '-9999px'\n ta.style.top = '0'\n ta.style.opacity = '0'\n ta.style.width = '1px'\n ta.style.height = '1px'\n document.body.appendChild(ta)\n ta.focus()\n ta.select()\n const ok = document.execCommand('copy')\n document.body.removeChild(ta)\n if (ok) {\n showToast('已复制到剪贴板', true)\n return true\n }\n } catch { /* last resort below */ }\n\n // Last resort.\n try {\n void navigator.clipboard?.writeText(text)\n showToast('已复制到剪贴板', true)\n return true\n } catch {\n showToast('复制失败', false)\n return false\n }\n}","/**\r\n * Log view: the right pane showing real-time log output from a running\r\n * instance. Connects to the WebSocket endpoint, parses ANSI colors, and\r\n * renders log lines with auto-scroll.\r\n * @module dsh-better-sidebar-starter/client/LogView\r\n */\r\n\r\nimport { createElement, useState, useEffect, useRef, useCallback, type ReactNode } from 'react'\r\nimport type { Context } from 'cordis'\r\nimport type { LogEntry, RunInstance } from './types.ts'\r\nimport type { WsMessage } from './types.ts'\r\nimport { parseAnsi, segmentFgColor, segmentBgColor, type AnsiSegment } from './ansi.ts'\r\nimport { highlightLogText, type LogCodeLink } from './logHighlight.ts'\r\nimport { StopIcon, RestartIcon, TrashIcon, SendToChatIcon } from './icons.tsx'\r\nimport { appendToDraft } from './conversationDraft.ts'\r\nimport { showToast } from './utils.ts'\r\n\r\n/** API base. */\r\nconst API_BASE = '/api/dsh-better-sidebar-starter'\r\n\r\n/** Structural face of ctx.betterSidebar (openFile only). */\r\ninterface BetterSidebarLike {\r\n openFile(scope: { sessionId: string; cwd?: string }, path: string, title?: string): void\r\n}\r\n\r\n/** Props for the log view. */\r\nexport interface LogViewProps {\r\n instance: RunInstance\r\n /** The sidebar tab's cordis context (used to reach the conversation service). */\r\n ctx: Context\r\n /** The session to send selected text into. */\r\n sessionId: string\r\n /** Session working directory (for locating source files / openFile scope). */\r\n cwd?: string\r\n onStop: () => void\r\n onRestart: () => void\r\n}\r\n\r\n/** One rendered log line. */\r\nexport interface LogLine {\r\n entries: LogEntry[]\r\n key: number\r\n}\r\n\r\n/** Structural face of ctx.betterSidebar (openFile only). */\r\ninterface BetterSidebarLike {\r\n openFile(scope: { sessionId: string; cwd?: string }, path: string, title?: string): void\r\n}\r\n\r\n/** Open the source behind a log token via the locate-source API. Shared by the\r\n * live LogView and the persisted LogHistoryView. `root` is the locating root\r\n * (live instance cwd, or the session workspace for history logs). */\r\nexport async function handleLogCodeClick(\r\n ctx: Context,\r\n sessionId: string,\r\n cwd: string | undefined,\r\n root: string | undefined,\r\n link: LogCodeLink,\r\n): Promise<void> {\r\n const payload: Record<string, unknown> = {}\r\n if (root !== undefined && root !== '') payload.root = root\r\n if (link.kind === 'class' || link.kind === 'frame') payload.className = link.className\r\n if (link.file !== undefined) payload.file = link.file\r\n if (link.method !== undefined && link.method !== '<init>' && link.method !== '<clinit>') payload.method = link.method\r\n if (link.line !== undefined) payload.line = link.line\r\n try {\r\n // The host resolves the session cwd from the query — sessionId is required.\r\n const params = new URLSearchParams({ sessionId })\r\n if (cwd !== undefined && cwd !== '') params.set('cwd', cwd)\r\n const resp = await fetch(`${API_BASE}/locate-source?${params.toString()}`, {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json' },\r\n body: JSON.stringify(payload),\r\n })\r\n if (!resp.ok) {\r\n // Surface the host's error message (e.g. missing sessionId) instead\r\n // of a generic failure.\r\n let reason = '定位源码失败'\r\n try {\r\n const err = await resp.json() as { error?: string }\r\n if (typeof err.error === 'string' && err.error !== '') reason = err.error\r\n } catch { /* non-JSON error body — keep generic message */ }\r\n showToast(reason, false)\r\n return\r\n }\r\n const data = await resp.json() as { ok: boolean; path?: string; line?: number; decompiled?: boolean; reason?: string }\r\n if (data.ok !== true || typeof data.path !== 'string') {\r\n showToast(data.reason ?? '未找到源文件', false)\r\n return\r\n }\r\n const base = data.path.split(/[\\\\/]/).pop() ?? data.path\r\n const line = typeof data.line === 'number' ? data.line : undefined\r\n const decompiled = data.decompiled === true\r\n const badge = decompiled ? ' [反编译]' : ''\r\n const title = line !== undefined ? `${base} : ${line}${badge}` : `${base}${badge}`\r\n const betterSidebar = (ctx as { betterSidebar?: BetterSidebarLike }).betterSidebar\r\n // Open in the session's sidebar editor.\r\n if (betterSidebar !== undefined) {\r\n betterSidebar.openFile({ sessionId, cwd }, data.path, title)\r\n }\r\n // Copy \"File.java:line\" so the user can jump in an unsupported editor.\r\n try {\r\n void navigator.clipboard?.writeText(`${base}${line !== undefined ? `:${line}` : ''}`)\r\n } catch { /* clipboard unavailable — ignore */ }\r\n showToast(\r\n line !== undefined\r\n ? `已打开 ${base},定位到第 ${line} 行(已复制 ${base}:${line})${decompiled ? '(反编译产物)' : ''}`\r\n : decompiled\r\n ? `已打开反编译产物 ${base}`\r\n : `已打开 ${base}`,\r\n true,\r\n )\r\n } catch {\r\n showToast('定位源码失败', false)\r\n }\r\n}\r\n\r\n/** Render ANSI-colored text segments, applying Java-log syntax highlighting\r\n * to text the program did not color itself (ANSI-colored spans keep the\r\n * program's own colors). Tokens with a source link become clickable. */\r\n/** Split plain text into plain/url parts. Trailing punctuation that is\r\n * almost certainly sentence text (.,;:)])。,) stays outside the link. */\r\nconst URL_RE = /https?:\\/\\/[^\\s\"'<>()\\[\\]{}]+/g\r\n\r\nfunction splitUrlParts(text: string): Array<{ text: string; url?: string }> {\r\n const out: Array<{ text: string; url?: string }> = []\r\n let last = 0\r\n for (const m of text.matchAll(URL_RE)) {\r\n const idx = m.index ?? 0\r\n if (idx > last) out.push({ text: text.slice(last, idx) })\r\n let url = m[0]\r\n let trail = ''\r\n while (url.length > 0 && /[.,;:)\\]}》」),。;:]$/.test(url)) {\r\n trail = url.slice(-1) + trail\r\n url = url.slice(0, -1)\r\n }\r\n if (url !== '') out.push({ text: url, url })\r\n if (trail !== '') out.push({ text: trail })\r\n last = idx + m[0].length\r\n }\r\n if (last < text.length) out.push({ text: text.slice(last) })\r\n return out\r\n}\r\n\r\nexport function renderAnsiLine(text: string, keyBase: string, onLinkClick: (link: LogCodeLink) => void): ReactNode {\r\n const segments = parseAnsi(text)\r\n const nodes: ReactNode[] = []\r\n if (segments.length === 0) return nodes\r\n let hlCounter = 0\r\n\r\n // Stitch the whole line into one plain string FIRST: Vite et al. split\r\n // their URLs across color runs (\"http://ip:\" + BOLD \"5173\" + \"/\"), so\r\n // per-segment detection loses the port. URLs are matched on the full\r\n // line, then mapped back onto the styled segments they overlap.\r\n let fullText = ''\r\n const ranges: Array<{ seg: AnsiSegment; start: number; end: number }> = []\r\n for (const seg of segments) {\r\n ranges.push({ seg, start: fullText.length, end: fullText.length + seg.text.length })\r\n fullText += seg.text\r\n }\r\n\r\n const parts = splitUrlParts(fullText)\r\n let offset = 0\r\n for (const part of parts) {\r\n const pStart = offset\r\n const pEnd = offset + part.text.length\r\n offset = pEnd\r\n for (const r of ranges) {\r\n const s = Math.max(r.start, pStart)\r\n const e = Math.min(r.end, pEnd)\r\n if (s >= e) continue\r\n const slice = fullText.slice(s, e)\r\n const seg = r.seg\r\n // Style carried from the ANSI segment (bold/dim/italic/underline).\r\n const style: Record<string, string> = {}\r\n if (seg.bold) style.fontWeight = 'bold'\r\n if (seg.dim) style.opacity = '0.6'\r\n if (seg.italic) style.fontStyle = 'italic'\r\n if (seg.underline) style.textDecoration = 'underline'\r\n const fg = segmentFgColor(seg)\r\n const bg = segmentBgColor(seg)\r\n if (fg !== null) style.color = fg\r\n if (bg !== null) style.backgroundColor = bg\r\n\r\n if (part.url !== undefined) {\r\n const url = part.url\r\n nodes.push(createElement('span', {\r\n key: `${keyBase}-u${hlCounter++}`,\r\n className: 'sts-log-url',\r\n style: { ...style, textDecoration: 'underline', cursor: 'pointer' },\r\n title: `在浏览器打开 ${url}`,\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation()\r\n try { window.open(url, '_blank', 'noopener') } catch { /* popup blocked */ }\r\n },\r\n }, slice))\r\n } else if (fg !== null || bg !== null) {\r\n // Program-colored text: keep the ANSI colors as-is.\r\n nodes.push(createElement('span', { key: `${keyBase}-c${hlCounter++}`, style }, slice))\r\n } else {\r\n // Uncolored text runs through the log syntax highlighter.\r\n const hl = highlightLogText(slice)\r\n for (const h of hl) {\r\n const hStyle: Record<string, string> = { ...style }\r\n if (h.color !== undefined) hStyle.color = h.color\r\n if (h.bold === true) hStyle.fontWeight = 'bold'\r\n nodes.push(createElement('span', {\r\n key: `${keyBase}-hl${hlCounter++}`,\r\n className: h.link !== undefined ? 'sts-log-link' : undefined,\r\n style: hStyle,\r\n onClick: h.link !== undefined\r\n ? (e: MouseEvent) => {\r\n e.stopPropagation()\r\n onLinkClick(h.link!)\r\n }\r\n : undefined,\r\n }, h.text))\r\n }\r\n }\r\n }\r\n }\r\n return nodes\r\n}\r\n\r\n/** The log view component. */\r\nexport function LogView({ instance, ctx, sessionId, cwd, onStop, onRestart }: LogViewProps): ReactNode {\r\n const [logLines, setLogLines] = useState<LogLine[]>([])\r\n const [autoScroll, setAutoScroll] = useState(true)\r\n const [exitInfo, setExitInfo] = useState<{ status: string; exitCode: number | null } | null>(null)\r\n const [selLength, setSelLength] = useState(0)\r\n const contentRef = useRef<HTMLDivElement>(null)\r\n const wsRef = useRef<WebSocket | null>(null)\r\n const lineKeyCounter = useRef(0)\r\n\r\n // Track the length of the current text selection inside this panel.\r\n useEffect(() => {\r\n const updateSel = (): void => {\r\n const sel = window.getSelection()\r\n const len = sel !== null && !sel.isCollapsed ? sel.toString().trim().length : 0\r\n setSelLength(len)\r\n }\r\n document.addEventListener('selectionchange', updateSel)\r\n return () => document.removeEventListener('selectionchange', updateSel)\r\n }, [])\r\n\r\n /** Send the currently selected log text into the conversation composer. */\r\n const handleSendSelection = (): void => {\r\n const sel = window.getSelection()\r\n const text = sel !== null && !sel.isCollapsed ? sel.toString().trim() : ''\r\n if (text === '') {\r\n showToast('请先在日志中选中文本', false)\r\n return\r\n }\r\n if (appendToDraft(ctx, sessionId, text)) {\r\n showToast(`已将选中文本(${text.length} 字符)发送到对话`, true)\r\n } else {\r\n showToast('发送失败:对话服务不可用', false)\r\n }\r\n }\r\n\r\n // Buffer accumulation: accumulate text entries into complete lines.\r\n const lineBuffer = useRef<{ current: LogEntry[] }>({ current: [] })\r\n\r\n /** Flush the buffer into a new log line. */\r\n const flushLine = useCallback((): void => {\r\n const entries = lineBuffer.current.current\r\n if (entries.length === 0) return\r\n lineBuffer.current.current = []\r\n setLogLines((prev) => {\r\n const key = lineKeyCounter.current++\r\n // Cap at 5000 lines to avoid memory issues.\r\n const next = [...prev, { entries, key }]\r\n if (next.length > 5000) next.splice(0, next.length - 5000)\r\n return next\r\n })\r\n }, [])\r\n\r\n /** Add an entry to the current line buffer, splitting on newlines. */\r\n const addEntry = useCallback((entry: LogEntry): void => {\r\n const parts = entry.text.split('\\n')\r\n for (let i = 0; i < parts.length; i++) {\r\n if (i > 0) {\r\n // Newline boundary: flush the current line.\r\n flushLine()\r\n }\r\n if (parts[i] !== '') {\r\n lineBuffer.current.current.push({\r\n stream: entry.stream,\r\n text: parts[i],\r\n ts: entry.ts,\r\n })\r\n }\r\n }\r\n // If the entry ends with \\n, flush.\r\n if (parts.length > 1 && parts[parts.length - 1] === '') {\r\n flushLine()\r\n }\r\n }, [flushLine])\r\n\r\n // Connect WebSocket.\r\n useEffect(() => {\r\n const url = `/api/dsh-better-sidebar-starter/logs?instanceId=${encodeURIComponent(instance.id)}`\r\n const ws = new WebSocket(url)\r\n wsRef.current = ws\r\n\r\n ws.onmessage = (event: MessageEvent): void => {\r\n try {\r\n const msg = JSON.parse(event.data as string) as WsMessage\r\n if (msg.type === 'history') {\r\n setLogLines([])\r\n lineBuffer.current.current = []\r\n for (const entry of msg.entries) {\r\n addEntry(entry)\r\n }\r\n flushLine()\r\n } else if (msg.type === 'log') {\r\n addEntry(msg.entry)\r\n } else if (msg.type === 'status') {\r\n if (msg.status !== 'running') {\r\n flushLine()\r\n setExitInfo({ status: msg.status, exitCode: msg.exitCode })\r\n }\r\n }\r\n } catch { /* ignore parse errors */ }\r\n }\r\n\r\n return () => {\r\n ws.close()\r\n wsRef.current = null\r\n }\r\n }, [instance.id, addEntry, flushLine])\r\n\r\n // Auto-scroll to bottom when new lines arrive.\r\n useEffect(() => {\r\n if (autoScroll && contentRef.current !== null) {\r\n contentRef.current.scrollTop = contentRef.current.scrollHeight\r\n }\r\n }, [logLines, autoScroll])\r\n\r\n const handleClear = (): void => {\r\n setLogLines([])\r\n lineBuffer.current.current = []\r\n }\r\n\r\n return createElement('div', { style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 } },\r\n // Toolbar\r\n createElement('div', { className: 'sts-log-toolbar' },\r\n instance.configName,\r\n createElement('span', { style: { opacity: 0.5, marginLeft: '4px' } }, `#${instance.id.slice(0, 8)}`),\r\n createElement('div', { className: 'sts-log-toolbar-spacer' }),\r\n instance.status === 'running'\r\n ? createElement('button', {\r\n className: 'sts-icon-btn stop',\r\n title: '停止',\r\n onClick: onStop,\r\n }, StopIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '停止'))\r\n : null,\r\n createElement('button', {\r\n className: 'sts-icon-btn',\r\n title: '重启',\r\n onClick: onRestart,\r\n }, RestartIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '重启')),\r\n createElement('button', {\r\n className: 'sts-icon-btn',\r\n title: '清空',\r\n onClick: handleClear,\r\n }, TrashIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '清空')),\r\n createElement('button', {\r\n className: 'sts-icon-btn send',\r\n title: selLength > 0 ? `发送选中的 ${selLength} 字符到对话` : '发送选中的文本到对话',\r\n onClick: handleSendSelection,\r\n }, SendToChatIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, selLength > 0 ? `发送选中(${selLength})` : '发送选中')),\r\n createElement('label', {\r\n className: 'sts-log-toolbar-label',\r\n onClick: () => setAutoScroll((v) => !v),\r\n },\r\n autoScroll ? '☑' : '☐',\r\n ' 自动滚动',\r\n ),\r\n ),\r\n // Log content\r\n createElement('div', { ref: contentRef, className: 'sts-log-content' },\r\n ...logLines.map((line) => {\r\n const keyStr = `line-${line.key}`\r\n return createElement('div', { key: keyStr, className: 'sts-log-line' },\r\n ...line.entries.map((entry, i) => {\r\n const segKey = `${keyStr}-e${i}`\r\n return createElement('span', {\r\n key: segKey,\r\n className: entry.stream === 'stderr' ? 'sts-log-stderr' : '',\r\n }, renderAnsiLine(entry.text, segKey, (link) => { void handleLogCodeClick(ctx, sessionId, cwd, instance.cwd, link) }))\r\n }),\r\n )\r\n }),\r\n // Exit info\r\n exitInfo !== null\r\n ? createElement('div', {\r\n key: 'exit-info',\r\n style: {\r\n color: exitInfo.status === 'exited' && exitInfo.exitCode === 0 ? '#4e9a06' : '#cc0000',\r\n fontWeight: 'bold',\r\n marginTop: '4px',\r\n },\r\n },\r\n `\\n进程${exitInfo.status === 'exited' ? '退出' : exitInfo.status === 'killed' ? '被终止' : '出错'}`,\r\n exitInfo.exitCode !== null ? `,退出码: ${exitInfo.exitCode}` : '',\r\n )\r\n : null,\r\n ),\r\n )\r\n}\r\n","/**\n * Log history view: read-only viewer for persisted run logs\n * (`GET /logs-history`). Two states: the file list under the workspace's\n * `.dsh/logs` directory, and the rendered content of one log file. Reuses\n * the LogView line renderer (ANSI + Java-log highlighting + code links),\n * so previous runs — including failed spawns — stay inspectable forever.\n * @module dsh-better-sidebar-starter/client/LogHistoryView\n */\n\nimport { createElement, useState, useRef, useCallback, type ReactNode } from 'react'\nimport type { Context } from 'cordis'\nimport type { LogEntry } from './types.ts'\nimport { renderAnsiLine, handleLogCodeClick, type LogLine } from './LogView.tsx'\nimport { BackIcon, CloseIcon, FileIcon, TrashIcon, RefreshIcon } from './icons.tsx'\nimport { showToast } from './utils.ts'\n\n/** API base. */\nconst API_BASE = '/api/dsh-better-sidebar-starter'\n\n/** One persisted log file (from the host list). */\ninterface LogFileInfo {\n file: string\n name: string\n mtime: number\n size: number\n}\n\n/** Render one history log line (stderr red, meta dim). */\nfunction renderLine(line: LogLine, onLinkClick: (link: Parameters<typeof handleLogCodeClick>[4]) => void): ReactNode {\n return createElement('div', { key: `line-${line.key}`, className: 'sts-log-line' },\n ...line.entries.map((entry, i) => {\n const segKey = `line-${line.key}-e${i}`\n const cls = entry.stream === 'stderr'\n ? 'sts-log-stderr'\n : entry.stream === 'meta'\n ? 'sts-log-meta'\n : ''\n return createElement('span', { key: segKey, className: cls },\n renderAnsiLine(entry.text, segKey, onLinkClick))\n }),\n )\n}\n\n/** Props. */\nexport interface LogHistoryViewProps {\n ctx: Context\n sessionId: string\n /** Session working directory (for host cwd resolution + openFile scope). */\n cwd?: string\n /** Close the whole history browser (back to the services view). */\n onClose: () => void\n /** Optional run config name: when set, only that config's log files are\n * shown (used when clicking a config in the tree). */\n filter?: string\n}\n\n/** Split a raw log entry into RenderLine-ready single-line entries. */\nfunction splitEntry(entry: LogEntry, keyBase: number, out: LogLine[]): void {\n const parts = entry.text.split('\\n')\n let current: LogEntry[] = []\n const pushLine = (): void => {\n if (current.length > 0) {\n out.push({ entries: current, key: keyBase++ })\n current = []\n }\n }\n for (let i = 0; i < parts.length; i++) {\n if (i > 0) pushLine()\n if (parts[i] !== '') current.push({ stream: entry.stream, text: parts[i], ts: entry.ts })\n }\n if (parts.length > 1 && parts[parts.length - 1] === '') pushLine()\n}\n\n/** The log history viewer. */\nexport function LogHistoryView({ ctx, sessionId, cwd, onClose, filter }: LogHistoryViewProps): ReactNode {\n const [files, setFiles] = useState<LogFileInfo[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [activeFile, setActiveFile] = useState<LogFileInfo | null>(null)\n const [lines, setLines] = useState<LogLine[]>([])\n const [loadedName, setLoadedName] = useState('')\n const lineKeyCounter = useRef(0)\n\n const loadList = useCallback(async (): Promise<void> => {\n setLoading(true)\n try {\n const params = new URLSearchParams({ sessionId })\n if (cwd !== undefined && cwd !== '') params.set('cwd', cwd)\n const resp = await fetch(`${API_BASE}/logs-history?${params.toString()}`)\n if (!resp.ok) {\n let reason = filter !== undefined ? '加载历史日志失败' : '加载历史日志失败'\n try {\n const err = await resp.json() as { error?: string }\n if (typeof err.error === 'string' && err.error !== '') reason = err.error\n } catch { /* keep generic */ }\n showToast(reason, false)\n setFiles([])\n return\n }\n const data = await resp.json() as { logs?: LogFileInfo[] }\n const all = data.logs ?? []\n // File names are `<safeName>_<stamp>.log`; configs never contain the\n // stamp, so a prefix match on the name string is unambiguous.\n const shown = filter !== undefined ? all.filter((f) => f.name.startsWith(filter)) : all\n setFiles(shown)\n } catch {\n showToast('加载历史日志失败', false)\n setFiles([])\n } finally {\n setLoading(false)\n }\n }, [sessionId, cwd, filter])\n\n // Initial load of the file list.\n const [initialized, setInitialized] = useState(false)\n if (!initialized) {\n setInitialized(true)\n void loadList()\n }\n\n /** Open one log file and render its content. */\n const openFile = useCallback(async (info: LogFileInfo): Promise<void> => {\n setLoading(true)\n try {\n const params = new URLSearchParams({ sessionId, file: info.file })\n if (cwd !== undefined && cwd !== '') params.set('cwd', cwd)\n const resp = await fetch(`${API_BASE}/logs-history?${params.toString()}`)\n if (!resp.ok) {\n let reason = '读取日志失败'\n try {\n const err = await resp.json() as { error?: string }\n if (typeof err.error === 'string' && err.error !== '') reason = err.error\n } catch { /* keep generic */ }\n showToast(reason, false)\n return\n }\n const data = await resp.json() as { logs?: LogEntry[]; file?: string }\n const out: LogLine[] = []\n for (const entry of data.logs ?? []) {\n splitEntry(entry, lineKeyCounter.current, out)\n lineKeyCounter.current += 200000\n }\n setActiveFile(info)\n setLoadedName(info.name)\n setLines(out)\n } catch {\n showToast('读取日志失败', false)\n } finally {\n setLoading(false)\n }\n }, [sessionId, cwd])\n\n // ---- list view ----\n if (activeFile === null) {\n const title = filter !== undefined ? `${filter} · 历史日志` : '历史日志'\n return createElement('div', { style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 } },\n createElement('div', { className: 'sts-log-toolbar' },\n title,\n createElement('div', { className: 'sts-log-toolbar-spacer' }),\n createElement('button', {\n className: 'sts-icon-btn',\n title: '刷新',\n onClick: () => { void loadList() },\n }, RefreshIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '刷新')),\n createElement('button', {\n className: 'sts-icon-btn',\n title: filter !== undefined ? '返回服务视图' : '关闭历史日志',\n onClick: onClose,\n }, CloseIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, filter !== undefined ? '返回' : '关闭')),\n ),\n createElement('div', {\n style: { overflow: 'auto', flex: 1, minHeight: 0, padding: '4px 0' },\n },\n loading && files === null\n ? createElement('div', { className: 'sts-empty' }, '加载中…')\n : files !== null && files.length === 0\n ? createElement('div', { className: 'sts-empty' },\n filter !== undefined\n ? `「${filter}」暂无历史日志。每次启动都会在 <工作空间>/logs/ 下永久保留一份日志,即使启动失败或 dsh 重启也能查看。`\n : '暂无历史日志(工作空间 logs/ 为空)。每次启动服务都会在 <工作空间>/logs/ 下永久保留一份日志,即使启动失败或 dsh 重启也能查看。')\n : createElement('div', {},\n (files ?? []).map((f) =>\n createElement('button', {\n key: f.file,\n className: 'sts-history-item',\n title: f.file,\n onClick: () => { void openFile(f) },\n },\n createElement(FileIcon, { size: 13 }),\n createElement('span', { style: { flex: 1, textAlign: 'left', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, f.name),\n createElement('span', { className: 'sts-history-item-meta' },\n `${new Date(f.mtime).toLocaleString()} ${(f.size / 1024).toFixed(1)} KB`),\n ),\n ),\n ),\n ),\n )\n }\n\n // ---- file view ----\n return createElement('div', { style: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 } },\n createElement('div', { className: 'sts-log-toolbar' },\n createElement('button', {\n className: 'sts-icon-btn',\n title: '返回文件列表',\n onClick: () => { setActiveFile(null); setLines([]) },\n }, BackIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '返回')),\n loadedName,\n createElement('span', { style: { opacity: 0.5, marginLeft: '4px' } }, '(历史日志)'),\n createElement('div', { className: 'sts-log-toolbar-spacer' }),\n createElement('button', {\n className: 'sts-icon-btn',\n title: '清空视图',\n onClick: () => setLines([]),\n }, TrashIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '清空')),\n createElement('button', {\n className: 'sts-icon-btn',\n title: '关闭历史日志',\n onClick: onClose,\n }, CloseIcon({ size: 14 }), createElement('span', { className: 'sts-icon-btn-label' }, '关闭')),\n ),\n createElement('div', { className: 'sts-log-content' },\n loading && lines.length === 0\n ? createElement('div', { className: 'sts-empty' }, '读取中…')\n : lines.length === 0\n ? createElement('div', { className: 'sts-empty' }, '日志文件为空。')\n : lines.map((line) => renderLine(line, (link) => { void handleLogCodeClick(ctx, sessionId, cwd, cwd, link) })),\n lines.length === 0\n ? null\n : createElement('div', { key: 'history-end', style: { opacity: 0.5, marginTop: '6px' } },\n `—— 历史日志已加载完毕(${lines.length} 行),此为只读视图 ——`),\n ),\n )\n}","/**\n * Shared directory chooser: a filesystem folder tree (backed by the sidebar\n * fs.tree API, which can list any absolute path) plus a browse button that\n * opens a modal popup. Browsing starts at the user's home directory and can\n * navigate up to any drive/root, so runtime tool paths (JDK/Maven/...) that\n * live outside the workspace can be selected.\n * @module dsh-better-sidebar-starter/client/DirChooser\n */\n\nimport { createElement, useState, useEffect, useRef, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { FolderIcon, ChevronRightIcon, ChevronDownIcon, CloseIcon, CheckIcon } from './icons.tsx'\n\n/** One directory entry from the sidebar fs.tree API. */\nexport interface FsEntry {\n name: string\n path: string\n isDir: boolean\n hidden: boolean\n isSymlink: boolean\n broken: boolean\n}\n\n/** Call the sidebar fs.tree API directly via fetch. Returns directories only. */\nexport async function fsTree(scope: SessionScope, path: string): Promise<FsEntry[]> {\n const payload: Record<string, string> = { sessionId: scope.sessionId, path }\n if (scope.cwd !== undefined && scope.cwd !== '') payload.cwd = scope.cwd\n const resp = await fetch('/sidebar/api/fs.tree', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!resp.ok) throw new Error(`fs.tree ${path}: ${resp.status}`)\n const parsed = await resp.json() as { ok?: boolean; value?: { entries: FsEntry[] }; error?: { message: string } }\n if (!parsed.ok || parsed.value === undefined) {\n throw new Error(`fs.tree ${path}: ${parsed.error?.message ?? 'unknown error'}`)\n }\n return parsed.value.entries.filter((e) => e.isDir && !e.hidden)\n}\n\n/** Fetch the user's home directory from the host. Falls back to cwd. */\nasync function fetchHomeDefault(scope: SessionScope): Promise<string> {\n try {\n const r = await fetch('/api/dsh-better-sidebar-starter/detect-env', { method: 'GET' })\n if (r.ok) {\n const data = await r.json() as { home?: string }\n if (data.home && data.home !== '') return data.home\n }\n } catch { /* ignore */ }\n return scope.cwd ?? '.'\n}\n\n/** Normalize a path, returning its parent or null if at the root. */\nfunction parentOf(p: string): string | null {\n const trimmed = p.replace(/[\\\\/]+$/, '')\n const lastSep = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\\\'))\n if (lastSep <= 0) return null\n return trimmed.substring(0, lastSep)\n}\n\n/** The folder tree panel: shows the current dir's subdirectories with up-nav. */\nexport function DirTree(props: {\n scope: SessionScope\n selectedPath: string\n onPick: (path: string) => void\n}): ReactNode {\n const { scope, selectedPath, onPick } = props\n const [cur, setCur] = useState<string>('.') // current directory being browsed\n const [entries, setEntries] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [expanded, setExpanded] = useState<Set<string>>(new Set())\n\n // Stash scope in a ref so the init effect only runs once regardless of the\n // parent re-rendering with a fresh scope object each frame.\n const scopeRef = useRef(scope)\n const initDoneRef = useRef(false)\n // Remember the initial selected value so the popup opens at, or as close to\n // as possible, the already-configured path.\n const initialValueRef = useRef(selectedPath)\n\n // Initialize browsing root: prefer an existing selected path, else home.\n useEffect(() => {\n if (initDoneRef.current) return\n initDoneRef.current = true\n let cancelled = false\n const s = scopeRef.current\n const startPath = initialValueRef.current?.trim() && initialValueRef.current !== '.' && initialValueRef.current !== './'\n ? initialValueRef.current.trim()\n : null\n if (startPath !== null) {\n setCur(startPath)\n loadDir(s, startPath)\n // If the start path points at a file dir inside a parent, still fine.\n } else {\n fetchHomeDefault(s).then((home) => {\n if (cancelled) return\n setCur(home)\n loadDir(s, home)\n })\n }\n return () => { cancelled = true }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const loadDir = (s: SessionScope, path: string): void => {\n setLoading(true)\n setError(null)\n fsTree(s, path)\n .then((dirs) => {\n setEntries(dirs)\n setLoading(false)\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : String(err))\n setLoading(false)\n })\n }\n\n const goUp = (): void => {\n const parent = parentOf(cur)\n if (parent !== null) {\n setCur(parent)\n loadDir(scopeRef.current, parent)\n }\n }\n\n const navigateInto = (path: string): void => {\n setCur(path)\n loadDir(scopeRef.current, path)\n }\n\n const toggleExpand = (path: string): void => {\n setExpanded((prev) => {\n const next = new Set(prev)\n if (next.has(path)) next.delete(path)\n else next.add(path)\n return next\n })\n }\n\n const isSelected = (p: string): boolean => selectedPath !== '' && selectedPath !== '.' && selectedPath === p\n\n return createElement('div', { className: 'sts-dirtree' },\n // Navigation bar: current path + up button\n createElement('div', { className: 'sts-dirtree-nav' },\n createElement('button', {\n className: 'sts-dirtree-up',\n onClick: goUp,\n disabled: parentOf(cur) === null,\n title: '\\u4e0a\\u7ea7\\u76ee\\u5f55',\n }, '\\u2191'),\n createElement('button', {\n className: 'sts-dirtree-home',\n onClick: () => loadDir(scopeRef.current, cur), // refresh current dir\n title: '\\u5237\\u65b0',\n }, '\\u21bb'),\n createElement('span', { className: 'sts-dirtree-cur', title: cur }, cur || '(.)'),\n ),\n // Current directory selection row + subdir tree\n createElement('div', {\n className: `sts-tree-row${isSelected(cur) ? ' selected' : ''}`,\n style: { paddingLeft: '8px' },\n onClick: () => onPick(cur),\n },\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, cur || '(.)'),\n createElement('span', { className: 'sts-tree-select-hint' }, '\\u25cf \\u9009\\u62e9\\u6b64\\u76ee\\u5f55'),\n ),\n loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: '16px' } }, '\\u52a0\\u8f7d\\u4e2d...')\n : error !== null\n ? createElement('div', { className: 'sts-tree-error', style: { paddingLeft: '16px' } }, error)\n : entries !== null && entries.length > 0\n ? createElement('div', { className: 'sts-dirtree-sub' },\n ...entries.map((entry) => createElement(DirRow, {\n key: entry.path,\n scope,\n entry,\n depth: 1,\n selectedPath,\n expanded,\n onToggleExpand: toggleExpand,\n onPick,\n onNavigate: navigateInto,\n })),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: '16px' } }, '\\u65e0\\u5b50\\u76ee\\u5f55'),\n )\n}\n\n/** One directory row with recursive expansion + click into. */\nfunction DirRow(props: {\n scope: SessionScope\n entry: FsEntry\n depth: number\n selectedPath: string\n expanded: Set<string>\n onToggleExpand: (p: string) => void\n onPick: (p: string) => void\n onNavigate: (p: string) => void\n}): ReactNode {\n const { scope, entry, depth, selectedPath, expanded, onToggleExpand, onPick, onNavigate } = props\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const isExpanded = expanded.has(entry.path)\n const isSelected = selectedPath === entry.path\n\n const handleClick = (): void => {\n // Clicking a row selects it AND expands to reveal children (unless already\n // expanded, in which case a second click collapses).\n onPick(entry.path)\n if (isExpanded) {\n onToggleExpand(entry.path)\n return\n }\n onToggleExpand(entry.path)\n if (children === null) {\n setLoading(true)\n fsTree(scope, entry.path)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch(() => { setChildren([]); setLoading(false) })\n }\n }\n\n return createElement('div', null,\n createElement('div', {\n className: `sts-tree-row${isSelected ? ' selected' : ''}`,\n style: { paddingLeft: `${depth * 16 + 8}px` },\n onClick: handleClick,\n },\n createElement('span', { className: `sts-tree-chevron${isExpanded ? ' expanded' : ''}` },\n createElement(isExpanded ? ChevronDownIcon : ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, entry.name),\n ),\n isExpanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u52a0\\u8f7d\\u4e2d...')\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(DirRow, {\n key: child.path,\n scope,\n entry: child,\n depth: depth + 1,\n selectedPath,\n expanded,\n onToggleExpand: onToggleExpand,\n onPick,\n onNavigate: props.onNavigate,\n }),\n ),\n )\n : null\n : null,\n )\n}\n\n/** Props for the browse button + modal chooser. */\nexport interface DirChooserProps {\n scope: SessionScope\n value: string\n onChange: (v: string) => void\n /** Button/input presentation. */\n compact?: boolean\n /** Placeholder for the value input. */\n placeholder?: string\n}\n\n/** A read-only path input + browse button that opens a modal folder tree. */\nexport function DirChooser(props: DirChooserProps): ReactNode {\n const { scope, value, onChange, compact, placeholder } = props\n const [open, setOpen] = useState(false)\n\n return createElement('div', { className: 'sts-settings-path-row' },\n createElement('span', { className: 'sts-settings-path-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('input', {\n className: compact ? 'sts-modal-input mono' : 'sts-settings-input',\n value: value,\n readOnly: true,\n placeholder: placeholder ?? '',\n title: value,\n onClick: () => setOpen(true),\n }),\n createElement('button', {\n className: 'sts-settings-browse',\n onClick: () => setOpen(true),\n title: '\\u9009\\u62e9\\u6587\\u4ef6\\u5939',\n }, '\\u6d4f\\u89c8'),\n open\n ? createElement(DirChooserModal, {\n scope,\n value,\n onConfirm: (p) => { onChange(p); setOpen(false) },\n onCancel: () => setOpen(false),\n })\n : null,\n )\n}\n\n/** Modal popup hosting the folder tree with confirm/cancel. */\nfunction DirChooserModal(props: {\n scope: SessionScope\n value: string\n onConfirm: (path: string) => void\n onCancel: () => void\n}): ReactNode {\n const { scope, value, onConfirm, onCancel } = props\n const [sel, setSel] = useState(value)\n\n return createElement('div', {\n className: 'sts-modal-overlay',\n onClick: (e: MouseEvent) => { if (e.target === e.currentTarget) onCancel() },\n },\n createElement('div', { className: 'sts-dir-modal' },\n createElement('div', { className: 'sts-modal-header' },\n '\\u9009\\u62e9\\u6587\\u4ef6\\u5939',\n createElement('button', { className: 'sts-icon-btn', onClick: onCancel },\n createElement(CloseIcon, { size: 16 }),\n ),\n ),\n createElement('div', { className: 'sts-dir-modal-tree' },\n createElement(DirTree, { scope, selectedPath: sel, onPick: setSel }),\n ),\n createElement('div', { className: 'sts-modal-footer' },\n createElement('button', { className: 'sts-modal-btn', onClick: onCancel }, '\\u53d6\\u6d88'),\n createElement('button', {\n className: 'sts-modal-btn primary',\n onClick: () => onConfirm(sel),\n style: {\n background: '#1a1a1e',\n border: '1px solid #3a3a3e',\n color: '#fff',\n borderRadius: '6px',\n padding: '8px 18px',\n cursor: 'pointer',\n fontSize: '13px',\n },\n },\n createElement(CheckIcon, { size: 14 }), ' \\u786e\\u5b9a'),\n ),\n ),\n )\n}\n","/**\n * Path picker: a read-only input + browse button that opens a folder tree\n * modal (DirChooser). Shared by the settings panel and the config editor.\n * @module dsh-better-sidebar-starter/client/PathPicker\n */\n\nimport { createElement, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { DirChooser } from './DirChooser.tsx'\n\nexport type PathKind = 'java' | 'node' | 'python' | 'mvn'\n\n/** Props for the path picker. */\nexport interface PathPickerProps {\n scope: SessionScope\n kind: PathKind\n value: string\n onChange: (v: string) => void\n /** Compact mode (no label/desc wrapper), used inside the modal. */\n compact?: boolean\n desc?: string\n}\n\n/** A folder-browse input with a hosted modal tree chooser. */\nexport function PathPicker(props: PathPickerProps): ReactNode {\n const { scope, value, onChange, compact, desc } = props\n return createElement(DirChooser, {\n scope,\n value,\n onChange,\n compact,\n placeholder: desc,\n })\n}\n","/**\n * Config editor modal dialog: create or edit a run configuration.\n * Lets the user set name, type, command, cwd (tree picker), jvmArgs, args,\n * and environment variables.\n * @module dsh-better-sidebar-starter/client/ConfigEditorModal\n */\n\nimport { createElement, useState, useEffect, type ReactNode } from 'react'\nimport type { SessionScope } from 'dsh-better-sidebar/client/service'\nimport { CloseIcon, PlusIcon, TrashIcon, ChevronRightIcon, FolderIcon, ClipboardIcon } from './icons.tsx'\nimport { PathPicker } from './PathPicker.tsx'\n\n/** One directory entry from the sidebar fs.tree API. */\ninterface FsEntry {\n name: string\n path: string\n isDir: boolean\n hidden: boolean\n isSymlink: boolean\n broken: boolean\n}\n\n/** Call the sidebar fs.tree API directly via fetch. Returns directories only.\n * If path is omitted, the server uses the session cwd (root listing).\n * The sidebar API wraps responses in { ok: true, value: ... } envelope. */\nasync function fsTree(scope: SessionScope, path?: string): Promise<FsEntry[]> {\n const payload: Record<string, string> = { sessionId: scope.sessionId }\n if (scope.cwd !== undefined && scope.cwd !== '') payload.cwd = scope.cwd\n if (path !== undefined) payload.path = path\n const resp = await fetch('/sidebar/api/fs.tree', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!resp.ok) throw new Error(`fs.tree ${path ?? '(root)'}: ${resp.status}`)\n const parsed = await resp.json() as { ok?: boolean; value?: { entries: FsEntry[] }; error?: { message: string } }\n if (!parsed.ok || parsed.value === undefined) {\n throw new Error(`fs.tree ${path ?? '(root)'}: ${parsed.error?.message ?? 'unknown error'}`)\n }\n return parsed.value.entries.filter((e) => e.isDir && !e.hidden)\n}\n\n/** Recursive tree node: loads its own children on expand. */\nfunction TreeNode(props: {\n scope: SessionScope\n entry: FsEntry\n depth: number\n selectedPath: string\n onSelect: (path: string) => void\n}): ReactNode {\n const { scope, entry, depth, selectedPath, onSelect } = props\n const [expanded, setExpanded] = useState(false)\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n\n const handleToggle = (): void => {\n if (!expanded && children === null) {\n setLoading(true)\n fsTree(scope, entry.path)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch(() => { setChildren([]); setLoading(false) })\n }\n setExpanded(!expanded)\n }\n\n const isSelected = selectedPath === entry.path\n\n return createElement('div', null,\n createElement('div', {\n className: `sts-tree-row${isSelected ? ' selected' : ''}`,\n style: { paddingLeft: `${depth * 16 + 8}px` },\n onClick: () => { onSelect(entry.path); handleToggle() },\n },\n createElement('span', { className: `sts-tree-chevron${expanded ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, entry.name),\n ),\n expanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u52a0\\u8f7d\\u4e2d...')\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(TreeNode, {\n key: child.path,\n scope,\n entry: child,\n depth: depth + 1,\n selectedPath,\n onSelect,\n }),\n ),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: `${(depth + 1) * 16 + 8}px` } }, '\\u65e0\\u5b50\\u76ee\\u5f55')\n : null,\n )\n}\n\n/** Directory tree picker: shows a collapsible tree rooted at session cwd. */\nfunction DirTreePicker(props: { scope: SessionScope; value: string; onSelect: (path: string) => void }): ReactNode {\n const { scope, value, onSelect } = props\n const [expanded, setExpanded] = useState(false)\n const [children, setChildren] = useState<FsEntry[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const handleToggle = (): void => {\n if (!expanded && children === null) {\n setLoading(true)\n setError(null)\n fsTree(scope)\n .then((dirs) => { setChildren(dirs); setLoading(false) })\n .catch((err) => { setError(err instanceof Error ? err.message : String(err)); setLoading(false) })\n }\n setExpanded(!expanded)\n }\n\n const rootSelected = value === '.' || value === '' || value === './'\n\n return createElement('div', { className: 'sts-tree-picker' },\n createElement('div', {\n className: `sts-tree-row${rootSelected ? ' selected' : ''}`,\n style: { paddingLeft: '8px' },\n onClick: () => { onSelect('.'); handleToggle() },\n },\n createElement('span', { className: `sts-tree-chevron${expanded ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', { className: 'sts-tree-icon' },\n createElement(FolderIcon, { size: 14 }),\n ),\n createElement('span', { className: 'sts-tree-label' }, '(\\u5de5\\u4f5c\\u7a7a\\u95f4\\u6839\\u76ee\\u5f55)'),\n ),\n expanded\n ? loading\n ? createElement('div', { className: 'sts-tree-loading', style: { paddingLeft: '24px' } }, '\\u52a0\\u8f7d\\u4e2d...')\n : error !== null\n ? createElement('div', { className: 'sts-tree-error', style: { paddingLeft: '24px' } }, error)\n : children !== null && children.length > 0\n ? createElement('div', null,\n ...children.map((child) =>\n createElement(TreeNode, {\n key: child.path,\n scope,\n entry: child,\n depth: 1,\n selectedPath: value,\n onSelect,\n }),\n ),\n )\n : createElement('div', { className: 'sts-tree-empty', style: { paddingLeft: '24px' } }, '\\u65e0\\u5b50\\u76ee\\u5f55')\n : null,\n )\n}\n\n/** One env var row. */\ninterface EnvRow {\n key: string\n value: string\n}\n\n/** Props for the modal. */\nexport interface ConfigEditorModalProps {\n /** Existing config to edit, or null for a new config. */\n config: {\n id?: string\n name: string\n type: string\n command: string\n cwd: string\n env: Record<string, string>\n jvmArgs?: string\n args?: string\n runtime?: { java?: string; node?: string; python?: string; mvn?: string }\n } | null\n /** Session scope for fsTree calls. */\n scope: SessionScope\n /** Called when the user saves. */\n onSave: (config: {\n id?: string\n name: string\n type: string\n command: string\n cwd: string\n env: Record<string, string>\n jvmArgs: string\n args: string\n runtime: Record<string, string>\n }) => void\n /** Called when the user cancels. */\n onCancel: () => void\n}\n\n/** Type \\u2192 default command mapping. */\nconst TYPE_DEFAULTS: Record<string, string> = {\n npm: 'npm run dev',\n springboot: 'mvn spring-boot:run',\n python: 'python main.py',\n custom: '',\n}\n\nconst TYPE_LABELS: Record<string, string> = {\n npm: 'npm',\n springboot: 'Spring Boot',\n python: 'Python',\n custom: 'Custom',\n}\n\n/** The modal dialog component. */\nexport function ConfigEditorModal({ config, scope, onSave, onCancel }: ConfigEditorModalProps): ReactNode {\n const [name, setName] = useState(config?.name ?? '')\n const [type, setType] = useState(config?.type ?? 'custom')\n const [command, setCommand] = useState(config?.command ?? '')\n const [cwd, setCwd] = useState(config?.cwd ?? '.')\n const [jvmArgs, setJvmArgs] = useState(config?.jvmArgs ?? '')\n const [args, setArgs] = useState(config?.args ?? '')\n const [envRows, setEnvRows] = useState<EnvRow[]>(\n config\n ? Object.entries(config.env).map(([key, value]) => ({ key, value }))\n : [],\n )\n const [showTree, setShowTree] = useState(false)\n const [showRuntime, setShowRuntime] = useState(false)\n const [rtJava, setRtJava] = useState(config?.runtime?.java ?? '')\n const [rtNode, setRtNode] = useState(config?.runtime?.node ?? '')\n const [rtPython, setRtPython] = useState(config?.runtime?.python ?? '')\n const [rtMvn, setRtMvn] = useState(config?.runtime?.mvn ?? '')\n\n // When type changes and command is empty (new config), auto-fill template.\n useEffect(() => {\n if (command === '' && TYPE_DEFAULTS[type] !== '') {\n setCommand(TYPE_DEFAULTS[type])\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [type])\n\n const handleSave = (): void => {\n const env: Record<string, string> = {}\n for (const row of envRows) {\n if (row.key.trim() !== '') {\n env[row.key.trim()] = row.value\n }\n }\n const runtime: Record<string, string> = {}\n if (rtJava.trim() !== '') runtime.java = rtJava.trim()\n if (rtNode.trim() !== '') runtime.node = rtNode.trim()\n if (rtPython.trim() !== '') runtime.python = rtPython.trim()\n if (rtMvn.trim() !== '') runtime.mvn = rtMvn.trim()\n onSave({\n id: config?.id,\n name: name.trim(),\n type,\n command: command.trim(),\n cwd: cwd.trim() === '' ? '.' : cwd.trim(),\n env,\n jvmArgs: jvmArgs.trim(),\n args: args.trim(),\n runtime,\n })\n }\n\n const updateEnvRow = (idx: number, field: 'key' | 'value', val: string): void => {\n setEnvRows((prev) => prev.map((row, i) => i === idx ? { ...row, [field]: val } : row))\n }\n const addEnvRow = (): void => setEnvRows((prev) => [...prev, { key: '', value: '' }])\n const removeEnvRow = (idx: number): void => setEnvRows((prev) => prev.filter((_, i) => i !== idx))\n\n // Bulk paste state.\n const [showEnvPaste, setShowEnvPaste] = useState(false)\n const [envPasteText, setEnvPasteText] = useState('')\n\n /** Parse \"K=V;K=V,...\" (or newline/comma separated) into env rows, merging\n * into the existing rows (later values override earlier for duplicate keys). */\n const applyEnvPaste = (): void => {\n const existing = new Map(envRows.filter((r) => r.key.trim() !== '').map((r) => [r.key.trim(), r.value]))\n const text = envPasteText.trim()\n if (text === '') return\n // Split on semicolons, commas, or newlines.\n const pairs = text.split(/[;,\\n]+/)\n for (const pair of pairs) {\n const p = pair.trim()\n if (p === '') continue\n const eq = p.indexOf('=')\n if (eq <= 0) continue // ignore malformed entries\n const key = p.substring(0, eq).trim()\n const value = p.substring(eq + 1).trim()\n if (key !== '') existing.set(key, value)\n }\n setEnvRows(Array.from(existing, ([key, value]) => ({ key, value })))\n setEnvPasteText('')\n setShowEnvPaste(false)\n }\n\n const isSpringboot = type === 'springboot'\n\n return createElement('div', {\n className: 'sts-modal-overlay',\n onClick: (e: MouseEvent) => { if (e.target === e.currentTarget) onCancel() },\n },\n createElement('div', { className: 'sts-modal' },\n // Header\n createElement('div', { className: 'sts-modal-header' },\n config?.id ? '\\u7f16\\u8f91\\u8fd0\\u884c\\u914d\\u7f6e' : '\\u65b0\\u5efa\\u8fd0\\u884c\\u914d\\u7f6e',\n createElement('button', {\n className: 'sts-icon-btn',\n onClick: onCancel,\n }, createElement(CloseIcon, { size: 16 })),\n ),\n // Body\n createElement('div', { className: 'sts-modal-body' },\n // Name\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u540d\\u79f0'),\n createElement('input', {\n className: 'sts-modal-input',\n value: name,\n onChange: (e: { target: { value: string } }) => setName(e.target.value),\n placeholder: '\\u914d\\u7f6e\\u540d\\u79f0',\n }),\n ),\n // Type\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u7c7b\\u578b'),\n createElement('select', {\n className: 'sts-modal-select',\n value: type,\n onChange: (e: { target: { value: string } }) => setType(e.target.value),\n },\n ...Object.entries(TYPE_LABELS).map(([val, label]) =>\n createElement('option', { key: val, value: val }, label),\n ),\n ),\n ),\n // Command\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u547d\\u4ee4'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: command,\n onChange: (e: { target: { value: string } }) => setCommand(e.target.value),\n placeholder: '\\u8981\\u6267\\u884c\\u7684\\u547d\\u4ee4',\n }),\n ),\n // Working directory (tree picker)\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u5de5\\u4f5c\\u76ee\\u5f55'),\n createElement('div', { className: 'sts-cwd-display', onClick: () => setShowTree(!showTree) },\n createElement('span', { className: 'sts-cwd-icon' }, createElement(FolderIcon, { size: 14 })),\n createElement('span', { className: 'sts-cwd-value' }, cwd),\n ),\n showTree\n ? createElement('div', { className: 'sts-tree-picker-container' },\n createElement(DirTreePicker, { scope, value: cwd, onSelect: (p) => setCwd(p) }),\n )\n : null,\n ),\n // JVM args (springboot only)\n isSpringboot\n ? createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u865a\\u62df\\u673a\\u53c2\\u6570 (JVM Args)'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: jvmArgs,\n onChange: (e: { target: { value: string } }) => setJvmArgs(e.target.value),\n placeholder: '\\u4f8b: -Xmx512m -Dspring.profiles.active=dev',\n }),\n )\n : null,\n // Program args (all types)\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u7a0b\\u5e8f\\u5b9e\\u53c2'),\n createElement('input', {\n className: 'sts-modal-input mono',\n value: args,\n onChange: (e: { target: { value: string } }) => setArgs(e.target.value),\n placeholder: '\\u4f8b: --server.port=8080 --debug',\n }),\n ),\n // Runtime environment (collapsible advanced section)\n createElement('div', { className: 'sts-modal-field' },\n createElement('div', {\n className: `sts-collapse-header${showRuntime ? ' open' : ''}`,\n onClick: () => setShowRuntime(!showRuntime),\n },\n createElement('span', { className: `sts-collapse-chevron${showRuntime ? ' expanded' : ''}` },\n createElement(ChevronRightIcon, { size: 12 }),\n ),\n createElement('span', null, '\\u8fd0\\u884c\\u65f6\\u73af\\u5883 (\\u9ad8\\u7ea7)'),\n createElement('span', { className: 'sts-collapse-hint' }, '\\u9ed8\\u8ba4\\u7528\\u5168\\u5c40\\u914d\\u7f6e'),\n ),\n showRuntime\n ? createElement('div', { className: 'sts-collapse-body' },\n createElement('div', { className: 'sts-modal-label' }, '\\u7559\\u7a7a\\u5219\\u7528\\u5168\\u5c40/\\u7cfb\\u7edf\\u914d\\u7f6e'),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Java \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'java',\n value: rtJava,\n onChange: setRtJava,\n compact: true,\n desc: '\\u9009\\u62e9 JDK \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Node.js \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'node',\n value: rtNode,\n onChange: setRtNode,\n compact: true,\n desc: '\\u9009\\u62e9 Node \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Python \\u8def\\u5f84'),\n createElement(PathPicker, {\n scope,\n kind: 'python',\n value: rtPython,\n onChange: setRtPython,\n compact: true,\n desc: '\\u9009\\u62e9 Python \\u76ee\\u5f55',\n }),\n ),\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, 'Maven \\u8def\\u5f84 (MVN)'),\n createElement(PathPicker, {\n scope,\n kind: 'mvn',\n value: rtMvn,\n onChange: setRtMvn,\n compact: true,\n desc: '\\u9009\\u62e9 Maven \\u76ee\\u5f55',\n }),\n ),\n )\n : null,\n ),\n // Environment variables\n createElement('div', { className: 'sts-modal-field' },\n createElement('label', { className: 'sts-modal-label' }, '\\u73af\\u5883\\u53d8\\u91cf'),\n ...envRows.map((row, idx) =>\n createElement('div', { key: idx, className: 'sts-env-row' },\n createElement('input', {\n className: 'sts-modal-input sts-env-key',\n value: row.key,\n onChange: (e: { target: { value: string } }) => updateEnvRow(idx, 'key', e.target.value),\n placeholder: '\\u53d8\\u91cf\\u540d (\\u5982 JAVA_HOME)',\n }),\n createElement('span', { className: 'sts-env-sep' }, '='),\n createElement('input', {\n className: 'sts-modal-input sts-env-val',\n value: row.value,\n onChange: (e: { target: { value: string } }) => updateEnvRow(idx, 'value', e.target.value),\n placeholder: '\\u53d8\\u91cf\\u503c',\n }),\n createElement('button', {\n className: 'sts-icon-btn trash',\n onClick: () => removeEnvRow(idx),\n }, createElement(TrashIcon, { size: 14 })),\n ),\n ),\n createElement('div', { className: 'sts-env-actions' },\n createElement('button', {\n className: 'sts-env-add', onClick: addEnvRow,\n },\n createElement(PlusIcon, { size: 12 }), ' \\u6dfb\\u52a0\\u73af\\u5883\\u53d8\\u91cf',\n ),\n createElement('button', {\n className: 'sts-env-add', onClick: () => setShowEnvPaste(!showEnvPaste),\n },\n createElement(ClipboardIcon, { size: 12 }), showEnvPaste ? ' \\u53d6\\u6d88' : ' \\u6279\\u91cf\\u7c98\\u8d34',\n ),\n ),\n showEnvPaste\n ? createElement('div', { className: 'sts-env-paste' },\n createElement('textarea', {\n className: 'sts-env-paste-input',\n value: envPasteText,\n onChange: (e: { target: { value: string } }) => setEnvPasteText(e.target.value),\n placeholder: '\\u7c98\\u8d34 KEY=VALUE;KEY=VALUE;\\u6216\\u4ee5\\u6362\\u884c/\\u9017\\u53f7\\u5206\\u9694\\uff0c\\u5982\\uff1a\\nDB_DATABASE=cassiopeia_ap;DB_HOST=10.18.18.18;DB_PORT=3306;MQ_HOST=10.18.18.18;MQ_PASSWORD=admin123;MQ_PORT=5672;MQ_USERNAME=admin;MQ_VIRTUAL_HOST=/;REDIS_HOST=10.18.18.17;REDIS_PASSWORD=Qaz!wsx2;REDIS_PORT=6379',\n rows: 4,\n }),\n createElement('div', { className: 'sts-env-paste-footer' },\n createElement('button', {\n className: 'sts-env-paste-apply', onClick: applyEnvPaste,\n },\n '\\u786e\\u5b9a\\u5e76\\u751f\\u6210\\u73af\\u5883\\u53d8\\u91cf'),\n createElement('span', { className: 'sts-env-paste-hint' }, '\\u91cd\\u590d\\u5c06\\u8986\\u76d6'),\n ),\n )\n : null,\n ),\n ),\n // Footer\n createElement('div', { className: 'sts-modal-footer' },\n createElement('button', { className: 'sts-modal-btn', onClick: onCancel }, '\\u53d6\\u6d88'),\n createElement('button', {\n className: 'sts-modal-btn primary',\n onClick: handleSave,\n disabled: name.trim() === '',\n style: {\n background: '#1a1a1e',\n border: '1px solid #3a3a3e',\n color: '#fff',\n borderRadius: '6px',\n padding: '8px 18px',\n cursor: name.trim() === '' ? 'not-allowed' : 'pointer',\n fontSize: '13px',\n opacity: name.trim() === '' ? 0.5 : 1,\n },\n }, '\\u4fdd\\u5b58'),\n ),\n ),\n )\n}\n","/**\r\n * CSS styles for the starter plugin panel. Uses .sts- prefixed class names\r\n * (scoped enough to avoid collisions with the host shell).\r\n * Uses --dsw-alias-* CSS variables from the DSH design system to follow the\r\n * active theme automatically (dark/light).\r\n * @module dsh-better-sidebar-starter/client/styles\r\n */\r\n\r\nexport const STYLES_CSS = `\r\n/* ===== Root container ===== */\r\n.sts-root {\r\n display: flex;\r\n flex-direction: column;\r\n height: 100%;\r\n min-height: 0;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-bg-base, #1a1a1e);\r\n}\r\n\r\n/* ===== Toolbar ===== */\r\n.sts-toolbar {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 0 12px;\r\n height: 40px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n flex-shrink: 0;\r\n}\r\n.sts-toolbar-title {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n font: var(--dsw-font-m-14, 500 14px/20px sans-serif);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-toolbar-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 4px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 12px;\r\n transition: background 0.12s, border-color 0.12s;\r\n}\r\n.sts-toolbar-btn:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-toolbar-btn.active {\r\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.18));\r\n border-color: var(--dsw-alias-brand, #007acc);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n\r\n/* ===== Log history viewer ===== */\r\n.sts-history-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n width: calc(100% - 8px);\r\n margin: 2px 4px;\r\n padding: 6px 8px;\r\n border: 1px solid transparent;\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 12px;\r\n text-align: left;\r\n transition: background 0.12s, border-color 0.12s;\r\n}\r\n.sts-history-item:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n border-color: var(--dsw-alias-border-l2, #2a2a2e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-history-item-meta {\r\n flex-shrink: 0;\r\n font-size: 11px;\r\n opacity: 0.55;\r\n}\r\n.sts-log-meta {\r\n opacity: 0.72;\r\n font-style: italic;\r\n}\r\n\r\n/* ===== Split pane (horizontal: default for right panel) ===== */\r\n.sts-split {\r\n display: flex;\r\n flex: 1;\r\n min-height: 0;\r\n}\r\n.sts-split-first {\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n border-right: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n flex-shrink: 0;\r\n}\r\n.sts-split-gutter {\r\n width: 1px;\r\n background: var(--dsw-alias-border-l2, #2a2a2e);\r\n cursor: col-resize;\r\n flex-shrink: 0;\r\n transition: width 0.12s, background 0.12s;\r\n}\r\n.sts-split-gutter:hover,\r\n.sts-split-gutter.dragging {\r\n width: 3px;\r\n background: var(--dsw-alias-interactive-bg-hover-accent, #007acc);\r\n}\r\n.sts-split-second {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0;\r\n min-height: 0;\r\n}\r\n\r\n/* ===== Split pane (vertical: for bottom panel) ===== */\r\n.sts-split-vertical {\r\n flex-direction: column;\r\n}\r\n.sts-split-first-v {\r\n border-right: none;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n width: 100% !important;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n flex-shrink: 0;\r\n}\r\n.sts-split-gutter-v {\r\n width: auto;\r\n height: 1px;\r\n cursor: row-resize;\r\n transition: height 0.12s, background 0.12s;\r\n}\r\n.sts-split-gutter-v:hover,\r\n.sts-split-gutter-v.dragging {\r\n width: auto;\r\n height: 3px;\r\n background: var(--dsw-alias-interactive-bg-hover-accent, #007acc);\r\n}\r\n.sts-split-second-v {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n min-width: 0;\r\n min-height: 0;\r\n}\r\n\r\n/* ===== Config tree ===== */\r\n.sts-tree {\r\n padding: 4px 4px 8px;\r\n user-select: none;\r\n}\r\n.sts-group-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 6px 8px;\r\n font: var(--dsw-font-xxxs-11, 500 11px/16px sans-serif);\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n cursor: pointer;\r\n border-radius: 6px;\r\n}\r\n.sts-group-header:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-group-count {\r\n color: rgba(255,255,255,0.4);\r\n font-weight: 400;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n.sts-group-children {\r\n overflow: hidden;\r\n}\r\n.sts-group-children.collapsed {\r\n display: none;\r\n}\r\n\r\n/* ===== Config row ===== */\r\n.sts-config-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 5px 8px 5px 24px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n border-radius: 6px;\r\n margin: 1px 2px;\r\n transition: background 0.1s;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-config-row:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-config-row.selected {\r\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\r\n}\r\n.sts-config-name {\r\n flex: 1;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n font-weight: 400;\r\n}\r\n.sts-config-cmd {\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n max-width: 100px;\r\n}\r\n.sts-config-actions {\r\n display: flex;\r\n align-items: center;\r\n gap: 1px;\r\n flex-shrink: 0;\r\n}\r\n.sts-icon-btn {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 22px;\r\n height: 22px;\r\n border: none;\r\n border-radius: 4px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n opacity: 0;\r\n transition: opacity 0.1s, background 0.1s;\r\n}\r\n.sts-config-row:hover .sts-icon-btn {\r\n opacity: 0.7;\r\n}\r\n.sts-icon-btn:hover {\r\n opacity: 1 !important;\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.08));\r\n}\r\n.sts-icon-btn.play { color: #6ab04c; }\r\n.sts-icon-btn.stop { color: #eb5757; }\r\n.sts-icon-btn.gear { color: var(--dsw-alias-label-secondary, #b0b0b0); }\r\n.sts-icon-btn.trash { color: #eb5757; }\r\n.sts-icon-btn.dup { color: var(--dsw-alias-label-secondary, #b0b0b0); }\r\n.sts-icon-btn.send { color: #4a90d9; }\r\n/* Toolbar buttons (log view) are icon-only; the label expands on hover. */\r\n.sts-log-toolbar .sts-icon-btn {\r\n width: 26px;\r\n height: 26px;\r\n padding: 0 6px;\r\n gap: 0;\r\n font-size: 11px;\r\n opacity: 1;\r\n}\r\n.sts-log-toolbar .sts-icon-btn .sts-icon-btn-label {\r\n display: inline-block;\r\n max-width: 0;\r\n overflow: hidden;\r\n white-space: nowrap;\r\n opacity: 0;\r\n transition: max-width 0.18s ease, opacity 0.18s ease, margin-left 0.18s ease;\r\n}\r\n.sts-log-toolbar .sts-icon-btn:hover .sts-icon-btn-label {\r\n max-width: 120px;\r\n margin-left: 4px;\r\n opacity: 1;\r\n}\r\n\r\n/* ===== Instance row ===== */\r\n.sts-instance-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 3px 8px 3px 40px;\r\n font-size: 11px;\r\n cursor: pointer;\r\n border-radius: 6px;\r\n margin: 1px 2px;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n transition: background 0.1s;\r\n}\r\n.sts-instance-row:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-instance-row.selected {\r\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-instance-pid {\r\n color: rgba(255,255,255,0.4);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n/* ===== Empty states ===== */\r\n.sts-empty {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n height: 100%;\r\n color: rgba(255,255,255,0.4);\r\n font-size: 12px;\r\n padding: 24px;\r\n text-align: center;\r\n gap: 8px;\r\n line-height: 20px;\r\n}\r\n\r\n/* ===== Log view ===== */\r\n.sts-log-toolbar {\r\n display: flex;\r\n align-items: center;\r\n gap: 2px;\r\n padding: 0 8px;\r\n height: 36px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n flex-shrink: 0;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n font-size: 12px;\r\n}\r\n.sts-log-toolbar-spacer {\r\n flex: 1;\r\n}\r\n.sts-log-toolbar-label {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n font-size: 11px;\r\n cursor: pointer;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n padding: 2px 6px;\r\n border-radius: 4px;\r\n}\r\n.sts-log-toolbar-label:hover {\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n}\r\n.sts-log-content {\r\n flex: 1;\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n padding: 8px 12px;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n font-size: 12px;\r\n line-height: 1.6;\r\n white-space: pre-wrap;\r\n word-break: break-all;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-bg-base, #1a1a1e);\r\n}\r\n.sts-log-line {\r\n min-height: 1em;\r\n}\r\n/* Clickable source tokens (classes / stack frames) inside the log. */\r\n.sts-log-link {\r\n cursor: pointer;\r\n border-bottom: 1px dashed rgba(97, 175, 239, 0.4);\r\n transition: background 0.1s ease;\r\n}\r\n.sts-log-link:hover {\r\n background: rgba(97, 175, 239, 0.14);\r\n border-bottom-color: #61afef;\r\n}\r\n/* Clickable http(s) URLs inside the log (open in a new browser tab). */\r\n.sts-log-url {\r\n color: #61afef;\r\n}\r\n.sts-log-url:hover {\r\n background: rgba(97, 175, 239, 0.18);\r\n}\r\n\r\n/* ===== Config detail ===== */\r\n.sts-detail {\r\n padding: 20px;\r\n overflow-y: auto;\r\n flex: 1;\r\n}\r\n.sts-detail-card {\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n background: var(--dsw-alias-bg-layer-2, #202024);\r\n border-radius: 12px;\r\n padding: 20px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n}\r\n.sts-detail-title {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n font-size: 16px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n padding-bottom: 12px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-detail-row {\r\n display: flex;\r\n gap: 12px;\r\n font-size: 13px;\r\n align-items: baseline;\r\n}\r\n.sts-detail-label {\r\n width: 72px;\r\n flex-shrink: 0;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n font-size: 11px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.3px;\r\n}\r\n.sts-detail-value {\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n word-break: break-all;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-detail-actions {\r\n display: flex;\r\n gap: 8px;\r\n justify-content: flex-end;\r\n margin-top: 12px;\r\n padding-top: 16px;\r\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-detail-btn {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 6px 14px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 12px;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-detail-btn:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-detail-btn.danger {\r\n color: #eb5757;\r\n border-color: rgba(235,87,87,0.4);\r\n}\r\n.sts-detail-btn.danger:hover {\r\n background: rgba(235,87,87,0.1);\r\n}\r\n\r\n/* ===== Modal ===== */\r\n.sts-modal-overlay {\r\n position: fixed;\r\n top: 0; left: 0; right: 0; bottom: 0;\r\n background: rgba(0,0,0,0.45);\r\n backdrop-filter: blur(2px);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 10000;\r\n}\r\n.sts-modal {\r\n background: var(--dsw-alias-bg-layer-3, #242428);\r\n border: 1px solid var(--dsw-alias-border-l3, #3a3a3e);\r\n border-radius: 12px;\r\n width: 480px;\r\n max-width: 90vw;\r\n max-height: 80vh;\r\n overflow-y: auto;\r\n display: flex;\r\n flex-direction: column;\r\n box-shadow: 0 8px 32px rgba(0,0,0,0.3);\r\n}\r\n.sts-modal-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 14px 20px;\r\n font-size: 15px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-modal-body {\r\n padding: 20px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 16px;\r\n}\r\n.sts-modal-field {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n.sts-modal-label {\r\n font-size: 11px;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n text-transform: uppercase;\r\n letter-spacing: 0.3px;\r\n}\r\n.sts-modal-input,\r\n.sts-modal-select {\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n font-size: 13px;\r\n font-family: inherit;\r\n outline: none;\r\n transition: border-color 0.12s, background 0.12s;\r\n}\r\n.sts-modal-input:focus,\r\n.sts-modal-select:focus {\r\n border-color: var(--dsw-alias-button-primary-fill, #007acc);\r\n background: var(--dsw-alias-bg-base, #1a1a1e);\r\n}\r\n.sts-modal-input.mono {\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n}\r\n.sts-env-row {\r\n display: flex;\r\n gap: 6px;\r\n align-items: center;\r\n}\r\n.sts-env-key {\r\n flex: 1;\r\n}\r\n.sts-env-val {\r\n flex: 2;\r\n}\r\n.sts-env-sep {\r\n color: var(--dsw-alias-label-dimmed, #555);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, \"DejaVu Sans Mono\", \"Liberation Mono\", monospace;\r\n}\r\n.sts-env-add {\r\n font-size: 12px;\r\n padding: 6px 10px;\r\n border: 1px dashed var(--dsw-alias-border-l3, #3a3a3e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n cursor: pointer;\r\n align-self: flex-start;\r\n transition: color 0.12s, border-color 0.12s;\r\n}\r\n.sts-env-add:hover {\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n border-color: var(--dsw-alias-button-primary-fill, #007acc);\r\n}\r\n.sts-modal-footer {\r\n display: flex;\r\n justify-content: flex-end;\r\n gap: 8px;\r\n padding: 14px 20px;\r\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n}\r\n.sts-modal-btn {\r\n padding: 8px 18px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 13px;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-modal-btn:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n\r\n/* ===== Directory tree picker ===== */\r\n.sts-cwd-display {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n cursor: pointer;\r\n transition: border-color 0.12s;\r\n font-size: 13px;\r\n}\r\n.sts-cwd-display:hover {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-cwd-icon {\r\n display: flex;\r\n align-items: center;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n}\r\n.sts-cwd-value {\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n.sts-tree-picker-container {\r\n margin-top: 4px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n max-height: 240px;\r\n overflow-y: auto;\r\n padding: 4px;\r\n}\r\n.sts-tree-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 4px 8px;\r\n cursor: pointer;\r\n border-radius: 4px;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n transition: background 0.08s;\r\n white-space: nowrap;\r\n}\r\n.sts-tree-row:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.04));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-tree-row.selected {\r\n background: var(--dsw-alias-interactive-bg-active, rgba(0,122,204,0.12));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-tree-chevron {\r\n display: flex;\r\n align-items: center;\r\n transition: transform 0.1s;\r\n flex-shrink: 0;\r\n}\r\n.sts-tree-chevron.expanded {\r\n transform: rotate(90deg);\r\n}\r\n.sts-tree-icon {\r\n display: flex;\r\n align-items: center;\r\n flex-shrink: 0;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n}\r\n.sts-tree-label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n}\r\n.sts-tree-loading,\r\n.sts-tree-error,\r\n.sts-tree-empty {\r\n padding: 8px 12px;\r\n font-size: 12px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-tree-error {\r\n color: #eb5757;\r\n}\r\n\r\n/* ===== Settings panel ===== */\r\n.sts-settings-panel {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n padding: 8px;\r\n min-width: 320px;\r\n}\r\n.sts-settings-row {\r\n padding: 4px 0;\r\n}\r\n.sts-settings-switch-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-settings-switch {\r\n width: 16px;\r\n height: 16px;\r\n cursor: pointer;\r\n}\r\n.sts-settings-sep {\r\n margin: 8px 0 4px;\r\n padding: 0 4px;\r\n font-size: 10px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-settings-path {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n padding: 4px 0;\r\n}\r\n.sts-settings-path-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n}\r\n.sts-settings-label {\r\n font-size: 12px;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n}\r\n.sts-settings-badge {\r\n display: flex;\r\n align-items: center;\r\n gap: 2px;\r\n font-size: 10px;\r\n padding: 1px 6px;\r\n border-radius: 10px;\r\n}\r\n.sts-settings-badge.ok {\r\n color: #4e9a06;\r\n background: rgba(78,154,6,0.12);\r\n}\r\n.sts-settings-badge.err {\r\n color: #eb5757;\r\n background: rgba(235,87,87,0.12);\r\n}\r\n.sts-settings-path-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n}\r\n.sts-settings-path-icon {\r\n display: flex;\r\n align-items: center;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n flex-shrink: 0;\r\n}\r\n.sts-settings-input {\r\n flex: 1;\r\n padding: 6px 8px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 4px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n font-size: 12px;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n outline: none;\r\n min-width: 0;\r\n}\r\n.sts-settings-input:focus {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-settings-input::placeholder {\r\n color: rgba(255,255,255,0.3);\r\n}\r\n.sts-settings-browse {\r\n padding: 6px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 4px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 12px;\r\n flex-shrink: 0;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-settings-browse:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-settings-msg {\r\n font-size: 10px;\r\n padding: 0 4px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-settings-msg.valid {\r\n color: #4e9a06;\r\n}\r\n.sts-settings-msg.invalid {\r\n color: #eb5757;\r\n}\r\n\r\n/* ===== Collapsible advanced section ===== */\r\n.sts-collapse-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n transition: border-color 0.12s;\r\n user-select: none;\r\n}\r\n.sts-collapse-header:hover {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-collapse-chevron {\r\n display: flex;\r\n align-items: center;\r\n transition: transform 0.12s;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n}\r\n.sts-collapse-chevron.expanded {\r\n transform: rotate(90deg);\r\n}\r\n.sts-collapse-hint {\r\n margin-left: auto;\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-collapse-body {\r\n margin-top: 8px;\r\n padding: 12px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n display: flex;\r\n flex-direction: column;\r\n gap: 10px;\r\n}\r\n\r\n/* ===== Settings picker rows & dir chooser modal ===== */\r\n.sts-settings-picker-row {\r\n margin: 6px 0;\r\n}\r\n.sts-settings-picker-row .sts-settings-label {\r\n display: block;\r\n margin-bottom: 4px;\r\n font-size: 11px;\r\n}\r\n.sts-dir-modal {\r\n background: var(--dsw-alias-bg-layer-3, #242428);\r\n border: 1px solid var(--dsw-alias-border-l3, #3a3a3e);\r\n border-radius: 12px;\r\n width: 420px;\r\n max-width: 90vw;\r\n max-height: 70vh;\r\n display: flex;\r\n flex-direction: column;\r\n box-shadow: 0 8px 32px rgba(0,0,0,0.3);\r\n}\r\n.sts-dir-modal-tree {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 8px;\r\n min-height: 160px;\r\n}\r\n.sts-dirtree {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 2px;\r\n}\r\n.sts-dirtree-nav {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 4px 4px 8px;\r\n border-bottom: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n margin-bottom: 4px;\r\n}\r\n.sts-dirtree-up,\r\n.sts-dirtree-home {\r\n width: 24px;\r\n height: 24px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 4px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary, #b0b0b0);\r\n cursor: pointer;\r\n font-size: 13px;\r\n flex-shrink: 0;\r\n transition: background 0.12s, color 0.12s;\r\n}\r\n.sts-dirtree-up:hover,\r\n.sts-dirtree-home:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n}\r\n.sts-dirtree-up:disabled {\r\n opacity: 0.4;\r\n cursor: default;\r\n}\r\n.sts-dirtree-cur {\r\n flex: 1;\r\n font-size: 11px;\r\n color: var(--dsw-alias-label-tertiary, #888);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n direction: rtl;\r\n text-align: left;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n}\r\n.sts-tree-select-hint {\r\n margin-left: auto;\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n.sts-dirtree-sub {\r\n border-top: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n margin-top: 2px;\r\n padding-top: 2px;\r\n}\r\n\r\n/* ===== Env bulk paste ===== */\r\n.sts-env-actions {\r\n display: flex;\r\n gap: 6px;\r\n align-items: center;\r\n margin-top: 2px;\r\n}\r\n.sts-env-paste {\r\n margin-top: 8px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n.sts-env-paste-input {\r\n width: 100%;\r\n padding: 8px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: var(--dsw-alias-bg-layer-1, #1a1a1e);\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n font-size: 12px;\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n outline: none;\r\n resize: vertical;\r\n min-height: 80px;\r\n box-sizing: border-box;\r\n}\r\n.sts-env-paste-input:focus {\r\n border-color: var(--dsw-alias-border-l3, #3a3a3e);\r\n}\r\n.sts-env-paste-input::placeholder {\r\n color: rgba(255,255,255,0.3);\r\n font-family: ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Cascadia Code\", Menlo, Consolas, monospace;\r\n}\r\n.sts-env-paste-footer {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n}\r\n.sts-env-paste-apply {\r\n padding: 6px 14px;\r\n border: 1px solid var(--dsw-alias-border-l2, #2a2a2e);\r\n border-radius: 6px;\r\n background: transparent;\r\n color: var(--dsw-alias-label-primary, #e6e6e6);\r\n cursor: pointer;\r\n font-size: 12px;\r\n transition: background 0.12s;\r\n}\r\n.sts-env-paste-apply:hover {\r\n background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.06));\r\n}\r\n.sts-env-paste-hint {\r\n font-size: 10px;\r\n color: rgba(255,255,255,0.4);\r\n}\r\n`\r\n","/**\n * Services tab: the main sidebar panel component. Left 1/3 config tree,\n * right 2/3 log view or config detail. Manages config CRUD via the host\n * REST API and tracks running instances.\n * @module dsh-better-sidebar-starter/client/ServicesTab\n */\n\nimport { createElement, useState, useEffect, useRef, useCallback, type ReactNode } from 'react'\nimport type { TabComponentProps } from 'dsh-better-sidebar/client/service'\nimport type { RunConfig, RunInstance } from './types.ts'\nimport { ConfigTree } from './ConfigTree.tsx'\nimport { LogView } from './LogView.tsx'\nimport { LogHistoryView } from './LogHistoryView.tsx'\nimport { ConfigEditorModal, type ConfigEditorModalProps } from './ConfigEditorModal.tsx'\nimport { RocketIcon, PlusIcon } from './icons.tsx'\nimport { STYLES_CSS } from './styles.ts'\n\n/** API base. */\nconst API_BASE = '/api/dsh-better-sidebar-starter'\n\n/** Instance poll interval (ms). */\nconst POLL_INTERVAL = 3000\n\n/** Inject styles once. */\nlet STYLES_INJECTED = false\nfunction injectStyles(): void {\n if (STYLES_INJECTED) return\n STYLES_INJECTED = true\n const tag = document.createElement('style')\n tag.id = 'dsh-better-sidebar-starter-styles'\n tag.setAttribute('data-plugin', '@dong-victor/dsh-better-sidebar-starter')\n tag.textContent = STYLES_CSS\n document.head.appendChild(tag)\n}\n\n/** The main services tab component. */\nexport function ServicesTab(props: TabComponentProps): ReactNode {\n injectStyles()\n\n const { scope, store } = props\n const [configs, setConfigs] = useState<RunConfig[]>([])\n const [instances, setInstances] = useState<RunInstance[]>([])\n const [selectedConfigId, setSelectedConfigId] = useState<string | null>(null)\n const [selectedInstanceId, setSelectedInstanceId] = useState<string | null>(null)\n const [editingConfig, setEditingConfig] = useState<ConfigEditorModalProps['config'] | null>(null)\n const [showModal, setShowModal] = useState(false)\n const [splitRatio, setSplitRatio] = useState(0.33)\n const [dragging, setDragging] = useState(false)\n const [isHorizontal, setIsHorizontal] = useState(true) // true = left/right split (right panel), false = top/bottom (bottom panel)\n\n const containerRef = useRef<HTMLDivElement>(null)\n\n // Detect panel orientation via ResizeObserver (aspect ratio).\n useEffect(() => {\n const el = containerRef.current\n if (el === null) return\n const check = (): void => {\n const rect = el.getBoundingClientRect()\n // If wider than tall → horizontal split (left/right). If taller → vertical (top/bottom).\n setIsHorizontal(rect.width > rect.height * 1.2)\n }\n check()\n const ro = new ResizeObserver(check)\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n // Read split ratio from plugin settings on mount.\n useEffect(() => {\n const blob = store.getPrefs()?.pluginSettings?.['dsh-better-sidebar-starter:services'] ?? {}\n if (typeof blob.splitRatio === 'number') {\n setSplitRatio(Math.max(0.2, Math.min(0.5, blob.splitRatio)))\n }\n }, [store])\n\n /** Persist split ratio to plugin settings. */\n const persistSplitRatio = useCallback((ratio: number): void => {\n const service = (store as any).service\n if (service !== undefined && typeof service.updateTab === 'function') {\n // Use pluginSettings persistence if available.\n }\n }, [store])\n\n /** Build a query string for the session scope. */\n const sessionQuery = useCallback((): string => {\n const params = new URLSearchParams({ sessionId: scope.sessionId })\n if (scope.cwd !== undefined) params.set('cwd', scope.cwd)\n return params.toString()\n }, [scope])\n\n /** Fetch configs from the host. */\n const fetchConfigs = useCallback(async (): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}`)\n if (resp.ok) {\n const data = await resp.json() as { configs: RunConfig[] }\n setConfigs(data.configs)\n }\n } catch { /* network error — ignore */ }\n }, [sessionQuery])\n\n /** Fetch instances from the host. */\n const fetchInstances = useCallback(async (): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/instances?${sessionQuery()}`)\n if (resp.ok) {\n const data = await resp.json() as { instances: RunInstance[] }\n setInstances(data.instances)\n }\n } catch { /* network error — ignore */ }\n }, [sessionQuery])\n\n // Initial load.\n useEffect(() => {\n void fetchConfigs()\n void fetchInstances()\n }, [fetchConfigs, fetchInstances])\n\n // Poll instances.\n useEffect(() => {\n const timer = setInterval(() => void fetchInstances(), POLL_INTERVAL)\n return () => clearInterval(timer)\n }, [fetchInstances])\n\n /** Start a config. */\n const handleStart = useCallback(async (configId: string): Promise<void> => {\n try {\n // Read global env paths from plugin settings.\n const blob = store.getPrefs()?.pluginSettings?.['dsh-better-sidebar-starter:services'] ?? {}\n const globalEnv: Record<string, string> = {}\n if (typeof blob.javaHome === 'string' && blob.javaHome.trim() !== '') globalEnv.JAVA_HOME = blob.javaHome.trim()\n if (typeof blob.nodePath === 'string' && blob.nodePath.trim() !== '') globalEnv.NODE_PATH = blob.nodePath.trim()\n if (typeof blob.pythonPath === 'string' && blob.pythonPath.trim() !== '') globalEnv.PYTHON_PATH = blob.pythonPath.trim()\n if (typeof blob.mvnPath === 'string' && blob.mvnPath.trim() !== '') globalEnv.MVN_PATH = blob.mvnPath.trim()\n\n const resp = await fetch(`${API_BASE}/run?${sessionQuery()}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ configId, globalEnv }),\n })\n if (resp.ok) {\n const data = await resp.json() as { instance: RunInstance }\n setSelectedInstanceId(data.instance.id)\n setSelectedConfigId(null)\n void fetchInstances()\n void fetchConfigs() // lastRunAt update\n }\n } catch { /* ignore */ }\n }, [sessionQuery, fetchInstances, fetchConfigs, store])\n\n /** Stop an instance. */\n const handleStop = useCallback(async (instanceId: string): Promise<void> => {\n try {\n await fetch(`${API_BASE}/stop`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ instanceId }),\n })\n void fetchInstances()\n } catch { /* ignore */ }\n }, [fetchInstances])\n\n /** Restart = stop + start the same config. */\n const handleRestart = useCallback(async (instanceId: string): Promise<void> => {\n const inst = instances.find((i) => i.id === instanceId)\n if (inst === undefined) return\n await handleStop(instanceId)\n // Small delay to let the process die.\n await new Promise((r) => setTimeout(r, 200))\n await handleStart(inst.configId)\n }, [instances, handleStop, handleStart])\n\n /** Open the new config modal. */\n const handleNewConfig = (): void => {\n setEditingConfig(null)\n setShowModal(true)\n }\n\n /** Open the edit config modal. */\n const handleEditConfig = (configId: string): void => {\n const config = configs.find((c) => c.id === configId)\n if (config === undefined) return\n setEditingConfig({\n id: config.id,\n name: config.name,\n type: config.type,\n command: config.command,\n cwd: config.cwd,\n env: config.env,\n jvmArgs: config.jvmArgs,\n args: config.args,\n runtime: config.runtime,\n })\n setShowModal(true)\n }\n\n /**\n * Duplicate a config: open the editor prefilled with ALL of the original\n * config's info under an auto-generated \"xxx-副本\" name (deduped against\n * existing names). Saving it (id omitted) creates a brand-new config.\n */\n const handleDuplicateConfig = (configId: string): void => {\n const config = configs.find((c) => c.id === configId)\n if (config === undefined) return\n // Derive a unique copy name: strip a trailing \"-副本\", then append\n // \"-副本\" (or \"-副本2\", \"-副本3\", …) while avoiding collisions.\n const stem = config.name.endsWith('-副本') ? config.name.slice(0, -3) : config.name\n const taken = new Set(configs.map((c) => c.name))\n let dupName = `${stem}-副本`\n let suffix = 2\n while (taken.has(dupName)) {\n dupName = `${stem}-副本${suffix}`\n suffix++\n }\n setEditingConfig({\n // No id → saving creates a new config instead of updating the source.\n name: dupName,\n type: config.type,\n command: config.command,\n cwd: config.cwd,\n env: config.env,\n jvmArgs: config.jvmArgs,\n args: config.args,\n runtime: config.runtime,\n })\n setShowModal(true)\n }\n\n /** Save a config (create or update). */\n const handleSaveConfig = useCallback(async (config: ConfigEditorModalProps['config']): Promise<void> => {\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ config }),\n })\n if (resp.ok) {\n setShowModal(false)\n setEditingConfig(null)\n void fetchConfigs()\n }\n } catch { /* ignore */ }\n }, [sessionQuery, fetchConfigs])\n\n /** Delete a config. */\n const handleDeleteConfig = useCallback(async (configId: string): Promise<void> => {\n const target = configs.find((c) => c.id === configId)\n if (!globalThis.confirm(`确定删除配置「${target?.name ?? configId}」?运行中的实例将被一并停止。`)) return\n // Stop any running instances first.\n for (const inst of instances) {\n if (inst.configId === configId && inst.status === 'running') {\n await handleStop(inst.id)\n }\n }\n try {\n const resp = await fetch(`${API_BASE}/configs?${sessionQuery()}&id=${encodeURIComponent(configId)}`, {\n method: 'DELETE',\n })\n if (resp.ok) {\n if (selectedConfigId === configId) setSelectedConfigId(null)\n void fetchConfigs()\n void fetchInstances()\n }\n } catch { /* ignore */ }\n }, [configs, instances, handleStop, sessionQuery, fetchConfigs, fetchInstances, selectedConfigId])\n\n /** Select a config (deselect any instance). */\n const handleSelectConfig = (configId: string): void => {\n setSelectedConfigId(configId)\n setSelectedInstanceId(null)\n }\n\n /** Select an instance. */\n const handleSelectInstance = (instanceId: string): void => {\n setSelectedInstanceId(instanceId)\n setSelectedConfigId(null)\n }\n\n // -- Split pane drag handling --\n const handleMouseDown = (e: MouseEvent): void => {\n e.preventDefault()\n setDragging(true)\n }\n\n useEffect(() => {\n if (!dragging) return\n const handleMove = (e: MouseEvent): void => {\n if (containerRef.current === null) return\n const rect = containerRef.current.getBoundingClientRect()\n const ratio = isHorizontal\n ? (e.clientX - rect.left) / rect.width\n : (e.clientY - rect.top) / rect.height\n const clamped = Math.max(0.2, Math.min(0.5, ratio))\n setSplitRatio(clamped)\n persistSplitRatio(clamped)\n }\n const handleUp = (): void => setDragging(false)\n document.addEventListener('mousemove', handleMove)\n document.addEventListener('mouseup', handleUp)\n return () => {\n document.removeEventListener('mousemove', handleMove)\n document.removeEventListener('mouseup', handleUp)\n }\n }, [dragging, isHorizontal, persistSplitRatio])\n\n // Resolve what to show in the right pane.\n const selectedInstance = selectedInstanceId !== null\n ? instances.find((i) => i.id === selectedInstanceId) ?? null\n : null\n const selectedConfig = selectedConfigId !== null\n ? configs.find((c) => c.id === selectedConfigId) ?? null\n : null\n\n return createElement('div', { ref: containerRef, className: 'sts-root' },\n // Toolbar\n createElement('div', { className: 'sts-toolbar' },\n createElement('div', { className: 'sts-toolbar-title' },\n createElement(RocketIcon, { size: 16 }),\n '服务',\n ),\n createElement('button', {\n className: 'sts-toolbar-btn',\n onClick: handleNewConfig,\n },\n createElement(PlusIcon, { size: 13 }), '新建配置'),\n ),\n // Split pane (horizontal or vertical based on panel position)\n createElement('div', { className: `sts-split${isHorizontal ? '' : ' sts-split-vertical'}` },\n // First pane: config tree\n createElement('div', {\n className: `sts-split-first${isHorizontal ? '' : ' sts-split-first-v'}`,\n style: isHorizontal\n ? { width: `${splitRatio * 100}%` }\n : { height: `${splitRatio * 100}%` },\n },\n configs.length === 0\n ? createElement('div', { className: 'sts-empty' }, '暂无运行配置,点击右上角 + 新建')\n : createElement(ConfigTree, {\n configs,\n instances,\n selectedInstanceId,\n selectedConfigId,\n onSelectConfig: handleSelectConfig,\n onSelectInstance: handleSelectInstance,\n onStartConfig: (id: string) => { void handleStart(id) },\n onStopInstance: (id: string) => { void handleStop(id) },\n onEditConfig: handleEditConfig,\n onDuplicateConfig: handleDuplicateConfig,\n onDeleteConfig: (id: string) => { void handleDeleteConfig(id) },\n }),\n ),\n // Gutter\n createElement('div', {\n className: `sts-split-gutter${dragging ? ' dragging' : ''}${isHorizontal ? '' : ' sts-split-gutter-v'}`,\n onMouseDown: handleMouseDown,\n }),\n // Second pane: log view or config history or empty\n createElement('div', { className: `sts-split-second${isHorizontal ? '' : ' sts-split-second-v'}` },\n selectedInstance !== null\n ? createElement(LogView, {\n instance: selectedInstance,\n ctx: props.ctx,\n sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? '',\n cwd: scope.cwd,\n onStop: () => { void handleStop(selectedInstance.id) },\n onRestart: () => { void handleRestart(selectedInstance.id) },\n })\n : selectedConfig !== null\n // Clicking a config shows its persisted run logs directly\n // (no config-detail pane).\n ? createElement(LogHistoryView, {\n ctx: props.ctx,\n sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? '',\n cwd: scope.cwd,\n filter: selectedConfig.name,\n onClose: () => { setSelectedConfigId(null) },\n })\n : createElement('div', { className: 'sts-empty' }, '选择左侧配置查看其历史日志,或选择运行中的服务查看实时日志'),\n ),\n ),\n // Modal\n showModal\n ? createElement(ConfigEditorModal, {\n config: editingConfig,\n scope: { sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? '', cwd: scope.cwd },\n onSave: (c) => { void handleSaveConfig(c) },\n onCancel: () => { setShowModal(false); setEditingConfig(null) },\n })\n : null,\n )\n}\n","/**\n * Custom settings panel for the \"服务\" tab: renders switch toggles plus\n * validated directory pickers for Java/Node/Python/Maven paths.\n * @module dsh-better-sidebar-starter/client/SettingsPanel\n */\n\nimport { createElement, useState, useEffect, type ReactNode } from 'react'\nimport type { SidebarSettingsRenderProps, SessionScope } from 'dsh-better-sidebar/client/service'\nimport { PathPicker } from './PathPicker.tsx'\n\n/** The custom settings panel rendered in the gear popup. */\nexport function SettingsPanel(props: SidebarSettingsRenderProps): ReactNode {\n const { store, pluginSettings, updatePluginSetting } = props\n const scope: SessionScope = { sessionId: store.getSnapshot().sessionId ?? '' }\n\n const get = (key: string): string =>\n typeof pluginSettings[key] === 'string' ? pluginSettings[key] as string : ''\n const getBool = (key: string): boolean =>\n pluginSettings[key] === true\n\n const [autoSelect, setAutoSelect] = useState(getBool('autoSelectOnStart'))\n const [autoScroll, setAutoScroll] = useState(getBool('autoScroll'))\n const [javaHome, setJavaHome] = useState(get('javaHome'))\n const [nodePath, setNodePath] = useState(get('nodePath'))\n const [pythonPath, setPythonPath] = useState(get('pythonPath'))\n const [mvnPath, setMvnPath] = useState(get('mvnPath'))\n\n // Auto-detect environment paths on first open if not yet configured.\n useEffect(() => {\n const needDetect = get('javaHome') === '' && get('nodePath') === '' && get('pythonPath') === '' && get('mvnPath') === ''\n if (!needDetect) return\n let cancelled = false\n fetch('/api/dsh-better-sidebar-starter/detect-env')\n .then((r) => r.ok ? r.json() as Promise<{ javaHome?: string; nodePath?: string; pythonPath?: string; mvnPath?: string }> : null)\n .then((detected) => {\n if (cancelled || detected === null) return\n if (detected.javaHome !== undefined && detected.javaHome !== '') {\n setJavaHome(detected.javaHome)\n updatePluginSetting('javaHome', detected.javaHome)\n }\n if (detected.nodePath !== undefined && detected.nodePath !== '') {\n setNodePath(detected.nodePath)\n updatePluginSetting('nodePath', detected.nodePath)\n }\n if (detected.pythonPath !== undefined && detected.pythonPath !== '') {\n setPythonPath(detected.pythonPath)\n updatePluginSetting('pythonPath', detected.pythonPath)\n }\n if (detected.mvnPath !== undefined && detected.mvnPath !== '') {\n setMvnPath(detected.mvnPath)\n updatePluginSetting('mvnPath', detected.mvnPath)\n }\n })\n .catch(() => { /* ignore */ })\n return () => { cancelled = true }\n }, [])\n\n const handleSwitch = (key: string, setter: (v: boolean) => void) => (v: boolean): void => {\n setter(v)\n updatePluginSetting(key, v)\n }\n const handlePath = (key: string, setter: (v: string) => void) => (v: string): void => {\n setter(v)\n updatePluginSetting(key, v)\n }\n\n return createElement('div', { className: 'sts-settings-panel' },\n // Switches\n createElement('div', { className: 'sts-settings-row' },\n createElement('label', { className: 'sts-settings-switch-row' },\n createElement('input', {\n type: 'checkbox',\n className: 'sts-settings-switch',\n checked: autoSelect,\n onChange: (e: { target: { checked: boolean } }) => handleSwitch('autoSelectOnStart', setAutoSelect)(e.target.checked),\n }),\n createElement('span', null, '\\u542f\\u52a8\\u540e\\u81ea\\u52a8\\u9009\\u4e2d\\u65e5\\u5fd7'),\n ),\n ),\n createElement('div', { className: 'sts-settings-row' },\n createElement('label', { className: 'sts-settings-switch-row' },\n createElement('input', {\n type: 'checkbox',\n className: 'sts-settings-switch',\n checked: autoScroll,\n onChange: (e: { target: { checked: boolean } }) => handleSwitch('autoScroll', setAutoScroll)(e.target.checked),\n }),\n createElement('span', null, '\\u81ea\\u52a8\\u6eda\\u52a8\\u5230\\u6700\\u65b0\\u65e5\\u5fd7'),\n ),\n ),\n // Separator\n createElement('div', { className: 'sts-settings-sep' }, '\\u8fd0\\u884c\\u73af\\u5883\\u8def\\u5f84'),\n // Path pickers (each wrapped with a label row)\n createElement(PathPickerRow, {\n label: 'Java \\u4e3b\\u76ee\\u5f55 (JAVA_HOME)',\n scope,\n kind: 'java',\n value: javaHome,\n onChange: handlePath('javaHome', setJavaHome),\n desc: 'Spring Boot \\u7528\\u7684 JDK\\uff0c\\u7559\\u7a7a\\u5219\\u81ea\\u52a8\\u67e5\\u627e',\n }),\n createElement(PathPickerRow, {\n label: 'Node.js \\u8def\\u5f84',\n scope,\n kind: 'node',\n value: nodePath,\n onChange: handlePath('nodePath', setNodePath),\n desc: 'npm \\u7528\\u7684 Node\\uff0c\\u7559\\u7a7a\\u5219\\u7528\\u7cfb\\u7edf\\u9ed8\\u8ba4',\n }),\n createElement(PathPickerRow, {\n label: 'Python \\u8def\\u5f84',\n scope,\n kind: 'python',\n value: pythonPath,\n onChange: handlePath('pythonPath', setPythonPath),\n desc: 'Python \\u89e3\\u91ca\\u5668\\uff0c\\u7559\\u7a7a\\u5219\\u7528\\u7cfb\\u7edf\\u9ed8\\u8ba4',\n }),\n createElement(PathPickerRow, {\n label: 'Maven \\u8def\\u5f84 (MVN)',\n scope,\n kind: 'mvn',\n value: mvnPath,\n onChange: handlePath('mvnPath', setMvnPath),\n desc: 'springboot \\u7528\\u7684 Maven\\uff0c\\u7559\\u7a7a\\u5219\\u81ea\\u52a8\\u67e5\\u627e',\n }),\n )\n}\n\n/** A labeled path picker row for the settings panel. */\nfunction PathPickerRow(props: {\n label: string\n scope: SessionScope\n kind: 'java' | 'node' | 'python' | 'mvn'\n value: string\n onChange: (v: string) => void\n desc: string\n}): ReactNode {\n return createElement('div', { className: 'sts-settings-picker-row' },\n createElement('label', { className: 'sts-settings-label' }, props.label),\n createElement(PathPicker, {\n scope: props.scope,\n kind: props.kind,\n value: props.value,\n onChange: props.onChange,\n desc: props.desc,\n }),\n )\n}\n","/**\n * Client half of the dsh-better-sidebar-starter plugin: registers the\n * \"服务\" (Services) sidebar tab through the `ctx.betterSidebar` service.\n * The tab renders a split-pane panel: left config tree + right log/detail.\n * The host half (same package) owns the session-scoped config/process routes.\n * @module dsh-better-sidebar-starter/client\n */\n\nimport { createElement, type ReactNode } from 'react'\nimport type { Context } from 'cordis'\n// Triggers the ctx.betterSidebar type augmentation (erased at build).\nimport type {} from 'dsh-better-sidebar'\nimport { ServicesTab } from './ServicesTab.tsx'\nimport { SettingsPanel } from './SettingsPanel.tsx'\nimport { RocketIcon } from './icons.tsx'\n\n/** Services required before mounting: the betterSidebar registry (provided\n * by dsh-better-sidebar's own client half) and the session store. */\nexport const inject = ['betterSidebar', 'sessions']\n\n/** Plugin body. */\nexport function apply(ctx: Context): void {\n // The \"服务\" sidebar tab.\n ctx.effect(() =>\n ctx.betterSidebar.registerTab({\n id: 'dsh-better-sidebar-starter:services',\n title: () => '服务',\n icon: (size: number): ReactNode => createElement(RocketIcon, { size }),\n order: 50,\n single: true,\n settings: {\n pluginToggles: [\n { key: 'autoSelectOnStart', type: 'switch', title: '启动后自动选中日志' },\n { key: 'autoScroll', type: 'switch', title: '自动滚动到最新日志' },\n { key: 'javaHome', type: 'text', title: 'Java 主目录 (JAVA_HOME)', desc: 'Spring Boot 用的 JDK 路径,留空则自动查找' },\n { key: 'nodePath', type: 'text', title: 'Node.js 路径', desc: 'npm 命令用的 Node 路径,留空则用系统默认' },\n { key: 'pythonPath', type: 'text', title: 'Python 路径', desc: 'Python 命令用的解释器路径,留空则用系统默认' },\n ],\n render: (props): ReactNode => createElement(SettingsPanel, props),\n },\n component: (props): ReactNode => createElement(ServicesTab, props),\n }),\n 'dsh-better-sidebar-starter: services tab',\n )\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dong-victor/dsh-better-sidebar-starter",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "dsh-better-sidebar 的运行配置插件:模仿 IntelliJ IDEA 运行/调试配置,支持 npm/springboot/python/custom 命令执行、历史保存、快捷启动、实时日志查看,单 Tab 内分栏布局(左配置树 + 右日志流)",
5
5
  "type": "module",
6
6
  "engines": {