dsh-config-manager 0.1.38 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -253,16 +253,6 @@ const uiZh = {
253
253
  "market.detail.sectionsEmpty": "未声明分区",
254
254
  "market.detail.statusValid": "校验通过",
255
255
  "market.detail.statusInvalid": "校验未通过",
256
- "marketPublish.stepSelect": "选择配置包",
257
- "marketPublish.stepValidate": "本地校验",
258
- "marketPublish.stepPrepare": "生成条目包",
259
- "marketPublish.stepPush": "推送作者仓库",
260
- "marketPublish.stepSubmit": "提交收录申请",
261
- "marketPublish.errorItemId": "条目 id 仅允许字母数字开头,字符限 . _ -(最长 128)",
262
- "marketPublish.errorName": "条目名称不能为空",
263
- "marketPublish.repoWhitespace": "仓库地址不能包含空白字符",
264
- "marketPublish.repoScheme": "仓库地址必须为 http(s) 链接",
265
- "marketPublish.repoUserinfo": "仓库地址不能包含用户名或密码(userinfo);凭据请通过 DSH credentials 提供",
266
256
  "myConfigs.autoField": "系统自动",
267
257
  "myConfigs.field.id": "条目 ID",
268
258
  "myConfigs.field.author": "作者",
@@ -507,16 +497,6 @@ const uiEn = {
507
497
  "market.detail.sectionsEmpty": "No sections declared",
508
498
  "market.detail.statusValid": "Verified",
509
499
  "market.detail.statusInvalid": "Failed verification",
510
- "marketPublish.stepSelect": "Select config package",
511
- "marketPublish.stepValidate": "Local validation",
512
- "marketPublish.stepPrepare": "Generate item package",
513
- "marketPublish.stepPush": "Push to author repo",
514
- "marketPublish.stepSubmit": "Submit listing request",
515
- "marketPublish.errorItemId": "Item id must start with a letter or digit; only . _ - allowed (max 128)",
516
- "marketPublish.errorName": "Item name is required",
517
- "marketPublish.repoWhitespace": "Repository URL must not contain whitespace",
518
- "marketPublish.repoScheme": "Repository URL must be an http(s) link",
519
- "marketPublish.repoUserinfo": "Repository URL must not contain username/password (userinfo); provide credentials via DSH credentials",
520
500
  "myConfigs.autoField": "Auto",
521
501
  "myConfigs.field.id": "Item ID",
522
502
  "myConfigs.field.author": "Author",
@@ -2930,111 +2910,111 @@ if (typeof document !== "undefined" && document.querySelector("style[data-tag=\"
2930
2910
  document.head.appendChild(tag);
2931
2911
  }
2932
2912
  var config_manager_module_css_default = {
2933
- "confirmScroll": "_367UGW_confirmScroll",
2934
- "kindTag": "_367UGW_kindTag",
2913
+ "warnText": "_367UGW_warnText",
2914
+ "progressPercent": "_367UGW_progressPercent",
2915
+ "planScroll": "_367UGW_planScroll",
2935
2916
  "pullScroll": "_367UGW_pullScroll",
2936
- "formError": "_367UGW_formError",
2937
- "pathOld": "_367UGW_pathOld",
2938
- "hint": "_367UGW_hint",
2939
- "viewBody": "_367UGW_viewBody",
2940
- "errorList": "_367UGW_errorList",
2941
- "snapshotRowHeader": "_367UGW_snapshotRowHeader",
2942
- "reportList": "_367UGW_reportList",
2943
- "errorBanner": "_367UGW_errorBanner",
2944
- "viewTabs": "_367UGW_viewTabs",
2945
- "section": "_367UGW_section",
2946
- "badgeOk": "_367UGW_badgeOk",
2947
- "groupItems": "_367UGW_groupItems",
2917
+ "pathIssueKind": "_367UGW_pathIssueKind",
2918
+ "reportScroll": "_367UGW_reportScroll",
2919
+ "primaryButton": "_367UGW_primaryButton",
2920
+ "progressMeta": "_367UGW_progressMeta",
2921
+ "errorLine": "_367UGW_errorLine",
2922
+ "progressBadgeSection": "_367UGW_progressBadgeSection",
2923
+ "reportView": "_367UGW_reportView",
2924
+ "progressBadge": "_367UGW_progressBadge",
2925
+ "snapshotRow": "_367UGW_snapshotRow",
2926
+ "groupList": "_367UGW_groupList",
2927
+ "badgeInfo": "_367UGW_badgeInfo",
2928
+ "statRow": "_367UGW_statRow",
2929
+ "sectionSubtitle": "_367UGW_sectionSubtitle",
2930
+ "select": "_367UGW_select",
2948
2931
  "errorReason": "_367UGW_errorReason",
2949
- "diffScroll": "_367UGW_diffScroll",
2950
- "modeHint": "_367UGW_modeHint",
2951
- "dangerButton": "_367UGW_dangerButton",
2952
- "warnList": "_367UGW_warnList",
2953
- "radioLabel": "_367UGW_radioLabel",
2954
- "errorItem": "_367UGW_errorItem",
2932
+ "badgeError": "_367UGW_badgeError",
2955
2933
  "checkboxRow": "_367UGW_checkboxRow",
2956
- "pathMappingList": "_367UGW_pathMappingList",
2957
- "pathIssueKind": "_367UGW_pathIssueKind",
2934
+ "hiddenFile": "_367UGW_hiddenFile",
2935
+ "progressLabel": "_367UGW_progressLabel",
2936
+ "spinner": "_367UGW_spinner",
2937
+ "reportList": "_367UGW_reportList",
2938
+ "warnList": "_367UGW_warnList",
2939
+ "aboutLinkRow": "_367UGW_aboutLinkRow",
2940
+ "banner": "_367UGW_banner",
2941
+ "badgeOk": "_367UGW_badgeOk",
2942
+ "conflictItem": "_367UGW_conflictItem",
2943
+ "errorBanner": "_367UGW_errorBanner",
2944
+ "conflictId": "_367UGW_conflictId",
2945
+ "secretsList": "_367UGW_secretsList",
2946
+ "sectionTitleBlock": "_367UGW_sectionTitleBlock",
2947
+ "input": "_367UGW_input",
2948
+ "spinnerLabel": "_367UGW_spinnerLabel",
2949
+ "progressBarDone": "_367UGW_progressBarDone",
2950
+ "categoryDesc": "_367UGW_categoryDesc",
2951
+ "sectionBody": "_367UGW_sectionBody",
2958
2952
  "badge": "_367UGW_badge",
2953
+ "groupItems": "_367UGW_groupItems",
2954
+ "progressTrack": "_367UGW_progressTrack",
2959
2955
  "conflictDetail": "_367UGW_conflictDetail",
2960
- "ghostButton": "_367UGW_ghostButton",
2961
- "input": "_367UGW_input",
2962
- "badgeInfo": "_367UGW_badgeInfo",
2956
+ "errorActionLabel": "_367UGW_errorActionLabel",
2957
+ "kindTag": "_367UGW_kindTag",
2958
+ "progressBlock": "_367UGW_progressBlock",
2963
2959
  "badgeWarn": "_367UGW_badgeWarn",
2964
- "viewTab": "_367UGW_viewTab",
2965
- "dshCmIndeterminate": "_367UGW_dshCmIndeterminate",
2966
- "progressBar": "_367UGW_progressBar",
2967
- "groupCard": "_367UGW_groupCard",
2968
- "statRow": "_367UGW_statRow",
2960
+ "groupNote": "_367UGW_groupNote",
2961
+ "groupHeader": "_367UGW_groupHeader",
2962
+ "actionRow": "_367UGW_actionRow",
2963
+ "optionsCard": "_367UGW_optionsCard",
2964
+ "pathOld": "_367UGW_pathOld",
2965
+ "errorItem": "_367UGW_errorItem",
2966
+ "snapshotList": "_367UGW_snapshotList",
2967
+ "pathNew": "_367UGW_pathNew",
2968
+ "confirmScroll": "_367UGW_confirmScroll",
2969
+ "conflictList": "_367UGW_conflictList",
2969
2970
  "categoryName": "_367UGW_categoryName",
2970
- "sectionHeader": "_367UGW_sectionHeader",
2971
+ "groupLabel": "_367UGW_groupLabel",
2972
+ "errorList": "_367UGW_errorList",
2971
2973
  "card": "_367UGW_card",
2972
- "hiddenFile": "_367UGW_hiddenFile",
2974
+ "sectionTitle": "_367UGW_sectionTitle",
2975
+ "optionsHeader": "_367UGW_optionsHeader",
2976
+ "aboutAuthor": "_367UGW_aboutAuthor",
2977
+ "progressBadgeCount": "_367UGW_progressBadgeCount",
2978
+ "fieldLabel": "_367UGW_fieldLabel",
2973
2979
  "reportText": "_367UGW_reportText",
2974
- "conflictItem": "_367UGW_conflictItem",
2975
- "pathNew": "_367UGW_pathNew",
2980
+ "severityError": "_367UGW_severityError",
2981
+ "formError": "_367UGW_formError",
2982
+ "pathMappingList": "_367UGW_pathMappingList",
2976
2983
  "empty": "_367UGW_empty",
2977
2984
  "spinnerWrap": "_367UGW_spinnerWrap",
2985
+ "field": "_367UGW_field",
2986
+ "secretFields": "_367UGW_secretFields",
2978
2987
  "rollbackBox": "_367UGW_rollbackBox",
2979
- "spinnerLabel": "_367UGW_spinnerLabel",
2980
- "progressBlock": "_367UGW_progressBlock",
2981
- "progressBadge": "_367UGW_progressBadge",
2982
- "categoryItem": "_367UGW_categoryItem",
2983
- "progressBadgeSection": "_367UGW_progressBadgeSection",
2984
- "planScroll": "_367UGW_planScroll",
2988
+ "rowActions": "_367UGW_rowActions",
2989
+ "dshCmIndeterminate": "_367UGW_dshCmIndeterminate",
2990
+ "ghostButton": "_367UGW_ghostButton",
2991
+ "diffScroll": "_367UGW_diffScroll",
2992
+ "viewTab": "_367UGW_viewTab",
2993
+ "modeTab": "_367UGW_modeTab",
2994
+ "radioLabel": "_367UGW_radioLabel",
2995
+ "groupCard": "_367UGW_groupCard",
2985
2996
  "reportFooter": "_367UGW_reportFooter",
2986
- "aboutAuthor": "_367UGW_aboutAuthor",
2997
+ "sectionHeader": "_367UGW_sectionHeader",
2998
+ "pathValue": "_367UGW_pathValue",
2999
+ "progressIndeterminate": "_367UGW_progressIndeterminate",
3000
+ "dangerButton": "_367UGW_dangerButton",
3001
+ "hint": "_367UGW_hint",
3002
+ "modeHint": "_367UGW_modeHint",
3003
+ "categoryItem": "_367UGW_categoryItem",
3004
+ "errorFooter": "_367UGW_errorFooter",
3005
+ "pathRow": "_367UGW_pathRow",
2987
3006
  "progressDetail": "_367UGW_progressDetail",
2988
- "conflictList": "_367UGW_conflictList",
2989
- "severityError": "_367UGW_severityError",
2990
- "optionsCard": "_367UGW_optionsCard",
2991
- "modeTab": "_367UGW_modeTab",
2992
- "progressPercent": "_367UGW_progressPercent",
2993
- "groupHeader": "_367UGW_groupHeader",
2994
- "errorLine": "_367UGW_errorLine",
2995
- "badgeError": "_367UGW_badgeError",
2996
- "conflictHead": "_367UGW_conflictHead",
2997
- "snapshotList": "_367UGW_snapshotList",
2998
- "actionRow": "_367UGW_actionRow",
3007
+ "progressBar": "_367UGW_progressBar",
3008
+ "snapshotRowHeader": "_367UGW_snapshotRowHeader",
3009
+ "viewBody": "_367UGW_viewBody",
2999
3010
  "modeTabs": "_367UGW_modeTabs",
3000
3011
  "errorTitle": "_367UGW_errorTitle",
3001
- "banner": "_367UGW_banner",
3002
- "groupLabel": "_367UGW_groupLabel",
3003
- "conflictOptions": "_367UGW_conflictOptions",
3004
- "sectionTitle": "_367UGW_sectionTitle",
3005
- "sectionSubtitle": "_367UGW_sectionSubtitle",
3006
- "sectionBody": "_367UGW_sectionBody",
3007
- "field": "_367UGW_field",
3008
- "pathRow": "_367UGW_pathRow",
3009
- "secretsList": "_367UGW_secretsList",
3010
- "errorFooter": "_367UGW_errorFooter",
3011
- "aboutLinkRow": "_367UGW_aboutLinkRow",
3012
- "groupList": "_367UGW_groupList",
3013
- "reportScroll": "_367UGW_reportScroll",
3014
- "select": "_367UGW_select",
3015
- "progressTrack": "_367UGW_progressTrack",
3016
- "dshCmSpin": "_367UGW_dshCmSpin",
3017
- "rowActions": "_367UGW_rowActions",
3018
- "groupNote": "_367UGW_groupNote",
3019
- "progressIndeterminate": "_367UGW_progressIndeterminate",
3020
- "secretFields": "_367UGW_secretFields",
3021
- "primaryButton": "_367UGW_primaryButton",
3022
- "progressBarDone": "_367UGW_progressBarDone",
3023
- "errorActionLabel": "_367UGW_errorActionLabel",
3024
- "fieldLabel": "_367UGW_fieldLabel",
3025
- "warnText": "_367UGW_warnText",
3026
- "progressMeta": "_367UGW_progressMeta",
3027
- "progressLabel": "_367UGW_progressLabel",
3028
3012
  "errorAction": "_367UGW_errorAction",
3029
- "categoryDesc": "_367UGW_categoryDesc",
3030
- "conflictId": "_367UGW_conflictId",
3031
- "optionsHeader": "_367UGW_optionsHeader",
3032
- "progressBadgeCount": "_367UGW_progressBadgeCount",
3033
- "reportView": "_367UGW_reportView",
3034
- "pathValue": "_367UGW_pathValue",
3035
- "spinner": "_367UGW_spinner",
3036
- "snapshotRow": "_367UGW_snapshotRow",
3037
- "sectionTitleBlock": "_367UGW_sectionTitleBlock"
3013
+ "conflictHead": "_367UGW_conflictHead",
3014
+ "section": "_367UGW_section",
3015
+ "viewTabs": "_367UGW_viewTabs",
3016
+ "conflictOptions": "_367UGW_conflictOptions",
3017
+ "dshCmSpin": "_367UGW_dshCmSpin"
3038
3018
  };
3039
3019
  //#endregion
3040
3020
  //#region src/client/common/ui.tsx
@@ -8526,671 +8506,6 @@ function MyConfigsView({ meApi, api, importApi, syncApi, t, myItems, myItemsErro
8526
8506
  });
8527
8507
  }
8528
8508
  //#endregion
8529
- //#region src/market/types.ts
8530
- /**
8531
- * itemId / 仓库 url-hash 的安全字符集(防路径穿越 + 用作目录名):
8532
- * 字母数字开头,仅 . _ -,最长 128。与 sync GitTransport 的 SAFE_ID_RE 同构。
8533
- */
8534
- const SAFE_ITEM_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
8535
- //#endregion
8536
- //#region src/ui/market-publish.ts
8537
- /**
8538
- * 配置市场「发布到市场」向导的框架无关渲染/校验模型(m-market-publish,node 可测)。
8539
- *
8540
- * 设计纪律(docs/design/2026-08-19-market-publish-design.md §3.4):
8541
- * - 全部无副作用纯函数,React 壳(PublishView)只装配,不承载业务逻辑;
8542
- * - 5 步流程:选择配置包 → 本地校验 → 生成条目包 → 推送作者仓库 → 提交收录申请;
8543
- * - 状态组件内自持(同 MarketPanel 策略,不进 sessionStorage)。
8544
- *
8545
- * 安全硬约束(与安全不变量同款):
8546
- * - itemId 须过 SAFE_ITEM_ID_RE(防 items/<id>/ 路径越界,assertSafeItemId 同源正则);
8547
- * - repoUrl 可选但若填必须为 http(s) 且无 userinfo(轻量等价实现 sync validateRepoUrl 的
8548
- * 语义,不 import node 模块、浏览器/node 双端可跑;凭据永不拼入 URL);
8549
- * - 本层只生成 git 命令模板与 index.json 收录片段**文本**,不做任何 git 写操作、
8550
- * 不持有凭据(与 About 页「Star 由用户在 GitHub 完成」同款产品纪律);
8551
- * - 内容校验(zip 加固解包 / containsSecrets / SHA-256)在 Host 侧 prepare 端点,本层不管。
8552
- */
8553
- /** 发布向导 5 步(设计文档 §3.4:选择配置包 / 本地校验 / 生成条目包 / 推送作者仓库 / 提交收录申请) */
8554
- const PUBLISH_STEP_IDS = [
8555
- "select",
8556
- "validate",
8557
- "prepare",
8558
- "push",
8559
- "submit"
8560
- ];
8561
- /** 5 步模型(顺序即流程;文案走 i18n 字典,不硬编码中文)。 */
8562
- function publishSteps(t = zhUiT) {
8563
- return [
8564
- {
8565
- id: "select",
8566
- title: t("marketPublish.stepSelect")
8567
- },
8568
- {
8569
- id: "validate",
8570
- title: t("marketPublish.stepValidate")
8571
- },
8572
- {
8573
- id: "prepare",
8574
- title: t("marketPublish.stepPrepare")
8575
- },
8576
- {
8577
- id: "push",
8578
- title: t("marketPublish.stepPush")
8579
- },
8580
- {
8581
- id: "submit",
8582
- title: t("marketPublish.stepSubmit")
8583
- }
8584
- ];
8585
- }
8586
- /** 空表单初值(React 壳 useState 初始 + 测试用)。 */
8587
- const EMPTY_PUBLISH_FORM = {
8588
- itemId: "",
8589
- name: "",
8590
- version: "",
8591
- description: "",
8592
- author: "",
8593
- categories: "",
8594
- repoUrl: ""
8595
- };
8596
- /**
8597
- * 发布仓库 URL 合法性校验(返回错误消息;null = 合法)。
8598
- * **可选字段**:空串/空白视为「未填」= 合法;非空须:
8599
- * - 不含空白字符(\s);
8600
- * - http(s) 开头(拒绝 git@ / ssh / ftp 等非 http(s) 形态,市场仓库只走 https clone);
8601
- * - authority 段不含 userinfo(`user[:password]@host`),凭据必须走 DSH credentials。
8602
- * 语义与 sync 的 validateRepoUrl 对齐(拒绝 userinfo / 空白),另按市场设计补强「非 http(s) 拒绝」。
8603
- */
8604
- function validatePublishRepoUrl(repoUrl, t = zhUiT) {
8605
- if (typeof repoUrl !== "string" || repoUrl.trim() === "") return null;
8606
- const url = repoUrl.trim();
8607
- if (/\s/.test(url)) return t("marketPublish.repoWhitespace");
8608
- if (!/^https?:\/\//i.test(url)) return t("marketPublish.repoScheme");
8609
- if ((url.replace(/^https?:\/\//i, "").split(/[/?#]/)[0] ?? "").includes("@")) return t("marketPublish.repoUserinfo");
8610
- return null;
8611
- }
8612
- /** 表单校验:itemId(SAFE_ITEM_ID_RE)+ name 必填 + repoUrl 可选校验。返回逐字段错误。 */
8613
- function validatePublishForm(form, t = zhUiT) {
8614
- const errors = {
8615
- itemId: null,
8616
- name: null,
8617
- repoUrl: null
8618
- };
8619
- if (!SAFE_ITEM_ID_RE.test(form.itemId)) errors.itemId = t("marketPublish.errorItemId");
8620
- if (form.name.trim() === "") errors.name = t("marketPublish.errorName");
8621
- const repoErr = validatePublishRepoUrl(form.repoUrl, t);
8622
- if (repoErr !== null) errors.repoUrl = repoErr;
8623
- return errors;
8624
- }
8625
- /** 表单是否全部合法(generate 按钮可用性 / canProceed 的 prepare 前置)。 */
8626
- function publishFormValid(errors) {
8627
- return errors.itemId === null && errors.name === null && errors.repoUrl === null;
8628
- }
8629
- /**
8630
- * 生成「推送到作者仓库」的 git 命令模板(用户在**自己的公开仓库**里执行;
8631
- * 插件不做任何 git 写操作、不持有凭据)。
8632
- *
8633
- * 语义:clone 空仓库 → 进入 → 建 items/<id>/ → 从发布目录复制条目文件
8634
- * (prepare 产物解压为 `dist/`,含 dist/items/<id>/manifest.json + config.zip)→
8635
- * add → commit → push。
8636
- */
8637
- function buildGitPushCommands(input) {
8638
- const { repoUrl, itemId, dir } = input;
8639
- return [
8640
- `git clone ${repoUrl} ${dir}`,
8641
- `cd ${dir}`,
8642
- `mkdir -p items/${itemId}`,
8643
- `cp -r ../dist/items/${itemId}/. items/${itemId}/`,
8644
- `git add items/${itemId}`,
8645
- `git commit -m "publish ${itemId}"`,
8646
- `git push origin HEAD`
8647
- ];
8648
- }
8649
- /**
8650
- * 生成「提交收录申请」(PR)时追加进官方 index.json `items[]` 的条目片段
8651
- * (pretty JSON 文本,`JSON.parse` 可直接通过;可选字段空值不写入)。
8652
- * 字段形状与 src/market/index-parser.ts 的白名单解析面一致。
8653
- */
8654
- function buildIndexEntrySnippet(item) {
8655
- const entry = {
8656
- id: item.id,
8657
- name: item.name
8658
- };
8659
- if (item.description !== void 0 && item.description.trim() !== "") entry.description = item.description.trim();
8660
- if (item.author !== void 0 && item.author.trim() !== "") entry.author = item.author.trim();
8661
- if (item.version !== void 0 && item.version.trim() !== "") entry.version = item.version.trim();
8662
- if (item.updatedAt !== void 0 && item.updatedAt.trim() !== "") entry.updatedAt = item.updatedAt.trim();
8663
- if (item.categories !== void 0 && item.categories.length > 0) entry.categories = item.categories;
8664
- entry.repo = item.repo;
8665
- return JSON.stringify(entry, null, 2);
8666
- }
8667
- /**
8668
- * 当前步骤是否可前进到下一步(纯函数推导,React 壳只装配):
8669
- * - `select` :须已选择配置 zip(zipSelected);
8670
- * - `validate`:须本地校验通过(validated,dry-run 合法 + 无 secrets);
8671
- * - `prepare` :须条目包已生成(prepared,含表单校验通过——生成动作本身以 publishFormValid 为前提);
8672
- * - `push` :引导页无硬性前置(复制命令即可前进到收录申请);
8673
- * - `submit` :最后一步,不可再前进。
8674
- */
8675
- function canProceed(state) {
8676
- switch (state.step) {
8677
- case "select": return state.zipSelected;
8678
- case "validate": return state.validated;
8679
- case "prepare": return state.prepared;
8680
- case "push": return true;
8681
- case "submit": return false;
8682
- }
8683
- }
8684
- //#endregion
8685
- //#region src/client/market/PublishView.tsx
8686
- /**
8687
- * 配置市场「发布到市场」发布向导(阶段 2,docs/design/2026-08-19-market-publish-design.md §3.4)。
8688
- *
8689
- * 5 步流程(全部渲染/校验模型来自 src/ui/market-publish.ts 纯函数,node 已测;本组件只装配):
8690
- * ①选择配置包 —— 复用导出 zip(本地文件选择 → upload 受控临时区,零新增导入逻辑);
8691
- * ②本地校验 —— analyzeImport dry-run(零写入):内容合法且不含密钥(secretCount===0);
8692
- * ③生成条目包 —— 表单(name/description/author/categories/id/repoUrl 可选)+ POST /market/prepare
8693
- * → 展示 manifest 文本 / SHA-256 / sections(可复制);
8694
- * ④推送作者仓库 —— t4 控制器生成的 git 命令模板(可复制);插件不做任何 git 写操作、不持有凭据;
8695
- * ⑤提交收录申请 —— index.json 追加片段(可复制)+ fork 官方仓库提 PR 指引。
8696
- *
8697
- * 状态策略:组件内自持(useState,不进 sessionStorage —— 发布为一次性低频率流程,同 MarketPanel 的低频面板策略)。
8698
- * 安全:市场通道永不携带秘密(步骤 2 拒绝含密钥 zip,prepare 亦双保险拒绝 containsSecrets);
8699
- * 展示文本渲染前过 redact() 兜底;repoUrl 已由 validatePublishRepoUrl 拒绝 userinfo,凭据绝不拼入命令/片段。
8700
- */
8701
- /**
8702
- * 发布向导(React 壳,只装配):
8703
- * - 步骤门控用 t4 控制器 canProceed(zipSelected / validated / prepared);
8704
- * - 表单校验用 validatePublishForm / publishFormValid(行内 formError);
8705
- * - git 命令 / index 片段文本来自 buildGitPushCommands / buildIndexEntrySnippet。
8706
- */
8707
- function PublishView({ api, importApi, t, onBack }) {
8708
- const uiT = api.t;
8709
- const [stepIndex, setStepIndex] = (0, react.useState)(0);
8710
- const [fileName, setFileName] = (0, react.useState)(null);
8711
- const [uploading, setUploading] = (0, react.useState)(false);
8712
- const [zipPath, setZipPath] = (0, react.useState)(null);
8713
- const [validating, setValidating] = (0, react.useState)(false);
8714
- const [validated, setValidated] = (0, react.useState)(false);
8715
- const [validationError, setValidationError] = (0, react.useState)(null);
8716
- const [form, setForm] = (0, react.useState)(EMPTY_PUBLISH_FORM);
8717
- const [formErrors, setFormErrors] = (0, react.useState)({
8718
- itemId: null,
8719
- name: null,
8720
- repoUrl: null
8721
- });
8722
- const [generating, setGenerating] = (0, react.useState)(false);
8723
- const [prepareResult, setPrepareResult] = (0, react.useState)(null);
8724
- const [prepareError, setPrepareError] = (0, react.useState)(null);
8725
- const [error, setError] = (0, react.useState)(null);
8726
- const [copied, setCopied] = (0, react.useState)(null);
8727
- const fileInput = (0, react.useRef)(null);
8728
- const steps = publishSteps(uiT);
8729
- const currentStep = PUBLISH_STEP_IDS[stepIndex];
8730
- /** t4 状态机:zipSelected / validated / prepared 推导当前步可否前进 */
8731
- const canNext = canProceed({
8732
- step: currentStep,
8733
- zipSelected: zipPath !== null,
8734
- validated,
8735
- prepared: prepareResult !== null,
8736
- pushAcknowledged: false
8737
- });
8738
- /** 解析逗号分隔类别 → 去空白数组(空结果省略)。 */
8739
- const parseCategories = () => form.categories.split(",").map((c) => c.trim()).filter((c) => c !== "");
8740
- /** 步骤 1:选择 zip → upload 受控临时区(换选以最新文件为准,恒清空 input value)。 */
8741
- const onPickFile = async (file) => {
8742
- if (file === void 0) return;
8743
- setUploading(true);
8744
- setError(null);
8745
- setValidated(false);
8746
- setPrepareResult(null);
8747
- try {
8748
- const uploaded = await importApi.upload(file);
8749
- setZipPath(uploaded.zipPath);
8750
- setFileName(file.name);
8751
- } catch (err) {
8752
- setError(err instanceof Error ? err.message : String(err));
8753
- } finally {
8754
- setUploading(false);
8755
- }
8756
- };
8757
- /** 步骤 2:analyzeImport dry-run(零写入)—— 无密钥 + 内容合法才放行。 */
8758
- const runValidate = async () => {
8759
- if (zipPath === null) return;
8760
- setValidating(true);
8761
- setValidationError(null);
8762
- try {
8763
- const analysis = await importApi.analyzeImport(zipPath);
8764
- if (analysis.secretCount > 0) {
8765
- setValidated(false);
8766
- setValidationError(t("publish.validate.secrets"));
8767
- } else if (!analysis.valid) {
8768
- setValidated(false);
8769
- setValidationError(t("publish.validate.invalid"));
8770
- } else setValidated(true);
8771
- } catch (err) {
8772
- setValidated(false);
8773
- setValidationError(err instanceof Error ? err.message : String(err));
8774
- } finally {
8775
- setValidating(false);
8776
- }
8777
- };
8778
- /** 步骤 3:表单字段更新 + 实时校验(t4 控制器)。 */
8779
- const onFormField = (field, value) => {
8780
- const next = {
8781
- ...form,
8782
- [field]: value
8783
- };
8784
- setForm(next);
8785
- setFormErrors(validatePublishForm(next));
8786
- };
8787
- /** 步骤 3:POST /market/prepare 生成条目包(manifest + SHA-256 + sections)。 */
8788
- const runGenerate = async () => {
8789
- if (zipPath === null) return;
8790
- const errs = validatePublishForm(form);
8791
- setFormErrors(errs);
8792
- if (!publishFormValid(errs)) return;
8793
- const categories = parseCategories();
8794
- setGenerating(true);
8795
- setPrepareError(null);
8796
- try {
8797
- const result = await api.prepare({
8798
- zipPath,
8799
- itemId: form.itemId,
8800
- name: form.name,
8801
- ...form.version !== "" ? { version: form.version } : {},
8802
- ...form.description !== "" ? { description: form.description } : {},
8803
- ...form.author !== "" ? { author: form.author } : {},
8804
- ...form.repoUrl !== "" ? { repoUrl: form.repoUrl } : {},
8805
- ...categories.length > 0 ? { categories } : {}
8806
- });
8807
- setPrepareResult(result);
8808
- } catch (err) {
8809
- setPrepareError(err instanceof Error ? err.message : String(err));
8810
- } finally {
8811
- setGenerating(false);
8812
- }
8813
- };
8814
- /** 复制文本到剪贴板(失败降级提示手动选择)。 */
8815
- const copyText = async (label, text) => {
8816
- try {
8817
- await navigator.clipboard.writeText(text);
8818
- setCopied(label);
8819
- } catch {
8820
- setCopied(`${label}-fail`);
8821
- }
8822
- };
8823
- /** 复制反馈徽章:成功 → ok「已复制」;失败 → warn「复制失败」。 */
8824
- const copyBadge = (label) => copied === label ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
8825
- kind: "ok",
8826
- children: t("publish.copied")
8827
- }) : copied === `${label}-fail` ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
8828
- kind: "warn",
8829
- children: t("publish.copyFailed")
8830
- }) : null;
8831
- const renderSelect = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, { children: [
8832
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8833
- className: config_manager_module_css_default.hint,
8834
- children: t("publish.select.hint")
8835
- }),
8836
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8837
- ref: fileInput,
8838
- type: "file",
8839
- accept: ".zip,application/zip",
8840
- className: config_manager_module_css_default.hiddenFile,
8841
- onChange: (e) => {
8842
- const picked = e.target.files?.[0];
8843
- e.target.value = "";
8844
- onPickFile(picked);
8845
- }
8846
- }),
8847
- fileName !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8848
- className: config_manager_module_css_default.statRow,
8849
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
8850
- kind: "info",
8851
- children: t("publish.select.file", { name: fileName })
8852
- })
8853
- }),
8854
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8855
- className: config_manager_module_css_default.actionRow,
8856
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
8857
- variant: "primary",
8858
- disabled: uploading,
8859
- onClick: () => {
8860
- fileInput.current?.click();
8861
- },
8862
- children: uploading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spinner, { label: t("publish.select.uploading") }) : t(fileName !== null ? "publish.select.reselect" : "publish.select.browse")
8863
- })
8864
- })
8865
- ] });
8866
- const renderValidate = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, { children: [
8867
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8868
- className: config_manager_module_css_default.hint,
8869
- children: t("publish.validate.hint")
8870
- }),
8871
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8872
- className: config_manager_module_css_default.actionRow,
8873
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
8874
- variant: "primary",
8875
- disabled: zipPath === null || validating,
8876
- onClick: () => {
8877
- runValidate();
8878
- },
8879
- children: validating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spinner, { label: t("publish.validate.running") }) : validated ? t("publish.validate.retry") : t("publish.validate.run")
8880
- })
8881
- }),
8882
- validated && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
8883
- kind: "ok",
8884
- children: t("publish.validate.ok")
8885
- }),
8886
- validationError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Banner, {
8887
- kind: "error",
8888
- children: redact(validationError)
8889
- })
8890
- ] });
8891
- const renderForm = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, { children: [
8892
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8893
- className: config_manager_module_css_default.groupLabel,
8894
- children: t("publish.form.title")
8895
- }),
8896
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Field, {
8897
- label: t("publish.form.id"),
8898
- hint: t("publish.form.idHint"),
8899
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8900
- className: config_manager_module_css_default.input,
8901
- value: form.itemId,
8902
- onChange: (e) => {
8903
- onFormField("itemId", e.target.value);
8904
- }
8905
- }), formErrors.itemId !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8906
- className: config_manager_module_css_default.formError,
8907
- children: redact(formErrors.itemId)
8908
- })]
8909
- }),
8910
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Field, {
8911
- label: t("publish.form.name"),
8912
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8913
- className: config_manager_module_css_default.input,
8914
- value: form.name,
8915
- onChange: (e) => {
8916
- onFormField("name", e.target.value);
8917
- }
8918
- }), formErrors.name !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8919
- className: config_manager_module_css_default.formError,
8920
- children: redact(formErrors.name)
8921
- })]
8922
- }),
8923
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
8924
- label: t("publish.form.description"),
8925
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
8926
- className: config_manager_module_css_default.input,
8927
- value: form.description,
8928
- onChange: (e) => {
8929
- onFormField("description", e.target.value);
8930
- }
8931
- })
8932
- }),
8933
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
8934
- label: t("publish.form.author"),
8935
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8936
- className: config_manager_module_css_default.input,
8937
- value: form.author,
8938
- onChange: (e) => {
8939
- onFormField("author", e.target.value);
8940
- }
8941
- })
8942
- }),
8943
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
8944
- label: t("publish.form.version"),
8945
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8946
- className: config_manager_module_css_default.input,
8947
- value: form.version,
8948
- onChange: (e) => {
8949
- onFormField("version", e.target.value);
8950
- }
8951
- })
8952
- }),
8953
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
8954
- label: t("publish.form.categories"),
8955
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8956
- className: config_manager_module_css_default.input,
8957
- value: form.categories,
8958
- onChange: (e) => {
8959
- onFormField("categories", e.target.value);
8960
- }
8961
- })
8962
- }),
8963
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Field, {
8964
- label: t("publish.form.repoUrl"),
8965
- hint: t("publish.form.repoUrlHint"),
8966
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8967
- className: config_manager_module_css_default.input,
8968
- value: form.repoUrl,
8969
- onChange: (e) => {
8970
- onFormField("repoUrl", e.target.value);
8971
- }
8972
- }), formErrors.repoUrl !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8973
- className: config_manager_module_css_default.formError,
8974
- children: redact(formErrors.repoUrl)
8975
- })]
8976
- }),
8977
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8978
- className: config_manager_module_css_default.actionRow,
8979
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
8980
- variant: "primary",
8981
- disabled: generating || !publishFormValid(formErrors),
8982
- onClick: () => {
8983
- runGenerate();
8984
- },
8985
- children: generating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spinner, { label: t("publish.form.generating") }) : t("publish.form.generate")
8986
- })
8987
- }),
8988
- prepareError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Banner, {
8989
- kind: "error",
8990
- children: redact(prepareError)
8991
- }),
8992
- prepareResult !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
8993
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8994
- className: config_manager_module_css_default.groupLabel,
8995
- children: t("publish.prepare.title")
8996
- }),
8997
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Banner, {
8998
- kind: "warn",
8999
- children: t("publish.prepare.warnings")
9000
- }),
9001
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9002
- className: config_manager_module_css_default.statRow,
9003
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
9004
- kind: "info",
9005
- children: t("publish.prepare.sha256", { hash: prepareResult.sha256 })
9006
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
9007
- kind: "info",
9008
- children: t("publish.prepare.sections", { sections: prepareResult.sections.join(", ") })
9009
- })]
9010
- }),
9011
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9012
- className: config_manager_module_css_default.fieldLabel,
9013
- children: t("publish.prepare.manifest")
9014
- }),
9015
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9016
- className: config_manager_module_css_default.actionRow,
9017
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9018
- onClick: () => {
9019
- copyText("manifest", prepareResult.manifestText);
9020
- },
9021
- children: t("publish.prepare.copyManifest")
9022
- }), copyBadge("manifest")]
9023
- }),
9024
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
9025
- className: config_manager_module_css_default.reportText,
9026
- children: redact(prepareResult.manifestText)
9027
- }),
9028
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9029
- className: config_manager_module_css_default.hint,
9030
- children: t("publish.prepare.dirHint", { dir: prepareResult.dir })
9031
- })
9032
- ] })
9033
- ] });
9034
- const renderPush = () => {
9035
- const repoUrl = form.repoUrl.trim();
9036
- const zipPath = prepareResult?.zipPath;
9037
- const downloadBtn = zipPath !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9038
- onClick: () => {
9039
- window.open(api.downloadPublishUrl(zipPath), "_blank");
9040
- },
9041
- children: t("publish.push.download")
9042
- });
9043
- if (repoUrl === "") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Banner, {
9044
- kind: "warn",
9045
- children: t("publish.push.repoMissing", { id: form.itemId })
9046
- }), downloadBtn !== false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9047
- className: config_manager_module_css_default.actionRow,
9048
- children: downloadBtn
9049
- })] });
9050
- const commands = buildGitPushCommands({
9051
- repoUrl,
9052
- itemId: form.itemId,
9053
- dir: "market-items"
9054
- });
9055
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, { children: [
9056
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9057
- className: config_manager_module_css_default.hint,
9058
- children: t("publish.push.hint")
9059
- }),
9060
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9061
- className: config_manager_module_css_default.actionRow,
9062
- children: [
9063
- downloadBtn !== false && downloadBtn,
9064
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9065
- onClick: () => {
9066
- copyText("commands", commands.join("\n"));
9067
- },
9068
- children: t("publish.push.copy")
9069
- }),
9070
- copyBadge("commands")
9071
- ]
9072
- }),
9073
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
9074
- className: config_manager_module_css_default.reportText,
9075
- children: redact(commands.join("\n"))
9076
- })
9077
- ] });
9078
- };
9079
- const renderSubmit = () => {
9080
- const repoUrl = form.repoUrl.trim();
9081
- if (repoUrl === "") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Banner, {
9082
- kind: "warn",
9083
- children: t("publish.submit.repoMissing")
9084
- }) });
9085
- const categories = parseCategories();
9086
- const snippet = buildIndexEntrySnippet({
9087
- id: form.itemId,
9088
- name: form.name,
9089
- ...form.description !== "" ? { description: form.description } : {},
9090
- ...form.author !== "" ? { author: form.author } : {},
9091
- ...form.version !== "" ? { version: form.version } : {},
9092
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
9093
- ...categories.length > 0 ? { categories } : {},
9094
- repo: repoUrl
9095
- });
9096
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, { children: [
9097
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9098
- className: config_manager_module_css_default.hint,
9099
- children: t("publish.submit.hint")
9100
- }),
9101
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9102
- className: config_manager_module_css_default.actionRow,
9103
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9104
- onClick: () => {
9105
- copyText("snippet", snippet);
9106
- },
9107
- children: t("publish.submit.copy")
9108
- }), copyBadge("snippet")]
9109
- }),
9110
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9111
- className: config_manager_module_css_default.fieldLabel,
9112
- children: t("publish.submit.snippet")
9113
- }),
9114
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
9115
- className: config_manager_module_css_default.reportText,
9116
- children: redact(snippet)
9117
- }),
9118
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9119
- className: config_manager_module_css_default.hint,
9120
- children: t("publish.submit.fork", { url: "https://github.com/xiajiajun516/dsh-config-market" })
9121
- }),
9122
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9123
- className: config_manager_module_css_default.hint,
9124
- children: t("publish.submit.manual")
9125
- })
9126
- ] });
9127
- };
9128
- const renderStep = () => {
9129
- switch (currentStep) {
9130
- case "select": return renderSelect();
9131
- case "validate": return renderValidate();
9132
- case "prepare": return renderForm();
9133
- case "push": return renderPush();
9134
- case "submit": return renderSubmit();
9135
- }
9136
- };
9137
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9138
- className: config_manager_module_css_default.viewBody,
9139
- children: [
9140
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SectionTitle, {
9141
- title: t("publish.title"),
9142
- subtitle: t("publish.subtitle")
9143
- }),
9144
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9145
- className: config_manager_module_css_default.actionRow,
9146
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9147
- onClick: onBack,
9148
- children: t("publish.back")
9149
- })
9150
- }),
9151
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9152
- className: config_manager_module_css_default.statRow,
9153
- children: steps.map((s, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
9154
- kind: i === stepIndex ? "ok" : "info",
9155
- children: `${i + 1}. ${s.title}`
9156
- }, s.id))
9157
- }),
9158
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9159
- className: config_manager_module_css_default.statRow,
9160
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
9161
- kind: "info",
9162
- children: t("publish.stepIndicator", {
9163
- current: String(stepIndex + 1),
9164
- total: String(PUBLISH_STEP_IDS.length),
9165
- label: steps[stepIndex]?.title ?? ""
9166
- })
9167
- })
9168
- }),
9169
- error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Banner, {
9170
- kind: "error",
9171
- children: redact(error)
9172
- }),
9173
- renderStep(),
9174
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9175
- className: config_manager_module_css_default.actionRow,
9176
- children: [stepIndex > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9177
- onClick: () => {
9178
- setStepIndex((i) => i - 1);
9179
- },
9180
- children: t("publish.prev")
9181
- }), stepIndex < PUBLISH_STEP_IDS.length - 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9182
- variant: "primary",
9183
- disabled: !canNext,
9184
- onClick: () => {
9185
- setStepIndex((i) => i + 1);
9186
- },
9187
- children: t("publish.next")
9188
- })]
9189
- })
9190
- ]
9191
- });
9192
- }
9193
- //#endregion
9194
8509
  //#region src/client/market/MarketPanel.tsx
9195
8510
  /**
9196
8511
  * 配置市场面板(备份与迁移页的第 5 个 tab 内容)。
@@ -9255,8 +8570,6 @@ function initFromStore() {
9255
8570
  }
9256
8571
  function MarketPanel({ api, myConfigsApi, importApi, syncApi, t }) {
9257
8572
  const uiT = api.t;
9258
- /** 发布向导开关(组件内状态,不进 sessionStorage —— 发布为一次性低频率流程) */
9259
- const [publishOpen, setPublishOpen] = (0, react.useState)(false);
9260
8573
  const [state, setState] = (0, react.useState)(initFromStore);
9261
8574
  /** 最新 state 镜像(commit/卸载 flush 读取,避免闭包过期值) */
9262
8575
  const stateRef = (0, react.useRef)(state);
@@ -9413,16 +8726,9 @@ function MarketPanel({ api, myConfigsApi, importApi, syncApi, t }) {
9413
8726
  if (cacheState === "fresh") return t("list.cacheFresh");
9414
8727
  return t("list.cacheNone");
9415
8728
  };
9416
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8729
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9417
8730
  className: config_manager_module_css_default.viewBody,
9418
- children: publishOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PublishView, {
9419
- api,
9420
- importApi,
9421
- t,
9422
- onBack: () => {
9423
- setPublishOpen(false);
9424
- }
9425
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8731
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9426
8732
  className: config_manager_module_css_default.modeTabs,
9427
8733
  role: "tablist",
9428
8734
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -9507,29 +8813,20 @@ function MarketPanel({ api, myConfigsApi, importApi, syncApi, t }) {
9507
8813
  }),
9508
8814
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9509
8815
  className: config_manager_module_css_default.actionRow,
9510
- children: [
9511
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9512
- variant: "primary",
9513
- disabled: state.refreshing || state.importing,
9514
- onClick: () => {
9515
- runRefresh();
9516
- },
9517
- children: state.refreshing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spinner, { label: t("config.refreshing") }) : t("config.refresh")
9518
- }),
9519
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9520
- disabled: state.browsing,
9521
- onClick: () => {
9522
- runBrowse();
9523
- },
9524
- children: state.browsing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spinner, { label: t("list.loading") }) : t("list.browse")
9525
- }),
9526
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
9527
- onClick: () => {
9528
- setPublishOpen(true);
9529
- },
9530
- children: t("publish.title")
9531
- })
9532
- ]
8816
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
8817
+ variant: "primary",
8818
+ disabled: state.refreshing || state.importing,
8819
+ onClick: () => {
8820
+ runRefresh();
8821
+ },
8822
+ children: state.refreshing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spinner, { label: t("config.refreshing") }) : t("config.refresh")
8823
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
8824
+ disabled: state.browsing,
8825
+ onClick: () => {
8826
+ runBrowse();
8827
+ },
8828
+ children: state.browsing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Spinner, { label: t("list.loading") }) : t("list.browse")
8829
+ })]
9533
8830
  }),
9534
8831
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9535
8832
  className: config_manager_module_css_default.statRow,
@@ -9771,7 +9068,7 @@ function MarketPanel({ api, myConfigsApi, importApi, syncApi, t }) {
9771
9068
  })
9772
9069
  })
9773
9070
  ] })
9774
- ] })] })
9071
+ ] })]
9775
9072
  });
9776
9073
  }
9777
9074
  //#endregion
@@ -10939,61 +10236,6 @@ const zh = {
10939
10236
  "detail.approval.safe": "可导入",
10940
10237
  "detail.approval.count": "已批准 {selected}/{total} 分区",
10941
10238
  "detail.noApproval": "未批准任何分区,无法导入",
10942
- "publish.title": "发布到市场",
10943
- "publish.subtitle": "把本地导出配置分享到社区:作者自托管公开仓库 + 官方收录引用(方案 B),官方市场只读、零凭据",
10944
- "publish.back": "返回市场",
10945
- "publish.stepIndicator": "步骤 {current}/{total}:{label}",
10946
- "publish.next": "下一步",
10947
- "publish.prev": "上一步",
10948
- "publish.step.select": "选择配置包",
10949
- "publish.step.validate": "本地校验",
10950
- "publish.step.prepare": "生成条目包",
10951
- "publish.step.push": "推送作者仓库",
10952
- "publish.step.submit": "提交收录申请",
10953
- "publish.select.hint": "发布内容来自你的导出 zip:先在「导出」tab 生成备份文件,再回到这里选择。市场通道永不携带秘密——包含密钥的备份将被拒绝。",
10954
- "publish.select.browse": "选择导出 ZIP…",
10955
- "publish.select.reselect": "重新选择",
10956
- "publish.select.file": "已选择:{name}",
10957
- "publish.select.uploading": "上传中…",
10958
- "publish.validate.hint": "本地 dry-run 校验(零写入):确认内容合法且不含密钥,之后才能生成条目包。",
10959
- "publish.validate.run": "开始校验",
10960
- "publish.validate.running": "校验中…",
10961
- "publish.validate.ok": "校验通过:内容合法、不含密钥",
10962
- "publish.validate.secrets": "包含密钥:市场通道永不携带秘密,拒绝发布",
10963
- "publish.validate.invalid": "内容校验未通过,无法发布",
10964
- "publish.validate.retry": "重新校验",
10965
- "publish.form.title": "条目元数据",
10966
- "publish.form.id": "条目 ID(必需)",
10967
- "publish.form.idHint": "字母数字开头,仅 . _ -,最长 128;将作为 items/<id>/ 目录名",
10968
- "publish.form.name": "条目名称(必需)",
10969
- "publish.form.description": "描述",
10970
- "publish.form.author": "作者",
10971
- "publish.form.version": "版本",
10972
- "publish.form.categories": "类别(逗号分隔)",
10973
- "publish.form.repoUrl": "托管仓库 URL(可选)",
10974
- "publish.form.repoUrlHint": "作者公开 Git 仓库地址(http(s),不含用户名/密码)。留空表示暂不关联仓库;推送指引与收录引用需要它。",
10975
- "publish.form.generate": "生成条目包",
10976
- "publish.form.generating": "生成中…",
10977
- "publish.prepare.title": "条目包已生成",
10978
- "publish.prepare.manifest": "items/<id>/manifest.json",
10979
- "publish.prepare.sha256": "SHA-256:{hash}",
10980
- "publish.prepare.sections": "包含分区:{sections}",
10981
- "publish.prepare.warnings": "发布即公开、未经官方审核;下载方导入前会逐项核对",
10982
- "publish.prepare.dirHint": "发布目录已在宿主受控临时区生成({dir}):items/<id>/manifest.json + config.zip",
10983
- "publish.prepare.copyManifest": "复制 manifest",
10984
- "publish.push.hint": "先下载「发布包」并解压为 dist/,然后在你自己的公开仓库中按序执行以下命令(插件不做任何 git 写操作、不持有凭据):",
10985
- "publish.push.commands": "git 命令",
10986
- "publish.push.download": "下载发布包",
10987
- "publish.push.copy": "复制命令",
10988
- "publish.push.repoMissing": "尚未填写托管仓库 URL——请在上一步「生成条目包」中填写,或手动把 items/{id}/ 目录推送到你的公开仓库。",
10989
- "publish.submit.hint": "把下面的片段追加到官方市场仓库 index.json 的 items[],然后提 PR。收录由创建者人工审核。",
10990
- "publish.submit.snippet": "index.json 追加片段(items[] 内新增一条)",
10991
- "publish.submit.copy": "复制片段",
10992
- "publish.submit.fork": "提交流程:fork 官方仓库({url})→ 在 index.json 的 items[] 追加上述片段 → 提交 PR。",
10993
- "publish.submit.manual": "也可把「条目 ID + 仓库地址」提交到项目 issue,由创建者人工收录。",
10994
- "publish.submit.repoMissing": "收录引用需要托管仓库 URL——请先在上一步「生成条目包」中填写。",
10995
- "publish.copied": "已复制",
10996
- "publish.copyFailed": "复制失败,请手动选择文本复制",
10997
10239
  "myconfigs.tab.browse": "浏览市场",
10998
10240
  "myconfigs.tab.myconfigs": "我的配置",
10999
10241
  "myconfigs.back": "返回市场",
@@ -11097,61 +10339,6 @@ const en = {
11097
10339
  "detail.approval.safe": "Importable",
11098
10340
  "detail.approval.count": "{selected}/{total} section(s) approved",
11099
10341
  "detail.noApproval": "No sections approved — cannot import",
11100
- "publish.title": "Publish to Market",
11101
- "publish.subtitle": "Share your local config with the community: author self-hosted public repo + official index reference (Option B); the official market stays read-only with zero credentials",
11102
- "publish.back": "Back to market",
11103
- "publish.stepIndicator": "Step {current}/{total}: {label}",
11104
- "publish.next": "Next",
11105
- "publish.prev": "Back",
11106
- "publish.step.select": "Select config package",
11107
- "publish.step.validate": "Local validation",
11108
- "publish.step.prepare": "Generate item package",
11109
- "publish.step.push": "Push to author repo",
11110
- "publish.step.submit": "Submit listing request",
11111
- "publish.select.hint": "Publish content comes from your exported zip: create a backup on the \"Export\" tab first, then pick it here. The market channel never carries secrets — backups containing secrets are rejected.",
11112
- "publish.select.browse": "Choose exported ZIP…",
11113
- "publish.select.reselect": "Choose again",
11114
- "publish.select.file": "Selected: {name}",
11115
- "publish.select.uploading": "Uploading…",
11116
- "publish.validate.hint": "Local dry-run validation (zero writes): confirms the content is valid and secret-free before generating the item package.",
11117
- "publish.validate.run": "Run validation",
11118
- "publish.validate.running": "Validating…",
11119
- "publish.validate.ok": "Validation passed: valid content, no secrets",
11120
- "publish.validate.secrets": "Contains secrets: the market channel never carries secrets — publishing rejected",
11121
- "publish.validate.invalid": "Content validation failed — cannot publish",
11122
- "publish.validate.retry": "Validate again",
11123
- "publish.form.title": "Item Metadata",
11124
- "publish.form.id": "Item ID (required)",
11125
- "publish.form.idHint": "Starts with a letter or digit; only . _ - allowed, max 128; used as the items/<id>/ directory name",
11126
- "publish.form.name": "Item name (required)",
11127
- "publish.form.description": "Description",
11128
- "publish.form.author": "Author",
11129
- "publish.form.version": "Version",
11130
- "publish.form.categories": "Categories (comma-separated)",
11131
- "publish.form.repoUrl": "Host repo URL (optional)",
11132
- "publish.form.repoUrlHint": "Your public Git repository URL (http(s), no username/password). Leave empty to skip the repo link; the push guide and listing reference need it.",
11133
- "publish.form.generate": "Generate item package",
11134
- "publish.form.generating": "Generating…",
11135
- "publish.prepare.title": "Item package generated",
11136
- "publish.prepare.manifest": "items/<id>/manifest.json",
11137
- "publish.prepare.sha256": "SHA-256: {hash}",
11138
- "publish.prepare.sections": "Sections: {sections}",
11139
- "publish.prepare.warnings": "Publishing makes it public and not officially reviewed; downloaders re-check items before importing",
11140
- "publish.prepare.dirHint": "Publish directory created in the host controlled temp area ({dir}): items/<id>/manifest.json + config.zip",
11141
- "publish.prepare.copyManifest": "Copy manifest",
11142
- "publish.push.hint": "First download the \"publish package\" and extract it as dist/, then run the commands below in your own public repository (the plugin performs no git writes and holds no credentials):",
11143
- "publish.push.commands": "git commands",
11144
- "publish.push.download": "Download publish package",
11145
- "publish.push.copy": "Copy commands",
11146
- "publish.push.repoMissing": "Host repo URL is not set — fill it in the previous \"Generate item package\" step, or push the items/{id}/ directory to your public repo manually.",
11147
- "publish.submit.hint": "Append the snippet below to the items[] array in the official market repo index.json, then open a PR. Listings are manually reviewed by the maintainer.",
11148
- "publish.submit.snippet": "index.json append snippet (a new entry in items[])",
11149
- "publish.submit.copy": "Copy snippet",
11150
- "publish.submit.fork": "Flow: fork the official repo ({url}) → append the snippet to items[] in index.json → open a PR.",
11151
- "publish.submit.manual": "Alternatively, file an issue with the \"item ID + repo URL\" and the maintainer will add it manually.",
11152
- "publish.submit.repoMissing": "The listing reference needs a host repo URL — fill it in the previous \"Generate item package\" step first.",
11153
- "publish.copied": "Copied",
11154
- "publish.copyFailed": "Copy failed — select the text manually",
11155
10342
  "myconfigs.tab.browse": "Browse Market",
11156
10343
  "myconfigs.tab.myconfigs": "My Configs",
11157
10344
  "myconfigs.back": "Back to market",