@yuhere/js-jsonrpc2 2.1.11 → 2.1.13

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.
Files changed (181) hide show
  1. package/dist/cli/cli.d.ts +0 -1
  2. package/dist/cli/cli.js +0 -1
  3. package/dist/cli/demo-methods.d.ts +0 -1
  4. package/dist/cli/demo-methods.js +0 -1
  5. package/dist/cli/express.d.ts +0 -1
  6. package/dist/cli/express.js +0 -1
  7. package/dist/cli/stdio.d.ts +0 -1
  8. package/dist/cli/stdio.js +0 -1
  9. package/dist/core/Repository.d.ts +0 -1
  10. package/dist/core/Repository.js +0 -1
  11. package/dist/core/convert.d.ts +0 -1
  12. package/dist/core/convert.js +0 -1
  13. package/dist/core/descriptor.d.ts +0 -1
  14. package/dist/core/descriptor.js +0 -1
  15. package/dist/core/index.d.ts +0 -1
  16. package/dist/core/index.js +0 -1
  17. package/dist/core/llm-tools.d.ts +0 -1
  18. package/dist/core/llm-tools.js +89 -3
  19. package/dist/core/permission.d.ts +0 -1
  20. package/dist/core/permission.js +0 -1
  21. package/dist/core/spec-check.d.ts +0 -1
  22. package/dist/core/spec-check.js +0 -1
  23. package/dist/core/system-methods.d.ts +0 -1
  24. package/dist/core/system-methods.js +12 -7
  25. package/dist/errors/RPCError.d.ts +0 -1
  26. package/dist/errors/RPCError.js +0 -1
  27. package/dist/errors/codes.d.ts +0 -1
  28. package/dist/errors/codes.js +0 -1
  29. package/dist/errors/index.d.ts +0 -1
  30. package/dist/errors/index.js +0 -1
  31. package/dist/index.d.ts +0 -1
  32. package/dist/index.js +0 -1
  33. package/dist/metrics/memory.d.ts +0 -1
  34. package/dist/metrics/memory.js +0 -1
  35. package/dist/metrics/prom.d.ts +0 -1
  36. package/dist/metrics/prom.js +0 -1
  37. package/dist/schema/json/index.d.ts +0 -1
  38. package/dist/schema/json/index.js +0 -1
  39. package/dist/schema/json/marker.d.ts +0 -1
  40. package/dist/schema/json/marker.js +0 -1
  41. package/dist/schema/json/primitives.d.ts +0 -1
  42. package/dist/schema/json/primitives.js +0 -1
  43. package/dist/schema/z/protocol.d.ts +0 -1
  44. package/dist/schema/z/protocol.js +0 -1
  45. package/dist/schema/z/register.d.ts +0 -1
  46. package/dist/schema/z/register.js +0 -1
  47. package/dist/stream/stringify-stream.d.ts +0 -1
  48. package/dist/stream/stringify-stream.js +94 -70
  49. package/dist/transport/express/body.d.ts +0 -1
  50. package/dist/transport/express/body.js +0 -1
  51. package/dist/transport/express/index.d.ts +0 -1
  52. package/dist/transport/express/index.js +0 -1
  53. package/dist/transport/express/middleware.d.ts +0 -1
  54. package/dist/transport/express/middleware.js +8 -6
  55. package/dist/transport/express/respond.d.ts +0 -1
  56. package/dist/transport/express/respond.js +0 -1
  57. package/dist/transport/stdio/index.d.ts +0 -1
  58. package/dist/transport/stdio/index.js +0 -1
  59. package/dist/transport/stdio/middleware.d.ts +0 -1
  60. package/dist/transport/stdio/middleware.js +7 -5
  61. package/dist/transport/ws/index.d.ts +0 -1
  62. package/dist/transport/ws/index.js +0 -1
  63. package/dist/transport/ws/middleware.d.ts +0 -1
  64. package/dist/transport/ws/middleware.js +7 -5
  65. package/dist/transport/ws/standalone.d.ts +0 -1
  66. package/dist/transport/ws/standalone.js +0 -1
  67. package/dist/types.d.ts +0 -1
  68. package/dist/types.js +0 -1
  69. package/dist/webclient/webui/src/client/httpTransport.d.ts +8 -0
  70. package/dist/webclient/webui/src/client/httpTransport.js +114 -0
  71. package/dist/webclient/webui/src/client/index.d.ts +4 -0
  72. package/dist/webclient/webui/src/client/index.js +2 -0
  73. package/dist/webclient/webui/src/client/rpc.d.ts +24 -0
  74. package/dist/webclient/webui/src/client/rpc.js +32 -0
  75. package/dist/webclient/webui/src/client/types.d.ts +14 -0
  76. package/dist/webclient/webui/src/client/types.js +1 -0
  77. package/dist/webclient/webui/src/client/wsTransport.d.ts +8 -0
  78. package/{webui/src/api/wsTransport.ts → dist/webclient/webui/src/client/wsTransport.js} +263 -305
  79. package/htdoc/assets/favicon-CXdFuLlv.png +0 -0
  80. package/htdoc/assets/{index-Cr0Jnrgb.js → index-CqI7vRAS.js} +1191 -327
  81. package/htdoc/assets/index-CqI7vRAS.js.map +1 -0
  82. package/htdoc/assets/{index-Ck-39Igh.css → index-Rq1yIwo2.css} +58 -0
  83. package/htdoc/index.html +4 -2
  84. package/package.json +10 -7
  85. package/dist/cli/cli.d.ts.map +0 -1
  86. package/dist/cli/cli.js.map +0 -1
  87. package/dist/cli/demo-methods.d.ts.map +0 -1
  88. package/dist/cli/demo-methods.js.map +0 -1
  89. package/dist/cli/express.d.ts.map +0 -1
  90. package/dist/cli/express.js.map +0 -1
  91. package/dist/cli/stdio.d.ts.map +0 -1
  92. package/dist/cli/stdio.js.map +0 -1
  93. package/dist/core/Repository.d.ts.map +0 -1
  94. package/dist/core/Repository.js.map +0 -1
  95. package/dist/core/convert.d.ts.map +0 -1
  96. package/dist/core/convert.js.map +0 -1
  97. package/dist/core/descriptor.d.ts.map +0 -1
  98. package/dist/core/descriptor.js.map +0 -1
  99. package/dist/core/index.d.ts.map +0 -1
  100. package/dist/core/index.js.map +0 -1
  101. package/dist/core/llm-tools.d.ts.map +0 -1
  102. package/dist/core/llm-tools.js.map +0 -1
  103. package/dist/core/permission.d.ts.map +0 -1
  104. package/dist/core/permission.js.map +0 -1
  105. package/dist/core/spec-check.d.ts.map +0 -1
  106. package/dist/core/spec-check.js.map +0 -1
  107. package/dist/core/system-methods.d.ts.map +0 -1
  108. package/dist/core/system-methods.js.map +0 -1
  109. package/dist/errors/RPCError.d.ts.map +0 -1
  110. package/dist/errors/RPCError.js.map +0 -1
  111. package/dist/errors/codes.d.ts.map +0 -1
  112. package/dist/errors/codes.js.map +0 -1
  113. package/dist/errors/index.d.ts.map +0 -1
  114. package/dist/errors/index.js.map +0 -1
  115. package/dist/index.d.ts.map +0 -1
  116. package/dist/index.js.map +0 -1
  117. package/dist/metrics/memory.d.ts.map +0 -1
  118. package/dist/metrics/memory.js.map +0 -1
  119. package/dist/metrics/prom.d.ts.map +0 -1
  120. package/dist/metrics/prom.js.map +0 -1
  121. package/dist/schema/json/index.d.ts.map +0 -1
  122. package/dist/schema/json/index.js.map +0 -1
  123. package/dist/schema/json/marker.d.ts.map +0 -1
  124. package/dist/schema/json/marker.js.map +0 -1
  125. package/dist/schema/json/primitives.d.ts.map +0 -1
  126. package/dist/schema/json/primitives.js.map +0 -1
  127. package/dist/schema/z/protocol.d.ts.map +0 -1
  128. package/dist/schema/z/protocol.js.map +0 -1
  129. package/dist/schema/z/register.d.ts.map +0 -1
  130. package/dist/schema/z/register.js.map +0 -1
  131. package/dist/stream/stringify-stream.d.ts.map +0 -1
  132. package/dist/stream/stringify-stream.js.map +0 -1
  133. package/dist/transport/express/body.d.ts.map +0 -1
  134. package/dist/transport/express/body.js.map +0 -1
  135. package/dist/transport/express/index.d.ts.map +0 -1
  136. package/dist/transport/express/index.js.map +0 -1
  137. package/dist/transport/express/middleware.d.ts.map +0 -1
  138. package/dist/transport/express/middleware.js.map +0 -1
  139. package/dist/transport/express/respond.d.ts.map +0 -1
  140. package/dist/transport/express/respond.js.map +0 -1
  141. package/dist/transport/stdio/index.d.ts.map +0 -1
  142. package/dist/transport/stdio/index.js.map +0 -1
  143. package/dist/transport/stdio/middleware.d.ts.map +0 -1
  144. package/dist/transport/stdio/middleware.js.map +0 -1
  145. package/dist/transport/ws/index.d.ts.map +0 -1
  146. package/dist/transport/ws/index.js.map +0 -1
  147. package/dist/transport/ws/middleware.d.ts.map +0 -1
  148. package/dist/transport/ws/middleware.js.map +0 -1
  149. package/dist/transport/ws/standalone.d.ts.map +0 -1
  150. package/dist/transport/ws/standalone.js.map +0 -1
  151. package/dist/types.d.ts.map +0 -1
  152. package/dist/types.js.map +0 -1
  153. package/htdoc/assets/index-Cr0Jnrgb.js.map +0 -1
  154. package/webui/index.html +0 -12
  155. package/webui/src/App.tsx +0 -164
  156. package/webui/src/api/docGen.ts +0 -188
  157. package/webui/src/api/glob.ts +0 -90
  158. package/webui/src/api/httpTransport.ts +0 -148
  159. package/webui/src/api/metrics.ts +0 -135
  160. package/webui/src/api/paramForm.ts +0 -80
  161. package/webui/src/api/rpc.ts +0 -57
  162. package/webui/src/api/schema.ts +0 -12
  163. package/webui/src/api/types.ts +0 -16
  164. package/webui/src/components/MethodDetail.tsx +0 -43
  165. package/webui/src/components/MethodTree.tsx +0 -118
  166. package/webui/src/components/MetricsPanel.md +0 -126
  167. package/webui/src/components/MetricsPanel.tsx +0 -290
  168. package/webui/src/components/SchemaForm.tsx +0 -697
  169. package/webui/src/components/SchemaView.tsx +0 -151
  170. package/webui/src/components/TestPanel.tsx +0 -322
  171. package/webui/src/components/Toolbar.tsx +0 -82
  172. package/webui/src/hooks/useMethodDetail.ts +0 -61
  173. package/webui/src/hooks/useMethods.ts +0 -27
  174. package/webui/src/hooks/useMetrics.ts +0 -91
  175. package/webui/src/main.tsx +0 -13
  176. package/webui/src/styles/geek.css +0 -963
  177. package/webui/src/theme/ThemeProvider.tsx +0 -79
  178. package/webui/src/vite-env.d.ts +0 -1
  179. package/webui/tsconfig.json +0 -34
  180. package/webui/tsconfig.node.json +0 -13
  181. package/webui/vite.config.ts +0 -15
@@ -7537,9 +7537,7 @@ var require_client = /* @__PURE__ */ __commonJSMin(((exports) => {
7537
7537
  exports.hydrateRoot = m.hydrateRoot;
7538
7538
  }));
7539
7539
  //#endregion
7540
- //#region webui/src/api/httpTransport.ts
7541
- var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
7542
- var import_client = /* @__PURE__ */ __toESM(require_client(), 1);
7540
+ //#region webui/src/client/httpTransport.ts
7543
7541
  var idCounter$1 = 0;
7544
7542
  async function readLine(reader, decoder, state) {
7545
7543
  while (true) {
@@ -7562,7 +7560,7 @@ async function readLine(reader, decoder, state) {
7562
7560
  function isStreamFrame$1(v) {
7563
7561
  return !!v && typeof v === "object" && typeof v.done === "boolean";
7564
7562
  }
7565
- function createHttpRpc(options = {}) {
7563
+ function createHttpRPC(options = {}) {
7566
7564
  const { endpoint = "", headers = {}, fetchImpl = fetch } = options;
7567
7565
  function call(method, ...params) {
7568
7566
  const body = {
@@ -7576,7 +7574,7 @@ function createHttpRpc(options = {}) {
7576
7574
  method: "POST",
7577
7575
  headers: {
7578
7576
  "Content-Type": "application/json",
7579
- "Accept": "application/x-ndjson",
7577
+ "Accept": "application/json, application/x-ndjson",
7580
7578
  ...headers
7581
7579
  },
7582
7580
  body: JSON.stringify(body)
@@ -7643,7 +7641,7 @@ function absoluteEndpoint(endpoint) {
7643
7641
  }
7644
7642
  }
7645
7643
  //#endregion
7646
- //#region webui/src/api/wsTransport.ts
7644
+ //#region webui/src/client/wsTransport.ts
7647
7645
  var idCounter = 0;
7648
7646
  function deriveWsEndpoint() {
7649
7647
  const loc = window.location;
@@ -7652,7 +7650,7 @@ function deriveWsEndpoint() {
7652
7650
  function isStreamFrame(v) {
7653
7651
  return !!v && typeof v === "object" && typeof v.done === "boolean";
7654
7652
  }
7655
- function createWsRpc(options = {}) {
7653
+ function createWsRPC(options = {}) {
7656
7654
  const { endpoint = deriveWsEndpoint(), connectTimeoutMs = 5e3, callTimeoutMs = 15e3 } = options;
7657
7655
  const pending = /* @__PURE__ */ new Map();
7658
7656
  const streams = /* @__PURE__ */ new Map();
@@ -7688,6 +7686,7 @@ function createWsRpc(options = {}) {
7688
7686
  }
7689
7687
  const list = Array.isArray(data) ? data : [data];
7690
7688
  for (const msg of list) {
7689
+ if (!msg || typeof msg !== "object") continue;
7691
7690
  if (msg.id == null) continue;
7692
7691
  const fw = firstWaiters.get(msg.id);
7693
7692
  if (fw) {
@@ -7902,7 +7901,7 @@ function _defineProperty(e, r, t) {
7902
7901
  }) : e[r] = t, e;
7903
7902
  }
7904
7903
  //#endregion
7905
- //#region webui/src/api/rpc.ts
7904
+ //#region webui/src/client/rpc.ts
7906
7905
  var RPCError = class extends Error {
7907
7906
  constructor(payload) {
7908
7907
  super(payload.message);
@@ -7920,13 +7919,13 @@ var RPCError = class extends Error {
7920
7919
  function isStreamResult(v) {
7921
7920
  return !!v && typeof v === "object" && typeof v[Symbol.asyncIterator] === "function";
7922
7921
  }
7923
- function createRpc(options) {
7924
- if (options.transport === "ws") return createWsRpc({
7922
+ function createRPC(options) {
7923
+ if (options.transport === "ws") return createWsRPC({
7925
7924
  endpoint: options.endpoint,
7926
7925
  connectTimeoutMs: options.connectTimeoutMs,
7927
7926
  callTimeoutMs: options.callTimeoutMs
7928
7927
  });
7929
- return createHttpRpc({
7928
+ return createHttpRPC({
7930
7929
  endpoint: options.endpoint,
7931
7930
  headers: options.headers,
7932
7931
  fetchImpl: options.fetchImpl
@@ -7934,6 +7933,8 @@ function createRpc(options) {
7934
7933
  }
7935
7934
  //#endregion
7936
7935
  //#region webui/src/hooks/useMethods.ts
7936
+ var import_client = /* @__PURE__ */ __toESM(require_client(), 1);
7937
+ var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
7937
7938
  function useMethods(rpc) {
7938
7939
  const [methods, setMethods] = (0, import_react.useState)([]);
7939
7940
  const [loading, setLoading] = (0, import_react.useState)(false);
@@ -8013,6 +8014,24 @@ function useMethodDetail(rpc, method) {
8013
8014
  return detail;
8014
8015
  }
8015
8016
  //#endregion
8017
+ //#region webui/src/hooks/useDebouncedValue.ts
8018
+ /**
8019
+ * 返回 `value` 的防抖版本:`value` 停止变化 `delayMs` 毫秒后才更新返回值。
8020
+ * 初始值立即生效(首次渲染不会延迟)。
8021
+ */
8022
+ function useDebouncedValue(value, delayMs = 250) {
8023
+ const [debounced, setDebounced] = (0, import_react.useState)(value);
8024
+ (0, import_react.useEffect)(() => {
8025
+ if (delayMs <= 0) {
8026
+ setDebounced(value);
8027
+ return;
8028
+ }
8029
+ const t = setTimeout(() => setDebounced(value), delayMs);
8030
+ return () => clearTimeout(t);
8031
+ }, [value, delayMs]);
8032
+ return debounced;
8033
+ }
8034
+ //#endregion
8016
8035
  //#region webui/src/api/metrics.ts
8017
8036
  /** 拉取 system.metrics;如果系统方法不可用(withSystemMethods=false),返回 undefined。 */
8018
8037
  async function fetchMetrics(rpc) {
@@ -8187,11 +8206,11 @@ var require_react_jsx_runtime_production_min = /* @__PURE__ */ __commonJSMin(((e
8187
8206
  var import_jsx_runtime = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
8188
8207
  module.exports = require_react_jsx_runtime_production_min();
8189
8208
  })))();
8190
- var STORAGE_KEY = "jsonrpc2.theme";
8209
+ var STORAGE_KEY$1 = "jsonrpc2.theme";
8191
8210
  var ThemeContext = (0, import_react.createContext)(null);
8192
- function readStoredMode() {
8211
+ function readStoredMode$1() {
8193
8212
  try {
8194
- const v = localStorage.getItem(STORAGE_KEY);
8213
+ const v = localStorage.getItem(STORAGE_KEY$1);
8195
8214
  if (v === "light" || v === "dark" || v === "system") return v;
8196
8215
  } catch {}
8197
8216
  return "system";
@@ -8200,7 +8219,7 @@ function prefersDark() {
8200
8219
  return typeof window !== "undefined" && window.matchMedia?.("(prefers-color-scheme: dark)").matches;
8201
8220
  }
8202
8221
  function ThemeProvider({ children }) {
8203
- const [mode, setModeState] = (0, import_react.useState)(readStoredMode);
8222
+ const [mode, setModeState] = (0, import_react.useState)(readStoredMode$1);
8204
8223
  const [systemDark, setSystemDark] = (0, import_react.useState)(prefersDark);
8205
8224
  (0, import_react.useEffect)(() => {
8206
8225
  const mql = window.matchMedia("(prefers-color-scheme: dark)");
@@ -8219,7 +8238,7 @@ function ThemeProvider({ children }) {
8219
8238
  (0, import_react.useEffect)(() => {
8220
8239
  document.documentElement.setAttribute("data-theme", resolved);
8221
8240
  try {
8222
- localStorage.setItem(STORAGE_KEY, mode);
8241
+ localStorage.setItem(STORAGE_KEY$1, mode);
8223
8242
  } catch {}
8224
8243
  }, [resolved, mode]);
8225
8244
  const setMode = (0, import_react.useCallback)((m) => setModeState(m), []);
@@ -8242,54 +8261,812 @@ function useTheme() {
8242
8261
  if (!ctx) throw new Error("useTheme must be used within <ThemeProvider>");
8243
8262
  return ctx;
8244
8263
  }
8245
- //#endregion
8246
- //#region webui/src/components/Toolbar.tsx
8247
- var THEME_OPTIONS = [
8248
- {
8249
- value: "system",
8250
- label: "跟随系统"
8264
+ var MESSAGES = {
8265
+ en: {
8266
+ common: {
8267
+ loading: "Loading…",
8268
+ refresh: "Refresh",
8269
+ refreshing: "Refreshing…",
8270
+ reset: "Reset",
8271
+ clear: "Clear",
8272
+ delete: "Delete",
8273
+ add: "Add",
8274
+ copy: "Copy",
8275
+ copied: "Copied ✓",
8276
+ copyFailed: "Copy failed: {error}",
8277
+ generate: "Generate",
8278
+ generating: "Generating…",
8279
+ none: "None",
8280
+ empty: "Empty"
8281
+ },
8282
+ toolbar: {
8283
+ title: "JSON-RPC 2.0",
8284
+ transport: "Transport",
8285
+ http: "HTTP",
8286
+ ws: "WebSocket",
8287
+ refreshMethods: "Refresh methods",
8288
+ refreshMethodsLoading: "Refreshing…",
8289
+ generateDocs: "Generate docs ⬇",
8290
+ generateDocsLoading: "Generating…",
8291
+ generateDocsTitle: "Fetch help and signatures of all methods, render Markdown and download",
8292
+ theme: "Theme",
8293
+ themeSystem: "System",
8294
+ themeLight: "Light",
8295
+ themeDark: "Dark",
8296
+ language: "Language",
8297
+ languageSystem: "Follow system",
8298
+ languageEn: "English",
8299
+ languageZhCN: "简体中文",
8300
+ languageZhTW: "繁體中文",
8301
+ languageJa: "日本語"
8302
+ },
8303
+ sidebar: {
8304
+ methods: "Methods",
8305
+ metrics: "Metrics",
8306
+ search: "Search",
8307
+ searchPlaceholder: "Type keywords, space separated",
8308
+ matchCount: "Matched {matched} / {total} methods",
8309
+ noMatch: "No matching methods.",
8310
+ noMethods: "No methods available."
8311
+ },
8312
+ view: {
8313
+ label: "View",
8314
+ methods: "Methods",
8315
+ metrics: "Metrics"
8316
+ },
8317
+ method: {
8318
+ selectHint: "← Select a method from the left to view help and signature.",
8319
+ loading: "Loading…",
8320
+ loadFailed: "Load failed: {error}",
8321
+ help: "Help",
8322
+ noHelp: "(no help available)",
8323
+ returnType: "Return type",
8324
+ returnTypeHint: "(the server does not validate execution results against this type; actual values may differ)",
8325
+ none: "(none)"
8326
+ },
8327
+ test: {
8328
+ params: "Parameters",
8329
+ paramsHint: "({count}, auto-generated from signature)",
8330
+ noParams: "This method takes no parameters.",
8331
+ execute: "Execute ▶",
8332
+ executing: "Executing…",
8333
+ executeStream: "Execute & receive stream ▶",
8334
+ receiving: "Receiving…",
8335
+ resetToSample: "Reset to sample",
8336
+ clearOutput: "Clear output",
8337
+ result: "Result",
8338
+ resultStream: " (streaming)",
8339
+ notExecuted: "(not executed yet)",
8340
+ copyCurl: "Copy curl (bash)",
8341
+ copyCurlCmd: "Copy curl (cmd)",
8342
+ copyFetch: "Copy fetch",
8343
+ copyTitle: "Copy equivalent code for current call",
8344
+ copyDisabledTitle: "Only HTTP transport can generate code",
8345
+ chooseFormat: "Choose format",
8346
+ streamEnded: "(stream ended, {count} frames, total {duration})",
8347
+ streamEndedEmpty: "(stream ended, no data, total {duration})",
8348
+ elapsed: "elapsed",
8349
+ firstFrame: "first frame",
8350
+ jsonError: "JSON error: {error}"
8351
+ },
8352
+ schema: {
8353
+ nullable: "nullable",
8354
+ setNull: "Set null",
8355
+ fillValue: "Fill value",
8356
+ fixedValue: "(fixed value)",
8357
+ unionNoBranch: "(union has no branch)",
8358
+ arrayNoItem: "(array has no item schema)",
8359
+ recordNoValue: "(record has no value schema)",
8360
+ recordAddPlaceholder: "New key",
8361
+ addRecordKey: "+ Add key",
8362
+ addItem: "+ Add item",
8363
+ strictHint: "(non-strict mode, extra fields allowed)",
8364
+ paramIndex: "Parameter [{index}]",
8365
+ items: "items",
8366
+ values: "values",
8367
+ optionIndex: "option[{index}]",
8368
+ inner: "inner"
8369
+ },
8370
+ metrics: {
8371
+ title: "Metrics",
8372
+ unavailable: "Server did not enable system.metrics (withSystemMethods: false or not registered).",
8373
+ autoRefresh: "Auto refresh",
8374
+ interval: "Interval",
8375
+ refreshNow: "Refresh now",
8376
+ refreshing: "Refreshing…",
8377
+ uptime: "uptime",
8378
+ loading: "Loading…",
8379
+ methods: "Methods",
8380
+ calls: "Calls",
8381
+ streams: "Streams",
8382
+ errors: "Errors",
8383
+ errorRate: "Error rate",
8384
+ invalidRequests: "Invalid requests",
8385
+ nonStreamTitle: "Non-stream methods",
8386
+ streamTitle: "Stream methods",
8387
+ noData: "No data.",
8388
+ noStreams: "No streaming calls.",
8389
+ countHint: " ({count})",
8390
+ colMethod: "method",
8391
+ colCalls: "calls",
8392
+ colRate: "rate/s",
8393
+ colErrors: "errors",
8394
+ colErrPct: "err%",
8395
+ colP50: "p50",
8396
+ colP95: "p95",
8397
+ colP99: "p99",
8398
+ colAvg: "avg",
8399
+ colOpened: "opened",
8400
+ colCompleted: "completed",
8401
+ colFailed: "failed",
8402
+ colAborted: "aborted",
8403
+ colCompletePct: "complete%",
8404
+ colAbortPct: "abort%",
8405
+ colAvgChunks: "avg chunks",
8406
+ colTtfbP50: "ttfb p50",
8407
+ colTtfbP95: "ttfb p95",
8408
+ colDurationAvg: "duration avg"
8409
+ },
8410
+ footer: {
8411
+ version: "version",
8412
+ npm: "npm package",
8413
+ github: "GitHub repository"
8414
+ },
8415
+ alerts: {
8416
+ emptyMethods: "Method list is empty. Please refresh methods first.",
8417
+ docsFailed: "Failed to generate docs: {error}"
8418
+ }
8419
+ },
8420
+ "zh-CN": {
8421
+ common: {
8422
+ loading: "加载中…",
8423
+ refresh: "刷新",
8424
+ refreshing: "刷新中…",
8425
+ reset: "重置",
8426
+ clear: "清空",
8427
+ delete: "删除",
8428
+ add: "添加",
8429
+ copy: "复制",
8430
+ copied: "已复制 ✓",
8431
+ copyFailed: "复制失败:{error}",
8432
+ generate: "生成",
8433
+ generating: "生成中…",
8434
+ none: "无",
8435
+ empty: "空"
8436
+ },
8437
+ toolbar: {
8438
+ title: "JSON-RPC 2.0",
8439
+ transport: "传输方式",
8440
+ http: "HTTP",
8441
+ ws: "WebSocket",
8442
+ refreshMethods: "刷新方法列表",
8443
+ refreshMethodsLoading: "刷新中…",
8444
+ generateDocs: "生成文档 ⬇",
8445
+ generateDocsLoading: "生成中…",
8446
+ generateDocsTitle: "拉取所有方法的帮助与签名,生成 Markdown 文档并下载",
8447
+ theme: "主题",
8448
+ themeSystem: "跟随系统",
8449
+ themeLight: "亮",
8450
+ themeDark: "暗",
8451
+ language: "语言",
8452
+ languageSystem: "跟随系统语言",
8453
+ languageEn: "English",
8454
+ languageZhCN: "简体中文",
8455
+ languageZhTW: "繁體中文",
8456
+ languageJa: "日本語"
8457
+ },
8458
+ sidebar: {
8459
+ methods: "Methods",
8460
+ metrics: "Metrics",
8461
+ search: "搜索",
8462
+ searchPlaceholder: "输入关键词,空格分隔",
8463
+ matchCount: "匹配 {matched} / {total} 个方法",
8464
+ noMatch: "没有匹配的方法。",
8465
+ noMethods: "暂无可用方法。"
8466
+ },
8467
+ view: {
8468
+ label: "视图",
8469
+ methods: "Methods",
8470
+ metrics: "Metrics"
8471
+ },
8472
+ method: {
8473
+ selectHint: "← 从左侧选择一个方法查看帮助与签名。",
8474
+ loading: "加载中…",
8475
+ loadFailed: "加载失败:{error}",
8476
+ help: "Help",
8477
+ noHelp: "(无帮助信息)",
8478
+ returnType: "返回类型",
8479
+ returnTypeHint: "(服务端不会校验执行结果是否符合此类型,实际返回值可能会有所不同)",
8480
+ none: "(暂无)"
8481
+ },
8482
+ test: {
8483
+ params: "参数",
8484
+ paramsHint: "({count} 个,按签名自动生成)",
8485
+ noParams: "此方法无需参数。",
8486
+ execute: "执行 ▶",
8487
+ executing: "执行中…",
8488
+ executeStream: "执行并接收流 ▶",
8489
+ receiving: "接收中…",
8490
+ resetToSample: "重置为示例",
8491
+ clearOutput: "清空输出",
8492
+ result: "结果",
8493
+ resultStream: "(流式)",
8494
+ notExecuted: "(尚未执行)",
8495
+ copyCurl: "复制 curl (bash)",
8496
+ copyCurlCmd: "复制 curl (cmd)",
8497
+ copyFetch: "复制 fetch",
8498
+ copyTitle: "复制当前调用的等价代码",
8499
+ copyDisabledTitle: "仅 HTTP 传输可生成代码",
8500
+ chooseFormat: "选择复制格式",
8501
+ streamEnded: "(流已结束,共 {count} 帧,总耗时 {duration})",
8502
+ streamEndedEmpty: "(流已结束,无数据,总耗时 {duration})",
8503
+ elapsed: "耗时",
8504
+ firstFrame: "首帧",
8505
+ jsonError: "JSON 错误:{error}"
8506
+ },
8507
+ schema: {
8508
+ nullable: "nullable",
8509
+ setNull: "置 null",
8510
+ fillValue: "填值",
8511
+ fixedValue: "(固定值)",
8512
+ unionNoBranch: "(union 无分支)",
8513
+ arrayNoItem: "(数组无 item schema)",
8514
+ recordNoValue: "(record 无 value schema)",
8515
+ recordAddPlaceholder: "新键名",
8516
+ addRecordKey: "+ 添加键",
8517
+ addItem: "+ 添加项",
8518
+ strictHint: "(非 strict 模式,允许额外字段)",
8519
+ paramIndex: "参数 [{index}]",
8520
+ items: "items",
8521
+ values: "values",
8522
+ optionIndex: "option[{index}]",
8523
+ inner: "inner"
8524
+ },
8525
+ metrics: {
8526
+ title: "Metrics",
8527
+ unavailable: "服务端未启用 system.metrics(withSystemMethods: false 或未注册)。",
8528
+ autoRefresh: "自动刷新",
8529
+ interval: "刷新间隔",
8530
+ refreshNow: "立即刷新",
8531
+ refreshing: "刷新中…",
8532
+ uptime: "uptime",
8533
+ loading: "加载中…",
8534
+ methods: "Methods",
8535
+ calls: "Calls",
8536
+ streams: "Streams",
8537
+ errors: "Errors",
8538
+ errorRate: "Error rate",
8539
+ invalidRequests: "Invalid requests",
8540
+ nonStreamTitle: "Non-stream methods",
8541
+ streamTitle: "Stream methods",
8542
+ noData: "暂无数据。",
8543
+ noStreams: "暂无流式调用。",
8544
+ countHint: "({count})",
8545
+ colMethod: "method",
8546
+ colCalls: "calls",
8547
+ colRate: "rate/s",
8548
+ colErrors: "errors",
8549
+ colErrPct: "err%",
8550
+ colP50: "p50",
8551
+ colP95: "p95",
8552
+ colP99: "p99",
8553
+ colAvg: "avg",
8554
+ colOpened: "opened",
8555
+ colCompleted: "completed",
8556
+ colFailed: "failed",
8557
+ colAborted: "aborted",
8558
+ colCompletePct: "complete%",
8559
+ colAbortPct: "abort%",
8560
+ colAvgChunks: "avg chunks",
8561
+ colTtfbP50: "ttfb p50",
8562
+ colTtfbP95: "ttfb p95",
8563
+ colDurationAvg: "duration avg"
8564
+ },
8565
+ footer: {
8566
+ version: "版本",
8567
+ npm: "npm 包",
8568
+ github: "GitHub 仓库"
8569
+ },
8570
+ alerts: {
8571
+ emptyMethods: "方法列表为空,请先刷新方法列表。",
8572
+ docsFailed: "生成文档失败:{error}"
8573
+ }
8251
8574
  },
8252
- {
8253
- value: "light",
8254
- label: "亮"
8575
+ "zh-TW": {
8576
+ common: {
8577
+ loading: "載入中…",
8578
+ refresh: "重新整理",
8579
+ refreshing: "重新整理中…",
8580
+ reset: "重設",
8581
+ clear: "清除",
8582
+ delete: "刪除",
8583
+ add: "新增",
8584
+ copy: "複製",
8585
+ copied: "已複製 ✓",
8586
+ copyFailed: "複製失敗:{error}",
8587
+ generate: "產生",
8588
+ generating: "產生中…",
8589
+ none: "無",
8590
+ empty: "空"
8591
+ },
8592
+ toolbar: {
8593
+ title: "JSON-RPC 2.0",
8594
+ transport: "傳輸方式",
8595
+ http: "HTTP",
8596
+ ws: "WebSocket",
8597
+ refreshMethods: "重新整理方法清單",
8598
+ refreshMethodsLoading: "重新整理中…",
8599
+ generateDocs: "產生文件 ⬇",
8600
+ generateDocsLoading: "產生中…",
8601
+ generateDocsTitle: "取得所有方法的說明與簽章,產生 Markdown 文件並下載",
8602
+ theme: "主題",
8603
+ themeSystem: "跟隨系統",
8604
+ themeLight: "亮",
8605
+ themeDark: "暗",
8606
+ language: "語言",
8607
+ languageSystem: "跟隨系統語言",
8608
+ languageEn: "English",
8609
+ languageZhCN: "简体中文",
8610
+ languageZhTW: "繁體中文",
8611
+ languageJa: "日本語"
8612
+ },
8613
+ sidebar: {
8614
+ methods: "Methods",
8615
+ metrics: "Metrics",
8616
+ search: "搜尋",
8617
+ searchPlaceholder: "輸入關鍵字,以空格分隔",
8618
+ matchCount: "符合 {matched} / {total} 個方法",
8619
+ noMatch: "沒有符合的方法。",
8620
+ noMethods: "尚無可用方法。"
8621
+ },
8622
+ view: {
8623
+ label: "檢視",
8624
+ methods: "Methods",
8625
+ metrics: "Metrics"
8626
+ },
8627
+ method: {
8628
+ selectHint: "← 從左側選擇一個方法以檢視說明與簽章。",
8629
+ loading: "載入中…",
8630
+ loadFailed: "載入失敗:{error}",
8631
+ help: "Help",
8632
+ noHelp: "(無說明資訊)",
8633
+ returnType: "回傳型別",
8634
+ returnTypeHint: "(伺服器不會驗證執行結果是否符合此型別,實際回傳值可能有所不同)",
8635
+ none: "(暫無)"
8636
+ },
8637
+ test: {
8638
+ params: "參數",
8639
+ paramsHint: "({count} 個,依簽章自動產生)",
8640
+ noParams: "此方法無需參數。",
8641
+ execute: "執行 ▶",
8642
+ executing: "執行中…",
8643
+ executeStream: "執行並接收串流 ▶",
8644
+ receiving: "接收中…",
8645
+ resetToSample: "重設為範例",
8646
+ clearOutput: "清除輸出",
8647
+ result: "結果",
8648
+ resultStream: "(串流)",
8649
+ notExecuted: "(尚未執行)",
8650
+ copyCurl: "複製 curl (bash)",
8651
+ copyCurlCmd: "複製 curl (cmd)",
8652
+ copyFetch: "複製 fetch",
8653
+ copyTitle: "複製目前呼叫的等效程式碼",
8654
+ copyDisabledTitle: "僅 HTTP 傳輸可產生程式碼",
8655
+ chooseFormat: "選擇複製格式",
8656
+ streamEnded: "(串流已結束,共 {count} 幀,總耗時 {duration})",
8657
+ streamEndedEmpty: "(串流已結束,無資料,總耗時 {duration})",
8658
+ elapsed: "耗時",
8659
+ firstFrame: "首幀",
8660
+ jsonError: "JSON 錯誤:{error}"
8661
+ },
8662
+ schema: {
8663
+ nullable: "nullable",
8664
+ setNull: "設為 null",
8665
+ fillValue: "填入值",
8666
+ fixedValue: "(固定值)",
8667
+ unionNoBranch: "(union 無分支)",
8668
+ arrayNoItem: "(陣列無 item schema)",
8669
+ recordNoValue: "(record 無 value schema)",
8670
+ recordAddPlaceholder: "新鍵名",
8671
+ addRecordKey: "+ 新增鍵",
8672
+ addItem: "+ 新增項目",
8673
+ strictHint: "(非 strict 模式,允許額外欄位)",
8674
+ paramIndex: "參數 [{index}]",
8675
+ items: "items",
8676
+ values: "values",
8677
+ optionIndex: "option[{index}]",
8678
+ inner: "inner"
8679
+ },
8680
+ metrics: {
8681
+ title: "Metrics",
8682
+ unavailable: "伺服器未啟用 system.metrics(withSystemMethods: false 或未註冊)。",
8683
+ autoRefresh: "自動重新整理",
8684
+ interval: "重新整理間隔",
8685
+ refreshNow: "立即重新整理",
8686
+ refreshing: "重新整理中…",
8687
+ uptime: "uptime",
8688
+ loading: "載入中…",
8689
+ methods: "Methods",
8690
+ calls: "Calls",
8691
+ streams: "Streams",
8692
+ errors: "Errors",
8693
+ errorRate: "Error rate",
8694
+ invalidRequests: "Invalid requests",
8695
+ nonStreamTitle: "Non-stream methods",
8696
+ streamTitle: "Stream methods",
8697
+ noData: "尚無資料。",
8698
+ noStreams: "尚無串流呼叫。",
8699
+ countHint: "({count})",
8700
+ colMethod: "method",
8701
+ colCalls: "calls",
8702
+ colRate: "rate/s",
8703
+ colErrors: "errors",
8704
+ colErrPct: "err%",
8705
+ colP50: "p50",
8706
+ colP95: "p95",
8707
+ colP99: "p99",
8708
+ colAvg: "avg",
8709
+ colOpened: "opened",
8710
+ colCompleted: "completed",
8711
+ colFailed: "failed",
8712
+ colAborted: "aborted",
8713
+ colCompletePct: "complete%",
8714
+ colAbortPct: "abort%",
8715
+ colAvgChunks: "avg chunks",
8716
+ colTtfbP50: "ttfb p50",
8717
+ colTtfbP95: "ttfb p95",
8718
+ colDurationAvg: "duration avg"
8719
+ },
8720
+ footer: {
8721
+ version: "版本",
8722
+ npm: "npm 套件",
8723
+ github: "GitHub 儲存庫"
8724
+ },
8725
+ alerts: {
8726
+ emptyMethods: "方法清單為空,請先重新整理方法清單。",
8727
+ docsFailed: "產生文件失敗:{error}"
8728
+ }
8255
8729
  },
8256
- {
8257
- value: "dark",
8258
- label: "暗"
8730
+ ja: {
8731
+ common: {
8732
+ loading: "読み込み中…",
8733
+ refresh: "更新",
8734
+ refreshing: "更新中…",
8735
+ reset: "リセット",
8736
+ clear: "クリア",
8737
+ delete: "削除",
8738
+ add: "追加",
8739
+ copy: "コピー",
8740
+ copied: "コピーしました ✓",
8741
+ copyFailed: "コピーに失敗しました:{error}",
8742
+ generate: "生成",
8743
+ generating: "生成中…",
8744
+ none: "なし",
8745
+ empty: "空"
8746
+ },
8747
+ toolbar: {
8748
+ title: "JSON-RPC 2.0",
8749
+ transport: "トランスポート",
8750
+ http: "HTTP",
8751
+ ws: "WebSocket",
8752
+ refreshMethods: "メソッド一覧を更新",
8753
+ refreshMethodsLoading: "更新中…",
8754
+ generateDocs: "ドキュメント生成 ⬇",
8755
+ generateDocsLoading: "生成中…",
8756
+ generateDocsTitle: "全メソッドのヘルプとシグネチャを取得し、Markdown を生成してダウンロード",
8757
+ theme: "テーマ",
8758
+ themeSystem: "システムに従う",
8759
+ themeLight: "ライト",
8760
+ themeDark: "ダーク",
8761
+ language: "言語",
8762
+ languageSystem: "システム言語に従う",
8763
+ languageEn: "English",
8764
+ languageZhCN: "简体中文",
8765
+ languageZhTW: "繁體中文",
8766
+ languageJa: "日本語"
8767
+ },
8768
+ sidebar: {
8769
+ methods: "Methods",
8770
+ metrics: "Metrics",
8771
+ search: "検索",
8772
+ searchPlaceholder: "キーワードを入力(スペース区切り)",
8773
+ matchCount: "{matched} / {total} 件のメソッドが一致",
8774
+ noMatch: "一致するメソッドはありません。",
8775
+ noMethods: "利用可能なメソッドがありません。"
8776
+ },
8777
+ view: {
8778
+ label: "ビュー",
8779
+ methods: "Methods",
8780
+ metrics: "Metrics"
8781
+ },
8782
+ method: {
8783
+ selectHint: "← 左側からメソッドを選択すると、ヘルプとシグネチャが表示されます。",
8784
+ loading: "読み込み中…",
8785
+ loadFailed: "読み込み失敗:{error}",
8786
+ help: "Help",
8787
+ noHelp: "(ヘルプ情報なし)",
8788
+ returnType: "戻り値の型",
8789
+ returnTypeHint: "(サーバーは実行結果がこの型に一致するか検証しません。実際の戻り値は異なる場合があります)",
8790
+ none: "(なし)"
8791
+ },
8792
+ test: {
8793
+ params: "パラメータ",
8794
+ paramsHint: "({count} 個、シグネチャから自動生成)",
8795
+ noParams: "このメソッドはパラメータを取りません。",
8796
+ execute: "実行 ▶",
8797
+ executing: "実行中…",
8798
+ executeStream: "実行してストリーム受信 ▶",
8799
+ receiving: "受信中…",
8800
+ resetToSample: "サンプルに戻す",
8801
+ clearOutput: "出力をクリア",
8802
+ result: "結果",
8803
+ resultStream: "(ストリーミング)",
8804
+ notExecuted: "(未実行)",
8805
+ copyCurl: "curl をコピー (bash)",
8806
+ copyCurlCmd: "curl をコピー (cmd)",
8807
+ copyFetch: "fetch をコピー",
8808
+ copyTitle: "現在の呼び出しに相当するコードをコピー",
8809
+ copyDisabledTitle: "コード生成は HTTP トランスポートのみ対応",
8810
+ chooseFormat: "コピー形式を選択",
8811
+ streamEnded: "(ストリーム終了、{count} フレーム、合計 {duration})",
8812
+ streamEndedEmpty: "(ストリーム終了、データなし、合計 {duration})",
8813
+ elapsed: "経過",
8814
+ firstFrame: "初回フレーム",
8815
+ jsonError: "JSON エラー:{error}"
8816
+ },
8817
+ schema: {
8818
+ nullable: "nullable",
8819
+ setNull: "null にする",
8820
+ fillValue: "値を入力",
8821
+ fixedValue: "(固定値)",
8822
+ unionNoBranch: "(union に分岐がありません)",
8823
+ arrayNoItem: "(配列に item schema がありません)",
8824
+ recordNoValue: "(record に value schema がありません)",
8825
+ recordAddPlaceholder: "新しいキー",
8826
+ addRecordKey: "+ キーを追加",
8827
+ addItem: "+ 項目を追加",
8828
+ strictHint: "(非 strict モード、追加フィールドを許可)",
8829
+ paramIndex: "パラメータ [{index}]",
8830
+ items: "items",
8831
+ values: "values",
8832
+ optionIndex: "option[{index}]",
8833
+ inner: "inner"
8834
+ },
8835
+ metrics: {
8836
+ title: "Metrics",
8837
+ unavailable: "サーバーで system.metrics が有効になっていません(withSystemMethods: false または未登録)。",
8838
+ autoRefresh: "自動更新",
8839
+ interval: "更新間隔",
8840
+ refreshNow: "今すぐ更新",
8841
+ refreshing: "更新中…",
8842
+ uptime: "uptime",
8843
+ loading: "読み込み中…",
8844
+ methods: "Methods",
8845
+ calls: "Calls",
8846
+ streams: "Streams",
8847
+ errors: "Errors",
8848
+ errorRate: "Error rate",
8849
+ invalidRequests: "Invalid requests",
8850
+ nonStreamTitle: "Non-stream methods",
8851
+ streamTitle: "Stream methods",
8852
+ noData: "データがありません。",
8853
+ noStreams: "ストリーミング呼び出しはありません。",
8854
+ countHint: "({count})",
8855
+ colMethod: "method",
8856
+ colCalls: "calls",
8857
+ colRate: "rate/s",
8858
+ colErrors: "errors",
8859
+ colErrPct: "err%",
8860
+ colP50: "p50",
8861
+ colP95: "p95",
8862
+ colP99: "p99",
8863
+ colAvg: "avg",
8864
+ colOpened: "opened",
8865
+ colCompleted: "completed",
8866
+ colFailed: "failed",
8867
+ colAborted: "aborted",
8868
+ colCompletePct: "complete%",
8869
+ colAbortPct: "abort%",
8870
+ colAvgChunks: "avg chunks",
8871
+ colTtfbP50: "ttfb p50",
8872
+ colTtfbP95: "ttfb p95",
8873
+ colDurationAvg: "duration avg"
8874
+ },
8875
+ footer: {
8876
+ version: "バージョン",
8877
+ npm: "npm パッケージ",
8878
+ github: "GitHub リポジトリ"
8879
+ },
8880
+ alerts: {
8881
+ emptyMethods: "メソッド一覧が空です。先にメソッド一覧を更新してください。",
8882
+ docsFailed: "ドキュメント生成に失敗しました:{error}"
8883
+ }
8259
8884
  }
8885
+ };
8886
+ //#endregion
8887
+ //#region webui/src/i18n/detect.ts
8888
+ /** 把 BCP-47 语言标签映射到我们支持的 4 种 locale。 */
8889
+ function normalizeLocale(tag) {
8890
+ if (!tag) return void 0;
8891
+ const t = tag.toLowerCase();
8892
+ if (t === "ja" || t.startsWith("ja-")) return "ja";
8893
+ if (t === "zh" || t.startsWith("zh-")) {
8894
+ if (t.includes("hant") || t.startsWith("zh-tw") || t.startsWith("zh-hk") || t.startsWith("zh-mo")) return "zh-TW";
8895
+ return "zh-CN";
8896
+ }
8897
+ if (t === "en" || t.startsWith("en-")) return "en";
8898
+ }
8899
+ /** 读取浏览器语言偏好,取第一个能映射的;否则回退 en。 */
8900
+ function detectSystemLocale() {
8901
+ const nav = typeof navigator !== "undefined" ? navigator : void 0;
8902
+ const candidates = [];
8903
+ if (nav?.languages && nav.languages.length > 0) candidates.push(...nav.languages);
8904
+ if (nav?.language) candidates.push(nav.language);
8905
+ for (const c of candidates) {
8906
+ const loc = normalizeLocale(c);
8907
+ if (loc) return loc;
8908
+ }
8909
+ return "en";
8910
+ }
8911
+ //#endregion
8912
+ //#region webui/src/i18n/I18nProvider.tsx
8913
+ var STORAGE_KEY = "jsonrpc2.locale";
8914
+ var I18nContext = (0, import_react.createContext)(null);
8915
+ var VALID_MODES = [
8916
+ "system",
8917
+ "en",
8918
+ "zh-CN",
8919
+ "zh-TW",
8920
+ "ja"
8260
8921
  ];
8922
+ function readStoredMode() {
8923
+ try {
8924
+ const v = localStorage.getItem(STORAGE_KEY);
8925
+ if (v && VALID_MODES.includes(v)) return v;
8926
+ } catch {}
8927
+ return "system";
8928
+ }
8929
+ function getByPath(obj, path) {
8930
+ const parts = path.split(".");
8931
+ let cur = obj;
8932
+ for (const p of parts) {
8933
+ if (cur == null || typeof cur !== "object") return void 0;
8934
+ cur = cur[p];
8935
+ }
8936
+ return cur;
8937
+ }
8938
+ function interpolate(text, vars) {
8939
+ if (!vars) return text;
8940
+ return text.replace(/\{(\w+)\}/g, (_, name) => {
8941
+ const v = vars[name];
8942
+ return v === void 0 ? `{${name}}` : String(v);
8943
+ });
8944
+ }
8945
+ function I18nProvider({ children }) {
8946
+ const [mode, setModeState] = (0, import_react.useState)(readStoredMode);
8947
+ const [systemLocale, setSystemLocale] = (0, import_react.useState)(detectSystemLocale);
8948
+ (0, import_react.useEffect)(() => {
8949
+ const onChange = () => setSystemLocale(detectSystemLocale());
8950
+ window.addEventListener("languagechange", onChange);
8951
+ return () => window.removeEventListener("languagechange", onChange);
8952
+ }, []);
8953
+ const locale = (0, import_react.useMemo)(() => {
8954
+ if (mode === "system") return systemLocale;
8955
+ return normalizeLocale(mode) ?? systemLocale;
8956
+ }, [mode, systemLocale]);
8957
+ (0, import_react.useEffect)(() => {
8958
+ document.documentElement.setAttribute("lang", locale);
8959
+ try {
8960
+ localStorage.setItem(STORAGE_KEY, mode);
8961
+ } catch {}
8962
+ }, [locale, mode]);
8963
+ const messages = MESSAGES[locale];
8964
+ const t = (0, import_react.useCallback)((key, vars) => {
8965
+ const raw = getByPath(messages, key);
8966
+ if (typeof raw === "string") return interpolate(raw, vars);
8967
+ const fallback = getByPath(MESSAGES.en, key);
8968
+ if (typeof fallback === "string") return interpolate(fallback, vars);
8969
+ return key;
8970
+ }, [messages]);
8971
+ const setMode = (0, import_react.useCallback)((m) => setModeState(m), []);
8972
+ const value = (0, import_react.useMemo)(() => ({
8973
+ mode,
8974
+ locale,
8975
+ messages,
8976
+ setMode,
8977
+ t
8978
+ }), [
8979
+ mode,
8980
+ locale,
8981
+ messages,
8982
+ setMode,
8983
+ t
8984
+ ]);
8985
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(I18nContext.Provider, {
8986
+ value,
8987
+ children
8988
+ });
8989
+ }
8990
+ function useI18n() {
8991
+ const ctx = (0, import_react.useContext)(I18nContext);
8992
+ if (!ctx) throw new Error("useI18n must be used within <I18nProvider>");
8993
+ return ctx;
8994
+ }
8995
+ /** 便捷 hook:只取 t */
8996
+ function useT() {
8997
+ return useI18n().t;
8998
+ }
8999
+ //#endregion
9000
+ //#region webui/src/components/Toolbar.tsx
8261
9001
  function Toolbar({ transport, onTransportChange, onRefresh, loading, error, onGenerateDocs, generatingDocs }) {
8262
9002
  const { mode, setMode } = useTheme();
9003
+ const { t, mode: localeMode, setMode: setLocaleMode } = useI18n();
9004
+ const themeOptions = [
9005
+ {
9006
+ value: "system",
9007
+ label: t("toolbar.themeSystem")
9008
+ },
9009
+ {
9010
+ value: "light",
9011
+ label: t("toolbar.themeLight")
9012
+ },
9013
+ {
9014
+ value: "dark",
9015
+ label: t("toolbar.themeDark")
9016
+ }
9017
+ ];
9018
+ const localeOptions = [
9019
+ {
9020
+ value: "system",
9021
+ label: t("toolbar.languageSystem")
9022
+ },
9023
+ {
9024
+ value: "en",
9025
+ label: t("toolbar.languageEn")
9026
+ },
9027
+ {
9028
+ value: "zh-CN",
9029
+ label: t("toolbar.languageZhCN")
9030
+ },
9031
+ {
9032
+ value: "zh-TW",
9033
+ label: t("toolbar.languageZhTW")
9034
+ },
9035
+ {
9036
+ value: "ja",
9037
+ label: t("toolbar.languageJa")
9038
+ }
9039
+ ];
8263
9040
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
8264
9041
  className: "toolbar",
8265
9042
  children: [
8266
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "JSON-RPC 2.0" }),
9043
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: t("toolbar.title") }),
8267
9044
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
8268
9045
  className: "segmented",
8269
9046
  role: "group",
8270
- "aria-label": "传输方式",
9047
+ "aria-label": t("toolbar.transport"),
8271
9048
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
8272
9049
  type: "button",
8273
9050
  className: transport === "http" ? "active" : "",
8274
9051
  onClick: () => onTransportChange("http"),
8275
- children: "HTTP"
9052
+ children: t("toolbar.http")
8276
9053
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
8277
9054
  type: "button",
8278
9055
  className: transport === "ws" ? "active" : "",
8279
9056
  onClick: () => onTransportChange("ws"),
8280
- children: "WebSocket"
9057
+ children: t("toolbar.ws")
8281
9058
  })]
8282
9059
  }),
8283
9060
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
8284
9061
  onClick: onRefresh,
8285
9062
  disabled: loading,
8286
- children: loading ? "刷新中…" : "刷新方法列表"
9063
+ children: loading ? t("toolbar.refreshMethodsLoading") : t("toolbar.refreshMethods")
8287
9064
  }),
8288
9065
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
8289
9066
  onClick: onGenerateDocs,
8290
- disabled: generatingDocs || loading || loading,
8291
- title: "拉取所有方法的帮助与签名,生成 Markdown 文档并下载",
8292
- children: generatingDocs ? "生成中…" : "生成文档 ⬇"
9067
+ disabled: generatingDocs || loading,
9068
+ title: t("toolbar.generateDocsTitle"),
9069
+ children: generatingDocs ? t("toolbar.generateDocsLoading") : t("toolbar.generateDocs")
8293
9070
  }),
8294
9071
  error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
8295
9072
  className: "error",
@@ -8297,14 +9074,29 @@ function Toolbar({ transport, onTransportChange, onRefresh, loading, error, onGe
8297
9074
  }),
8298
9075
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
8299
9076
  className: "theme-select",
8300
- title: "主题",
9077
+ title: t("toolbar.language"),
8301
9078
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
8302
9079
  className: "theme-select-label",
8303
- children: "主题"
9080
+ children: t("toolbar.language")
9081
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", {
9082
+ value: localeMode,
9083
+ onChange: (e) => setLocaleMode(e.target.value),
9084
+ children: localeOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
9085
+ value: opt.value,
9086
+ children: opt.label
9087
+ }, opt.value))
9088
+ })]
9089
+ }),
9090
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
9091
+ className: "theme-select",
9092
+ title: t("toolbar.theme"),
9093
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
9094
+ className: "theme-select-label",
9095
+ children: t("toolbar.theme")
8304
9096
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", {
8305
9097
  value: mode,
8306
9098
  onChange: (e) => setMode(e.target.value),
8307
- children: THEME_OPTIONS.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
9099
+ children: themeOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
8308
9100
  value: opt.value,
8309
9101
  children: opt.label
8310
9102
  }, opt.value))
@@ -8351,10 +9143,11 @@ function buildTree(methods) {
8351
9143
  return root.children;
8352
9144
  }
8353
9145
  function MethodTree({ methods, selected, onSelect }) {
9146
+ const t = useT();
8354
9147
  const tree = (0, import_react.useMemo)(() => buildTree(methods), [methods]);
8355
9148
  if (methods.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
8356
9149
  className: "empty",
8357
- children: "暂无可用方法。"
9150
+ children: t("sidebar.noMethods")
8358
9151
  });
8359
9152
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
8360
9153
  className: "tree",
@@ -8424,6 +9217,7 @@ function formatConstraints$1(constraints) {
8424
9217
  return entries.map(([k, v]) => `${k}=${typeof v === "string" ? JSON.stringify(v) : JSON.stringify(v)}`).join(", ");
8425
9218
  }
8426
9219
  function SchemaNode({ schema, depth, defaultOpen, label }) {
9220
+ const t = useT();
8427
9221
  const [open, setOpen] = (0, import_react.useState)(defaultOpen);
8428
9222
  const hasChildren = schema.children && Object.keys(schema.children).length > 0;
8429
9223
  const constraintsText = formatConstraints$1(schema.constraints);
@@ -8471,17 +9265,18 @@ function SchemaNode({ schema, depth, defaultOpen, label }) {
8471
9265
  ]
8472
9266
  }), open && hasChildren && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
8473
9267
  className: "schema-children",
8474
- children: renderChildren(schema, depth + 1)
9268
+ children: renderChildren(schema, depth + 1, t)
8475
9269
  })]
8476
9270
  });
8477
9271
  }
8478
- function renderChildren(schema, depth) {
9272
+ function renderChildren(schema, depth, t) {
8479
9273
  const c = schema.children ?? {};
8480
9274
  const out = [];
8481
9275
  if (c.inner && isSchemaDescription(c.inner)) out.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SchemaNode, {
8482
9276
  schema: c.inner,
8483
9277
  depth,
8484
- defaultOpen: true
9278
+ defaultOpen: true,
9279
+ label: t("schema.inner")
8485
9280
  }, "inner"));
8486
9281
  if (Array.isArray(c.items)) c.items.forEach((item, i) => {
8487
9282
  if (isSchemaDescription(item)) out.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SchemaNode, {
@@ -8495,14 +9290,14 @@ function renderChildren(schema, depth) {
8495
9290
  schema: c.items,
8496
9291
  depth,
8497
9292
  defaultOpen: true,
8498
- label: "items"
9293
+ label: t("schema.items")
8499
9294
  }, "items"));
8500
9295
  if (Array.isArray(c.options)) c.options.forEach((opt, i) => {
8501
9296
  if (isSchemaDescription(opt)) out.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SchemaNode, {
8502
9297
  schema: opt,
8503
9298
  depth,
8504
9299
  defaultOpen: false,
8505
- label: `option[${i}]`
9300
+ label: t("schema.optionIndex", { index: i })
8506
9301
  }, `opt-${i}`));
8507
9302
  });
8508
9303
  if (c.properties && typeof c.properties === "object" && !Array.isArray(c.properties)) {
@@ -8517,7 +9312,7 @@ function renderChildren(schema, depth) {
8517
9312
  schema: c.values,
8518
9313
  depth,
8519
9314
  defaultOpen: true,
8520
- label: "values"
9315
+ label: t("schema.values")
8521
9316
  }, "values"));
8522
9317
  return out;
8523
9318
  }
@@ -8534,11 +9329,12 @@ function SchemaView({ schema, defaultOpen = true, depth = 0 }) {
8534
9329
  //#endregion
8535
9330
  //#region webui/src/components/MethodDetail.tsx
8536
9331
  function MethodDetail({ method, detail }) {
9332
+ const t = useT();
8537
9333
  if (!method) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", {
8538
9334
  className: "detail",
8539
9335
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
8540
9336
  className: "hint",
8541
- children: "← 从左侧选择一个方法查看帮助与签名。"
9337
+ children: t("method.selectHint")
8542
9338
  })
8543
9339
  });
8544
9340
  const [returnSchema] = detail.signature;
@@ -8548,23 +9344,23 @@ function MethodDetail({ method, detail }) {
8548
9344
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: method }),
8549
9345
  detail.loading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
8550
9346
  className: "hint",
8551
- children: "加载中…"
9347
+ children: t("method.loading")
8552
9348
  }),
8553
- detail.error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
9349
+ detail.error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
8554
9350
  className: "error",
8555
- children: ["加载失败:", detail.error]
9351
+ children: t("method.loadFailed", { error: detail.error })
8556
9352
  }),
8557
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "Help" }),
9353
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: t("method.help") }),
8558
9354
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
8559
9355
  className: "help",
8560
- children: detail.help || "(无帮助信息)"
9356
+ children: detail.help || t("method.noHelp")
8561
9357
  }),
8562
9358
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { children: [
8563
- "返回类型",
9359
+ t("method.returnType"),
8564
9360
  " ",
8565
9361
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
8566
9362
  className: "hint",
8567
- children: "(服务端不会校验执行结果是否符合此类型,实际返回值可能会有所不同)"
9363
+ children: t("method.returnTypeHint")
8568
9364
  })
8569
9365
  ] }),
8570
9366
  returnSchema ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SchemaView, {
@@ -8573,7 +9369,7 @@ function MethodDetail({ method, detail }) {
8573
9369
  defaultOpen: true
8574
9370
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
8575
9371
  className: "hint",
8576
- children: "(暂无)"
9372
+ children: t("method.none")
8577
9373
  })
8578
9374
  ]
8579
9375
  });
@@ -8647,9 +9443,10 @@ function formatConstraints(c) {
8647
9443
  return Object.entries(c).filter(([, v]) => v !== void 0).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
8648
9444
  }
8649
9445
  function SchemaForm({ params, values, onChange, disabled }) {
9446
+ const t = useT();
8650
9447
  if (params.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
8651
9448
  className: "hint",
8652
- children: "此方法无需参数。"
9449
+ children: t("test.noParams")
8653
9450
  });
8654
9451
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
8655
9452
  className: "schema-form",
@@ -8657,13 +9454,14 @@ function SchemaForm({ params, values, onChange, disabled }) {
8657
9454
  schema,
8658
9455
  value: values[i],
8659
9456
  onChange: (v) => onChange(i, v),
8660
- label: `参数 [${i}]`,
9457
+ label: t("schema.paramIndex", { index: i }),
8661
9458
  depth: 0,
8662
9459
  disabled
8663
9460
  }, i))
8664
9461
  });
8665
9462
  }
8666
9463
  function SchemaField({ schema, value, onChange, label, depth = 0, defaultOpen = true, disabled }) {
9464
+ const t = useT();
8667
9465
  const [open, setOpen] = (0, import_react.useState)(defaultOpen);
8668
9466
  const kind = schema.kind;
8669
9467
  if (schema.nullable) {
@@ -8685,7 +9483,7 @@ function SchemaField({ schema, value, onChange, label, depth = 0, defaultOpen =
8685
9483
  }),
8686
9484
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
8687
9485
  className: "sf-nullable-badge",
8688
- children: "nullable"
9486
+ children: t("schema.nullable")
8689
9487
  }),
8690
9488
  schema.description && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
8691
9489
  className: "sf-desc",
@@ -8696,7 +9494,7 @@ function SchemaField({ schema, value, onChange, label, depth = 0, defaultOpen =
8696
9494
  className: "tiny",
8697
9495
  disabled,
8698
9496
  onClick: () => onChange(null),
8699
- children: "置 null"
9497
+ children: t("schema.setNull")
8700
9498
  }),
8701
9499
  isNull && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
8702
9500
  type: "button",
@@ -8706,7 +9504,7 @@ function SchemaField({ schema, value, onChange, label, depth = 0, defaultOpen =
8706
9504
  ...schema,
8707
9505
  nullable: false
8708
9506
  })),
8709
- children: "填值"
9507
+ children: t("schema.fillValue")
8710
9508
  })
8711
9509
  ]
8712
9510
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -8843,6 +9641,7 @@ function isScalarKind(kind) {
8843
9641
  return kind === "string" || kind === "number" || kind === "boolean" || kind === "enum" || kind === "literal" || kind === "null";
8844
9642
  }
8845
9643
  function ScalarInput({ schema, value, onChange, disabled }) {
9644
+ const t = useT();
8846
9645
  switch (schema.kind) {
8847
9646
  case "boolean": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
8848
9647
  className: "sf-scalar",
@@ -8880,7 +9679,7 @@ function ScalarInput({ schema, value, onChange, disabled }) {
8880
9679
  children: JSON.stringify(schema.constraints?.value ?? null)
8881
9680
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
8882
9681
  className: "hint",
8883
- children: "(固定值)"
9682
+ children: t("schema.fixedValue")
8884
9683
  })]
8885
9684
  });
8886
9685
  case "null": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -8890,7 +9689,7 @@ function ScalarInput({ schema, value, onChange, disabled }) {
8890
9689
  children: "null"
8891
9690
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
8892
9691
  className: "hint",
8893
- children: "(固定值)"
9692
+ children: t("schema.fixedValue")
8894
9693
  })]
8895
9694
  });
8896
9695
  default: return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
@@ -8903,11 +9702,12 @@ function ScalarInput({ schema, value, onChange, disabled }) {
8903
9702
  }
8904
9703
  }
8905
9704
  function UnionField({ options, value, onChange, depth, disabled }) {
9705
+ const t = useT();
8906
9706
  const [selected, setSelected] = (0, import_react.useState)(0);
8907
9707
  const active = options[selected];
8908
9708
  if (!active) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
8909
9709
  className: "hint",
8910
- children: "(union 无分支)"
9710
+ children: t("schema.unionNoBranch")
8911
9711
  });
8912
9712
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
8913
9713
  style: { paddingLeft: depth * 14 },
@@ -8926,9 +9726,8 @@ function UnionField({ options, value, onChange, depth, disabled }) {
8926
9726
  children: options.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", {
8927
9727
  value: i,
8928
9728
  children: [
8929
- "option[",
8930
- i,
8931
- "] — ",
9729
+ t("schema.optionIndex", { index: i }),
9730
+ " — ",
8932
9731
  opt.kind
8933
9732
  ]
8934
9733
  }, i))
@@ -8944,12 +9743,13 @@ function UnionField({ options, value, onChange, depth, disabled }) {
8944
9743
  });
8945
9744
  }
8946
9745
  function ContainerChildren({ schema, value, onChange, depth, disabled }) {
9746
+ const t = useT();
8947
9747
  const c = schema.children ?? {};
8948
9748
  if (schema.kind === "array") {
8949
9749
  const itemSchema = isSchema$1(c.items) ? c.items : void 0;
8950
9750
  if (!itemSchema) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
8951
9751
  className: "hint",
8952
- children: "(数组无 item schema)"
9752
+ children: t("schema.arrayNoItem")
8953
9753
  });
8954
9754
  const arr = Array.isArray(value) ? value : [];
8955
9755
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [arr.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -8971,14 +9771,14 @@ function ContainerChildren({ schema, value, onChange, depth, disabled }) {
8971
9771
  className: "tiny",
8972
9772
  disabled,
8973
9773
  onClick: () => onChange(arr.filter((_, j) => j !== i)),
8974
- children: "删除"
9774
+ children: t("common.delete")
8975
9775
  })]
8976
9776
  }, i)), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
8977
9777
  type: "button",
8978
9778
  className: "tiny",
8979
9779
  disabled,
8980
9780
  onClick: () => onChange([...arr, sampleFromSchema(itemSchema)]),
8981
- children: "+ 添加项"
9781
+ children: t("schema.addItem")
8982
9782
  })] });
8983
9783
  }
8984
9784
  if (schema.kind === "tuple") {
@@ -9024,14 +9824,14 @@ function ContainerChildren({ schema, value, onChange, depth, disabled }) {
9024
9824
  paddingLeft: depth * 14,
9025
9825
  margin: "4px 0"
9026
9826
  },
9027
- children: "(非 strict 模式,允许额外字段)"
9827
+ children: t("schema.strictHint")
9028
9828
  })] });
9029
9829
  }
9030
9830
  if (schema.kind === "record") {
9031
9831
  const valueSchema = isSchema$1(c.values) ? c.values : void 0;
9032
9832
  if (!valueSchema) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
9033
9833
  className: "hint",
9034
- children: "(record 无 value schema)"
9834
+ children: t("schema.recordNoValue")
9035
9835
  });
9036
9836
  const obj = value && typeof value === "object" && !Array.isArray(value) ? value : {};
9037
9837
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [Object.entries(obj).map(([key, v]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -9056,7 +9856,7 @@ function ContainerChildren({ schema, value, onChange, depth, disabled }) {
9056
9856
  delete next[key];
9057
9857
  onChange(next);
9058
9858
  },
9059
- children: "删除"
9859
+ children: t("common.delete")
9060
9860
  })]
9061
9861
  }, key)), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AddRecordKey, {
9062
9862
  onAdd: (key, v) => onChange({
@@ -9070,12 +9870,13 @@ function ContainerChildren({ schema, value, onChange, depth, disabled }) {
9070
9870
  return null;
9071
9871
  }
9072
9872
  function AddRecordKey({ onAdd, sample, disabled }) {
9873
+ const t = useT();
9073
9874
  const [key, setKey] = (0, import_react.useState)("");
9074
9875
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
9075
9876
  className: "sf-record-add",
9076
9877
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
9077
9878
  className: "sf-input",
9078
- placeholder: "新键名",
9879
+ placeholder: t("schema.recordAddPlaceholder"),
9079
9880
  value: key,
9080
9881
  onChange: (e) => setKey(e.target.value),
9081
9882
  disabled
@@ -9087,11 +9888,12 @@ function AddRecordKey({ onAdd, sample, disabled }) {
9087
9888
  onAdd(key, sample());
9088
9889
  setKey("");
9089
9890
  },
9090
- children: "+ 添加键"
9891
+ children: t("schema.addRecordKey")
9091
9892
  })]
9092
9893
  });
9093
9894
  }
9094
9895
  function UnknownLeaf({ schema, value, onChange, label, depth, disabled }) {
9896
+ const t = useT();
9095
9897
  const [text, setText] = (0, import_react.useState)(() => JSON.stringify(value ?? null, null, 2));
9096
9898
  const [error, setError] = (0, import_react.useState)(null);
9097
9899
  const commit = (next) => {
@@ -9133,9 +9935,9 @@ function UnknownLeaf({ schema, value, onChange, label, depth, disabled }) {
9133
9935
  value: text,
9134
9936
  disabled,
9135
9937
  onChange: (e) => commit(e.target.value)
9136
- }), error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
9938
+ }), error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
9137
9939
  className: "error small",
9138
- children: ["JSON 错误:", error]
9940
+ children: t("test.jsonError", { error })
9139
9941
  })]
9140
9942
  })]
9141
9943
  });
@@ -9149,19 +9951,27 @@ function Constraints({ schema }) {
9149
9951
  }
9150
9952
  //#endregion
9151
9953
  //#region webui/src/components/TestPanel.tsx
9152
- var COPY_OPTIONS = [{
9153
- value: "curl",
9154
- label: "复制 curl"
9155
- }, {
9156
- value: "fetch",
9157
- label: "复制 fetch"
9158
- }];
9159
9954
  /** 格式化耗时:<1s 用 ms,否则用 s */
9160
9955
  function formatElapsed(ms) {
9161
9956
  if (ms < 1e3) return `${ms.toFixed(0)} ms`;
9162
9957
  return `${(ms / 1e3).toFixed(2)} s`;
9163
9958
  }
9164
9959
  function TestPanel({ method, detail, rpc, transport, endpoint }) {
9960
+ const t = useT();
9961
+ const COPY_OPTIONS = [
9962
+ {
9963
+ value: "curl",
9964
+ label: t("test.copyCurl")
9965
+ },
9966
+ {
9967
+ value: "curl-cmd",
9968
+ label: t("test.copyCurlCmd")
9969
+ },
9970
+ {
9971
+ value: "fetch",
9972
+ label: t("test.copyFetch")
9973
+ }
9974
+ ];
9165
9975
  const paramSchemas = (0, import_react.useMemo)(() => detail.signature.slice(1), [detail.signature]);
9166
9976
  const isStreamReturn = detail.signature[0]?.kind === "stream";
9167
9977
  const [values, setValues] = (0, import_react.useState)([]);
@@ -9219,8 +10029,11 @@ function TestPanel({ method, detail, rpc, transport, endpoint }) {
9219
10029
  setOutput(lines.join("\n"));
9220
10030
  }
9221
10031
  const totalAt = performance.now() - startedAt;
9222
- if (idx === 0) lines.push(`(流已结束,无数据,总耗时 ${formatElapsed(totalAt)})`);
9223
- else lines.push(`(流已结束,共 ${idx} 帧,总耗时 ${formatElapsed(totalAt)})`);
10032
+ if (idx === 0) lines.push(t("test.streamEndedEmpty", { duration: formatElapsed(totalAt) }));
10033
+ else lines.push(t("test.streamEnded", {
10034
+ count: idx,
10035
+ duration: formatElapsed(totalAt)
10036
+ }));
9224
10037
  setOutput(lines.join("\n"));
9225
10038
  setElapsed({
9226
10039
  total: totalAt,
@@ -9252,7 +10065,7 @@ function TestPanel({ method, detail, rpc, transport, endpoint }) {
9252
10065
  id: 1
9253
10066
  };
9254
10067
  const url = absoluteEndpoint(endpoint);
9255
- code = kind === "curl" ? buildCurl(url, body) : buildFetch(url, body);
10068
+ code = kind === "curl" ? buildCurl(url, body) : kind === "curl-cmd" ? buildCurlCmd(url, body) : buildFetch(url, body);
9256
10069
  } catch (e) {
9257
10070
  setCopyError(e instanceof Error ? e.message : String(e));
9258
10071
  return;
@@ -9262,24 +10075,20 @@ function TestPanel({ method, detail, rpc, transport, endpoint }) {
9262
10075
  setCopied(true);
9263
10076
  setTimeout(() => setCopied(false), 2e3);
9264
10077
  } catch (e) {
9265
- setCopyError(`复制失败:${e instanceof Error ? e.message : String(e)}`);
10078
+ setCopyError(t("common.copyFailed", { error: e instanceof Error ? e.message : String(e) }));
9266
10079
  }
9267
10080
  };
9268
10081
  const canCopy = transport === "http";
9269
- const currentLabel = COPY_OPTIONS.find((o) => o.value === copyKind)?.label ?? "复制";
10082
+ const currentLabel = COPY_OPTIONS.find((o) => o.value === copyKind)?.label ?? t("common.copy");
9270
10083
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
9271
10084
  className: "test",
9272
10085
  children: [
9273
10086
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { children: [
9274
- "参数",
10087
+ t("test.params"),
9275
10088
  " ",
9276
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
10089
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
9277
10090
  className: "hint",
9278
- children: [
9279
- "(",
9280
- paramSchemas.length,
9281
- " 个,按签名自动生成)"
9282
- ]
10091
+ children: t("test.paramsHint", { count: paramSchemas.length })
9283
10092
  })
9284
10093
  ] }),
9285
10094
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SchemaForm, {
@@ -9294,7 +10103,7 @@ function TestPanel({ method, detail, rpc, transport, endpoint }) {
9294
10103
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
9295
10104
  onClick: execute,
9296
10105
  disabled: !method || running,
9297
- children: running ? isStreamReturn ? "接收中…" : "执行中…" : isStreamReturn ? "执行并接收流 ▶" : "执行 ▶"
10106
+ children: running ? isStreamReturn ? t("test.receiving") : t("test.executing") : isStreamReturn ? t("test.executeStream") : t("test.execute")
9298
10107
  }),
9299
10108
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
9300
10109
  className: "split-button",
@@ -9305,8 +10114,8 @@ function TestPanel({ method, detail, rpc, transport, endpoint }) {
9305
10114
  className: "split-button-main",
9306
10115
  onClick: () => void copyAs(copyKind),
9307
10116
  disabled: !method || !canCopy,
9308
- title: canCopy ? "复制当前调用的等价代码" : "仅 HTTP 传输可生成代码",
9309
- children: copied ? "已复制 ✓" : currentLabel
10117
+ title: canCopy ? t("test.copyTitle") : t("test.copyDisabledTitle"),
10118
+ children: copied ? t("common.copied") : currentLabel
9310
10119
  }),
9311
10120
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
9312
10121
  type: "button",
@@ -9315,7 +10124,7 @@ function TestPanel({ method, detail, rpc, transport, endpoint }) {
9315
10124
  disabled: !method || !canCopy,
9316
10125
  "aria-haspopup": "menu",
9317
10126
  "aria-expanded": menuOpen,
9318
- title: "选择复制格式",
10127
+ title: t("test.chooseFormat"),
9319
10128
  children: "▾"
9320
10129
  }),
9321
10130
  menuOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
@@ -9343,7 +10152,7 @@ function TestPanel({ method, detail, rpc, transport, endpoint }) {
9343
10152
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
9344
10153
  onClick: () => setValues(paramSchemas.map(sampleFromSchema)),
9345
10154
  disabled: !method,
9346
- children: "重置为示例"
10155
+ children: t("test.resetToSample")
9347
10156
  }),
9348
10157
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
9349
10158
  onClick: () => {
@@ -9351,15 +10160,15 @@ function TestPanel({ method, detail, rpc, transport, endpoint }) {
9351
10160
  setElapsed(null);
9352
10161
  },
9353
10162
  disabled: !output && !elapsed,
9354
- children: "清空输出"
10163
+ children: t("test.clearOutput")
9355
10164
  })
9356
10165
  ]
9357
10166
  }),
9358
10167
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { children: [
9359
- "结果",
10168
+ t("test.result"),
9360
10169
  isStreamReturn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
9361
10170
  className: "hint",
9362
- children: "(流式)"
10171
+ children: t("test.resultStream")
9363
10172
  }),
9364
10173
  elapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
9365
10174
  className: "elapsed-badge",
@@ -9371,7 +10180,9 @@ function TestPanel({ method, detail, rpc, transport, endpoint }) {
9371
10180
  className: "hint",
9372
10181
  children: [
9373
10182
  " ",
9374
- "· 首帧 ",
10183
+ "· ",
10184
+ t("test.firstFrame"),
10185
+ " ",
9375
10186
  formatElapsed(elapsed.firstFrame)
9376
10187
  ]
9377
10188
  })
@@ -9381,11 +10192,12 @@ function TestPanel({ method, detail, rpc, transport, endpoint }) {
9381
10192
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
9382
10193
  className: "output",
9383
10194
  ref: outRef,
9384
- children: output || "(尚未执行)"
10195
+ children: output || t("test.notExecuted")
9385
10196
  })
9386
10197
  ]
9387
10198
  });
9388
10199
  }
10200
+ /** bash / POSIX sh:单引号包裹,内部单引号用 '\'' 转义;反斜杠续行。 */
9389
10201
  function buildCurl(url, body) {
9390
10202
  const quotedBody = `'${JSON.stringify(body, null, 2).replace(/'/g, `'\\''`)}'`;
9391
10203
  return [
@@ -9394,6 +10206,23 @@ function buildCurl(url, body) {
9394
10206
  ` -d ${quotedBody}`
9395
10207
  ].join(" \\\n");
9396
10208
  }
10209
+ /**
10210
+ * Windows CMD:双引号包裹。
10211
+ * - 双引号 " 用 \" 转义(curl.exe 在 cmd 下按 C 运行时规则解析)
10212
+ * - % 用 %% 转义(避免被 cmd 当作变量展开)
10213
+ * - 换行用 ^ 续行
10214
+ * - 其余字符原样保留
10215
+ */
10216
+ function buildCurlCmd(url, body) {
10217
+ const bodyText = JSON.stringify(body);
10218
+ const escapeCmdDoubleQuoted = (s) => s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/%/g, "%%");
10219
+ const quotedBody = `"${escapeCmdDoubleQuoted(bodyText)}"`;
10220
+ return [
10221
+ `curl -X POST ${`"${escapeCmdDoubleQuoted(url)}"`} ^`,
10222
+ ` -H "Content-Type: application/json" ^`,
10223
+ ` -d ${quotedBody}`
10224
+ ].join("\n");
10225
+ }
9397
10226
  function buildFetch(url, body) {
9398
10227
  const indented = JSON.stringify(body, null, 2).split("\n").map((line, i) => i === 0 ? line : " " + line).join("\n");
9399
10228
  return [
@@ -9445,6 +10274,7 @@ function rateClass(v) {
9445
10274
  return "rate-cold";
9446
10275
  }
9447
10276
  function MetricsPanel({ snapshot, previous, fetchedAt, loading, error, available, onRefresh, autoRefresh, onAutoRefreshChange, intervalMs, onIntervalChange }) {
10277
+ const t = useT();
9448
10278
  const derived = (0, import_react.useMemo)(() => {
9449
10279
  if (!snapshot) return [];
9450
10280
  const dtSeconds = previous && fetchedAt ? (fetchedAt - previous.fetchedAt) / 1e3 : void 0;
@@ -9486,15 +10316,9 @@ function MetricsPanel({ snapshot, previous, fetchedAt, loading, error, available
9486
10316
  }, [snapshot]);
9487
10317
  if (!available) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
9488
10318
  className: "metrics",
9489
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: "Metrics" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
10319
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: t("metrics.title") }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
9490
10320
  className: "hint",
9491
- children: [
9492
- "服务端未启用 ",
9493
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "system.metrics" }),
9494
- "(",
9495
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "withSystemMethods: false" }),
9496
- " 或未注册)。"
9497
- ]
10321
+ children: t("metrics.unavailable")
9498
10322
  })]
9499
10323
  });
9500
10324
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
@@ -9502,7 +10326,7 @@ function MetricsPanel({ snapshot, previous, fetchedAt, loading, error, available
9502
10326
  children: [
9503
10327
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
9504
10328
  className: "metrics-header",
9505
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: "Metrics" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
10329
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: t("metrics.title") }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
9506
10330
  className: "metrics-controls",
9507
10331
  children: [
9508
10332
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
@@ -9511,14 +10335,14 @@ function MetricsPanel({ snapshot, previous, fetchedAt, loading, error, available
9511
10335
  type: "checkbox",
9512
10336
  checked: autoRefresh,
9513
10337
  onChange: (e) => onAutoRefreshChange(e.target.checked)
9514
- }), "自动刷新"]
10338
+ }), t("metrics.autoRefresh")]
9515
10339
  }),
9516
10340
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
9517
10341
  className: "sf-input",
9518
10342
  value: intervalMs,
9519
10343
  onChange: (e) => onIntervalChange(Number(e.target.value)),
9520
10344
  disabled: !autoRefresh,
9521
- title: "刷新间隔",
10345
+ title: t("metrics.interval"),
9522
10346
  children: [
9523
10347
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
9524
10348
  value: 1e3,
@@ -9541,11 +10365,15 @@ function MetricsPanel({ snapshot, previous, fetchedAt, loading, error, available
9541
10365
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
9542
10366
  onClick: onRefresh,
9543
10367
  disabled: loading,
9544
- children: loading ? "刷新中…" : "立即刷新"
10368
+ children: loading ? t("metrics.refreshing") : t("metrics.refreshNow")
9545
10369
  }),
9546
10370
  snapshot && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
9547
10371
  className: "hint",
9548
- children: ["uptime ", formatDuration(snapshot.uptimeMs / 1e3)]
10372
+ children: [
10373
+ t("metrics.uptime"),
10374
+ " ",
10375
+ formatDuration(snapshot.uptimeMs / 1e3)
10376
+ ]
9549
10377
  })
9550
10378
  ]
9551
10379
  })]
@@ -9556,64 +10384,56 @@ function MetricsPanel({ snapshot, previous, fetchedAt, loading, error, available
9556
10384
  }),
9557
10385
  !snapshot && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
9558
10386
  className: "hint",
9559
- children: "加载中…"
10387
+ children: t("metrics.loading")
9560
10388
  }),
9561
10389
  snapshot && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
9562
10390
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
9563
10391
  className: "metrics-cards",
9564
10392
  children: [
9565
10393
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetricCard, {
9566
- label: "Methods",
10394
+ label: t("metrics.methods"),
9567
10395
  value: String(totals.methods)
9568
10396
  }),
9569
10397
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetricCard, {
9570
- label: "Calls",
10398
+ label: t("metrics.calls"),
9571
10399
  value: String(totals.calls)
9572
10400
  }),
9573
10401
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetricCard, {
9574
- label: "Streams",
10402
+ label: t("metrics.streams"),
9575
10403
  value: String(totals.streams)
9576
10404
  }),
9577
10405
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetricCard, {
9578
- label: "Errors",
10406
+ label: t("metrics.errors"),
9579
10407
  value: String(totals.errors),
9580
10408
  tone: totals.errors > 0 ? "danger" : void 0
9581
10409
  }),
9582
10410
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetricCard, {
9583
- label: "Error rate",
10411
+ label: t("metrics.errorRate"),
9584
10412
  value: totals.calls > 0 ? formatPercent(totals.errors / totals.calls) : "—",
9585
10413
  tone: totals.calls > 0 && totals.errors / totals.calls > .05 ? "danger" : void 0
9586
10414
  }),
9587
10415
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetricCard, {
9588
- label: "Invalid requests",
10416
+ label: t("metrics.invalidRequests"),
9589
10417
  value: String(snapshot.invalidRequests),
9590
10418
  tone: snapshot.invalidRequests > 0 ? "warn" : void 0
9591
10419
  })
9592
10420
  ]
9593
10421
  }),
9594
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { children: ["Non-stream methods", /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
10422
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { children: [t("metrics.nonStreamTitle"), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
9595
10423
  className: "hint",
9596
- children: [
9597
- " (",
9598
- nonStreamMethods.length,
9599
- ")"
9600
- ]
10424
+ children: [" ", t("metrics.countHint", { count: nonStreamMethods.length })]
9601
10425
  })] }),
9602
10426
  nonStreamMethods.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
9603
10427
  className: "hint",
9604
- children: "暂无数据。"
10428
+ children: t("metrics.noData")
9605
10429
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NonStreamTable, { rows: nonStreamMethods }),
9606
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { children: ["Stream methods", /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
10430
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { children: [t("metrics.streamTitle"), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
9607
10431
  className: "hint",
9608
- children: [
9609
- " (",
9610
- streamMethods.length,
9611
- ")"
9612
- ]
10432
+ children: [" ", t("metrics.countHint", { count: streamMethods.length })]
9613
10433
  })] }),
9614
10434
  streamMethods.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
9615
10435
  className: "hint",
9616
- children: "暂无流式调用。"
10436
+ children: t("metrics.noStreams")
9617
10437
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StreamTable, { rows: streamMethods })
9618
10438
  ] })
9619
10439
  ]
@@ -9632,41 +10452,42 @@ function MetricCard({ label, value, tone }) {
9632
10452
  });
9633
10453
  }
9634
10454
  function NonStreamTable({ rows }) {
10455
+ const t = useT();
9635
10456
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", {
9636
10457
  className: "metrics-table",
9637
10458
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
9638
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "method" }),
10459
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: t("metrics.colMethod") }),
9639
10460
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9640
10461
  className: "num",
9641
- children: "calls"
10462
+ children: t("metrics.colCalls")
9642
10463
  }),
9643
10464
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9644
10465
  className: "num",
9645
- children: "rate/s"
10466
+ children: t("metrics.colRate")
9646
10467
  }),
9647
10468
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9648
10469
  className: "num",
9649
- children: "errors"
10470
+ children: t("metrics.colErrors")
9650
10471
  }),
9651
10472
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9652
10473
  className: "num",
9653
- children: "err%"
10474
+ children: t("metrics.colErrPct")
9654
10475
  }),
9655
10476
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9656
10477
  className: "num",
9657
- children: "p50"
10478
+ children: t("metrics.colP50")
9658
10479
  }),
9659
10480
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9660
10481
  className: "num",
9661
- children: "p95"
10482
+ children: t("metrics.colP95")
9662
10483
  }),
9663
10484
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9664
10485
  className: "num",
9665
- children: "p99"
10486
+ children: t("metrics.colP99")
9666
10487
  }),
9667
10488
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9668
10489
  className: "num",
9669
- children: "avg"
10490
+ children: t("metrics.colAvg")
9670
10491
  })
9671
10492
  ] }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: rows.map((r) => {
9672
10493
  const errorCount = Object.values(r.stats.errorsByCode).reduce((a, b) => a + b, 0);
@@ -9714,49 +10535,50 @@ function NonStreamTable({ rows }) {
9714
10535
  });
9715
10536
  }
9716
10537
  function StreamTable({ rows }) {
10538
+ const t = useT();
9717
10539
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", {
9718
10540
  className: "metrics-table",
9719
10541
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
9720
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "method" }),
10542
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: t("metrics.colMethod") }),
9721
10543
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9722
10544
  className: "num",
9723
- children: "opened"
10545
+ children: t("metrics.colOpened")
9724
10546
  }),
9725
10547
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9726
10548
  className: "num",
9727
- children: "completed"
10549
+ children: t("metrics.colCompleted")
9728
10550
  }),
9729
10551
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9730
10552
  className: "num",
9731
- children: "failed"
10553
+ children: t("metrics.colFailed")
9732
10554
  }),
9733
10555
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9734
10556
  className: "num",
9735
- children: "aborted"
10557
+ children: t("metrics.colAborted")
9736
10558
  }),
9737
10559
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9738
10560
  className: "num",
9739
- children: "complete%"
10561
+ children: t("metrics.colCompletePct")
9740
10562
  }),
9741
10563
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9742
10564
  className: "num",
9743
- children: "abort%"
10565
+ children: t("metrics.colAbortPct")
9744
10566
  }),
9745
10567
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9746
10568
  className: "num",
9747
- children: "avg chunks"
10569
+ children: t("metrics.colAvgChunks")
9748
10570
  }),
9749
10571
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9750
10572
  className: "num",
9751
- children: "ttfb p50"
10573
+ children: t("metrics.colTtfbP50")
9752
10574
  }),
9753
10575
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9754
10576
  className: "num",
9755
- children: "ttfb p95"
10577
+ children: t("metrics.colTtfbP95")
9756
10578
  }),
9757
10579
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
9758
10580
  className: "num",
9759
- children: "duration avg"
10581
+ children: t("metrics.colDurationAvg")
9760
10582
  })
9761
10583
  ] }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: rows.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
9762
10584
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
@@ -9808,58 +10630,88 @@ function StreamTable({ rows }) {
9808
10630
  });
9809
10631
  }
9810
10632
  //#endregion
9811
- //#region webui/src/api/glob.ts
9812
- function globToRegExp(glob) {
9813
- let re = "^";
9814
- for (let i = 0; i < glob.length; i++) {
9815
- const ch = glob[i];
9816
- if (ch === "*") {
9817
- while (i + 1 < glob.length && glob[i + 1] === "*") i++;
9818
- re += ".*";
9819
- } else if (ch === "?") re += ".";
9820
- else if ("\\^$+{}()|[]".includes(ch)) re += "\\" + ch;
9821
- else re += ch;
9822
- }
9823
- re += "$";
9824
- return new RegExp(re);
10633
+ //#region webui/src/components/Footer.tsx
10634
+ var NPM_URL = "https://www.npmjs.com/package/@yuhere/js-jsonrpc2/";
10635
+ var GITHUB_URL = "https://github.com/yuhere/js-jsonrpc2";
10636
+ function Footer() {
10637
+ const t = useT();
10638
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("footer", {
10639
+ className: "footer",
10640
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
10641
+ className: "footer-version",
10642
+ title: t("footer.version"),
10643
+ children: ["v", "2.1.13"]
10644
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
10645
+ className: "footer-links",
10646
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
10647
+ className: "npm-link",
10648
+ href: NPM_URL,
10649
+ target: "_blank",
10650
+ rel: "noopener noreferrer",
10651
+ title: t("footer.npm"),
10652
+ "aria-label": t("footer.npm"),
10653
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", {
10654
+ viewBox: "0 0 18 7",
10655
+ width: "36",
10656
+ height: "14",
10657
+ role: "img",
10658
+ "aria-hidden": "true",
10659
+ focusable: "false",
10660
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
10661
+ fill: "#CB3837",
10662
+ d: "M0 0h18v7H9v-1.5H7.5V7H0z"
10663
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
10664
+ fill: "#fff",
10665
+ d: "M1.5 1.5h3v4h-1v-3h-1v3h-1zm4 0h3v4h-1v-3h-1v3h-1zm4 0h4v4h-1v-3h-1v3h-1v-3h-1z"
10666
+ })]
10667
+ })
10668
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
10669
+ className: "github-link",
10670
+ href: GITHUB_URL,
10671
+ target: "_blank",
10672
+ rel: "noopener noreferrer",
10673
+ title: t("footer.github"),
10674
+ "aria-label": t("footer.github"),
10675
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
10676
+ viewBox: "0 0 16 16",
10677
+ width: "16",
10678
+ height: "16",
10679
+ role: "img",
10680
+ "aria-hidden": "true",
10681
+ focusable: "false",
10682
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
10683
+ fill: "currentColor",
10684
+ d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"
10685
+ })
10686
+ })
10687
+ })]
10688
+ })]
10689
+ });
9825
10690
  }
9826
- function compileGlobFilter(input) {
10691
+ //#endregion
10692
+ //#region webui/src/utils/fuzzy.ts
10693
+ function compileFuzzyQuery(input) {
9827
10694
  const raw = input ?? "";
9828
- const trimmed = raw.trim();
9829
- if (trimmed.length === 0) return {
9830
- includes: [],
9831
- excludes: [],
9832
- raw,
9833
- passthrough: true
9834
- };
9835
- const includes = [];
9836
- const excludes = [];
9837
- for (const part of trimmed.split(",")) {
9838
- const pat = part.trim();
9839
- if (pat.length === 0) continue;
9840
- if (pat.startsWith("!")) {
9841
- const body = pat.slice(1).trim();
9842
- if (body.length > 0) excludes.push(globToRegExp(body));
9843
- } else includes.push(globToRegExp(pat));
9844
- }
10695
+ const terms = raw.trim().toLowerCase().split(/\s+/).filter((t) => t.length > 0);
9845
10696
  return {
9846
- includes,
9847
- excludes,
10697
+ terms,
9848
10698
  raw,
9849
- passthrough: false
10699
+ passthrough: terms.length === 0
9850
10700
  };
9851
10701
  }
9852
- function matchMethod(filter, method) {
9853
- if (filter.passthrough) return true;
9854
- if (filter.excludes.some((re) => re.test(method))) return false;
9855
- if (filter.includes.length === 0) return true;
9856
- return filter.includes.some((re) => re.test(method));
10702
+ function matchFuzzy(query, method) {
10703
+ if (query.passthrough) return true;
10704
+ const target = method.toLowerCase();
10705
+ return query.terms.every((t) => target.includes(t));
10706
+ }
10707
+ function filterFuzzyMethods(query, methods) {
10708
+ if (query.passthrough) return methods;
10709
+ return methods.filter((m) => matchFuzzy(query, m));
9857
10710
  }
9858
- /** 便捷:直接对 MetricsSnapshot 的 methods 做过滤,返回浅拷贝。 */
9859
- function filterSnapshotMethods(filter, methods) {
9860
- if (filter.passthrough) return methods;
10711
+ function filterFuzzySnapshotMethods(query, methods) {
10712
+ if (query.passthrough) return methods;
9861
10713
  const out = {};
9862
- for (const [method, stats] of Object.entries(methods)) if (matchMethod(filter, method)) out[method] = stats;
10714
+ for (const [method, stats] of Object.entries(methods)) if (matchFuzzy(query, method)) out[method] = stats;
9863
10715
  return out;
9864
10716
  }
9865
10717
  //#endregion
@@ -9996,12 +10848,16 @@ function downloadText(filename, text, mime = "text/plain") {
9996
10848
  //#region webui/src/App.tsx
9997
10849
  var ENDPOINT = "";
9998
10850
  function App() {
10851
+ const { t } = useI18n();
9999
10852
  const [transport, setTransport] = (0, import_react.useState)("http");
10000
10853
  const [selected, setSelected] = (0, import_react.useState)("");
10001
10854
  const [generatingDocs, setGeneratingDocs] = (0, import_react.useState)(false);
10002
10855
  const [view, setView] = (0, import_react.useState)("methods");
10003
- const [methodFilter, setMethodFilter] = (0, import_react.useState)("");
10004
- const rpc = (0, import_react.useMemo)(() => createRpc({
10856
+ const [methodSearch, setMethodSearch] = (0, import_react.useState)("");
10857
+ const [metricsSearch, setMetricsSearch] = (0, import_react.useState)("");
10858
+ const debouncedMethodSearch = useDebouncedValue(methodSearch, 250);
10859
+ const debouncedMetricsSearch = useDebouncedValue(metricsSearch, 250);
10860
+ const rpc = (0, import_react.useMemo)(() => createRPC({
10005
10861
  transport,
10006
10862
  endpoint: ENDPOINT
10007
10863
  }), [transport]);
@@ -10009,18 +10865,20 @@ function App() {
10009
10865
  const { methods, loading, error, refresh } = useMethods(rpc);
10010
10866
  const detail = useMethodDetail(rpc, selected);
10011
10867
  const metrics = useMetrics(rpc, view === "metrics");
10012
- const compiledFilter = (0, import_react.useMemo)(() => compileGlobFilter(methodFilter), [methodFilter]);
10013
- const filteredSnapshot = (0, import_react.useMemo)(() => {
10868
+ const methodQuery = (0, import_react.useMemo)(() => compileFuzzyQuery(debouncedMethodSearch), [debouncedMethodSearch]);
10869
+ const metricsQuery = (0, import_react.useMemo)(() => compileFuzzyQuery(debouncedMetricsSearch), [debouncedMetricsSearch]);
10870
+ const filteredMethods = (0, import_react.useMemo)(() => filterFuzzyMethods(methodQuery, methods), [methodQuery, methods]);
10871
+ const filteredMetrics = (0, import_react.useMemo)(() => {
10014
10872
  if (!metrics.snapshot) return void 0;
10015
- if (compiledFilter.passthrough) return metrics.snapshot;
10873
+ if (metricsQuery.passthrough) return metrics.snapshot;
10016
10874
  return {
10017
10875
  ...metrics.snapshot,
10018
- methods: filterSnapshotMethods(compiledFilter, metrics.snapshot.methods)
10876
+ methods: filterFuzzySnapshotMethods(metricsQuery, metrics.snapshot.methods)
10019
10877
  };
10020
- }, [metrics.snapshot, compiledFilter]);
10878
+ }, [metrics.snapshot, metricsQuery]);
10021
10879
  const generateDocs = (0, import_react.useCallback)(async () => {
10022
10880
  if (methods.length === 0) {
10023
- alert("方法列表为空,请先刷新方法列表。");
10881
+ alert(t("alerts.emptyMethods"));
10024
10882
  return;
10025
10883
  }
10026
10884
  setGeneratingDocs(true);
@@ -10028,131 +10886,137 @@ function App() {
10028
10886
  const md = renderMarkdown(await collectApiDoc(rpc, methods));
10029
10887
  downloadText(`jsonrpc-api-doc-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}.md`, md, "text/markdown");
10030
10888
  } catch (e) {
10031
- alert(`生成文档失败:${e instanceof Error ? e.message : String(e)}`);
10889
+ alert(t("alerts.docsFailed", { error: e instanceof Error ? e.message : String(e) }));
10032
10890
  } finally {
10033
10891
  setGeneratingDocs(false);
10034
10892
  }
10035
- }, [rpc, methods]);
10893
+ }, [
10894
+ rpc,
10895
+ methods,
10896
+ t
10897
+ ]);
10036
10898
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
10037
10899
  className: "app",
10038
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toolbar, {
10039
- transport,
10040
- onTransportChange: setTransport,
10041
- onRefresh: refresh,
10042
- loading,
10043
- error,
10044
- onGenerateDocs: generateDocs,
10045
- generatingDocs
10046
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", {
10047
- className: "sidebar",
10048
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
10049
- className: "view-switch segmented",
10050
- role: "group",
10051
- "aria-label": "视图",
10052
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
10053
- type: "button",
10054
- className: view === "methods" ? "active" : "",
10055
- onClick: () => setView("methods"),
10056
- children: "Methods"
10057
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
10058
- type: "button",
10059
- className: view === "metrics" ? "active" : "",
10060
- onClick: () => setView("metrics"),
10061
- children: "Metrics"
10062
- })]
10063
- }), view === "methods" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: "Methods" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MethodTree, {
10064
- methods,
10065
- selected,
10066
- onSelect: setSelected
10067
- })] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
10068
- className: "metrics-sidebar",
10069
- children: [
10070
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: "Metrics" }),
10071
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
10072
- className: "hint",
10073
- style: { margin: "0 0 8px" },
10074
- children: "📊 服务端 RPC 调用统计。"
10075
- }),
10076
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
10077
- className: "metrics-filter",
10078
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
10079
- className: "metrics-filter-label",
10080
- children: "Filter"
10081
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
10082
- className: "sf-input",
10083
- type: "text",
10084
- placeholder: "user.*, !system.*",
10085
- value: methodFilter,
10086
- onChange: (e) => setMethodFilter(e.target.value),
10087
- spellCheck: false
10088
- })]
10089
- }),
10090
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
10091
- className: "hint metrics-filter-help",
10092
- children: [
10093
- "支持 ",
10094
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "*" }),
10095
- " / ",
10096
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "?" }),
10097
- " / ",
10098
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "!" }),
10099
- ",多个模式用 ",
10100
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "," }),
10101
- " 分隔。",
10102
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
10103
- "例:",
10104
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "user.*" }),
10105
- "、",
10106
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "*.get" }),
10107
- "、",
10108
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "!system.*" }),
10109
- "、",
10110
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "user.*,!user.secret" })
10111
- ]
10112
- }),
10113
- metrics.snapshot && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
10114
- className: "hint metrics-filter-stat",
10115
- children: [
10116
- "匹配 ",
10117
- Object.keys(filteredSnapshot?.methods ?? {}).length,
10118
- " /",
10119
- " ",
10120
- Object.keys(metrics.snapshot.methods).length,
10121
- " 个方法"
10122
- ]
10123
- })
10124
- ]
10125
- })]
10126
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
10127
- className: "content",
10128
- children: view === "methods" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MethodDetail, {
10129
- method: selected,
10130
- detail
10131
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TestPanel, {
10132
- method: selected,
10133
- detail,
10134
- rpc,
10900
+ children: [
10901
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toolbar, {
10135
10902
  transport,
10136
- endpoint: ENDPOINT
10137
- })] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetricsPanel, {
10138
- snapshot: filteredSnapshot,
10139
- previous: metrics.previous,
10140
- fetchedAt: metrics.fetchedAt,
10141
- loading: metrics.loading,
10142
- error: metrics.error,
10143
- available: metrics.available,
10144
- onRefresh: () => void metrics.refresh(),
10145
- autoRefresh: metrics.autoRefresh,
10146
- onAutoRefreshChange: metrics.setAutoRefresh,
10147
- intervalMs: metrics.intervalMs,
10148
- onIntervalChange: metrics.setIntervalMs
10149
- })
10150
- })] })]
10903
+ onTransportChange: setTransport,
10904
+ onRefresh: refresh,
10905
+ loading,
10906
+ error,
10907
+ onGenerateDocs: generateDocs,
10908
+ generatingDocs
10909
+ }),
10910
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", {
10911
+ className: "sidebar",
10912
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
10913
+ className: "view-switch segmented",
10914
+ role: "group",
10915
+ "aria-label": t("view.label"),
10916
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
10917
+ type: "button",
10918
+ className: view === "methods" ? "active" : "",
10919
+ onClick: () => setView("methods"),
10920
+ children: t("view.methods")
10921
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
10922
+ type: "button",
10923
+ className: view === "metrics" ? "active" : "",
10924
+ onClick: () => setView("metrics"),
10925
+ children: t("view.metrics")
10926
+ })]
10927
+ }), view === "methods" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
10928
+ className: "metrics-sidebar",
10929
+ children: [
10930
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: t("sidebar.methods") }),
10931
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
10932
+ className: "metrics-filter",
10933
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
10934
+ className: "metrics-filter-label",
10935
+ children: t("sidebar.search")
10936
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
10937
+ className: "sf-input",
10938
+ type: "text",
10939
+ placeholder: t("sidebar.searchPlaceholder"),
10940
+ value: methodSearch,
10941
+ onChange: (e) => setMethodSearch(e.target.value),
10942
+ spellCheck: false
10943
+ })]
10944
+ }),
10945
+ !methodQuery.passthrough && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
10946
+ className: "hint metrics-filter-stat",
10947
+ children: t("sidebar.matchCount", {
10948
+ matched: filteredMethods.length,
10949
+ total: methods.length
10950
+ })
10951
+ }),
10952
+ filteredMethods.length === 0 && methods.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
10953
+ className: "empty",
10954
+ children: t("sidebar.noMatch")
10955
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MethodTree, {
10956
+ methods: filteredMethods,
10957
+ selected,
10958
+ onSelect: setSelected
10959
+ })
10960
+ ]
10961
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
10962
+ className: "metrics-sidebar",
10963
+ children: [
10964
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: t("sidebar.metrics") }),
10965
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
10966
+ className: "metrics-filter",
10967
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
10968
+ className: "metrics-filter-label",
10969
+ children: t("sidebar.search")
10970
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
10971
+ className: "sf-input",
10972
+ type: "text",
10973
+ placeholder: t("sidebar.searchPlaceholder"),
10974
+ value: metricsSearch,
10975
+ onChange: (e) => setMetricsSearch(e.target.value),
10976
+ spellCheck: false
10977
+ })]
10978
+ }),
10979
+ metrics.snapshot && !metricsQuery.passthrough && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
10980
+ className: "hint metrics-filter-stat",
10981
+ children: t("sidebar.matchCount", {
10982
+ matched: Object.keys(filteredMetrics?.methods ?? {}).length,
10983
+ total: Object.keys(metrics.snapshot.methods).length
10984
+ })
10985
+ })
10986
+ ]
10987
+ })]
10988
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
10989
+ className: "content",
10990
+ children: view === "methods" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MethodDetail, {
10991
+ method: selected,
10992
+ detail
10993
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TestPanel, {
10994
+ method: selected,
10995
+ detail,
10996
+ rpc,
10997
+ transport,
10998
+ endpoint: ENDPOINT
10999
+ })] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetricsPanel, {
11000
+ snapshot: filteredMetrics,
11001
+ previous: metrics.previous,
11002
+ fetchedAt: metrics.fetchedAt,
11003
+ loading: metrics.loading,
11004
+ error: metrics.error,
11005
+ available: metrics.available,
11006
+ onRefresh: () => void metrics.refresh(),
11007
+ autoRefresh: metrics.autoRefresh,
11008
+ onAutoRefreshChange: metrics.setAutoRefresh,
11009
+ intervalMs: metrics.intervalMs,
11010
+ onIntervalChange: metrics.setIntervalMs
11011
+ })
11012
+ })] }),
11013
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Footer, {})
11014
+ ]
10151
11015
  });
10152
11016
  }
10153
11017
  //#endregion
10154
11018
  //#region webui/src/main.tsx
10155
- import_client.createRoot(document.getElementById("root")).render(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.StrictMode, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(App, {}) }) }));
11019
+ import_client.createRoot(document.getElementById("root")).render(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.StrictMode, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(I18nProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(App, {}) }) }) }));
10156
11020
  //#endregion
10157
11021
 
10158
- //# sourceMappingURL=index-Cr0Jnrgb.js.map
11022
+ //# sourceMappingURL=index-CqI7vRAS.js.map