@zhhoceanfly/protoview 0.1.3 → 0.1.4

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/dist/cli.js CHANGED
@@ -41,7 +41,8 @@ function parseArgs(argv) {
41
41
  git: "",
42
42
  branch: "main",
43
43
  port: 3920,
44
- open: true,
44
+ // 开发 watch 会反复重启进程;默认不弹浏览器,避免每次开新标签
45
+ open: process.env.PROTOVIEW_DEV !== "1",
45
46
  showGoogle: false,
46
47
  help: false,
47
48
  };
@@ -59,6 +60,8 @@ function parseArgs(argv) {
59
60
  args.port = Math.max(1, Number(argv[++i] || 3920) || 3920);
60
61
  else if (a === "--no-open")
61
62
  args.open = false;
63
+ else if (a === "--open")
64
+ args.open = true;
62
65
  else if (a === "--show-google")
63
66
  args.showGoogle = true;
64
67
  else if (!a.startsWith("-"))
@@ -77,7 +80,8 @@ Options:
77
80
  --git, -g <url> Use Git repo as proto source (clone to temp dir)
78
81
  --branch, -b <name> Git branch (default: main)
79
82
  --port <n> HTTP port (default: 3920)
80
- --no-open Do not open browser
83
+ --no-open Do not open browser (default in npm run dev:cli)
84
+ --open Open browser on start
81
85
  --show-google Include google.* packages in tree
82
86
  -h, --help Show help
83
87
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhhoceanfly/protoview",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "ProtoView Client CLI:本地启动浏览器调试 gRPC / HTTP / SSE / WebSocket(Rider / 任意 IDE 可用)",
5
5
  "license": "MIT",
6
6
  "author": "zoceanfly",
package/public/app.js CHANGED
@@ -2,8 +2,12 @@
2
2
  const isVsCodeWebview = typeof acquireVsCodeApi === "function";
3
3
  const vscode = isVsCodeWebview ? acquireVsCodeApi() : createWsBridge();
4
4
  const THEME_KEY = "protoview.client.theme";
5
+ const SIDEBAR_BREAKPOINT = 960;
5
6
  const DRAFT_KEY = "protoview.client.drafts";
6
7
  const SPLIT_KEY = "protoview.client.split";
8
+ /** null=跟宽度走;true/false=手动覆盖(窗口跨过断点后清除) */
9
+ let sidebarManual = null;
10
+ let sidebarWasNarrow = null;
7
11
 
8
12
  /** Browser / CLI: talk to local Node host over WebSocket (same message protocol as the extension). */
9
13
  function createWsBridge() {
@@ -178,6 +182,52 @@
178
182
  els.themeBtn.textContent = next === "light" ? "☾" : "◐";
179
183
  }
180
184
 
185
+ function isSidebarCollapsed() {
186
+ return document.getElementById("app")?.classList.contains("sidebar-collapsed");
187
+ }
188
+
189
+ function setSidebarCollapsed(collapsed) {
190
+ const app = document.getElementById("app");
191
+ if (!app) return;
192
+ app.classList.toggle("sidebar-collapsed", Boolean(collapsed));
193
+ const toggle = $("sidebarToggleBtn");
194
+ if (toggle) {
195
+ toggle.textContent = collapsed ? "⟩" : "⟨";
196
+ toggle.title = collapsed ? "展开侧栏" : "收起侧栏";
197
+ toggle.setAttribute("aria-label", toggle.title);
198
+ }
199
+ }
200
+
201
+ function appWidth() {
202
+ return document.getElementById("app")?.clientWidth || window.innerWidth || 0;
203
+ }
204
+
205
+ function syncSidebarByWidth() {
206
+ const narrow = appWidth() < SIDEBAR_BREAKPOINT;
207
+ if (sidebarWasNarrow !== null && sidebarWasNarrow !== narrow) {
208
+ sidebarManual = null;
209
+ }
210
+ sidebarWasNarrow = narrow;
211
+ const collapsed = sidebarManual !== null ? sidebarManual : narrow;
212
+ setSidebarCollapsed(collapsed);
213
+ }
214
+
215
+ function initSidebarCollapsed() {
216
+ syncSidebarByWidth();
217
+ const app = document.getElementById("app");
218
+ if (!app || typeof ResizeObserver === "undefined") {
219
+ window.addEventListener("resize", syncSidebarByWidth);
220
+ return;
221
+ }
222
+ const ro = new ResizeObserver(() => syncSidebarByWidth());
223
+ ro.observe(app);
224
+ }
225
+
226
+ function toggleSidebar() {
227
+ sidebarManual = !isSidebarCollapsed();
228
+ setSidebarCollapsed(sidebarManual);
229
+ }
230
+
181
231
  function esc(value) {
182
232
  return String(value ?? "")
183
233
  .replace(/&/g, "&amp;")
@@ -1165,7 +1215,9 @@
1165
1215
 
1166
1216
  // events
1167
1217
  initTheme();
1218
+ initSidebarCollapsed();
1168
1219
  els.themeBtn.addEventListener("click", toggleTheme);
1220
+ $("sidebarToggleBtn")?.addEventListener("click", toggleSidebar);
1169
1221
  $("aiBtn").addEventListener("click", openAi);
1170
1222
  $("closeAiBtn").addEventListener("click", closeAi);
1171
1223
  $("aiShuffle").addEventListener("click", () => {
package/public/index.html CHANGED
@@ -30,6 +30,7 @@
30
30
  </div>
31
31
  <div class="tree" id="tree"></div>
32
32
  </aside>
33
+ <button class="sidebar-rail" id="sidebarToggleBtn" type="button" title="收起侧栏" aria-label="收起侧栏">⟨</button>
33
34
 
34
35
  <main class="main" id="mainClient">
35
36
  <div class="topbar">
package/public/styles.css CHANGED
@@ -53,14 +53,44 @@ button { cursor: pointer; }
53
53
  button:disabled { opacity: .45; cursor: not-allowed; }
54
54
 
55
55
  #app {
56
+ --sidebar-w: 260px;
57
+ --rail-w: 22px;
56
58
  display: grid;
57
- grid-template-columns: 300px 1fr;
59
+ grid-template-columns: var(--sidebar-w) var(--rail-w) minmax(0, 1fr);
58
60
  grid-template-rows: minmax(0, 1fr);
59
61
  height: 100%;
60
62
  min-height: 0;
63
+ min-width: 0;
64
+ overflow: hidden;
65
+ }
66
+ #app > .sidebar {
67
+ grid-column: 1;
68
+ grid-row: 1;
69
+ min-height: 0;
70
+ min-width: 0;
71
+ overflow: hidden;
72
+ }
73
+ #app > .sidebar-rail {
74
+ grid-column: 2;
75
+ grid-row: 1;
76
+ }
77
+ #app > .main {
78
+ grid-column: 3;
79
+ grid-row: 1;
80
+ min-height: 0;
81
+ min-width: 0;
82
+ overflow: hidden;
83
+ }
84
+
85
+ /* 收起:侧栏宽度归零,轨道按钮始终占一列,右侧自适应 */
86
+ #app.sidebar-collapsed {
87
+ --sidebar-w: 0px;
88
+ }
89
+ #app.sidebar-collapsed .sidebar {
90
+ visibility: hidden;
91
+ pointer-events: none;
92
+ border-right: 0;
61
93
  }
62
- #app > .sidebar { grid-column: 1; grid-row: 1; min-height: 0; }
63
- #app > .main { grid-column: 2; grid-row: 1; min-height: 0; }
64
94
 
65
95
  .sidebar {
66
96
  border-right: 1px solid var(--border);
@@ -69,6 +99,30 @@ button:disabled { opacity: .45; cursor: not-allowed; }
69
99
  flex-direction: column;
70
100
  min-height: 0;
71
101
  }
102
+ .sidebar-rail {
103
+ z-index: 2;
104
+ width: 100%;
105
+ height: 100%;
106
+ margin: 0;
107
+ padding: 0;
108
+ border: 0;
109
+ border-right: 1px solid var(--border);
110
+ border-left: 1px solid var(--border);
111
+ border-radius: 0;
112
+ background: var(--panel-2);
113
+ color: var(--muted);
114
+ font-size: 12px;
115
+ line-height: 1;
116
+ display: inline-flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ cursor: pointer;
120
+ }
121
+ .sidebar-rail:hover {
122
+ color: var(--text);
123
+ background: var(--hover);
124
+ border-color: var(--accent);
125
+ }
72
126
  .side-head {
73
127
  padding: 12px 12px 8px;
74
128
  border-bottom: 1px solid var(--border);
@@ -80,8 +134,17 @@ button:disabled { opacity: .45; cursor: not-allowed; }
80
134
  align-items: center;
81
135
  justify-content: space-between;
82
136
  gap: 8px;
137
+ min-width: 0;
138
+ }
139
+ .brand {
140
+ font-weight: 700;
141
+ font-size: 14px;
142
+ letter-spacing: .02em;
143
+ min-width: 0;
144
+ overflow: hidden;
145
+ text-overflow: ellipsis;
146
+ white-space: nowrap;
83
147
  }
84
- .brand { font-weight: 700; font-size: 14px; letter-spacing: .02em; }
85
148
  .brand span { color: var(--accent-2); }
86
149
  .icon-btn {
87
150
  width: 30px; height: 30px;
@@ -658,7 +721,13 @@ button:disabled { opacity: .45; cursor: not-allowed; }
658
721
  .stress-path { flex: 1 1 100%; }
659
722
  .stress-path .source-dir-row { max-width: min(560px, 100%); }
660
723
  .stress-path .source-dir-row input { width: 100%; flex: 1; }
661
- .side-tools { display: flex; gap: 6px; }
724
+ .side-tools {
725
+ display: flex;
726
+ gap: 6px;
727
+ align-items: center;
728
+ flex-wrap: nowrap;
729
+ flex-shrink: 0;
730
+ }
662
731
  .side-search {
663
732
  width: 100%;
664
733
  padding: 7px 10px;
@@ -759,15 +828,15 @@ button:disabled { opacity: .45; cursor: not-allowed; }
759
828
  border-radius: 8px;
760
829
  padding: 7px 10px;
761
830
  }
762
- .addr { flex: 1 1 220px; font-family: var(--mono); min-width: 180px; }
831
+ .addr { flex: 1 1 140px; font-family: var(--mono); min-width: 100px; }
763
832
  .method {
764
- flex: 1 1 240px;
833
+ flex: 1 1 140px;
765
834
  font-family: var(--mono);
766
835
  color: var(--muted);
767
836
  overflow: hidden;
768
837
  text-overflow: ellipsis;
769
838
  white-space: nowrap;
770
- min-width: 160px;
839
+ min-width: 96px;
771
840
  }
772
841
  .btn {
773
842
  border: 1px solid var(--border);
@@ -1369,8 +1438,17 @@ textarea.editor {
1369
1438
  background: linear-gradient(120deg, #0f172a, #1d4ed8 45%, #0891b2);
1370
1439
  color: #f8fafc;
1371
1440
  animation: aiPulse 2.4s ease-in-out infinite;
1441
+ display: inline-flex;
1442
+ align-items: center;
1443
+ white-space: nowrap;
1444
+ flex-shrink: 0;
1445
+ }
1446
+ .ai-spark {
1447
+ display: inline-block;
1448
+ margin-right: 4px;
1449
+ flex-shrink: 0;
1450
+ animation: aiSpin 3.2s linear infinite;
1372
1451
  }
1373
- .ai-spark { display: inline-block; margin-right: 4px; animation: aiSpin 3.2s linear infinite; }
1374
1452
  @keyframes aiPulse {
1375
1453
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
1376
1454
  50% { box-shadow: 0 0 14px 1px color-mix(in srgb, #38bdf8 45%, transparent); }
@@ -1475,7 +1553,16 @@ textarea.editor {
1475
1553
  .env-item strong { display: block; }
1476
1554
  .env-item span { color: var(--muted); font-size: 12px; font-family: var(--mono); }
1477
1555
 
1478
- @media (max-width: 960px) {
1479
- #app { grid-template-columns: 1fr; grid-template-rows: 38vh 1fr; }
1480
- .sidebar { border-right: 0; border-bottom: 1px solid var(--border); }
1556
+ /* 窄宽不再上下堆叠侧栏;由 JS 自动收起,右侧自适应 */
1557
+ @media (max-width: 900px) {
1558
+ #app { --sidebar-w: 220px; }
1559
+ .side-meta-row { flex-wrap: wrap; }
1560
+ .topbar { gap: 6px; padding: 8px; }
1561
+ .pill { padding: 6px 8px; }
1562
+ .tabs { overflow-x: auto; flex-wrap: nowrap; }
1563
+ .tab { padding: 8px 10px; white-space: nowrap; }
1564
+ }
1565
+ @media (max-width: 560px), (max-height: 420px) {
1566
+ .brand { font-size: 12px; }
1567
+ .toolbar .hint { display: none; }
1481
1568
  }