@xiaoso/dsh-tool-plus 0.1.0-beta.5 → 0.1.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +2 -2
- package/THIRD_PARTY_NOTICES.md +42 -42
- package/lib/{abort-dIkZBV7Q.mjs → abort-CsjZitYv.mjs} +1 -1
- package/lib/{amazon-bedrock-WQhvjHYu.mjs → amazon-bedrock-CfNIDi5k.mjs} +7 -7
- package/lib/{anthropic-CJCauvYz.mjs → anthropic-BVfZdnMp.mjs} +12 -12
- package/lib/{anthropic-tTntD8BD.mjs → anthropic-DGXmmg43.mjs} +2 -2
- package/lib/{api-key-login-BzqNdM2y.mjs → api-key-login-BikQAfD-.mjs} +1 -1
- package/lib/{aws-credentials-CXryMobh.mjs → aws-credentials-DrssNMn_.mjs} +4 -4
- package/lib/{azure-openai-responses-DuukbD0F.mjs → azure-openai-responses-C-5q-5K2.mjs} +8 -8
- package/lib/browser-render-z6b8Ljo9.mjs +183 -0
- package/lib/{bundled-references-Cavaffah.mjs → bundled-references-D71z7_T3.mjs} +2 -2
- package/lib/{callback-server-CQ5Jo8F-.mjs → callback-server-Di6C4RqI.mjs} +1 -1
- package/lib/client.js +613 -107
- package/lib/{cursor-B6TUEmgP.mjs → cursor-CQQtygwl.mjs} +1 -1
- package/lib/{cursor-DkSX2h3H.mjs → cursor-DXJxwdcg.mjs} +7 -7
- package/lib/{cursor-aJrXeYCb.mjs → cursor-Hr5Zw1gt.mjs} +3 -3
- package/lib/{device-code-DBo3B5nh.mjs → device-code-wZUbgPdB.mjs} +1 -1
- package/lib/{devin-BHZZyMhO.mjs → devin-CF6UhCFw.mjs} +2 -2
- package/lib/{devin-BK7DHeca.mjs → devin-DXJoDlW2.mjs} +1 -1
- package/lib/{devin-vNZTNUON.mjs → devin-P37gURs_.mjs} +5 -5
- package/lib/{dom-DGdYAVTv.mjs → dom-CawWi8fa.mjs} +14 -1
- package/lib/{error-Dd8DIqs-.mjs → error-DoHuatWw.mjs} +1 -1
- package/lib/{event-stream-BbFZJy-z.mjs → event-stream-C-FmBYUt.mjs} +1 -1
- package/lib/{github-copilot-CYkjeTi3.mjs → github-copilot-C2dTonBX.mjs} +3 -3
- package/lib/{github-copilot-B1yiyVRh.mjs → github-copilot-qETlj_bD.mjs} +1 -1
- package/lib/{gitlab-duo-CM7Ref1H.mjs → gitlab-duo-FQZiFRQe.mjs} +3 -3
- package/lib/{gitlab-duo-workflow-BGAF6JDw.mjs → gitlab-duo-workflow-DLTabzOy.mjs} +2 -2
- package/lib/{google-BS4T25Pn.mjs → google-BilwFSzx.mjs} +3 -3
- package/lib/{google-antigravity-DGmwC2KV.mjs → google-antigravity-BfBiINdu.mjs} +2 -2
- package/lib/{google-auth-B6gjsZvg.mjs → google-auth-CWlKXJ-H.mjs} +3 -3
- package/lib/{google-gemini-cli-Dsztq0N5.mjs → google-gemini-cli-CB89ljNz.mjs} +3 -3
- package/lib/{google-gemini-cli-Cqds_Q5T.mjs → google-gemini-cli-CygFcR6N.mjs} +8 -8
- package/lib/{google-oauth-shared-CuEHaDDj.mjs → google-oauth-shared-Cn82h1o2.mjs} +2 -2
- package/lib/{google-shared-Dr-VG-ap.mjs → google-shared-CF1JrqTM.mjs} +6 -6
- package/lib/{google-vertex-BDN2tLaD.mjs → google-vertex-DgMhhFw8.mjs} +5 -5
- package/lib/{identity-Da1GPU9a.mjs → identity-C0SPVyIZ.mjs} +1 -1
- package/lib/{idle-iterator-DUjKQEGT.mjs → idle-iterator-HzbbfXP4.mjs} +2 -2
- package/lib/index.mjs +310 -29
- package/lib/{kimi-Dh0pv3WZ.mjs → kimi-DHDO6Fde.mjs} +3 -3
- package/lib/launch-DwpE70Pv.mjs +148 -0
- package/lib/{markit-BL3cH47U.mjs → markit-B9exZ0ZV.mjs} +1 -1
- package/lib/{minimax-code-Uq8Qu9LW.mjs → minimax-code-vAhEXhev.mjs} +1 -1
- package/lib/{models-SGASygJ3.mjs → models-Cp3PzU3l.mjs} +1 -1
- package/lib/{ollama-DPzBmq89.mjs → ollama-TrkXTNqE.mjs} +8 -8
- package/lib/{openai-codex-Ch8CkT3p.mjs → openai-codex-CrjHtSH4.mjs} +3 -3
- package/lib/{openai-codex-responses-pejZgdZo.mjs → openai-codex-responses-C4uMpWNy.mjs} +10 -10
- package/lib/{openai-completions-DIf361kF.mjs → openai-completions-1ydXm5vF.mjs} +13 -13
- package/lib/{openai-reasoning-fallback-Nj6yB2rY.mjs → openai-reasoning-fallback-CigKCyzc.mjs} +2 -2
- package/lib/{openai-responses-Db_a-U4-.mjs → openai-responses-CN79gRGQ.mjs} +10 -10
- package/lib/{opencode-Rq_Q2vAO.mjs → opencode-CEGlimzL.mjs} +1 -1
- package/lib/{perplexity-C4iLVMUj.mjs → perplexity-BLJN-8dM.mjs} +2 -2
- package/lib/{proxy-pM_NIv-H.mjs → proxy-XOYi58TM.mjs} +1 -1
- package/lib/readability-Cpm-ocau.mjs +455 -0
- package/lib/{scrapers-1FTlHNQV.mjs → scrapers-DrSYgPyf.mjs} +11 -10
- package/lib/{src-BgpusnsB.mjs → src-D_e1qMwi.mjs} +5 -1
- package/lib/{stream-CRtexYLe.mjs → stream-DveNFWSN.mjs} +56 -56
- package/lib/{stream-markup-healing-KMkiAA00.mjs → stream-markup-healing-C9eC7Gf8.mjs} +3 -3
- package/lib/tool-errors-DoyxivAR.mjs +47 -0
- package/lib/types-BUO0JXO-.mjs +251 -0
- package/lib/{utils-D2YknRk4.mjs → utils-B-mTYQBc.mjs} +1 -1
- package/lib/{utils-C616y7eF.mjs → utils-DSEEKy3G.mjs} +28 -268
- package/lib/{utils-Cp76gCyk.mjs → utils-fnV2zzp3.mjs} +3 -3
- package/lib/{vision-guard-01qSwFpf.mjs → vision-guard-BsvOWmfB.mjs} +2 -2
- package/lib/{wafer-CgtPHerf.mjs → wafer-Dpy7tL3l.mjs} +1 -1
- package/lib/{xai-oauth-Buk8XVvM.mjs → xai-oauth-BkPNQS-q.mjs} +2 -2
- package/lib/{xiaomi-D2qT5cAC.mjs → xiaomi-CG3TsYRz.mjs} +1 -1
- package/lib/{zai-C59YnomG.mjs → zai--cewobzN.mjs} +2 -2
- package/lib/{zip-Cg9ANN_U.mjs → zip-CqKW2-TK.mjs} +3 -46
- package/package.json +3 -1
- package/presets/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -7,6 +7,7 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
let react = require("react");
|
|
8
8
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
let _deepseek_ai_dsh_client_connection_client = require("@deepseek-ai/dsh-client-connection/client");
|
|
10
11
|
//#region src/config/fields.ts
|
|
11
12
|
/** Field group ids -> locale key of the group heading. */
|
|
12
13
|
const TOOL_PLUS_GROUP_LABELS = {
|
|
@@ -442,6 +443,64 @@ window.__ModuleLoader__.load({
|
|
|
442
443
|
tool: "read",
|
|
443
444
|
visibility: { requiresEnabled: "fetchEnabled" }
|
|
444
445
|
},
|
|
446
|
+
{
|
|
447
|
+
name: "fetchReader",
|
|
448
|
+
kind: "select",
|
|
449
|
+
default: "auto",
|
|
450
|
+
labelKey: "fetchReader",
|
|
451
|
+
hintKey: "fetchReaderHint",
|
|
452
|
+
group: "fetch",
|
|
453
|
+
tool: "read",
|
|
454
|
+
options: [
|
|
455
|
+
{
|
|
456
|
+
value: "auto",
|
|
457
|
+
labelKey: "optReaderAuto"
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
value: "native",
|
|
461
|
+
labelKey: "optReaderNative"
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
value: "trafilatura",
|
|
465
|
+
labelKey: "optReaderTrafilatura"
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
value: "lynx",
|
|
469
|
+
labelKey: "optReaderLynx"
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
value: "parallel",
|
|
473
|
+
labelKey: "optReaderParallel"
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
value: "jina",
|
|
477
|
+
labelKey: "optReaderJina"
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
value: "browser",
|
|
481
|
+
labelKey: "optReaderBrowser"
|
|
482
|
+
}
|
|
483
|
+
]
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
name: "browserReaderEnabled",
|
|
487
|
+
kind: "boolean",
|
|
488
|
+
default: true,
|
|
489
|
+
labelKey: "browserReaderEnabled",
|
|
490
|
+
hintKey: "browserReaderEnabledHint",
|
|
491
|
+
group: "fetch",
|
|
492
|
+
tool: "read"
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
name: "browserProbe",
|
|
496
|
+
kind: "action",
|
|
497
|
+
default: "",
|
|
498
|
+
labelKey: "browserProbe",
|
|
499
|
+
hintKey: "browserProbeHint",
|
|
500
|
+
group: "fetch",
|
|
501
|
+
tool: "read",
|
|
502
|
+
actionKey: "browser/detect"
|
|
503
|
+
},
|
|
445
504
|
{
|
|
446
505
|
name: "imagesBlockImages",
|
|
447
506
|
kind: "boolean",
|
|
@@ -667,44 +726,84 @@ window.__ModuleLoader__.load({
|
|
|
667
726
|
TOOL_PLUS_FIELDS.map((field) => field.name);
|
|
668
727
|
//#endregion
|
|
669
728
|
//#region src/client/rows.tsx
|
|
670
|
-
/**
|
|
729
|
+
/**
|
|
730
|
+
* Shared settings-row controls for the tool-plus settings surfaces: number
|
|
731
|
+
* input, select pill, and toggle switch rows, styled against the app's
|
|
732
|
+
* `--dsw-alias-*` design tokens. Extracted from the Bash card so the Plugins
|
|
733
|
+
* card and the Tool Plus settings section render identical controls.
|
|
734
|
+
* @module @xiaoso/dsh-tool-plus/client
|
|
735
|
+
*/
|
|
736
|
+
/**
|
|
737
|
+
* Shared row control CSS, keyed by `data-plugin-css` and injected once.
|
|
738
|
+
*
|
|
739
|
+
* Motion discipline: every moving control animates transform/opacity only
|
|
740
|
+
* (GPU-composited); color transitions fire on hover/focus state changes
|
|
741
|
+
* only. Everything is disabled under prefers-reduced-motion. All colors are
|
|
742
|
+
* `--dsw-alias-*` host tokens (or color-mix of them), so light/dark themes
|
|
743
|
+
* adapt automatically.
|
|
744
|
+
*/
|
|
671
745
|
const SETTINGS_ROWS_CSS = `
|
|
672
|
-
.tp-row{display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid var(--dsw-alias-border-
|
|
746
|
+
.tp-row{display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid var(--dsw-alias-border-l1);transition:background .16s ease}
|
|
673
747
|
.tp-row:last-child{border-bottom:none}
|
|
748
|
+
.tp-rowClickable{cursor:pointer}
|
|
674
749
|
.tp-rowText{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;padding-right:24px}
|
|
675
750
|
.tp-rowTitle{font-size:14px;font-weight:400;line-height:22px;color:var(--dsw-alias-label-primary)}
|
|
676
751
|
.tp-rowDesc{font-size:12px;font-weight:400;line-height:18px;color:var(--dsw-alias-label-tertiary)}
|
|
677
752
|
.tp-badges{display:inline-flex;align-items:center;gap:8px}
|
|
678
|
-
.tp-badge{
|
|
679
|
-
.tp-
|
|
753
|
+
.tp-badge{display:inline-flex;align-items:center;gap:6px;padding:1px 0;font-size:11px;line-height:17px;white-space:nowrap;font-weight:500;color:var(--dsw-alias-label-tertiary)}
|
|
754
|
+
.tp-badge::before{content:'';flex:none;width:5px;height:5px;border-radius:999px;background:var(--dsw-alias-state-business-primary)}
|
|
755
|
+
.tp-reset{position:relative;border:none;background:none;padding:2px 0;font:inherit;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary);cursor:pointer;transition:color .16s ease}
|
|
756
|
+
.tp-reset::after{content:'';position:absolute;left:0;right:0;bottom:0;height:1px;border-radius:1px;background:currentColor;opacity:0;transform:scaleX(.6);transform-origin:left center;transition:opacity .16s ease,transform .2s cubic-bezier(.22,1,.36,1)}
|
|
680
757
|
.tp-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
|
|
758
|
+
.tp-reset:hover:not(:disabled)::after{opacity:.6;transform:scaleX(1)}
|
|
681
759
|
.tp-reset:disabled{cursor:default}
|
|
682
|
-
.tp-
|
|
683
|
-
.tp-
|
|
684
|
-
.tp-
|
|
685
|
-
.tp-
|
|
686
|
-
.tp-input{height:34px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-3);font:inherit;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-primary);width:100%;box-sizing:border-box}
|
|
687
|
-
.tp-input:focus-visible{outline:none;border-color:var(--dsw-alias-brand-primary)}
|
|
760
|
+
.tp-reset:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;border-radius:2px}
|
|
761
|
+
.tp-input{flex:none;width:128px;height:34px;padding:0 11px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);font:inherit;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-primary);box-sizing:border-box;transition:border-color .16s ease}
|
|
762
|
+
.tp-input:hover:not(:disabled):not(:focus-visible){border-color:var(--dsw-alias-border-l3)}
|
|
763
|
+
.tp-input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px;border-color:var(--dsw-alias-border-l3)}
|
|
688
764
|
.tp-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
689
765
|
.tp-input.tp-invalid{border-color:var(--dsw-alias-label-error)}
|
|
690
|
-
.tp-
|
|
691
|
-
.tp-
|
|
692
|
-
.tp-selectTrigger{display:inline-flex;align-items:center;gap:12px;height:36px;padding:0 14px;border:none;border-radius:18px;background:var(--dsw-alias-bg-module-platform);font:inherit;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary);cursor:pointer;flex-shrink:0}
|
|
766
|
+
.tp-input.tp-invalid:focus-visible{outline-color:var(--dsw-alias-state-error-primary)}
|
|
767
|
+
.tp-invalidText{color:var(--dsw-alias-label-error);animation:tp-soft-in .18s ease both}
|
|
693
768
|
.tp-selectAnchor{display:inline-flex;flex-shrink:0}
|
|
694
|
-
.tp-selectTrigger:
|
|
695
|
-
.tp-selectTrigger:disabled{
|
|
696
|
-
.tp-selectTrigger:
|
|
697
|
-
.tp-
|
|
769
|
+
.tp-selectTrigger{display:inline-flex;align-items:center;gap:8px;height:34px;padding:0 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:var(--dsw-alias-bg-module-platform);font:inherit;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary);cursor:pointer;flex-shrink:0;transition:background .16s ease,border-color .16s ease,transform .1s ease}
|
|
770
|
+
.tp-selectTrigger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l3)}
|
|
771
|
+
.tp-selectTrigger:active:not(:disabled){transform:scale(.97)}
|
|
772
|
+
.tp-selectTrigger:disabled{opacity:.55;cursor:default}
|
|
773
|
+
.tp-selectTrigger:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
|
|
774
|
+
.tp-selectChevron{flex:none;color:var(--dsw-alias-label-tertiary);display:inline-flex;transition:transform .2s cubic-bezier(.22,1,.36,1)}
|
|
698
775
|
.tp-selectTrigger[aria-expanded="true"] .tp-selectChevron{transform:rotate(180deg)}
|
|
699
|
-
.tp-switch{position:relative;width:
|
|
700
|
-
.tp-switch input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
|
|
701
|
-
.tp-switchTrack{position:absolute;inset:0;border-radius:999px;background:var(--dsw-alias-border-l2);transition:background .
|
|
776
|
+
.tp-switch{position:relative;width:40px;height:24px;flex:none}
|
|
777
|
+
.tp-switch input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer;z-index:1}
|
|
778
|
+
.tp-switchTrack{position:absolute;inset:0;border-radius:999px;background:var(--dsw-alias-border-l2);transition:background .18s ease;pointer-events:none}
|
|
779
|
+
.tp-row:hover .tp-switchTrack{background:var(--dsw-alias-border-l3)}
|
|
702
780
|
.tp-switch input:checked+.tp-switchTrack{background:var(--dsw-alias-brand-primary)}
|
|
703
|
-
.tp-switch input:
|
|
704
|
-
.tp-
|
|
705
|
-
.tp-
|
|
781
|
+
.tp-row:hover .tp-switch input:checked+.tp-switchTrack{background:var(--dsw-alias-brand-primary)}
|
|
782
|
+
.tp-switch input:focus-visible+.tp-switchTrack{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
|
|
783
|
+
.tp-thumb{position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:999px;background:var(--dsw-alias-bg-layer-3);box-shadow:0 1px 3px rgba(0,0,0,.14),0 1px 1px rgba(0,0,0,.08);transition:transform .2s cubic-bezier(.34,1.3,.5,1);transform-origin:left center;pointer-events:none}
|
|
784
|
+
.tp-switch input:checked~.tp-thumb{transform:translateX(16px);transform-origin:right center}
|
|
785
|
+
.tp-switch input:active:not(:disabled)~.tp-thumb{transform:scaleX(1.2)}
|
|
786
|
+
.tp-switch input:checked:active:not(:disabled)~.tp-thumb{transform:translateX(16px) scaleX(1.2)}
|
|
787
|
+
.tp-switch input:disabled{cursor:default}
|
|
788
|
+
.tp-switch input:disabled~.tp-switchTrack,.tp-switch input:disabled~.tp-thumb{opacity:.45}
|
|
789
|
+
.tp-actionField{display:flex;flex-direction:column;gap:8px;padding:12px 0;border-bottom:1px solid var(--dsw-alias-border-l1)}
|
|
790
|
+
.tp-actionField:last-child{border-bottom:none}
|
|
791
|
+
.tp-actionHead{display:flex;align-items:center;gap:12px}
|
|
792
|
+
.tp-actionInfo{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
|
|
793
|
+
.tp-actionTitle{font-size:14px;font-weight:400;line-height:22px;color:var(--dsw-alias-label-primary)}
|
|
794
|
+
.tp-actionDesc{font-size:12px;font-weight:400;line-height:18px;color:var(--dsw-alias-label-tertiary)}
|
|
795
|
+
.tp-actionButton{appearance:none;flex:none;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);padding:5px 14px;font:inherit;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-primary);cursor:pointer;transition:background .16s ease,border-color .16s ease,transform .1s ease}
|
|
796
|
+
.tp-actionButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l3)}
|
|
797
|
+
.tp-actionButton:active:not(:disabled){transform:scale(.97)}
|
|
798
|
+
.tp-actionButton:disabled{opacity:.55;cursor:default}
|
|
799
|
+
.tp-actionButton:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
|
|
800
|
+
.tp-actionResult{margin:0;font-size:12px;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere;animation:tp-soft-in .22s cubic-bezier(.22,1,.36,1) both}
|
|
801
|
+
.tp-actionResult[data-state="ok"]{color:var(--dsw-alias-label-secondary)}
|
|
802
|
+
.tp-actionResult[data-state="error"]{color:var(--dsw-alias-label-error)}
|
|
803
|
+
@keyframes tp-soft-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
|
|
706
804
|
@media (prefers-reduced-motion: reduce){
|
|
707
|
-
.tp-
|
|
805
|
+
.tp-row,.tp-reset,.tp-reset::after,.tp-input,.tp-selectTrigger,.tp-selectChevron,.tp-switchTrack,.tp-thumb,.tp-actionButton{transition:none}
|
|
806
|
+
.tp-invalidText,.tp-actionResult{animation:none}
|
|
708
807
|
}
|
|
709
808
|
`;
|
|
710
809
|
/** Inject the shared row stylesheet once per page. */
|
|
@@ -723,46 +822,53 @@ window.__ModuleLoader__.load({
|
|
|
723
822
|
function hasOwn(obj, key) {
|
|
724
823
|
return typeof obj === "object" && obj !== null && Object.prototype.hasOwnProperty.call(obj, key);
|
|
725
824
|
}
|
|
726
|
-
/**
|
|
825
|
+
/**
|
|
826
|
+
* Number row: same left-text / right-control anatomy as toggle and select
|
|
827
|
+
* rows, with the input right-aligned at a fixed width. The label is wired to
|
|
828
|
+
* the input (htmlFor) and clicking anywhere on the row focuses the input.
|
|
829
|
+
*/
|
|
727
830
|
function NumberRow(props) {
|
|
728
831
|
const { t, control, disabled, onEdit, onReset } = props;
|
|
832
|
+
const inputId = (0, react.useId)();
|
|
729
833
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
730
|
-
className: "tp-
|
|
834
|
+
className: "tp-row" + (disabled ? "" : " tp-rowClickable"),
|
|
835
|
+
onClick: (event) => {
|
|
836
|
+
if (event.target.closest("input,button,select,textarea,a")) return;
|
|
837
|
+
event.currentTarget.querySelector(".tp-input")?.focus();
|
|
838
|
+
},
|
|
731
839
|
children: [
|
|
732
840
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
733
|
-
className: "tp-
|
|
841
|
+
className: "tp-rowText",
|
|
734
842
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
735
|
-
className: "tp-
|
|
843
|
+
className: "tp-rowTitle",
|
|
844
|
+
htmlFor: inputId,
|
|
736
845
|
children: t(control.labelKey)
|
|
737
|
-
}),
|
|
738
|
-
className: "tp-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
743
|
-
type: "button",
|
|
744
|
-
className: "tp-reset",
|
|
745
|
-
disabled,
|
|
746
|
-
onClick: () => onReset(control.field),
|
|
747
|
-
children: t("reset")
|
|
748
|
-
})]
|
|
749
|
-
}) : null]
|
|
846
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
847
|
+
className: "tp-rowDesc" + (control.invalid ? " tp-invalidText" : ""),
|
|
848
|
+
role: control.invalid ? "status" : void 0,
|
|
849
|
+
children: control.invalid ? t("invalidNumber") : t(control.hintKey)
|
|
850
|
+
})]
|
|
750
851
|
}),
|
|
852
|
+
control.overridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
853
|
+
className: "tp-badges",
|
|
854
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
855
|
+
className: "tp-badge",
|
|
856
|
+
children: t("overridden")
|
|
857
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
858
|
+
type: "button",
|
|
859
|
+
className: "tp-reset",
|
|
860
|
+
disabled,
|
|
861
|
+
onClick: () => onReset(control.field),
|
|
862
|
+
children: t("reset")
|
|
863
|
+
})]
|
|
864
|
+
}) : null,
|
|
751
865
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
866
|
+
id: inputId,
|
|
752
867
|
type: "number",
|
|
753
|
-
"aria-label": t(control.labelKey),
|
|
754
868
|
className: "tp-input" + (control.invalid ? " tp-invalid" : ""),
|
|
755
869
|
disabled,
|
|
756
870
|
value: control.text,
|
|
757
871
|
onChange: (e) => onEdit(control.field, e.target.value)
|
|
758
|
-
}),
|
|
759
|
-
control.invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
760
|
-
className: "tp-invalidMsg",
|
|
761
|
-
role: "status",
|
|
762
|
-
children: t("invalidNumber")
|
|
763
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
764
|
-
className: "tp-hint",
|
|
765
|
-
children: t(control.hintKey)
|
|
766
872
|
})
|
|
767
873
|
]
|
|
768
874
|
});
|
|
@@ -777,7 +883,11 @@ window.__ModuleLoader__.load({
|
|
|
777
883
|
}));
|
|
778
884
|
const close = () => onOpenChange(null);
|
|
779
885
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
780
|
-
className: "tp-row",
|
|
886
|
+
className: "tp-row" + (disabled ? "" : " tp-rowClickable"),
|
|
887
|
+
onClick: (event) => {
|
|
888
|
+
if (event.target.closest("input,button,select,textarea,a")) return;
|
|
889
|
+
event.currentTarget.querySelector(".tp-selectTrigger")?.click();
|
|
890
|
+
},
|
|
781
891
|
children: [
|
|
782
892
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
783
893
|
className: "tp-rowText",
|
|
@@ -831,7 +941,11 @@ window.__ModuleLoader__.load({
|
|
|
831
941
|
function ToggleRow(props) {
|
|
832
942
|
const { t, control, disabled, onToggle, onReset } = props;
|
|
833
943
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
834
|
-
className: "tp-row",
|
|
944
|
+
className: "tp-row" + (disabled ? "" : " tp-rowClickable"),
|
|
945
|
+
onClick: (event) => {
|
|
946
|
+
if (event.target.closest("input,button,select,textarea,a")) return;
|
|
947
|
+
event.currentTarget.querySelector(".tp-switch input")?.click();
|
|
948
|
+
},
|
|
835
949
|
children: [
|
|
836
950
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
837
951
|
className: "tp-rowText",
|
|
@@ -873,6 +987,42 @@ window.__ModuleLoader__.load({
|
|
|
873
987
|
]
|
|
874
988
|
});
|
|
875
989
|
}
|
|
990
|
+
/**
|
|
991
|
+
* Action row: Title + desc stacked on left, a button on the right that calls
|
|
992
|
+
* the host RPC endpoint named by `control.actionKey` (via the Connection
|
|
993
|
+
* channel; the caller owns the RPC). The result line renders below, colored
|
|
994
|
+
* by outcome (`ok` / `error`).
|
|
995
|
+
*/
|
|
996
|
+
function ActionRow(props) {
|
|
997
|
+
const { t, control, disabled, running, result, onRun } = props;
|
|
998
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
999
|
+
className: "tp-actionField",
|
|
1000
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1001
|
+
className: "tp-actionHead",
|
|
1002
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1003
|
+
className: "tp-actionInfo",
|
|
1004
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1005
|
+
className: "tp-actionTitle",
|
|
1006
|
+
children: t(control.labelKey)
|
|
1007
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1008
|
+
className: "tp-actionDesc",
|
|
1009
|
+
children: t(control.hintKey)
|
|
1010
|
+
})]
|
|
1011
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1012
|
+
type: "button",
|
|
1013
|
+
className: "tp-actionButton",
|
|
1014
|
+
disabled: disabled || running,
|
|
1015
|
+
onClick: () => onRun(control.actionKey),
|
|
1016
|
+
children: running ? t("browserProbeRunning") : t(control.labelKey)
|
|
1017
|
+
})]
|
|
1018
|
+
}), result !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1019
|
+
className: "tp-actionResult",
|
|
1020
|
+
role: "status",
|
|
1021
|
+
"data-state": result.ok ? "ok" : "error",
|
|
1022
|
+
children: result.text
|
|
1023
|
+
}) : null]
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
876
1026
|
//#endregion
|
|
877
1027
|
//#region src/client/BashPlusCard.tsx
|
|
878
1028
|
/**
|
|
@@ -952,11 +1102,13 @@ window.__ModuleLoader__.load({
|
|
|
952
1102
|
.tp-card.tp-open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
|
|
953
1103
|
.tp-header{width:100%;appearance:none;border:0;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer;display:flex;align-items:center;gap:12px;padding:14px 16px;border-radius:12px}
|
|
954
1104
|
.tp-header:active{background:var(--dsw-alias-bg-layer-2)}
|
|
955
|
-
.tp-header:focus-visible{outline:2px solid var(--dsw-alias-
|
|
1105
|
+
.tp-header:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}
|
|
956
1106
|
.tp-headText{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
|
|
957
1107
|
.tp-name{font-size:15px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary)}
|
|
958
1108
|
.tp-desc{font-size:13px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
|
|
959
|
-
.tp-pending{flex:none;border-radius:999px;padding:
|
|
1109
|
+
.tp-pending{display:inline-flex;align-items:center;gap:6px;flex:none;border-radius:999px;padding:2px 10px;font-size:11px;line-height:17px;font-weight:500;white-space:nowrap;background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-state-business-primary)}
|
|
1110
|
+
.tp-pending::before{content:'';flex:none;width:6px;height:6px;border-radius:999px;background:var(--dsw-alias-state-business-primary);animation:tp-pulse 1.8s ease-in-out infinite}
|
|
1111
|
+
@keyframes tp-pulse{0%,100%{opacity:.35;transform:scale(.85)}50%{opacity:1;transform:scale(1)}}
|
|
960
1112
|
.tp-chevron{flex:none;color:var(--dsw-alias-label-tertiary);transition:transform .16s;display:inline-flex}
|
|
961
1113
|
.tp-chevron.tp-open{transform:rotate(180deg)}
|
|
962
1114
|
.tp-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-top:4px;display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .22s cubic-bezier(.22,.61,.36,1),opacity .16s ease}
|
|
@@ -974,10 +1126,12 @@ window.__ModuleLoader__.load({
|
|
|
974
1126
|
.tp-discard{border-color:var(--dsw-alias-border-l2);background:none;color:var(--dsw-alias-label-secondary)}
|
|
975
1127
|
.tp-discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}
|
|
976
1128
|
.tp-save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
|
|
1129
|
+
.tp-save:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}
|
|
977
1130
|
.tp-discard:disabled,.tp-save:disabled{opacity:.4;cursor:default}
|
|
978
|
-
.tp-discard:focus-visible,.tp-save:focus-visible{outline:2px solid var(--dsw-alias-
|
|
1131
|
+
.tp-discard:focus-visible,.tp-save:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
|
|
979
1132
|
@media (prefers-reduced-motion: reduce){
|
|
980
1133
|
.tp-card,.tp-header,.tp-chevron,.tp-body,.tp-thumb,.tp-switchTrack,.tp-discard,.tp-save{transition:none}
|
|
1134
|
+
.tp-pending::before{animation:none}
|
|
981
1135
|
}
|
|
982
1136
|
`;
|
|
983
1137
|
/** Inject the card stylesheet once per page; the loader removes plugin-owned style tags on unload. */
|
|
@@ -1473,6 +1627,113 @@ window.__ModuleLoader__.load({
|
|
|
1473
1627
|
});
|
|
1474
1628
|
}
|
|
1475
1629
|
//#endregion
|
|
1630
|
+
//#region src/tools/shared/browser-rpc-channel.ts
|
|
1631
|
+
/**
|
|
1632
|
+
* Shared RPC channel constants for the browser-detection round trip between
|
|
1633
|
+
* the settings panel (client half) and the host. Both halves import this
|
|
1634
|
+
* module so the channel/endpoint never drift.
|
|
1635
|
+
*
|
|
1636
|
+
* The channel rides the official `@deepseek-ai/dsh-client-connection` generic
|
|
1637
|
+
* RPC transport (host registers with `ctx.connection.rpc.handle`; the client
|
|
1638
|
+
* calls via `createWebConnectionRpc().call`). Channel names must match
|
|
1639
|
+
* `/^\/[A-Za-z0-9._~-]+$/` and `/api` is reserved — `/tool-plus` is ours.
|
|
1640
|
+
* @module @xiaoso/dsh-tool-plus/browser-rpc-channel
|
|
1641
|
+
*/
|
|
1642
|
+
/** Logical RPC channel registered by the host half of this plugin. */
|
|
1643
|
+
const TOOL_PLUS_RPC_CHANNEL = "/tool-plus";
|
|
1644
|
+
//#endregion
|
|
1645
|
+
//#region src/client/web-connection-rpc.ts
|
|
1646
|
+
/**
|
|
1647
|
+
* Browser caller for the plugin's Connection RPC channel.
|
|
1648
|
+
*
|
|
1649
|
+
* The official `@deepseek-ai/dsh-client-connection/client` package ships the
|
|
1650
|
+
* protocol primitives as types (`RpcFetch`, `ClientConnectionRpc`, `RpcResult`)
|
|
1651
|
+
* and the `RpcId` brand, but its published `createWebConnectionRpc` factory is
|
|
1652
|
+
* internal to the bundle — not re-exported. This module re-implements that
|
|
1653
|
+
* factory against the same public wire contract (docs/api-gateway.md, the
|
|
1654
|
+
* client `rpc.ts` source): a `POST {origin}/{channel}/{endpoint}` with a
|
|
1655
|
+
* `client-request` JSON envelope, answered by a `server-response` envelope
|
|
1656
|
+
* echoing the rpcId. Host-side registration uses the official
|
|
1657
|
+
* `ctx.connection.rpc.handle` — this file only mirrors the caller half.
|
|
1658
|
+
* @module @xiaoso/dsh-tool-plus/client/web-connection-rpc
|
|
1659
|
+
*/
|
|
1660
|
+
const INTERNAL_BASE = "http://dsh.internal";
|
|
1661
|
+
const CHANNEL_PATTERN = /^\/[A-Za-z0-9._~-]+$/;
|
|
1662
|
+
const ENDPOINT_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
|
|
1663
|
+
function randomUuid() {
|
|
1664
|
+
const g = globalThis;
|
|
1665
|
+
if (g.crypto?.randomUUID !== void 0) return g.crypto.randomUUID();
|
|
1666
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
1667
|
+
const r = Math.random() * 16 | 0;
|
|
1668
|
+
return (c === "x" ? r : r & 3 | 8).toString(16);
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
function isRecord(value) {
|
|
1672
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1673
|
+
}
|
|
1674
|
+
function parseConnectionResponse(value) {
|
|
1675
|
+
if (!isRecord(value) || value.type !== "server-response" || typeof value.rpcId !== "string") throw new TypeError("connection: invalid server-response envelope");
|
|
1676
|
+
const result = value.result;
|
|
1677
|
+
if (!isRecord(result)) throw new TypeError("connection: invalid server-response result");
|
|
1678
|
+
if (result.ok === true) return {
|
|
1679
|
+
rpcId: value.rpcId,
|
|
1680
|
+
result: {
|
|
1681
|
+
ok: true,
|
|
1682
|
+
value: result.value
|
|
1683
|
+
}
|
|
1684
|
+
};
|
|
1685
|
+
if (result.ok !== false || !isRecord(result.error)) throw new TypeError("connection: invalid server-response result");
|
|
1686
|
+
const error = result.error;
|
|
1687
|
+
if (typeof error.code !== "string" || typeof error.message !== "string" || !isRecord(error.details)) throw new TypeError("connection: invalid server-response failure");
|
|
1688
|
+
return {
|
|
1689
|
+
rpcId: value.rpcId,
|
|
1690
|
+
result: {
|
|
1691
|
+
ok: false,
|
|
1692
|
+
error: {
|
|
1693
|
+
code: error.code,
|
|
1694
|
+
message: error.message,
|
|
1695
|
+
details: error.details
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
function resolveBase() {
|
|
1701
|
+
const location = globalThis.location;
|
|
1702
|
+
return location?.origin !== void 0 && location.origin !== "null" ? location.origin : INTERNAL_BASE;
|
|
1703
|
+
}
|
|
1704
|
+
function assertTarget(channel, endpoint) {
|
|
1705
|
+
const segments = endpoint.split("/");
|
|
1706
|
+
if (!CHANNEL_PATTERN.test(channel) || segments.some((segment) => segment === "" || segment === "." || segment === ".." || !ENDPOINT_SEGMENT_PATTERN.test(segment))) throw new Error(`connection: invalid RPC target ${JSON.stringify(`${channel}/${endpoint}`)}`);
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Create the browser-backed generic RPC caller for the plugin's channel
|
|
1710
|
+
* (the `/tool-plus` host registration). Wire-compatible with the official
|
|
1711
|
+
* ClientConnectionRpc contract; only the transport override differs.
|
|
1712
|
+
* @param doFetch - transport override; defaults to the page's global fetch.
|
|
1713
|
+
*/
|
|
1714
|
+
function createWebConnectionRpc(doFetch) {
|
|
1715
|
+
const send = doFetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
1716
|
+
return { async call(channel, endpoint, payload, signal) {
|
|
1717
|
+
assertTarget(channel, endpoint);
|
|
1718
|
+
const rpcId = (0, _deepseek_ai_dsh_client_connection_client.RpcId)(randomUuid());
|
|
1719
|
+
const response = await send(new URL(`${channel}/${endpoint}`, resolveBase()), {
|
|
1720
|
+
method: "POST",
|
|
1721
|
+
headers: { "content-type": "application/json" },
|
|
1722
|
+
body: JSON.stringify({
|
|
1723
|
+
type: "client-request",
|
|
1724
|
+
rpcId,
|
|
1725
|
+
method: endpoint,
|
|
1726
|
+
payload
|
|
1727
|
+
}),
|
|
1728
|
+
...signal === void 0 ? {} : { signal }
|
|
1729
|
+
});
|
|
1730
|
+
if (!response.ok) throw new Error(`transport failure for ${channel}/${endpoint}: HTTP ${response.status}`);
|
|
1731
|
+
const full = parseConnectionResponse(await response.json());
|
|
1732
|
+
if (full.rpcId !== rpcId) throw new Error(`rpcId mismatch for ${endpoint}: sent ${rpcId}, got ${full.rpcId}`);
|
|
1733
|
+
return full.result;
|
|
1734
|
+
} };
|
|
1735
|
+
}
|
|
1736
|
+
//#endregion
|
|
1476
1737
|
//#region src/client/forms.ts
|
|
1477
1738
|
/**
|
|
1478
1739
|
* Generic staged-form model over the plugin's flat settings namespace,
|
|
@@ -1643,7 +1904,7 @@ window.__ModuleLoader__.load({
|
|
|
1643
1904
|
}
|
|
1644
1905
|
if (field.kind === "number") group.numbers.push(renderNumber(field, numbers, value, snap, defaults));
|
|
1645
1906
|
else if (field.kind === "select") group.selects.push(renderSelect(field, selects, value, snap, defaults));
|
|
1646
|
-
else group.toggles.push(renderToggle(field, toggles, value, snap, defaults));
|
|
1907
|
+
else if (field.kind === "boolean") group.toggles.push(renderToggle(field, toggles, value, snap, defaults));
|
|
1647
1908
|
}
|
|
1648
1909
|
return order.map((key) => map.get(key));
|
|
1649
1910
|
}, [
|
|
@@ -1780,40 +2041,76 @@ window.__ModuleLoader__.load({
|
|
|
1780
2041
|
* without keeping hidden panels mounted — the same flat schema the Bash card
|
|
1781
2042
|
* edits, from the same single-source field table.
|
|
1782
2043
|
*
|
|
2044
|
+
* `action`-kind fields (e.g. 探测浏览器) are not part of the staged form:
|
|
2045
|
+
* they run a host RPC endpoint over the official Connection channel
|
|
2046
|
+
* (`/tool-plus`), with per-field running/result state kept at the page level.
|
|
2047
|
+
*
|
|
1783
2048
|
* Tab chrome mirrors the official Plugins settings section
|
|
1784
2049
|
* (ui-settings-plugins): `role="tablist"` + tab/panel roles, aria wiring,
|
|
1785
2050
|
* and arrow/Home/End keyboard roaming.
|
|
2051
|
+
*
|
|
2052
|
+
* The page is pinned to the host settings pane: the heading, intro and tablist
|
|
2053
|
+
* stay put, the save bar sits at the bottom, and the tab panel is the only
|
|
2054
|
+
* scroller — so the scrollbar spans just the band between them.
|
|
1786
2055
|
* @module @xiaoso/dsh-tool-plus/client
|
|
1787
2056
|
*/
|
|
1788
|
-
/**
|
|
2057
|
+
/**
|
|
2058
|
+
* Page CSS, keyed by `data-plugin-css` and injected once.
|
|
2059
|
+
*
|
|
2060
|
+
* Design language: official settings-section rhythm (18/600 heading, 13px
|
|
2061
|
+
* tertiary intro, 760px content column) on the host's `--dsw-alias-*`
|
|
2062
|
+
* tokens, so both themes adapt automatically. Tab chrome is the official
|
|
2063
|
+
* underline tablist (like the Plugins section), upgraded with a single
|
|
2064
|
+
* sliding brand-blue underline; group cards float above the modal surface
|
|
2065
|
+
* (layer-3). No backdrop-filter anywhere — solid token colors only.
|
|
2066
|
+
* All motion is transform/opacity only (GPU-composited): the underline
|
|
2067
|
+
* FLIP is driven from JS, entrance stagger and panel fades are CSS
|
|
2068
|
+
* keyframes, and the unsaved dot pulses opacity/scale. Everything shuts
|
|
2069
|
+
* off under prefers-reduced-motion (CSS here, matchMedia for the
|
|
2070
|
+
* JS-driven underline).
|
|
2071
|
+
*/
|
|
1789
2072
|
const CSS = `
|
|
1790
|
-
.tps{display:flex;flex-direction:column;gap:16px;padding:
|
|
1791
|
-
.tps-heading{margin:0;font-size:18px;font-weight:600;line-height:26px;color:var(--dsw-alias-label-primary)}
|
|
1792
|
-
.tps-intro{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
|
|
1793
|
-
.tps-tabs{display:flex;flex-
|
|
1794
|
-
.tps-tab{appearance:none;border:none;background:none;font:inherit;font-size:13px;line-height:20px;color:var(--dsw-alias-label-
|
|
1795
|
-
.tps-tab:hover:not([data-active="true"]){
|
|
1796
|
-
.tps-tab[data-active="true"]{
|
|
1797
|
-
.tps-tab:focus-visible{outline:2px solid var(--dsw-alias-
|
|
1798
|
-
.tps-
|
|
1799
|
-
.tps-
|
|
1800
|
-
.tps-
|
|
1801
|
-
.tps-
|
|
2073
|
+
.tps-section{box-sizing:border-box;display:flex;flex-direction:column;gap:16px;padding-top:2px}
|
|
2074
|
+
.tps-heading{margin:0;font-size:18px;font-weight:600;line-height:26px;color:var(--dsw-alias-label-primary);animation:tps-rise .38s cubic-bezier(.22,1,.36,1) both}
|
|
2075
|
+
.tps-intro{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary);max-width:64ch;animation:tps-rise .38s cubic-bezier(.22,1,.36,1) .05s both}
|
|
2076
|
+
.tps-tabs{position:relative;display:flex;align-items:flex-end;gap:22px;border-bottom:1px solid var(--dsw-alias-border-l2);margin-top:2px;animation:tps-rise .38s cubic-bezier(.22,1,.36,1) .1s both}
|
|
2077
|
+
.tps-tab{position:relative;appearance:none;border:none;background:none;font:inherit;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary);padding:7px 1px 9px;cursor:pointer;white-space:nowrap;transition:color .16s ease}
|
|
2078
|
+
.tps-tab:hover:not([data-active="true"]){color:var(--dsw-alias-label-primary)}
|
|
2079
|
+
.tps-tab[data-active="true"]{color:var(--dsw-alias-label-primary)}
|
|
2080
|
+
.tps-tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;border-radius:2px}
|
|
2081
|
+
.tps-tabIndicator{position:absolute;bottom:-1px;left:0;height:2px;border-radius:2px 2px 0 0;background:var(--dsw-alias-state-business-primary);pointer-events:none;transform-origin:left center;will-change:transform}
|
|
2082
|
+
.tps-panel{display:flex;flex:1 1 auto;flex-direction:column;gap:14px;min-width:0;min-height:0;overflow-y:auto;overscroll-behavior:contain;scrollbar-gutter:stable;scrollbar-width:thin;animation:tps-panel-in .24s cubic-bezier(.22,1,.36,1) both}
|
|
2083
|
+
.tps-panel::-webkit-scrollbar{width:10px}
|
|
2084
|
+
.tps-panel::-webkit-scrollbar-track{background:transparent}
|
|
2085
|
+
.tps-panel::-webkit-scrollbar-thumb{border:3px solid transparent;border-radius:999px;background-color:color-mix(in srgb, var(--dsw-alias-label-tertiary) 45%, transparent);background-clip:content-box}
|
|
2086
|
+
.tps-panel::-webkit-scrollbar-thumb:hover{background-color:color-mix(in srgb, var(--dsw-alias-label-tertiary) 75%, transparent)}
|
|
2087
|
+
.tps-group{display:flex;flex-direction:column}
|
|
2088
|
+
.tps-groupTitle{font-size:13px;font-weight:600;line-height:20px;color:var(--dsw-alias-label-primary);margin:0 0 6px;padding:0 2px}
|
|
2089
|
+
.tps-card{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3);padding:0 16px}
|
|
2090
|
+
.tps-empty{border:1px dashed var(--dsw-alias-border-l3);border-radius:12px;padding:24px 16px;display:flex;flex-direction:column;align-items:center;text-align:center;gap:4px}
|
|
1802
2091
|
.tps-emptyTitle{margin:0;font-size:14px;font-weight:600;line-height:22px;color:var(--dsw-alias-label-primary)}
|
|
1803
2092
|
.tps-emptyHint{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
|
|
1804
|
-
.tps-readOnly{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
|
|
1805
|
-
.tps-footer{display:flex;align-items:center;justify-content:flex-end;gap:
|
|
2093
|
+
.tps-readOnly{margin:0;padding:8px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-3);font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
|
|
2094
|
+
.tps-footer{flex:none;display:flex;align-items:center;justify-content:flex-end;gap:10px;padding:12px 0;border-top:1px solid var(--dsw-alias-border-l2)}
|
|
1806
2095
|
.tps-failed{flex:1;min-width:0;margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-error)}
|
|
1807
2096
|
.tps-applies{flex:1;min-width:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
|
|
1808
|
-
.tps-
|
|
2097
|
+
.tps-unsaved{display:inline-flex;align-items:center;gap:6px;flex:none;border-radius:999px;padding:2px 10px;font-size:11px;line-height:17px;font-weight:500;color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary)}
|
|
2098
|
+
.tps-unsavedDot{flex:none;width:6px;height:6px;border-radius:999px;background:var(--dsw-alias-state-business-primary);animation:tps-pulse 1.8s ease-in-out infinite}
|
|
2099
|
+
.tps-discard,.tps-save{appearance:none;border:1px solid transparent;border-radius:8px;padding:5px 14px;font:inherit;font-size:13px;line-height:1.5;cursor:pointer;transition:transform .1s ease,background .16s ease,border-color .16s ease,color .16s ease}
|
|
1809
2100
|
.tps-discard:active:not(:disabled),.tps-save:active:not(:disabled){transform:scale(.97)}
|
|
1810
2101
|
.tps-discard{border-color:var(--dsw-alias-border-l2);background:none;color:var(--dsw-alias-label-secondary)}
|
|
1811
|
-
.tps-discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-
|
|
2102
|
+
.tps-discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l4)}
|
|
1812
2103
|
.tps-save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
|
|
2104
|
+
.tps-save:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}
|
|
1813
2105
|
.tps-discard:disabled,.tps-save:disabled{opacity:.4;cursor:default}
|
|
1814
|
-
.tps-discard:focus-visible,.tps-save:focus-visible{outline:2px solid var(--dsw-alias-
|
|
2106
|
+
.tps-discard:focus-visible,.tps-save:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
|
|
2107
|
+
@keyframes tps-rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
|
2108
|
+
@keyframes tps-panel-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
|
|
2109
|
+
@keyframes tps-pulse{0%,100%{opacity:.35;transform:scale(.85)}50%{opacity:1;transform:scale(1)}}
|
|
1815
2110
|
@media (prefers-reduced-motion: reduce){
|
|
2111
|
+
.tps-heading,.tps-intro,.tps-tabs,.tps-panel,.tps-unsavedDot{animation:none}
|
|
1816
2112
|
.tps-tab,.tps-discard,.tps-save{transition:none}
|
|
2113
|
+
.tps-tabIndicator{will-change:auto}
|
|
1817
2114
|
}
|
|
1818
2115
|
`;
|
|
1819
2116
|
/** Inject the section stylesheet once per page. */
|
|
@@ -1833,6 +2130,17 @@ window.__ModuleLoader__.load({
|
|
|
1833
2130
|
const TOOL_TABS = TOOL_PLUS_TABS;
|
|
1834
2131
|
/** All fields the page edits (one shared form). */
|
|
1835
2132
|
const ALL_FIELDS = TOOL_PLUS_FIELDS;
|
|
2133
|
+
/** Floor for the pinned page height, so an unusually short pane never collapses the panel. */
|
|
2134
|
+
const MIN_PAGE_HEIGHT = 240;
|
|
2135
|
+
/**
|
|
2136
|
+
* Nearest ancestor that scrolls vertically — the host settings pane hosting
|
|
2137
|
+
* this section. The pane is app chrome with no stable class, so it is found by
|
|
2138
|
+
* walking up from the section root.
|
|
2139
|
+
*/
|
|
2140
|
+
function scrollPaneOf(element) {
|
|
2141
|
+
for (let node = element.parentElement; node !== null; node = node.parentElement) if (/(auto|scroll|overlay)/.test(getComputedStyle(node).overflowY)) return node;
|
|
2142
|
+
return null;
|
|
2143
|
+
}
|
|
1836
2144
|
/** Current effective value of a boolean/select control (staged or resolved). */
|
|
1837
2145
|
function controlValue(form, field) {
|
|
1838
2146
|
for (const group of form.groups) {
|
|
@@ -1853,10 +2161,11 @@ window.__ModuleLoader__.load({
|
|
|
1853
2161
|
* Render one tool tab's panel content: fields in table order (master
|
|
1854
2162
|
* switches first, per their declaration order), grouped under their group
|
|
1855
2163
|
* heading, or the no-config placeholder for tools without global settings.
|
|
1856
|
-
* Fields whose visibility condition is unmet are omitted.
|
|
2164
|
+
* Fields whose visibility condition is unmet are omitted. `action` fields
|
|
2165
|
+
* render their own button row (host RPC via the caller's `run`).
|
|
1857
2166
|
*/
|
|
1858
2167
|
function ToolTabPanel(props) {
|
|
1859
|
-
const { tab, t, form, writable, openSelect, onOpenChange } = props;
|
|
2168
|
+
const { tab, t, form, writable, openSelect, onOpenChange, actionState, actionResults, runAction } = props;
|
|
1860
2169
|
if (tab.fields.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1861
2170
|
className: "tps-empty",
|
|
1862
2171
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -1904,32 +2213,51 @@ window.__ModuleLoader__.load({
|
|
|
1904
2213
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
|
|
1905
2214
|
className: "tps-groupTitle",
|
|
1906
2215
|
children: t(TOOL_PLUS_GROUP_LABELS[group.group] ?? "title")
|
|
1907
|
-
}),
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2216
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2217
|
+
className: "tps-card",
|
|
2218
|
+
children: group.fields.map((field) => {
|
|
2219
|
+
if (field.kind === "action") {
|
|
2220
|
+
const actionControl = {
|
|
2221
|
+
field: field.name,
|
|
2222
|
+
labelKey: field.labelKey,
|
|
2223
|
+
hintKey: field.hintKey,
|
|
2224
|
+
actionKey: field.actionKey ?? ""
|
|
2225
|
+
};
|
|
2226
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionRow, {
|
|
2227
|
+
t,
|
|
2228
|
+
control: actionControl,
|
|
2229
|
+
disabled: !writable,
|
|
2230
|
+
running: actionState[field.name] === "running",
|
|
2231
|
+
result: actionResults[field.name] ?? null,
|
|
2232
|
+
onRun: (_actionKey) => runAction(field)
|
|
2233
|
+
}, field.name);
|
|
2234
|
+
}
|
|
2235
|
+
const entry = controlOf(field);
|
|
2236
|
+
if (entry === void 0) return null;
|
|
2237
|
+
if (entry.kind === "number") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberRow, {
|
|
2238
|
+
t,
|
|
2239
|
+
control: entry.control,
|
|
2240
|
+
disabled: !writable,
|
|
2241
|
+
onEdit: form.actions.edit,
|
|
2242
|
+
onReset: form.actions.resetField
|
|
2243
|
+
}, field.name);
|
|
2244
|
+
if (entry.kind === "select") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectRow, {
|
|
2245
|
+
t,
|
|
2246
|
+
control: entry.control,
|
|
2247
|
+
disabled: !writable,
|
|
2248
|
+
open: openSelect === field.name,
|
|
2249
|
+
onOpenChange,
|
|
2250
|
+
onEdit: form.editSelect,
|
|
2251
|
+
onReset: form.actions.resetField
|
|
2252
|
+
}, field.name);
|
|
2253
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
|
|
2254
|
+
t,
|
|
2255
|
+
control: entry.control,
|
|
2256
|
+
disabled: !writable,
|
|
2257
|
+
onToggle: form.onToggle,
|
|
2258
|
+
onReset: form.actions.resetField
|
|
2259
|
+
}, field.name);
|
|
2260
|
+
})
|
|
1933
2261
|
})]
|
|
1934
2262
|
}, group.group)) });
|
|
1935
2263
|
}
|
|
@@ -1937,20 +2265,147 @@ window.__ModuleLoader__.load({
|
|
|
1937
2265
|
function ToolPlusSection(props) {
|
|
1938
2266
|
const { t, scope } = props;
|
|
1939
2267
|
const form = useToolForm(scope, ALL_FIELDS);
|
|
2268
|
+
const sectionRef = (0, react.useRef)(null);
|
|
1940
2269
|
const tabsId = (0, react.useRef)(`tps-${Math.random().toString(36).slice(2, 8)}`);
|
|
1941
2270
|
const tabRefs = (0, react.useRef)([]);
|
|
1942
2271
|
const [activeId, setActiveId] = (0, react.useState)();
|
|
1943
2272
|
const [openSelect, setOpenSelect] = (0, react.useState)(null);
|
|
2273
|
+
const [actionState, setActionState] = (0, react.useState)({});
|
|
2274
|
+
const [actionResults, setActionResults] = (0, react.useState)({});
|
|
2275
|
+
const runAction = (0, react.useCallback)((field) => {
|
|
2276
|
+
const actionKey = field.actionKey;
|
|
2277
|
+
if (!actionKey) return;
|
|
2278
|
+
setActionState((prev) => ({
|
|
2279
|
+
...prev,
|
|
2280
|
+
[field.name]: "running"
|
|
2281
|
+
}));
|
|
2282
|
+
setActionResults((prev) => ({
|
|
2283
|
+
...prev,
|
|
2284
|
+
[field.name]: null
|
|
2285
|
+
}));
|
|
2286
|
+
(async () => {
|
|
2287
|
+
try {
|
|
2288
|
+
const result = await createWebConnectionRpc().call(TOOL_PLUS_RPC_CHANNEL, actionKey, {});
|
|
2289
|
+
if (!result.ok) setActionResults((prev) => ({
|
|
2290
|
+
...prev,
|
|
2291
|
+
[field.name]: {
|
|
2292
|
+
ok: false,
|
|
2293
|
+
text: result.error.message
|
|
2294
|
+
}
|
|
2295
|
+
}));
|
|
2296
|
+
else {
|
|
2297
|
+
const found = result.value?.found ?? [];
|
|
2298
|
+
const text = found.length === 0 ? t("browserProbeNone") : found.map((browser) => `${browser.name}: ${browser.path}`).join("\n");
|
|
2299
|
+
setActionResults((prev) => ({
|
|
2300
|
+
...prev,
|
|
2301
|
+
[field.name]: {
|
|
2302
|
+
ok: found.length > 0,
|
|
2303
|
+
text
|
|
2304
|
+
}
|
|
2305
|
+
}));
|
|
2306
|
+
}
|
|
2307
|
+
} catch {
|
|
2308
|
+
setActionResults((prev) => ({
|
|
2309
|
+
...prev,
|
|
2310
|
+
[field.name]: {
|
|
2311
|
+
ok: false,
|
|
2312
|
+
text: t("browserProbeUnavailable")
|
|
2313
|
+
}
|
|
2314
|
+
}));
|
|
2315
|
+
} finally {
|
|
2316
|
+
setActionState((prev) => ({
|
|
2317
|
+
...prev,
|
|
2318
|
+
[field.name]: "done"
|
|
2319
|
+
}));
|
|
2320
|
+
}
|
|
2321
|
+
})();
|
|
2322
|
+
}, [t]);
|
|
1944
2323
|
(0, react.useEffect)(() => {
|
|
1945
2324
|
injectSectionCss();
|
|
1946
2325
|
injectSettingsRowsCss();
|
|
1947
2326
|
}, []);
|
|
1948
|
-
if (!form.shell.available) return null;
|
|
1949
2327
|
const active = TOOL_TABS.find((row) => row.id === activeId)?.id ?? TOOL_TABS[0]?.id;
|
|
2328
|
+
const tabsRef = (0, react.useRef)(null);
|
|
2329
|
+
const indicatorRef = (0, react.useRef)(null);
|
|
2330
|
+
const committedRect = (0, react.useRef)(null);
|
|
2331
|
+
const commitIndicator = (0, react.useCallback)((animate) => {
|
|
2332
|
+
const tabs = tabsRef.current;
|
|
2333
|
+
const indicator = indicatorRef.current;
|
|
2334
|
+
if (tabs === null || indicator === null || active === void 0) return;
|
|
2335
|
+
const tab = tabs.querySelector(`[data-tps-tab="${active}"]`);
|
|
2336
|
+
if (tab === null) return;
|
|
2337
|
+
const left = tab.offsetLeft;
|
|
2338
|
+
const width = tab.offsetWidth;
|
|
2339
|
+
const previous = committedRect.current;
|
|
2340
|
+
committedRect.current = {
|
|
2341
|
+
left,
|
|
2342
|
+
width
|
|
2343
|
+
};
|
|
2344
|
+
if (previous !== null && previous.left === left && previous.width === width) return;
|
|
2345
|
+
const reduce = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
2346
|
+
if (!animate || reduce || previous === null) {
|
|
2347
|
+
indicator.style.transition = "none";
|
|
2348
|
+
indicator.style.width = `${width}px`;
|
|
2349
|
+
indicator.style.transform = `translateX(${left}px)`;
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
indicator.style.transition = "none";
|
|
2353
|
+
indicator.style.width = `${width}px`;
|
|
2354
|
+
indicator.style.transform = `translateX(${previous.left}px) scaleX(${previous.width / width})`;
|
|
2355
|
+
indicator.offsetWidth;
|
|
2356
|
+
indicator.style.transition = "transform .24s cubic-bezier(.22,1,.36,1)";
|
|
2357
|
+
indicator.style.transform = `translateX(${left}px) scaleX(1)`;
|
|
2358
|
+
}, [active]);
|
|
2359
|
+
(0, react.useLayoutEffect)(() => {
|
|
2360
|
+
commitIndicator(true);
|
|
2361
|
+
}, [commitIndicator]);
|
|
2362
|
+
const commitRef = (0, react.useRef)(commitIndicator);
|
|
2363
|
+
commitRef.current = commitIndicator;
|
|
2364
|
+
(0, react.useEffect)(() => {
|
|
2365
|
+
const tabs = tabsRef.current;
|
|
2366
|
+
if (tabs === null || typeof ResizeObserver === "undefined") return;
|
|
2367
|
+
const observer = new ResizeObserver(() => commitRef.current(false));
|
|
2368
|
+
observer.observe(tabs);
|
|
2369
|
+
for (const tab of Array.from(tabs.querySelectorAll("[data-tps-tab]"))) observer.observe(tab);
|
|
2370
|
+
return () => {
|
|
2371
|
+
observer.disconnect();
|
|
2372
|
+
};
|
|
2373
|
+
}, []);
|
|
2374
|
+
(0, react.useLayoutEffect)(() => {
|
|
2375
|
+
const root = sectionRef.current;
|
|
2376
|
+
if (root === null || typeof ResizeObserver === "undefined") return;
|
|
2377
|
+
const pane = scrollPaneOf(root);
|
|
2378
|
+
if (pane === null) return;
|
|
2379
|
+
let frame = 0;
|
|
2380
|
+
const measure = () => {
|
|
2381
|
+
frame = 0;
|
|
2382
|
+
const style = getComputedStyle(pane);
|
|
2383
|
+
const paddingTop = parseFloat(style.paddingTop) || 0;
|
|
2384
|
+
const contentHeight = pane.clientHeight - paddingTop - (parseFloat(style.paddingBottom) || 0);
|
|
2385
|
+
const offset = root.getBoundingClientRect().top - pane.getBoundingClientRect().top - (parseFloat(style.borderTopWidth) || 0) - paddingTop + pane.scrollTop;
|
|
2386
|
+
const next = `${Math.max(MIN_PAGE_HEIGHT, Math.round(contentHeight - offset))}px`;
|
|
2387
|
+
if (root.style.height !== next) root.style.height = next;
|
|
2388
|
+
};
|
|
2389
|
+
const schedule = () => {
|
|
2390
|
+
if (frame === 0) frame = requestAnimationFrame(measure);
|
|
2391
|
+
};
|
|
2392
|
+
measure();
|
|
2393
|
+
const observer = new ResizeObserver(schedule);
|
|
2394
|
+
observer.observe(pane);
|
|
2395
|
+
window.addEventListener("resize", schedule);
|
|
2396
|
+
return () => {
|
|
2397
|
+
if (frame !== 0) cancelAnimationFrame(frame);
|
|
2398
|
+
observer.disconnect();
|
|
2399
|
+
window.removeEventListener("resize", schedule);
|
|
2400
|
+
root.style.height = "";
|
|
2401
|
+
};
|
|
2402
|
+
}, [form.shell.available]);
|
|
2403
|
+
if (!form.shell.available) return null;
|
|
1950
2404
|
const selectTab = (id) => setActiveId(id);
|
|
1951
2405
|
const blocked = !form.shell.dirty || form.shell.invalid || form.shell.saving;
|
|
1952
2406
|
const writable = form.shell.writable;
|
|
1953
2407
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2408
|
+
ref: sectionRef,
|
|
1954
2409
|
className: "tps-section",
|
|
1955
2410
|
children: [
|
|
1956
2411
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
@@ -1961,11 +2416,16 @@ window.__ModuleLoader__.load({
|
|
|
1961
2416
|
className: "tps-intro",
|
|
1962
2417
|
children: t("pageDescription")
|
|
1963
2418
|
}),
|
|
1964
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2419
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2420
|
+
ref: tabsRef,
|
|
1965
2421
|
className: "tps-tabs",
|
|
1966
2422
|
role: "tablist",
|
|
1967
2423
|
"aria-label": t("pageTitle"),
|
|
1968
|
-
children:
|
|
2424
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2425
|
+
ref: indicatorRef,
|
|
2426
|
+
className: "tps-tabIndicator",
|
|
2427
|
+
"aria-hidden": "true"
|
|
2428
|
+
}), TOOL_TABS.map((row, index) => {
|
|
1969
2429
|
const selected = row.id === active;
|
|
1970
2430
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1971
2431
|
ref: (element) => {
|
|
@@ -1978,6 +2438,7 @@ window.__ModuleLoader__.load({
|
|
|
1978
2438
|
"aria-selected": selected,
|
|
1979
2439
|
"aria-controls": `${tabsId.current}-panel-${row.id}`,
|
|
1980
2440
|
"data-active": selected ? "true" : void 0,
|
|
2441
|
+
"data-tps-tab": row.id,
|
|
1981
2442
|
tabIndex: selected ? 0 : -1,
|
|
1982
2443
|
onClick: () => selectTab(row.id),
|
|
1983
2444
|
onKeyDown: (event) => {
|
|
@@ -2005,7 +2466,7 @@ window.__ModuleLoader__.load({
|
|
|
2005
2466
|
},
|
|
2006
2467
|
children: t(row.labelKey)
|
|
2007
2468
|
}, row.id);
|
|
2008
|
-
})
|
|
2469
|
+
})]
|
|
2009
2470
|
}),
|
|
2010
2471
|
TOOL_TABS.filter((row) => row.id === active).map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2011
2472
|
id: `${tabsId.current}-panel-${row.id}`,
|
|
@@ -2022,7 +2483,10 @@ window.__ModuleLoader__.load({
|
|
|
2022
2483
|
form,
|
|
2023
2484
|
writable,
|
|
2024
2485
|
openSelect,
|
|
2025
|
-
onOpenChange: setOpenSelect
|
|
2486
|
+
onOpenChange: setOpenSelect,
|
|
2487
|
+
actionState,
|
|
2488
|
+
actionResults,
|
|
2489
|
+
runAction
|
|
2026
2490
|
})]
|
|
2027
2491
|
}, row.id)),
|
|
2028
2492
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2037,6 +2501,14 @@ window.__ModuleLoader__.load({
|
|
|
2037
2501
|
className: "tps-applies",
|
|
2038
2502
|
children: t("appliesTo")
|
|
2039
2503
|
}) : null,
|
|
2504
|
+
!form.shell.failed && form.shell.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2505
|
+
className: "tps-unsaved",
|
|
2506
|
+
role: "status",
|
|
2507
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2508
|
+
className: "tps-unsavedDot",
|
|
2509
|
+
"aria-hidden": "true"
|
|
2510
|
+
}), t("unsaved")]
|
|
2511
|
+
}) : null,
|
|
2040
2512
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2041
2513
|
type: "button",
|
|
2042
2514
|
className: "tps-discard",
|
|
@@ -2104,6 +2576,23 @@ window.__ModuleLoader__.load({
|
|
|
2104
2576
|
fetchEnabledHint: "read 处理 http(s) URL(网页转 Markdown 供模型阅读)时是否启用抓取。",
|
|
2105
2577
|
fetchMaxTimeoutSeconds: "抓取超时(秒)",
|
|
2106
2578
|
fetchMaxTimeoutSecondsHint: "URL 抓取超时上限;0 表示使用默认的 30 秒。",
|
|
2579
|
+
fetchReader: "网页转 Markdown 后端",
|
|
2580
|
+
fetchReaderHint: "优先使用的网页正文提取后端;auto 按内置顺序依次尝试(含浏览器 JS 渲染)。设为具体后端时,该后端优先,其余按原顺序兜底。",
|
|
2581
|
+
browserReaderEnabled: "浏览器 JS 渲染",
|
|
2582
|
+
browserReaderEnabledHint: "启用本地无头浏览器渲染(SPA 页面需执行 JavaScript 才能出正文)。未安装可用浏览器时自动跳过,不影响其他后端。",
|
|
2583
|
+
browserProbe: "探测浏览器",
|
|
2584
|
+
browserProbeHint: "检测本机可用的浏览器(Edge / Chrome / Chromium),read 的浏览器渲染将使用第一个命中的。",
|
|
2585
|
+
browserProbeRunning: "探测中…",
|
|
2586
|
+
browserProbeNone: "未找到可用浏览器:URL 渲染将跳过浏览器后端。",
|
|
2587
|
+
browserProbeFailed: "探测失败:",
|
|
2588
|
+
browserProbeUnavailable: "探测不可用:当前部署未启用浏览器 RPC 通道。",
|
|
2589
|
+
optReaderAuto: "auto(自动)",
|
|
2590
|
+
optReaderNative: "native(内置转换)",
|
|
2591
|
+
optReaderTrafilatura: "trafilatura",
|
|
2592
|
+
optReaderLynx: "lynx",
|
|
2593
|
+
optReaderParallel: "Parallel Reader",
|
|
2594
|
+
optReaderJina: "Jina Reader",
|
|
2595
|
+
optReaderBrowser: "browser(浏览器渲染)",
|
|
2107
2596
|
groupFetch: "抓取",
|
|
2108
2597
|
groupImages: "图片",
|
|
2109
2598
|
imagesAutoResize: "图片自动缩放",
|
|
@@ -2258,6 +2747,23 @@ window.__ModuleLoader__.load({
|
|
|
2258
2747
|
fetchEnabledHint: "Whether fetching is enabled when read handles http(s) URLs.",
|
|
2259
2748
|
fetchMaxTimeoutSeconds: "Fetch timeout (s)",
|
|
2260
2749
|
fetchMaxTimeoutSecondsHint: "URL fetch timeout cap; 0 = default 30s.",
|
|
2750
|
+
fetchReader: "HTML-to-Markdown backend",
|
|
2751
|
+
fetchReaderHint: "Preferred reader backend for extracting page bodies; auto tries the built-in order (including browser JS rendering). A concrete backend goes first, the rest fall back in order.",
|
|
2752
|
+
browserReaderEnabled: "Browser JS rendering",
|
|
2753
|
+
browserReaderEnabledHint: "Render pages in a local headless browser (SPA pages need JavaScript to produce a body). Skipped automatically when no usable browser is installed.",
|
|
2754
|
+
browserProbe: "Probe browsers",
|
|
2755
|
+
browserProbeHint: "Detect usable browsers on this machine (Edge / Chrome / Chromium); read’s browser rendering uses the first hit.",
|
|
2756
|
+
browserProbeRunning: "Probing…",
|
|
2757
|
+
browserProbeNone: "No usable browser found: URL rendering will skip the browser backend.",
|
|
2758
|
+
browserProbeFailed: "Probe failed: ",
|
|
2759
|
+
browserProbeUnavailable: "Probing unavailable: this deployment does not enable the browser RPC channel.",
|
|
2760
|
+
optReaderAuto: "auto",
|
|
2761
|
+
optReaderNative: "native (built-in conversion)",
|
|
2762
|
+
optReaderTrafilatura: "trafilatura",
|
|
2763
|
+
optReaderLynx: "lynx",
|
|
2764
|
+
optReaderParallel: "Parallel Reader",
|
|
2765
|
+
optReaderJina: "Jina Reader",
|
|
2766
|
+
optReaderBrowser: "browser (JS rendering)",
|
|
2261
2767
|
groupFetch: "Fetching",
|
|
2262
2768
|
groupImages: "Images",
|
|
2263
2769
|
imagesAutoResize: "Auto-resize images",
|