@zhhoceanfly/protoview 0.0.0-stage → 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.
@@ -0,0 +1,425 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN" data-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>ProtoView Client</title>
7
+ <link rel="stylesheet" href="./styles.css" />
8
+ </head>
9
+ <body>
10
+ <div id="app">
11
+ <aside class="sidebar">
12
+ <div class="side-head">
13
+ <div class="brand-row">
14
+ <div class="brand">Proto<span>View</span> Client</div>
15
+ <div class="side-tools">
16
+ <button class="icon-btn" id="treeToggleBtn" type="button" title="全部收起">▸</button>
17
+ <button class="icon-btn" id="themeBtn" type="button" title="切换亮/暗色">◐</button>
18
+ <button class="icon-btn" id="refreshBtn" type="button" title="刷新">↻</button>
19
+ <button class="btn ai-btn" id="aiBtn" type="button" title="AI"><span class="ai-spark">✦</span>AI</button>
20
+ </div>
21
+ </div>
22
+ <div class="side-meta-row">
23
+ <div class="side-meta-group">
24
+ <div class="side-meta" id="sourceMeta">独立模式</div>
25
+ <button class="btn side-source-btn" id="sourceConfigBtn" type="button" title="在 Client 内配置工作区或 Git 来源">自定义源</button>
26
+ </div>
27
+ <button class="btn side-flow-btn primary" id="flowBtn" type="button" title="链路调试">链路调试</button>
28
+ </div>
29
+ <input class="side-search" id="filter" placeholder="筛选 package / service / 接口" />
30
+ </div>
31
+ <div class="tree" id="tree"></div>
32
+ </aside>
33
+ <button class="sidebar-rail" id="sidebarToggleBtn" type="button" title="收起侧栏" aria-label="收起侧栏">⟨</button>
34
+
35
+ <main class="main" id="mainClient">
36
+ <div class="topbar">
37
+ <select class="pill" id="protocolSelect" title="协议">
38
+ <option value="grpc">gRPC</option>
39
+ <option value="http">HTTP</option>
40
+ <option value="sse">SSE</option>
41
+ <option value="websocket">WebSocket</option>
42
+ </select>
43
+ <select class="pill hidden" id="httpMethodSelect" title="HTTP 方法">
44
+ <option>GET</option>
45
+ <option selected>POST</option>
46
+ <option>PUT</option>
47
+ <option>PATCH</option>
48
+ <option>DELETE</option>
49
+ </select>
50
+ <input class="pill hidden http-path" id="httpPath" placeholder="/path" title="HTTP 路径" />
51
+ <select class="pill" id="envSelect" title="环境"></select>
52
+ <button class="btn" id="envConfigBtn" type="button">环境配置</button>
53
+ <input class="pill addr" id="address" placeholder="gRPC host:port" />
54
+ <div class="pill method" id="methodLabel">未选择接口</div>
55
+ <button class="btn" id="grpcurlBtn" type="button" title="Generate grpcurl command">&lt;/&gt;</button>
56
+ <label class="tls-toggle" title="不勾选时使用明文 gRPC"><input id="tlsCheck" type="checkbox" /> TLS</label>
57
+ <button class="btn primary" id="sendBtn" type="button" disabled>发送</button>
58
+ </div>
59
+
60
+ <div class="tabs" id="tabs">
61
+ <button class="tab active" data-tab="message" type="button">Message</button>
62
+ <button class="tab" data-tab="metadata" type="button">Metadata</button>
63
+ <button class="tab" data-tab="auth" type="button">Auth</button>
64
+ <button class="tab" data-tab="docs" type="button">Docs</button>
65
+ <button class="tab" data-tab="stress" type="button">压测</button>
66
+ <button class="tab" data-tab="history" type="button">History</button>
67
+ </div>
68
+
69
+ <div class="toolbar" id="toolbar">
70
+ <button class="btn" id="genMsgBtn" type="button">自动生成</button>
71
+ <button class="btn" id="formatMsgBtn" type="button">格式化</button>
72
+ <button class="btn" id="clearMsgBtn" type="button">清空</button>
73
+ <div class="spacer"></div>
74
+ <div class="hint" id="toolbarHint">根据请求 message 字段生成 JSON</div>
75
+ </div>
76
+
77
+ <div class="workspace" id="workspace">
78
+ <section class="pane request-pane" id="requestPane">
79
+ <div class="pane-body">
80
+ <div class="view active" id="view-message">
81
+ <div class="message-layout">
82
+ <aside class="rpc-info" id="rpcInfo">
83
+ <div class="rpc-info-empty">选择左侧接口后显示定义与说明。</div>
84
+ </aside>
85
+ <textarea class="editor" id="messageEditor" spellcheck="false">{}</textarea>
86
+ </div>
87
+ </div>
88
+ <div class="view" id="view-metadata">
89
+ <div class="meta-wrap">
90
+ <table class="meta-table">
91
+ <thead>
92
+ <tr>
93
+ <th class="col-name">Name</th>
94
+ <th class="col-value">Value</th>
95
+ <th class="col-desc">Description</th>
96
+ <th class="col-act"></th>
97
+ </tr>
98
+ </thead>
99
+ <tbody id="metaBody"></tbody>
100
+ </table>
101
+ <div class="meta-foot">
102
+ <button class="btn" id="addMetaBtn" type="button">添加一行</button>
103
+ <button class="link" id="bulkMetaBtn" type="button">Bulk Edit</button>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ <div class="view" id="view-auth">
108
+ <div class="auth-panel">
109
+ <div class="auth-head">
110
+ <div>
111
+ <div class="auth-title">Auth</div>
112
+ <p class="auth-note">gRPC 无集合级 Inherit,请在本接口配置。鉴权会写入 Metadata 并在发送时生效。</p>
113
+ </div>
114
+ <select id="authType" class="pill auth-type" title="Auth 类型">
115
+ <option value="none">None</option>
116
+ <option value="bearer">Bearer Token</option>
117
+ <option value="basic">Basic Auth</option>
118
+ <option value="apikey">API Key</option>
119
+ <option value="oauth2">OAuth 2.0</option>
120
+ <option value="wsse">WSSE Auth</option>
121
+ </select>
122
+ </div>
123
+ <div class="auth-body" id="authBody"></div>
124
+ <div class="auth-preview" id="authPreview">将注入:无</div>
125
+ </div>
126
+ </div>
127
+ <div class="view" id="view-docs">
128
+ <div class="docs" id="docsView"><div class="doc-empty">选择左侧接口后显示注释与请求/响应字段。</div></div>
129
+ </div>
130
+ <div class="view" id="view-stress">
131
+ <div class="stress-panel">
132
+ <div class="stress-head">
133
+ <div class="stress-modes" id="stressModes">
134
+ <button class="btn stress-mode active" type="button" data-stress-mode="light">轻量压测</button>
135
+ <button class="btn stress-mode" type="button" data-stress-mode="ghz">ghz</button>
136
+ </div>
137
+ <p class="stress-note" id="stressNote">复用当前 Message / Auth / Metadata,在插件进程内有限并发调用。</p>
138
+ </div>
139
+ <div class="stress-light" id="stressLight">
140
+ <div class="stress-form">
141
+ <label>并发 <input id="stressConcurrency" type="number" min="1" max="10" value="2" /></label>
142
+ <label>总请求 <input id="stressTotal" type="number" min="1" max="200" value="20" /></label>
143
+ <button class="btn primary" id="stressStartBtn" type="button">开始压测</button>
144
+ <button class="btn" id="stressStopBtn" type="button" disabled>停止</button>
145
+ </div>
146
+ <div class="stress-progress-wrap">
147
+ <div class="stress-progress-bar"><i id="stressProgressBar"></i></div>
148
+ <div class="stress-progress-text" id="stressProgressText">未开始</div>
149
+ </div>
150
+ </div>
151
+ <div class="stress-ghz hidden" id="stressGhz">
152
+ <p class="stress-note">指定本机 <code>ghz</code> 可执行文件路径;留空则使用 PATH 中的 <code>ghz</code>。</p>
153
+ <div class="stress-form">
154
+ <label class="stress-path">ghz 路径
155
+ <div class="source-dir-row">
156
+ <input id="ghzPath" type="text" placeholder="留空则用 PATH 中的 ghz" />
157
+ <button class="btn" id="pickGhzPathBtn" type="button" title="选择本机 ghz 可执行文件">浏览</button>
158
+ </div>
159
+ </label>
160
+ <label>并发 <input id="ghzConcurrency" type="number" min="1" max="200" value="20" /></label>
161
+ <label>总请求 <input id="ghzTotal" type="number" min="1" max="100000" value="200" /></label>
162
+ <button class="btn" id="copyGhzBtn" type="button">复制 ghz 命令</button>
163
+ <button class="btn primary" id="runGhzBtn" type="button">尝试运行 ghz</button>
164
+ </div>
165
+ <textarea id="ghzCommand" class="stress-cmd" rows="8" readonly></textarea>
166
+ </div>
167
+ <div class="stress-result" id="stressResult">
168
+ <div class="stress-empty">压测结果会显示在这里。</div>
169
+ </div>
170
+ </div>
171
+ </div>
172
+ <div class="view" id="view-history">
173
+ <div class="history" id="historyView">发送记录会显示在这里(当前为本地会话历史,无数据库)。</div>
174
+ </div>
175
+ </div>
176
+ </section>
177
+ <div class="split-bar" id="splitBar" title="拖动调整高度">
178
+ <button class="split-btn" id="collapseReqBtn" type="button" title="收起请求区">▲</button>
179
+ <div class="split-grip" id="splitHandle"></div>
180
+ <button class="split-btn" id="collapseRespBtn" type="button" title="收起响应区">▼</button>
181
+ </div>
182
+ <section class="pane response-pane" id="responsePane">
183
+ <div class="toolbar">
184
+ <strong class="resp-title">Response</strong>
185
+ <span class="resp-badge" id="respBadge"></span>
186
+ <div class="spacer"></div>
187
+ <button class="btn" id="clearRespBtn" type="button">清空响应</button>
188
+ </div>
189
+ <div class="response" id="responseView"><div class="resp-empty">选择接口,配置地址后发送。</div></div>
190
+ </section>
191
+ </div>
192
+ </main>
193
+
194
+ <main class="main flow-main hidden" id="mainFlow">
195
+ <div class="flow-topbar">
196
+ <strong>链路调试</strong>
197
+ <span class="flow-hint">按住左侧接口拖到画布松手 · 双击也可添加 · 右键节点编辑</span>
198
+ <div class="spacer"></div>
199
+ <button class="btn" id="flowAddSelectedBtn" type="button">添加选中接口</button>
200
+ <button class="btn" id="flowClearBtn" type="button">清空画布</button>
201
+ <button class="btn primary" id="flowRunBtn" type="button">运行链路</button>
202
+ <button class="btn" id="flowStressBtn" type="button">压测</button>
203
+ <button class="btn" id="flowExportBtn" type="button" title="导出当前链路节点与连线配置">导出</button>
204
+ <button class="btn" id="flowImportBtn" type="button" title="从文件导入链路配置">导入</button>
205
+ <button class="btn" id="flowCloseBtn" type="button">返回 Client</button>
206
+ </div>
207
+ <div class="flow-stage" id="flowStage">
208
+ <div class="flow-empty" id="flowEmpty">从左侧拖入接口,或点「添加选中接口」</div>
209
+ <svg class="flow-edges" id="flowEdges"></svg>
210
+ <div class="flow-world" id="flowWorld"></div>
211
+ </div>
212
+ <div class="flow-menu hidden" id="flowMenu" style="display:none">
213
+ <button type="button" data-flow-act="edit">编辑</button>
214
+ <button type="button" data-flow-act="view-resp">查看响应</button>
215
+ <button type="button" data-flow-act="delete">删除</button>
216
+ </div>
217
+ <div class="flow-drawer-mask" id="flowStressDrawer">
218
+ <aside class="flow-drawer" role="dialog" aria-label="链路压测">
219
+ <div class="flow-drawer-head">
220
+ <strong>链路压测</strong>
221
+ <button class="icon-btn" id="flowStressClose" type="button" title="关闭">✕</button>
222
+ </div>
223
+ <div class="flow-drawer-body">
224
+ <p class="stress-note">按拓扑顺序重复跑整条链路(轻量并发,无 ghz)。复用节点 Message / Metadata / 变量提取。</p>
225
+ <div class="stress-form">
226
+ <label>并发 <input id="flowStressConcurrency" type="number" min="1" max="5" value="1" /></label>
227
+ <label>总次数 <input id="flowStressTotal" type="number" min="1" max="100" value="10" /></label>
228
+ </div>
229
+ <div class="flow-drawer-actions">
230
+ <button class="btn primary" id="flowStressStartBtn" type="button">开始压测</button>
231
+ <button class="btn" id="flowStressStopBtn" type="button" disabled>停止</button>
232
+ </div>
233
+ <div class="stress-progress-wrap">
234
+ <div class="stress-progress-bar"><i id="flowStressProgressBar"></i></div>
235
+ <div class="stress-progress-text" id="flowStressProgressText">未开始</div>
236
+ </div>
237
+ <div class="stress-result" id="flowStressResult">
238
+ <div class="stress-empty">压测结果会显示在这里。</div>
239
+ </div>
240
+ </div>
241
+ </aside>
242
+ </div>
243
+ </main>
244
+ </div>
245
+
246
+ <div class="modal-mask" id="flowEditModal">
247
+ <div class="modal flow-edit-modal">
248
+ <div class="modal-head">
249
+ <span id="flowEditTitle">编辑节点</span>
250
+ <button class="icon-btn" id="closeFlowEdit" type="button">✕</button>
251
+ </div>
252
+ <div class="modal-body">
253
+ <div class="flow-node-transport">
254
+ <label>协议
255
+ <select id="flowNodeProtocol">
256
+ <option value="grpc">gRPC</option>
257
+ <option value="http">HTTP</option>
258
+ <option value="sse">SSE</option>
259
+ <option value="websocket">WebSocket</option>
260
+ </select>
261
+ </label>
262
+ <label id="flowNodeMethodWrap" class="hidden">方法
263
+ <select id="flowNodeHttpMethod">
264
+ <option>GET</option>
265
+ <option>POST</option>
266
+ <option>PUT</option>
267
+ <option>PATCH</option>
268
+ <option>DELETE</option>
269
+ </select>
270
+ </label>
271
+ <label id="flowNodePathWrap" class="hidden">路径
272
+ <input id="flowNodeHttpPath" placeholder="/path" />
273
+ </label>
274
+ <label>环境
275
+ <select id="flowNodeEnv"></select>
276
+ </label>
277
+ </div>
278
+ <p class="stress-note">请求字段值支持 <code>{{变量名}}</code>;响应字段可填写「提取变量」供后续节点使用(如写入 Metadata / Header)。</p>
279
+ <h4 class="flow-edit-h">Message</h4>
280
+ <div id="flowMsgBody" class="flow-msg-tree"></div>
281
+ <h4 class="flow-edit-h">Metadata</h4>
282
+ <table class="meta-table flow-edit-table">
283
+ <thead><tr><th>Name</th><th>Value(可用 {{var}})</th><th></th></tr></thead>
284
+ <tbody id="flowMetaBody"></tbody>
285
+ </table>
286
+ <button class="btn" id="flowAddMetaBtn" type="button">添加 Metadata</button>
287
+ <h4 class="flow-edit-h">响应提取变量 <span class="flow-edit-h-sub">嵌套字段可展开,填写变量名供后续 {{var}} 引用</span></h4>
288
+ <div id="flowExtractBody" class="flow-extract-tree"></div>
289
+ </div>
290
+ <div class="modal-foot">
291
+ <button class="btn primary" id="saveFlowEditBtn" type="button">保存</button>
292
+ </div>
293
+ </div>
294
+ </div>
295
+
296
+ <div class="modal-mask" id="flowHistoryModal">
297
+ <div class="modal flow-history-modal">
298
+ <div class="modal-head">
299
+ <span id="flowHistoryTitle">响应历史</span>
300
+ <button class="icon-btn" id="closeFlowHistory" type="button">✕</button>
301
+ </div>
302
+ <div class="modal-body flow-history-layout">
303
+ <div class="flow-history-list" id="flowHistoryList"></div>
304
+ <pre class="flow-history-detail" id="flowHistoryDetail">选择左侧一条记录查看</pre>
305
+ </div>
306
+ </div>
307
+ </div>
308
+
309
+ <div class="modal-mask" id="sourceModal">
310
+ <div class="modal">
311
+ <div class="modal-head">
312
+ <span>自定义 Proto 来源</span>
313
+ <button class="icon-btn" id="closeSourceModal" type="button">✕</button>
314
+ </div>
315
+ <div class="modal-body">
316
+ <p class="stress-note">在此选择本地目录或 Git;侧边栏「Proto 来源」仍可单独使用工作区 / Git。</p>
317
+ <div class="source-mode-row">
318
+ <label class="source-mode-opt"><input type="radio" name="sourceMode" value="directory" /> 自定义目录</label>
319
+ <label class="source-mode-opt"><input type="radio" name="sourceMode" value="git" /> 自定义 Git 来源</label>
320
+ </div>
321
+ <div id="sourceDirFields" class="source-git-fields">
322
+ <label>目录路径
323
+ <div class="source-dir-row">
324
+ <input id="sourceDirPath" placeholder="例如 D:\\proto 或 /home/me/api" />
325
+ <button class="btn" id="pickSourceDirBtn" type="button" title="选择文件夹">浏览</button>
326
+ </div>
327
+ </label>
328
+ <p class="stress-note" id="sourceDirHint">扫描该目录下全部 .proto(含子目录)。</p>
329
+ </div>
330
+ <div id="sourceGitFields" class="source-git-fields hidden">
331
+ <label>Git 地址<input id="sourceGitUrl" placeholder="https://github.com/org/repo.git" /></label>
332
+ <label>分支<input id="sourceGitBranch" placeholder="main" /></label>
333
+ <p class="stress-note" id="sourceGitHint"></p>
334
+ </div>
335
+ </div>
336
+ <div class="modal-foot">
337
+ <button class="btn primary" id="applySourceBtn" type="button">应用并刷新</button>
338
+ </div>
339
+ </div>
340
+ </div>
341
+
342
+ <div class="modal-mask" id="envModal">
343
+ <div class="modal">
344
+ <div class="modal-head">
345
+ <span>环境配置</span>
346
+ <button class="icon-btn" id="closeEnvModal" type="button">✕</button>
347
+ </div>
348
+ <div class="modal-body">
349
+ <div class="env-list" id="envList"></div>
350
+ <label>环境名<input id="envName" /></label>
351
+ <label>接口地址<input id="envHost" placeholder="host:port" /></label>
352
+ <label>备注<input id="envNote" placeholder="可选" /></label>
353
+ </div>
354
+ <div class="modal-foot">
355
+ <button class="btn" id="addEnvBtn" type="button">新增环境</button>
356
+ <button class="btn primary" id="saveEnvBtn" type="button">保存</button>
357
+ </div>
358
+ </div>
359
+ </div>
360
+
361
+ <div class="modal-mask" id="curlModal">
362
+ <div class="modal">
363
+ <div class="modal-head">
364
+ <span>grpcurl</span>
365
+ <button class="icon-btn" id="closeCurlModal" type="button">✕</button>
366
+ </div>
367
+ <div class="modal-body">
368
+ <textarea id="curlText" rows="12" readonly></textarea>
369
+ </div>
370
+ <div class="modal-foot">
371
+ <button class="btn primary" id="copyCurlBtn" type="button">复制命令</button>
372
+ </div>
373
+ </div>
374
+ </div>
375
+ <div class="modal-mask" id="bulkModal">
376
+ <div class="modal">
377
+ <div class="modal-head">
378
+ <span>Metadata Bulk Edit</span>
379
+ <button class="icon-btn" id="closeBulkModal" type="button">✕</button>
380
+ </div>
381
+ <div class="modal-body">
382
+ <label>每行一条:Name: Value
383
+ <textarea id="bulkText" rows="10" placeholder="authorization: Bearer xxx&#10;x-request-id: 1"></textarea>
384
+ </label>
385
+ </div>
386
+ <div class="modal-foot">
387
+ <button class="btn primary" id="applyBulkBtn" type="button">应用</button>
388
+ </div>
389
+ </div>
390
+ </div>
391
+
392
+ <div class="ai-overlay" id="aiOverlay">
393
+ <div class="ai-page">
394
+ <div class="ai-top">
395
+ <div>
396
+ <div class="doc-crumbs"><span>ProtoView</span><span>/</span><span>AI</span></div>
397
+ <h2 class="doc-title">山海<span class="ai-heart">♥</span></h2>
398
+ <p class="ai-poem">所爱隔山海,山海亦可平<br/>消耗了 token,贡献点爱心<br/>尊重每个热爱开发的程序员</p>
399
+ </div>
400
+ <div class="ai-actions">
401
+ <button class="btn" id="closeAiBtn" type="button">关闭</button>
402
+ </div>
403
+ </div>
404
+ <div class="ai-stage">
405
+ <div class="ai-game" id="aiGame">
406
+ <div class="doc-kicker">解谜 · 山海归位</div>
407
+ <p class="doc-lead">点击两张牌交换,拼出「山海平爱」。成功后会点亮爱心。</p>
408
+ <div class="ai-board" id="aiBoard"></div>
409
+ <button class="btn" id="aiShuffle" type="button">打乱重来</button>
410
+ </div>
411
+ <div class="ai-reward hidden" id="aiReward">
412
+ <div class="ai-heart burst">♥</div>
413
+ <div class="doc-kicker">解谜成功</div>
414
+ <p class="doc-lead">谢谢你的心意。扫一扫,给热爱开发的人一点支持。</p>
415
+ <img class="ai-qr" id="aiQr" alt="微信支付二维码" hidden />
416
+ <p class="doc-file">微信支付 · 辉</p>
417
+ </div>
418
+ </div>
419
+ </div>
420
+ </div>
421
+
422
+ <script src="./app.js"></script>
423
+ <script src="./flow.js"></script>
424
+ </body>
425
+ </html>