@dong-victor/dsh-better-sidebar-starter 0.1.3 → 0.1.6

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
@@ -143,7 +143,7 @@ window.__ModuleLoader__.load({
143
143
  "python",
144
144
  "custom"
145
145
  ];
146
- const TYPE_LABELS$2 = {
146
+ const TYPE_LABELS$1 = {
147
147
  npm: "npm",
148
148
  springboot: "Spring Boot",
149
149
  python: "Python",
@@ -187,7 +187,7 @@ window.__ModuleLoader__.load({
187
187
  if (configs.length === 0) return (0, react.createElement)("div", { className: "sts-empty" }, "暂无运行配置", (0, react.createElement)("br"), "点击右上角 + 新建配置");
188
188
  return (0, react.createElement)("div", { className: "sts-tree" }, ...groups.map((group) => {
189
189
  const isCollapsed = collapsed.has(group.type);
190
- const label = TYPE_LABELS$2[group.type] ?? group.type;
190
+ const label = TYPE_LABELS$1[group.type] ?? group.type;
191
191
  const runningCount = instances.filter((inst) => inst.configType === group.type && inst.status === "running").length;
192
192
  return (0, react.createElement)("div", { key: group.type }, (0, react.createElement)("div", {
193
193
  className: "sts-group-header",
@@ -628,42 +628,105 @@ window.__ModuleLoader__.load({
628
628
  /** Render ANSI-colored text segments, applying Java-log syntax highlighting
629
629
  * to text the program did not color itself (ANSI-colored spans keep the
630
630
  * program's own colors). Tokens with a source link become clickable. */
631
+ /** Split plain text into plain/url parts. Trailing punctuation that is
632
+ * almost certainly sentence text (.,;:)])。,) stays outside the link. */
633
+ const URL_RE = /https?:\/\/[^\s"'<>()\[\]{}]+/g;
634
+ function splitUrlParts(text) {
635
+ const out = [];
636
+ let last = 0;
637
+ for (const m of text.matchAll(URL_RE)) {
638
+ const idx = m.index ?? 0;
639
+ if (idx > last) out.push({ text: text.slice(last, idx) });
640
+ let url = m[0];
641
+ let trail = "";
642
+ while (url.length > 0 && /[.,;:)\]}》」),。;:]$/.test(url)) {
643
+ trail = url.slice(-1) + trail;
644
+ url = url.slice(0, -1);
645
+ }
646
+ if (url !== "") out.push({
647
+ text: url,
648
+ url
649
+ });
650
+ if (trail !== "") out.push({ text: trail });
651
+ last = idx + m[0].length;
652
+ }
653
+ if (last < text.length) out.push({ text: text.slice(last) });
654
+ return out;
655
+ }
631
656
  function renderAnsiLine(text, keyBase, onLinkClick) {
632
657
  const segments = parseAnsi(text);
633
658
  const nodes = [];
659
+ if (segments.length === 0) return nodes;
634
660
  let hlCounter = 0;
635
- for (let i = 0; i < segments.length; i++) {
636
- const seg = segments[i];
637
- const fg = segmentFgColor(seg);
638
- const bg = segmentBgColor(seg);
639
- const baseStyle = {};
640
- if (seg.bold) baseStyle.fontWeight = "bold";
641
- if (seg.dim) baseStyle.opacity = "0.6";
642
- if (seg.italic) baseStyle.fontStyle = "italic";
643
- if (seg.underline) baseStyle.textDecoration = "underline";
644
- if (fg !== null || bg !== null) {
645
- const style = { ...baseStyle };
661
+ let fullText = "";
662
+ const ranges = [];
663
+ for (const seg of segments) {
664
+ ranges.push({
665
+ seg,
666
+ start: fullText.length,
667
+ end: fullText.length + seg.text.length
668
+ });
669
+ fullText += seg.text;
670
+ }
671
+ const parts = splitUrlParts(fullText);
672
+ let offset = 0;
673
+ for (const part of parts) {
674
+ const pStart = offset;
675
+ const pEnd = offset + part.text.length;
676
+ offset = pEnd;
677
+ for (const r of ranges) {
678
+ const s = Math.max(r.start, pStart);
679
+ const e = Math.min(r.end, pEnd);
680
+ if (s >= e) continue;
681
+ const slice = fullText.slice(s, e);
682
+ const seg = r.seg;
683
+ const style = {};
684
+ if (seg.bold) style.fontWeight = "bold";
685
+ if (seg.dim) style.opacity = "0.6";
686
+ if (seg.italic) style.fontStyle = "italic";
687
+ if (seg.underline) style.textDecoration = "underline";
688
+ const fg = segmentFgColor(seg);
689
+ const bg = segmentBgColor(seg);
646
690
  if (fg !== null) style.color = fg;
647
691
  if (bg !== null) style.backgroundColor = bg;
648
- nodes.push((0, react.createElement)("span", {
649
- key: `${keyBase}-${i}`,
650
- style
651
- }, seg.text));
652
- } else {
653
- const hl = highlightLogText(seg.text);
654
- for (const h of hl) {
655
- const style = { ...baseStyle };
656
- if (h.color !== void 0) style.color = h.color;
657
- if (h.bold === true) style.fontWeight = "bold";
692
+ if (part.url !== void 0) {
693
+ const url = part.url;
658
694
  nodes.push((0, react.createElement)("span", {
659
- key: `${keyBase}-${i}-hl${hlCounter++}`,
660
- className: h.link !== void 0 ? "sts-log-link" : void 0,
661
- style,
662
- onClick: h.link !== void 0 ? (e) => {
695
+ key: `${keyBase}-u${hlCounter++}`,
696
+ className: "sts-log-url",
697
+ style: {
698
+ ...style,
699
+ textDecoration: "underline",
700
+ cursor: "pointer"
701
+ },
702
+ title: `在浏览器打开 ${url}`,
703
+ onClick: (e) => {
663
704
  e.stopPropagation();
664
- onLinkClick(h.link);
665
- } : void 0
666
- }, h.text));
705
+ try {
706
+ window.open(url, "_blank", "noopener");
707
+ } catch {}
708
+ }
709
+ }, slice));
710
+ } else if (fg !== null || bg !== null) nodes.push((0, react.createElement)("span", {
711
+ key: `${keyBase}-c${hlCounter++}`,
712
+ style
713
+ }, slice));
714
+ else {
715
+ const hl = highlightLogText(slice);
716
+ for (const h of hl) {
717
+ const hStyle = { ...style };
718
+ if (h.color !== void 0) hStyle.color = h.color;
719
+ if (h.bold === true) hStyle.fontWeight = "bold";
720
+ nodes.push((0, react.createElement)("span", {
721
+ key: `${keyBase}-hl${hlCounter++}`,
722
+ className: h.link !== void 0 ? "sts-log-link" : void 0,
723
+ style: hStyle,
724
+ onClick: h.link !== void 0 ? (e) => {
725
+ e.stopPropagation();
726
+ onLinkClick(h.link);
727
+ } : void 0
728
+ }, h.text));
729
+ }
667
730
  }
668
731
  }
669
732
  }
@@ -870,7 +933,7 @@ window.__ModuleLoader__.load({
870
933
  if (parts.length > 1 && parts[parts.length - 1] === "") pushLine();
871
934
  }
872
935
  /** The log history viewer. */
873
- function LogHistoryView({ ctx, sessionId, cwd, onClose }) {
936
+ function LogHistoryView({ ctx, sessionId, cwd, onClose, filter }) {
874
937
  const [files, setFiles] = (0, react.useState)(null);
875
938
  const [loading, setLoading] = (0, react.useState)(false);
876
939
  const [activeFile, setActiveFile] = (0, react.useState)(null);
@@ -884,7 +947,7 @@ window.__ModuleLoader__.load({
884
947
  if (cwd !== void 0 && cwd !== "") params.set("cwd", cwd);
885
948
  const resp = await fetch(`${API_BASE$1}/logs-history?${params.toString()}`);
886
949
  if (!resp.ok) {
887
- let reason = "加载历史日志失败";
950
+ let reason = filter !== void 0 ? "加载历史日志失败" : "加载历史日志失败";
888
951
  try {
889
952
  const err = await resp.json();
890
953
  if (typeof err.error === "string" && err.error !== "") reason = err.error;
@@ -893,15 +956,20 @@ window.__ModuleLoader__.load({
893
956
  setFiles([]);
894
957
  return;
895
958
  }
896
- const data = await resp.json();
897
- setFiles(data.logs ?? []);
959
+ const all = (await resp.json()).logs ?? [];
960
+ const shown = filter !== void 0 ? all.filter((f) => f.name.startsWith(filter)) : all;
961
+ setFiles(shown);
898
962
  } catch {
899
963
  showToast("加载历史日志失败", false);
900
964
  setFiles([]);
901
965
  } finally {
902
966
  setLoading(false);
903
967
  }
904
- }, [sessionId, cwd]);
968
+ }, [
969
+ sessionId,
970
+ cwd,
971
+ filter
972
+ ]);
905
973
  const [initialized, setInitialized] = (0, react.useState)(false);
906
974
  if (!initialized) {
907
975
  setInitialized(true);
@@ -941,40 +1009,43 @@ window.__ModuleLoader__.load({
941
1009
  setLoading(false);
942
1010
  }
943
1011
  }, [sessionId, cwd]);
944
- if (activeFile === null) return (0, react.createElement)("div", { style: {
945
- display: "flex",
946
- flexDirection: "column",
947
- height: "100%",
948
- minHeight: 0
949
- } }, (0, react.createElement)("div", { className: "sts-log-toolbar" }, "历史日志", (0, react.createElement)("div", { className: "sts-log-toolbar-spacer" }), (0, react.createElement)("button", {
950
- className: "sts-icon-btn",
951
- title: "刷新",
952
- onClick: () => {
953
- loadList();
954
- }
955
- }, RefreshIcon({ size: 14 }), (0, react.createElement)("span", { className: "sts-icon-btn-label" }, "刷新")), (0, react.createElement)("button", {
956
- className: "sts-icon-btn",
957
- title: "关闭历史日志",
958
- onClick: onClose
959
- }, CloseIcon({ size: 14 }), (0, react.createElement)("span", { className: "sts-icon-btn-label" }, "关闭"))), (0, react.createElement)("div", { style: {
960
- overflow: "auto",
961
- flex: 1,
962
- minHeight: 0,
963
- padding: "4px 0"
964
- } }, loading && files === null ? (0, react.createElement)("div", { className: "sts-empty" }, "加载中…") : files !== null && files.length === 0 ? (0, react.createElement)("div", { className: "sts-empty" }, "暂无历史日志(工作空间 .dsh/logs 为空)。每次启动服务都会在 <工作空间>/.dsh/logs 下永久保留一份日志,即使启动失败或实例被清理也能查看。") : (0, react.createElement)("div", {}, (files ?? []).map((f) => (0, react.createElement)("button", {
965
- key: f.file,
966
- className: "sts-history-item",
967
- title: f.file,
968
- onClick: () => {
969
- openFile(f);
970
- }
971
- }, (0, react.createElement)(FileIcon, { size: 13 }), (0, react.createElement)("span", { style: {
972
- flex: 1,
973
- textAlign: "left",
974
- overflow: "hidden",
975
- textOverflow: "ellipsis",
976
- whiteSpace: "nowrap"
977
- } }, f.name), (0, react.createElement)("span", { className: "sts-history-item-meta" }, `${new Date(f.mtime).toLocaleString()} ${(f.size / 1024).toFixed(1)} KB`))))));
1012
+ if (activeFile === null) {
1013
+ const title = filter !== void 0 ? `${filter} · 历史日志` : "历史日志";
1014
+ return (0, react.createElement)("div", { style: {
1015
+ display: "flex",
1016
+ flexDirection: "column",
1017
+ height: "100%",
1018
+ minHeight: 0
1019
+ } }, (0, react.createElement)("div", { className: "sts-log-toolbar" }, title, (0, react.createElement)("div", { className: "sts-log-toolbar-spacer" }), (0, react.createElement)("button", {
1020
+ className: "sts-icon-btn",
1021
+ title: "刷新",
1022
+ onClick: () => {
1023
+ loadList();
1024
+ }
1025
+ }, RefreshIcon({ size: 14 }), (0, react.createElement)("span", { className: "sts-icon-btn-label" }, "刷新")), (0, react.createElement)("button", {
1026
+ className: "sts-icon-btn",
1027
+ title: filter !== void 0 ? "返回服务视图" : "关闭历史日志",
1028
+ onClick: onClose
1029
+ }, CloseIcon({ size: 14 }), (0, react.createElement)("span", { className: "sts-icon-btn-label" }, filter !== void 0 ? "返回" : "关闭"))), (0, react.createElement)("div", { style: {
1030
+ overflow: "auto",
1031
+ flex: 1,
1032
+ minHeight: 0,
1033
+ padding: "4px 0"
1034
+ } }, loading && files === null ? (0, react.createElement)("div", { className: "sts-empty" }, "加载中…") : files !== null && files.length === 0 ? (0, react.createElement)("div", { className: "sts-empty" }, filter !== void 0 ? `「${filter}」暂无历史日志。每次启动都会在 <工作空间>/logs/ 下永久保留一份日志,即使启动失败或 dsh 重启也能查看。` : "暂无历史日志(工作空间 logs/ 为空)。每次启动服务都会在 <工作空间>/logs/ 下永久保留一份日志,即使启动失败或 dsh 重启也能查看。") : (0, react.createElement)("div", {}, (files ?? []).map((f) => (0, react.createElement)("button", {
1035
+ key: f.file,
1036
+ className: "sts-history-item",
1037
+ title: f.file,
1038
+ onClick: () => {
1039
+ openFile(f);
1040
+ }
1041
+ }, (0, react.createElement)(FileIcon, { size: 13 }), (0, react.createElement)("span", { style: {
1042
+ flex: 1,
1043
+ textAlign: "left",
1044
+ overflow: "hidden",
1045
+ textOverflow: "ellipsis",
1046
+ whiteSpace: "nowrap"
1047
+ } }, f.name), (0, react.createElement)("span", { className: "sts-history-item-meta" }, `${new Date(f.mtime).toLocaleString()} ${(f.size / 1024).toFixed(1)} KB`))))));
1048
+ }
978
1049
  return (0, react.createElement)("div", { style: {
979
1050
  display: "flex",
980
1051
  flexDirection: "column",
@@ -1009,53 +1080,6 @@ window.__ModuleLoader__.load({
1009
1080
  }, `—— 历史日志已加载完毕(${lines.length} 行),此为只读视图 ——`)));
1010
1081
  }
1011
1082
  //#endregion
1012
- //#region src/client/ConfigDetail.tsx
1013
- /**
1014
- * Config detail card: shown in the right pane when an idle config is selected.
1015
- * Displays the config's full info and offers start / edit / delete buttons.
1016
- * @module dsh-better-sidebar-starter/client/ConfigDetail
1017
- */
1018
- const TYPE_LABELS$1 = {
1019
- npm: "npm",
1020
- springboot: "Spring Boot",
1021
- python: "Python",
1022
- custom: "Custom"
1023
- };
1024
- /** The detail card component. */
1025
- function ConfigDetail({ config, onStart, onEdit, onDuplicate, onDelete }) {
1026
- const envEntries = Object.entries(config.env);
1027
- const dateFmt = (ts) => {
1028
- if (ts === null) return "—";
1029
- return new Date(ts).toLocaleString("zh-CN", { hour12: false });
1030
- };
1031
- return (0, react.createElement)("div", { className: "sts-detail" }, (0, react.createElement)("div", { className: "sts-detail-card" }, (0, react.createElement)("div", { className: "sts-detail-title" }, typeIcon(config.type, 20), config.name), (0, react.createElement)("div", { className: "sts-detail-row" }, (0, react.createElement)("div", { className: "sts-detail-label" }, "类型"), (0, react.createElement)("div", { className: "sts-detail-value" }, TYPE_LABELS$1[config.type] ?? config.type)), (0, react.createElement)("div", { className: "sts-detail-row" }, (0, react.createElement)("div", { className: "sts-detail-label" }, "命令"), (0, react.createElement)("div", { className: "sts-detail-value" }, config.command)), (0, react.createElement)("div", { className: "sts-detail-row" }, (0, react.createElement)("div", { className: "sts-detail-label" }, "工作目录"), (0, react.createElement)("div", { className: "sts-detail-value" }, config.cwd)), config.type === "springboot" && config.jvmArgs ? (0, react.createElement)("div", { className: "sts-detail-row" }, (0, react.createElement)("div", { className: "sts-detail-label" }, "JVM参数"), (0, react.createElement)("div", { className: "sts-detail-value" }, config.jvmArgs)) : null, config.args ? (0, react.createElement)("div", { className: "sts-detail-row" }, (0, react.createElement)("div", { className: "sts-detail-label" }, "程序实参"), (0, react.createElement)("div", { className: "sts-detail-value" }, config.args)) : null, envEntries.length > 0 ? (0, react.createElement)("div", { className: "sts-detail-row" }, (0, react.createElement)("div", { className: "sts-detail-label" }, "环境变量"), (0, react.createElement)("div", { style: { flex: 1 } }, ...envEntries.map(([key, value]) => (0, react.createElement)("div", {
1032
- key,
1033
- className: "sts-detail-value"
1034
- }, `${key} = ${value}`)))) : null, (0, react.createElement)("div", { className: "sts-detail-row" }, (0, react.createElement)("div", { className: "sts-detail-label" }, "创建时间"), (0, react.createElement)("div", { className: "sts-detail-value" }, dateFmt(config.createdAt))), config.lastRunAt !== null ? (0, react.createElement)("div", { className: "sts-detail-row" }, (0, react.createElement)("div", { className: "sts-detail-label" }, "最近运行"), (0, react.createElement)("div", { className: "sts-detail-value" }, dateFmt(config.lastRunAt))) : null, (0, react.createElement)("div", { className: "sts-detail-actions" }, (0, react.createElement)("button", {
1035
- className: "sts-detail-btn danger",
1036
- onClick: onDelete
1037
- }, (0, react.createElement)(TrashIcon, { size: 14 }), " 删除"), (0, react.createElement)("button", {
1038
- className: "sts-detail-btn",
1039
- onClick: onDuplicate,
1040
- title: "复制为副本(保留全部信息)"
1041
- }, (0, react.createElement)(CopyIcon, { size: 14 }), " 复制"), (0, react.createElement)("button", {
1042
- className: "sts-detail-btn",
1043
- onClick: onEdit
1044
- }, (0, react.createElement)(GearIcon, { size: 14 }), " 编辑"), (0, react.createElement)("button", {
1045
- className: "sts-detail-btn primary",
1046
- onClick: onStart,
1047
- style: {
1048
- background: "#1a1a1e",
1049
- border: "1px solid #3a3a3e",
1050
- color: "#fff",
1051
- borderRadius: "6px",
1052
- padding: "6px 14px",
1053
- cursor: "pointer",
1054
- fontSize: "12px"
1055
- }
1056
- }, (0, react.createElement)(PlayIcon, { size: 14 }), " 启动"))));
1057
- }
1058
- //#endregion
1059
1083
  //#region src/client/DirChooser.tsx
1060
1084
  /**
1061
1085
  * Shared directory chooser: a filesystem folder tree (backed by the sidebar
@@ -2021,6 +2045,13 @@ window.__ModuleLoader__.load({
2021
2045
  background: rgba(97, 175, 239, 0.14);
2022
2046
  border-bottom-color: #61afef;
2023
2047
  }
2048
+ /* Clickable http(s) URLs inside the log (open in a new browser tab). */
2049
+ .sts-log-url {
2050
+ color: #61afef;
2051
+ }
2052
+ .sts-log-url:hover {
2053
+ background: rgba(97, 175, 239, 0.18);
2054
+ }
2024
2055
 
2025
2056
  /* ===== Config detail ===== */
2026
2057
  .sts-detail {
@@ -2646,7 +2677,6 @@ window.__ModuleLoader__.load({
2646
2677
  const [splitRatio, setSplitRatio] = (0, react.useState)(.33);
2647
2678
  const [dragging, setDragging] = (0, react.useState)(false);
2648
2679
  const [isHorizontal, setIsHorizontal] = (0, react.useState)(true);
2649
- const [historyOpen, setHistoryOpen] = (0, react.useState)(false);
2650
2680
  const containerRef = (0, react.useRef)(null);
2651
2681
  (0, react.useEffect)(() => {
2652
2682
  const el = containerRef.current;
@@ -2821,8 +2851,7 @@ window.__ModuleLoader__.load({
2821
2851
  }
2822
2852
  } catch {}
2823
2853
  }, [sessionQuery, fetchConfigs]);
2824
- /** Delete a config. */
2825
- const handleDeleteConfig = (0, react.useCallback)(async (configId) => {
2854
+ (0, react.useCallback)(async (configId) => {
2826
2855
  for (const inst of instances) if (inst.configId === configId && inst.status === "running") await handleStop(inst.id);
2827
2856
  try {
2828
2857
  if ((await fetch(`${API_BASE}/configs?${sessionQuery()}&id=${encodeURIComponent(configId)}`, { method: "DELETE" })).ok) {
@@ -2883,14 +2912,7 @@ window.__ModuleLoader__.load({
2883
2912
  }, (0, react.createElement)("div", { className: "sts-toolbar" }, (0, react.createElement)("div", { className: "sts-toolbar-title" }, (0, react.createElement)(RocketIcon, { size: 16 }), "服务"), (0, react.createElement)("button", {
2884
2913
  className: "sts-toolbar-btn",
2885
2914
  onClick: handleNewConfig
2886
- }, (0, react.createElement)(PlusIcon, { size: 13 }), "新建配置"), (0, react.createElement)("button", {
2887
- className: `sts-toolbar-btn${historyOpen ? " active" : ""}`,
2888
- title: "查看持久化历史日志(.dsh/logs,含启动失败记录)",
2889
- onClick: () => {
2890
- setHistoryOpen((v) => !v);
2891
- if (historyOpen) {}
2892
- }
2893
- }, (0, react.createElement)(FileIcon, { size: 13 }), "历史日志")), (0, react.createElement)("div", { className: `sts-split${isHorizontal ? "" : " sts-split-vertical"}` }, (0, react.createElement)("div", {
2915
+ }, (0, react.createElement)(PlusIcon, { size: 13 }), "新建配置")), (0, react.createElement)("div", { className: `sts-split${isHorizontal ? "" : " sts-split-vertical"}` }, (0, react.createElement)("div", {
2894
2916
  className: `sts-split-first${isHorizontal ? "" : " sts-split-first-v"}`,
2895
2917
  style: isHorizontal ? { width: `${splitRatio * 100}%` } : { height: `${splitRatio * 100}%` }
2896
2918
  }, configs.length === 0 ? (0, react.createElement)("div", { className: "sts-empty" }, "暂无运行配置,点击右上角 + 新建") : (0, react.createElement)(ConfigTree, {
@@ -2911,12 +2933,7 @@ window.__ModuleLoader__.load({
2911
2933
  })), (0, react.createElement)("div", {
2912
2934
  className: `sts-split-gutter${dragging ? " dragging" : ""}${isHorizontal ? "" : " sts-split-gutter-v"}`,
2913
2935
  onMouseDown: handleMouseDown
2914
- }), (0, react.createElement)("div", { className: `sts-split-second${isHorizontal ? "" : " sts-split-second-v"}` }, historyOpen ? (0, react.createElement)(LogHistoryView, {
2915
- ctx: props.ctx,
2916
- sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? "",
2917
- cwd: scope.cwd,
2918
- onClose: () => setHistoryOpen(false)
2919
- }) : selectedInstance !== null ? (0, react.createElement)(LogView, {
2936
+ }), (0, react.createElement)("div", { className: `sts-split-second${isHorizontal ? "" : " sts-split-second-v"}` }, selectedInstance !== null ? (0, react.createElement)(LogView, {
2920
2937
  instance: selectedInstance,
2921
2938
  ctx: props.ctx,
2922
2939
  sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? "",
@@ -2927,17 +2944,15 @@ window.__ModuleLoader__.load({
2927
2944
  onRestart: () => {
2928
2945
  handleRestart(selectedInstance.id);
2929
2946
  }
2930
- }) : selectedConfig !== null ? (0, react.createElement)(ConfigDetail, {
2931
- config: selectedConfig,
2932
- onStart: () => {
2933
- handleStart(selectedConfig.id);
2934
- },
2935
- onEdit: () => handleEditConfig(selectedConfig.id),
2936
- onDuplicate: () => handleDuplicateConfig(selectedConfig.id),
2937
- onDelete: () => {
2938
- handleDeleteConfig(selectedConfig.id);
2947
+ }) : selectedConfig !== null ? (0, react.createElement)(LogHistoryView, {
2948
+ ctx: props.ctx,
2949
+ sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? "",
2950
+ cwd: scope.cwd,
2951
+ filter: selectedConfig.name,
2952
+ onClose: () => {
2953
+ setSelectedConfigId(null);
2939
2954
  }
2940
- }) : (0, react.createElement)("div", { className: "sts-empty" }, "选择左侧配置查看详情或日志"))), showModal ? (0, react.createElement)(ConfigEditorModal, {
2955
+ }) : (0, react.createElement)("div", { className: "sts-empty" }, "选择左侧配置查看其历史日志,或选择运行中的服务查看实时日志"))), showModal ? (0, react.createElement)(ConfigEditorModal, {
2941
2956
  config: editingConfig,
2942
2957
  scope: {
2943
2958
  sessionId: scope.sessionId ?? store.getSnapshot().sessionId ?? "",