nx-sk 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ :root{--ink: #14161a;--paper: #fff;--soft: #f5f6f7;--soft-2: #ebedf0;--mid: #8b9096;--danger: #b3261e;--border: 1px solid var(--soft-2);--row-h: 28px;--btn-h: 32px;--fs-core: 12px;--radius-btn: 8px;--shadow-relief: -1px -1px 2px rgba(255, 255, 255, .85), 2px 2px 6px rgba(20, 22, 26, .07);--shadow-relief-press: inset 1px 1px 2px rgba(20, 22, 26, .1), inset -1px -1px 2px rgba(255, 255, 255, .7);--shadow-relief-inset: inset 1px 1px 2px rgba(20, 22, 26, .06), inset -1px -1px 2px rgba(255, 255, 255, .9)}*{box-sizing:border-box}html,body,#root{height:100%}body{margin:0;background:var(--paper);color:var(--ink);font-family:system-ui,-apple-system,Segoe UI,Microsoft YaHei,sans-serif;font-size:var(--fs-core);line-height:1.5;-webkit-font-smoothing:antialiased}.mono,code,.path,.cli-cmd{font-family:ui-monospace,Consolas,Courier New,monospace}.muted{color:var(--mid)}.bad{color:var(--danger)}.nowrap{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pad{padding:10px 14px}.rowgap{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.grow{flex:1;min-width:0}.right{margin-left:auto}.app{display:flex;flex-direction:column;height:100%;min-height:0}header.top{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:10px 14px;border-bottom:var(--border);flex-shrink:0}header.top .brand{font-size:15px;font-weight:700;letter-spacing:.2px;line-height:1;display:inline-flex;align-items:center;gap:8px}header.top .brand img{width:22px;height:22px;border-radius:6px;display:block}header.top .tagline{color:var(--mid);font-size:11px}header.top .ver{font-size:10px;color:var(--mid);background:var(--soft);border:var(--border);border-radius:999px;padding:1px 8px;line-height:1.7}header.top .store{display:inline-flex;align-items:center;gap:6px;min-width:0;font-size:11px;color:var(--mid)}header.top .store .store-label{color:var(--mid)}header.top .store .copyable{max-width:30ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nav.tabs{display:flex;flex-wrap:wrap;align-items:center;gap:6px;padding:6px 14px;border-bottom:var(--border);flex-shrink:0}.tabs-sep{width:1px;height:18px;background:var(--soft-2);margin:0 4px}.tab-group{position:relative;display:inline-flex}.tab-group .caret{width:0;height:0;margin-left:1px;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid var(--mid)}.tab-group .tab.active .caret{border-top-color:var(--ink)}.tab-menu{position:absolute;top:calc(100% + 4px);left:0;z-index:40;min-width:172px;padding:4px;display:flex;flex-direction:column;gap:2px;background:var(--paper);border:var(--border);border-radius:var(--radius-btn);box-shadow:var(--shadow-relief)}.tab-menu:before{content:"";position:absolute;left:0;right:0;top:-5px;height:5px}.tab-menu-row{display:flex;align-items:center;gap:10px;width:100%;height:var(--row-h);padding:0 8px;border:0;border-radius:6px;background:none;color:var(--ink);font:inherit;font-size:var(--fs-core);text-align:left;cursor:pointer}.tab-menu-row:hover{background:var(--soft)}.tab-menu-row.current{font-weight:600}.tab-menu-row .count{color:var(--mid);font-size:10px;font-variant-numeric:tabular-nums}main.body{flex:1;min-height:0;overflow:auto}.tab{font:inherit;font-size:var(--fs-core);height:var(--btn-h);padding:0 12px;border:0;border-radius:var(--radius-btn);background:var(--soft);color:var(--ink);cursor:pointer;display:inline-flex;align-items:center;gap:7px}.tab .count{color:var(--mid);font-size:10px;font-variant-numeric:tabular-nums}.tab.active{background:var(--paper);box-shadow:var(--shadow-relief);font-weight:600}.btn{font:inherit;font-size:var(--fs-core);cursor:pointer;height:var(--btn-h);padding:0 12px;border:0;border-radius:var(--radius-btn);background:var(--ink);color:var(--paper);box-shadow:var(--shadow-relief);display:inline-flex;align-items:center;gap:4px;transition:box-shadow .12s,transform .08s}.btn:hover:not(:disabled){box-shadow:var(--shadow-relief),0 0 0 3px #14161a0d}.btn:active:not(:disabled){box-shadow:var(--shadow-relief-press);transform:translateY(1px)}.btn:disabled{background:var(--soft);color:var(--mid);box-shadow:var(--shadow-relief-inset);cursor:not-allowed}.btn.ghost{background:var(--soft);color:var(--ink)}.btn.danger{background:var(--danger);color:#fff}.btn.small{height:22px;padding:0 8px;font-size:11px;border-radius:6px}input,select,textarea{font:inherit;font-size:var(--fs-core);color:var(--ink);background:var(--paper);border:var(--border);border-radius:6px;padding:0 8px}input,select{height:var(--row-h)}textarea{padding:5px 8px;resize:vertical;min-height:56px;line-height:1.5}input:focus,select:focus,textarea:focus{outline:none;border-color:var(--soft-2);box-shadow:var(--shadow-relief-inset)}label.inline{display:inline-flex;align-items:center;gap:6px}.card{border-bottom:var(--border)}.card:last-child{border-bottom:0}.card>.colhead{display:flex;align-items:center;gap:8px;height:var(--row-h);padding:0 14px;border-bottom:var(--border);font-size:13px;font-weight:600}.card>.colbody{padding:0}.row{display:flex;align-items:center;gap:8px;min-height:var(--row-h);padding:0 14px;border-bottom:var(--border);font-size:var(--fs-core);transition:background .12s}.row:hover{background:var(--soft)}.row:last-child{border-bottom:0}.row .name{font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row .desc{color:var(--mid);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row .acts{margin-left:auto;display:flex;align-items:center;gap:6px;flex-shrink:0}.row.selectable{cursor:pointer}.row.selected{background:var(--soft);box-shadow:inset 2px 0 0 var(--ink)}.tag{display:inline-flex;align-items:center;height:22px;padding:0 7px;border-radius:999px;background:var(--soft);color:var(--mid);font-size:11px;white-space:nowrap}.tag.strong{background:var(--ink);color:var(--paper)}.tag.bad{background:#fbeae9;color:var(--danger)}.copyable{cursor:copy;border-radius:4px;padding:0 3px}.copyable:hover{background:var(--soft)}.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:0 14px}.field{border-bottom:var(--border);padding:5px 0;min-width:0}.field>.flabel{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--mid)}.field>.flabel .fkey{font-family:ui-monospace,Consolas,monospace;font-size:10px;color:var(--soft-2)}.field>.fctrl{margin-top:3px}.field input,.field select,.field textarea{width:100%}.field.wide{grid-column:1 / -1}.group-title{display:flex;align-items:center;gap:8px;height:var(--row-h);padding:0 14px;border-bottom:var(--border);background:var(--soft);font-size:11px;color:var(--mid);letter-spacing:.5px}.cli-hint{display:flex;flex-wrap:wrap;align-items:center;gap:2px;padding:6px 14px;color:var(--mid);font-size:11px;border-top:var(--border)}.cli-hint-label{margin-right:4px}.cli-hint-sep{color:var(--soft-2)}.cli-cmd{color:var(--ink)}.toasts{position:fixed;right:14px;bottom:14px;display:flex;flex-direction:column;gap:6px;z-index:40}.toast{background:var(--ink);color:var(--paper);padding:7px 12px;border-radius:8px;font-size:var(--fs-core);box-shadow:var(--shadow-relief);max-width:420px}.toast.bad{background:var(--danger)}.mask{position:fixed;top:0;right:0;bottom:0;left:0;background:#14161a47;display:flex;align-items:center;justify-content:center;z-index:50;padding:20px}.dialog{background:var(--paper);border-radius:8px;min-width:320px;max-width:min(880px,92vw);max-height:86vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 10px 40px #14161a2e}.dialog-title{display:flex;align-items:center;gap:8px;height:var(--row-h);padding:0 14px;border-bottom:var(--border);font-size:13px;font-weight:600;flex-shrink:0}.dialog-body{padding:10px 14px;overflow:auto;min-height:0}.dialog-foot{display:flex;gap:8px;justify-content:flex-end;padding:8px 14px;border-top:var(--border);flex-shrink:0}pre.block{margin:0;padding:10px 14px;background:var(--soft);border-radius:8px;font-family:ui-monospace,Consolas,monospace;font-size:11px;line-height:1.55;white-space:pre-wrap;word-break:break-word;max-height:60vh;overflow:auto}.hint{color:var(--mid);font-size:11px}.stack{display:flex;flex-direction:column;gap:10px}.toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.cols{display:flex;gap:14px;align-items:flex-start;padding:0 14px 14px}.col{flex:1;min-width:0}.desc{color:var(--mid)}.cols>.col>.card{border:var(--border);border-radius:4px}.files-page{min-height:100%;position:relative}.files-page.drag-over{outline:2px dashed var(--ink);outline-offset:-8px;background:var(--soft)}.tab.small{height:22px;padding:0 8px;font-size:11px;border-radius:6px}.file-filters{padding-top:4px;padding-bottom:4px}.drop-hint{margin:0 14px;padding:18px;text-align:center;border:1px dashed var(--ink);border-radius:8px;background:var(--paper);font-weight:600}.file-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:10px}.file-card{display:flex;flex-direction:column;border:var(--border);border-radius:8px;overflow:hidden;background:var(--paper)}.file-thumb{display:flex;align-items:center;justify-content:center;height:120px;background:var(--soft);border-bottom:var(--border)}.file-thumb img{max-width:100%;max-height:100%;object-fit:contain}.ext-badge{font-size:22px;font-weight:700;letter-spacing:1px;color:var(--mid);font-family:ui-monospace,Consolas,monospace}.file-meta{padding:8px 10px 4px;display:flex;flex-direction:column;gap:4px}a.file-name{color:var(--ink);font-weight:600;text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}a.file-name:hover{text-decoration:underline}.file-acts{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:6px 10px 8px;margin-top:auto}.btn.ghost.danger-ghost{color:var(--danger)}.file-picker{display:flex;flex-direction:column;gap:4px}.file-picker.drag{outline:2px dashed var(--ink);outline-offset:-4px;background:var(--soft)}.file-picker .file-pick-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.file-picker select{flex:1;min-width:120px}.file-preview{display:flex;align-items:center;gap:8px;border:var(--border);border-radius:6px;padding:4px 8px;background:var(--soft)}.file-preview img{width:40px;height:40px;object-fit:cover;border-radius:4px;border:var(--border);background:#fff}.file-preview .file-preview-name{color:var(--ink);text-decoration:none;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-preview .file-preview-name:hover{text-decoration:underline}
@@ -1,4 +1,4 @@
1
- import{u as C,r as i,a as h,j as s,b,T as j,E as f,C as y,M as E}from"./index-Dxf_b602.js";import M from"./view-cl8ITPVa.js";function J(){var u,N,v;const{boot:r,toast:d,guard:w}=C(),[g,k]=i.useState([]),[a,S]=i.useState(null),[c,n]=i.useState({format:"both",section:"",out:"",withSecrets:!1}),[o,m]=i.useState(null),x=i.useCallback(async()=>{try{const[e,l]=await Promise.all([h("/api/sections"),h("/api/exports")]);k(e.sections),S(l)}catch(e){d(e.message,"bad")}},[d]);i.useEffect(()=>{x()},[x]),i.useEffect(()=>{const e=r==null?void 0:r.settings;e&&n(l=>({...l,format:e.exportFormat||"both",withSecrets:!!e.includeSecretsInExport,out:e.exportDir||""}))},[r]);const $=()=>({format:c.format,out:c.out.trim()||void 0,section:c.section||void 0,...c.withSecrets?{"with-secrets":!0}:{"no-secrets":!0}}),p=w(async e=>{const l=await h("/api/export",{method:"POST",body:{...$(),"dry-run":!!e}});if(e){m({title:"试运行(没有写盘)",body:s.jsx("pre",{className:"block",children:`目录: ${l.wouldWrite.dir}
1
+ import{u as C,r as i,a as h,j as s,b,T as j,E as f,C as y,M as E}from"./index-VCOWbzUk.js";import M from"./view-DAeNDUhQ.js";function J(){var u,N,v;const{boot:r,toast:d,guard:w}=C(),[g,k]=i.useState([]),[a,S]=i.useState(null),[c,n]=i.useState({format:"both",section:"",out:"",withSecrets:!1}),[o,m]=i.useState(null),x=i.useCallback(async()=>{try{const[e,l]=await Promise.all([h("/api/sections"),h("/api/exports")]);k(e.sections),S(l)}catch(e){d(e.message,"bad")}},[d]);i.useEffect(()=>{x()},[x]),i.useEffect(()=>{const e=r==null?void 0:r.settings;e&&n(l=>({...l,format:e.exportFormat||"both",withSecrets:!!e.includeSecretsInExport,out:e.exportDir||""}))},[r]);const $=()=>({format:c.format,out:c.out.trim()||void 0,section:c.section||void 0,...c.withSecrets?{"with-secrets":!0}:{"no-secrets":!0}}),p=w(async e=>{const l=await h("/api/export",{method:"POST",body:{...$(),"dry-run":!!e}});if(e){m({title:"试运行(没有写盘)",body:s.jsx("pre",{className:"block",children:`目录: ${l.wouldWrite.dir}
2
2
  栏目: ${l.wouldWrite.sections} 条目: ${l.wouldWrite.entries}
3
3
  凭据: ${l.wouldWrite.withSecrets?"含明文":"已打码"}
4
4
 
@@ -1 +1 @@
1
- import{u as R,r as j,a as h,j as e,T as N,E as T,C as I,M as U}from"./index-Dxf_b602.js";const P=["text","textarea","number","date","month","bool","select","tags","secret","file"];function M(){const{reload:d,toast:n,guard:o,dialog:u}=R(),[t,b]=j.useState(null),[f,r]=j.useState([]),[x,i]=j.useState(null),[c,m]=j.useState(null),a=j.useCallback(async()=>{try{const[s,l]=await Promise.all([h("/api/sections"),h("/api/sections/templates")]);b(s),r(l.templates),i(null)}catch(s){i(s.message)}},[]);j.useEffect(()=>{a()},[a]);const p=o(async s=>{const l=await h(`/api/sections/${encodeURIComponent(s.id)}/dump`);m({kind:"dump",title:`${s.title}(${s.id})的全部信息`,body:e.jsx("pre",{className:"block",children:JSON.stringify(l,null,2)})})}),C=o(async s=>{const l=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({kind:"edit",id:s.id,form:{title:l.title,description:l.description,order:String(l.order),titleLabel:l.titleLabel,titleField:l.titleField||""},full:l})}),w=o(async s=>{const l={id:s.id.trim(),title:s.title.trim()||s.id.trim(),template:s.template||void 0,description:s.description,order:Number(s.order)||50};await h("/api/sections",{method:"POST",body:l}),n("栏目已创建"),m(null),await a(),await d()}),$=o(async s=>{const l={title:s.form.title,description:s.form.description,order:Number(s.form.order)||50,titleLabel:s.form.titleLabel,titleField:s.form.titleField};await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:l}),n("栏目已更新");const v=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:v}),await a(),await d()}),S=o(async(s,l)=>{await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:{"add-field":l}}),n(`已新增字段 ${l.key}`);const v=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:v}),await a()}),F=o(async s=>{var y,k;const l=await h(`/api/sections/${encodeURIComponent(s.id)}/sync`,{method:"POST"});if(l.upToDate){n("已是最新,模板里没有需要补的字段");return}const v=[(y=l.addedGroups)!=null&&y.length?`分组 ${l.addedGroups.join("、")}`:"",(k=l.addedFields)!=null&&k.length?`字段 ${l.addedFields.join("、")}`:""].filter(Boolean).join(";");n(`已按模板补齐:${v||"无变化"}`);const g=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:g}),await a(),await d()}),E=o(async(s,l)=>{if(!await u({message:`从字段字典里删掉「${l}」?已填的值会留在条目里(只是不再显示/校验)。`,danger:!0,confirmText:"删除字段"}))return;await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:{"remove-field":l}}),n(`已删字段 ${l}`);const g=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:g}),await a()}),L=o(async s=>{await u({message:`删除栏目「${s.title}」,连带其中 ${s.entries} 条条目?删除前会自动留快照。`,danger:!0,confirmText:"连带删除"})&&(await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"DELETE",body:{force:!0}}),n("栏目已删除"),await a(),await d())});return x?e.jsxs("div",{className:"pad stack",children:[e.jsx("div",{className:"bad",children:e.jsx("strong",{children:"读不到栏目"})}),e.jsx("pre",{className:"block",children:x})]}):t?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:"栏目"}),e.jsxs("span",{className:"muted",children:["共 ",t.count," 个 · 条目 ",t.totalEntries," 条"]}),e.jsx("span",{className:"grow"}),e.jsx("button",{className:"btn small ghost",onClick:a,children:"刷新"}),e.jsx("button",{className:"btn small",onClick:()=>{var s;return m({kind:"create",form:{id:"",title:"",template:((s=f[0])==null?void 0:s.id)||"",description:"",order:"50"}})},children:"新建栏目"})]}),e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"colhead",children:"全部栏目"}),e.jsx("div",{className:"colbody",children:t.sections.length?t.sections.map(s=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:s.title}),e.jsx("span",{className:"mono muted",children:s.id}),s.template?e.jsx(N,{children:s.template}):e.jsx(N,{children:"自定义"}),e.jsx("span",{className:"desc",children:s.description}),e.jsxs("span",{className:"acts",children:[e.jsxs("span",{className:"tag",children:[s.fields," 字段"]}),e.jsxs("span",{className:"tag",children:[s.entries," 条目"]}),e.jsx("button",{className:"btn small ghost",onClick:()=>p(s),children:"全部信息"}),e.jsx("button",{className:"btn small ghost",onClick:()=>C(s),children:"编辑"}),e.jsx("button",{className:"btn small ghost",onClick:()=>L(s),children:"删除"})]})]},s.id)):e.jsx(T,{children:"还没有栏目。点「新建栏目」,用内置模板(求职 / 密钥)起步。"})})]}),e.jsx("div",{className:"pad hint",children:"栏目 = 一组同构条目 + 一份字段字典。字典是数据,所以这里加一个字段,CLI 与面板立刻就都能填它。"}),e.jsx(I,{module:"sections"}),c?e.jsxs(U,{title:z(c),onClose:()=>m(null),children:[c.kind==="dump"?c.body:null,c.kind==="create"?e.jsx(A,{templates:f,form:c.form,onChange:s=>m(l=>({...l,form:{...l.form,...s}})),onSubmit:()=>{if(!/^[a-z][a-z0-9_-]{0,63}$/.test(c.form.id.trim())){n("栏目 id 必须是小写字母开头,仅含 a-z 0-9 _ -","bad");return}w(c.form)}}):null,c.kind==="edit"?e.jsx(D,{modal:c,onChange:s=>m(l=>({...l,form:{...l.form,...s}})),onSave:()=>$(c),onSync:()=>F(c),onAddField:s=>S(c,s),onRemoveField:s=>E(c,s),onClose:()=>m(null)}):null]}):null]}):e.jsx("div",{className:"pad muted",children:"加载中…"})}function z(d){return d.kind==="dump"?d.title:d.kind==="create"?"新建栏目":`编辑栏目 ${d.id}`}function A({templates:d,form:n,onChange:o,onSubmit:u}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"grid",children:[e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"栏目 id"}),e.jsx("span",{className:"fkey",children:"小写字母开头,仅 a-z 0-9 _ -"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.id,onChange:t=>o({id:t.target.value}),placeholder:"例如 projects"})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"标题"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.title,onChange:t=>o({title:t.target.value}),placeholder:"例如 项目档案"})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"从模板起步"})}),e.jsx("div",{className:"fctrl",children:e.jsxs("select",{value:n.template,onChange:t=>o({template:t.target.value}),children:[e.jsx("option",{value:"",children:"(空栏目,自定义字段)"}),d.map(t=>e.jsxs("option",{value:t.id,children:[t.label,"(",t.fields," 字段)"]},t.id))]})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"排序"}),e.jsx("span",{className:"fkey",children:"越小越靠前"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{type:"number",value:n.order,onChange:t=>o({order:t.target.value})})})]})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"描述"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.description,onChange:t=>o({description:t.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:u,children:"创建"}),e.jsx("span",{className:"hint",children:"同 id 已存在会直接报冲突,不会静默覆盖。"})]})]})}function D({modal:d,onChange:n,onSave:o,onSync:u,onAddField:t,onRemoveField:b,onClose:f}){var m;const[r,x]=j.useState({key:"",label:"",type:"text",group:""}),i=d.full,c=(i==null?void 0:i.groupList)||[];return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"grid",children:[e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"标题"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.title,onChange:a=>n({title:a.target.value})})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"排序"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{type:"number",value:d.form.order,onChange:a=>n({order:a.target.value})})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"条目名字段"}),e.jsx("span",{className:"fkey",children:"列表里显示哪个字段"})]}),e.jsx("div",{className:"fctrl",children:e.jsxs("select",{value:d.form.titleField,onChange:a=>n({titleField:a.target.value}),children:[e.jsx("option",{value:"",children:"(用条目 title)"}),((i==null?void 0:i.fieldList)||[]).map(a=>e.jsxs("option",{value:a.key,children:[a.label,"(",a.key,")"]},a.key))]})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"名称标签"}),e.jsx("span",{className:"fkey",children:"新建条目时问什么"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.titleLabel,onChange:a=>n({titleLabel:a.target.value})})})]})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"描述"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.description,onChange:a=>n({description:a.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:o,children:"保存栏目属性"}),i!=null&&i.template?e.jsx("button",{className:"btn ghost",onClick:u,title:`按模板「${i.template}」补齐新增的分组与字段(只增不改不删)`,children:"同步模板字段"}):null,e.jsx("button",{className:"btn ghost",onClick:f,children:"关闭"})]}),i!=null&&i.template?e.jsxs("div",{className:"hint",children:["本栏目由模板「",i.template,"」创建。模板后来新增的字段**不会自动**出现在这里, 点「同步模板字段」补齐(例如求职模板新增的「附件资料」组 / 简历、头像等 file 字段)。"]}):null,e.jsxs("div",{className:"card",style:{border:"1px solid var(--soft-2)",borderRadius:4},children:[e.jsxs("div",{className:"colhead",children:["字段字典(",((m=i==null?void 0:i.fieldList)==null?void 0:m.length)||0,")"]}),e.jsxs("div",{className:"colbody",children:[c.map(a=>e.jsxs("div",{children:[e.jsxs("div",{className:"group-title",children:[a.title,"(",a.id,")"]}),(i.fieldList||[]).filter(p=>p.group===a.id).map(p=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:p.label}),e.jsx("span",{className:"mono muted",children:p.key}),e.jsx(N,{children:p.type}),p.sensitive?e.jsx(N,{kind:"bad",children:"密文"}):null,e.jsx("span",{className:"desc",children:p.hint||""}),e.jsx("span",{className:"acts",children:e.jsx("button",{className:"btn small ghost",onClick:()=>b(p.key),children:"删除"})})]},p.key))]},a.id)),((i==null?void 0:i.fieldList)||[]).filter(a=>!c.some(p=>p.id===a.group)).map(a=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:a.label}),e.jsx("span",{className:"mono muted",children:a.key}),e.jsx(N,{children:a.type}),e.jsxs("span",{className:"desc",children:["分组 ",a.group,"(不在 groups 列表里)"]}),e.jsx("span",{className:"acts",children:e.jsx("button",{className:"btn small ghost",onClick:()=>b(a.key),children:"删除"})})]},a.key))]})]}),e.jsxs("div",{className:"card",style:{border:"1px solid var(--soft-2)",borderRadius:4},children:[e.jsx("div",{className:"colhead",children:"新增字段"}),e.jsxs("div",{className:"pad rowgap",children:[e.jsx("input",{style:{width:150},placeholder:"key(英文字母开头)",value:r.key,onChange:a=>x({...r,key:a.target.value})}),e.jsx("input",{style:{width:150},placeholder:"标签(中文名)",value:r.label,onChange:a=>x({...r,label:a.target.value})}),e.jsx("select",{style:{width:110},value:r.type,onChange:a=>x({...r,type:a.target.value}),children:P.map(a=>e.jsx("option",{value:a,children:a},a))}),e.jsxs("select",{style:{width:150},value:r.group,onChange:a=>x({...r,group:a.target.value}),children:[e.jsx("option",{value:"",children:"(首个分组)"}),c.map(a=>e.jsx("option",{value:a.id,children:a.title},a.id))]}),e.jsx("button",{className:"btn small",onClick:()=>{const a={key:r.key.trim(),label:r.label.trim()||r.key.trim(),type:r.type};r.group&&(a.group=r.group),t(a),x({key:"",label:"",type:"text",group:r.group})},children:"加入字典"}),e.jsx("span",{className:"hint",children:"加完立刻生效:CLI 与面板都能填它。"})]})]})]})}export{M as default};
1
+ import{u as R,r as j,a as h,j as e,T as N,E as T,C as I,M as U}from"./index-VCOWbzUk.js";const P=["text","textarea","number","date","month","bool","select","tags","secret","file"];function M(){const{reload:d,toast:n,guard:o,dialog:u}=R(),[t,b]=j.useState(null),[f,r]=j.useState([]),[x,i]=j.useState(null),[c,m]=j.useState(null),a=j.useCallback(async()=>{try{const[s,l]=await Promise.all([h("/api/sections"),h("/api/sections/templates")]);b(s),r(l.templates),i(null)}catch(s){i(s.message)}},[]);j.useEffect(()=>{a()},[a]);const p=o(async s=>{const l=await h(`/api/sections/${encodeURIComponent(s.id)}/dump`);m({kind:"dump",title:`${s.title}(${s.id})的全部信息`,body:e.jsx("pre",{className:"block",children:JSON.stringify(l,null,2)})})}),C=o(async s=>{const l=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({kind:"edit",id:s.id,form:{title:l.title,description:l.description,order:String(l.order),titleLabel:l.titleLabel,titleField:l.titleField||""},full:l})}),w=o(async s=>{const l={id:s.id.trim(),title:s.title.trim()||s.id.trim(),template:s.template||void 0,description:s.description,order:Number(s.order)||50};await h("/api/sections",{method:"POST",body:l}),n("栏目已创建"),m(null),await a(),await d()}),$=o(async s=>{const l={title:s.form.title,description:s.form.description,order:Number(s.form.order)||50,titleLabel:s.form.titleLabel,titleField:s.form.titleField};await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:l}),n("栏目已更新");const v=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:v}),await a(),await d()}),S=o(async(s,l)=>{await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:{"add-field":l}}),n(`已新增字段 ${l.key}`);const v=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:v}),await a()}),F=o(async s=>{var y,k;const l=await h(`/api/sections/${encodeURIComponent(s.id)}/sync`,{method:"POST"});if(l.upToDate){n("已是最新,模板里没有需要补的字段");return}const v=[(y=l.addedGroups)!=null&&y.length?`分组 ${l.addedGroups.join("、")}`:"",(k=l.addedFields)!=null&&k.length?`字段 ${l.addedFields.join("、")}`:""].filter(Boolean).join(";");n(`已按模板补齐:${v||"无变化"}`);const g=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:g}),await a(),await d()}),E=o(async(s,l)=>{if(!await u({message:`从字段字典里删掉「${l}」?已填的值会留在条目里(只是不再显示/校验)。`,danger:!0,confirmText:"删除字段"}))return;await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:{"remove-field":l}}),n(`已删字段 ${l}`);const g=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:g}),await a()}),L=o(async s=>{await u({message:`删除栏目「${s.title}」,连带其中 ${s.entries} 条条目?删除前会自动留快照。`,danger:!0,confirmText:"连带删除"})&&(await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"DELETE",body:{force:!0}}),n("栏目已删除"),await a(),await d())});return x?e.jsxs("div",{className:"pad stack",children:[e.jsx("div",{className:"bad",children:e.jsx("strong",{children:"读不到栏目"})}),e.jsx("pre",{className:"block",children:x})]}):t?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:"栏目"}),e.jsxs("span",{className:"muted",children:["共 ",t.count," 个 · 条目 ",t.totalEntries," 条"]}),e.jsx("span",{className:"grow"}),e.jsx("button",{className:"btn small ghost",onClick:a,children:"刷新"}),e.jsx("button",{className:"btn small",onClick:()=>{var s;return m({kind:"create",form:{id:"",title:"",template:((s=f[0])==null?void 0:s.id)||"",description:"",order:"50"}})},children:"新建栏目"})]}),e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"colhead",children:"全部栏目"}),e.jsx("div",{className:"colbody",children:t.sections.length?t.sections.map(s=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:s.title}),e.jsx("span",{className:"mono muted",children:s.id}),s.template?e.jsx(N,{children:s.template}):e.jsx(N,{children:"自定义"}),e.jsx("span",{className:"desc",children:s.description}),e.jsxs("span",{className:"acts",children:[e.jsxs("span",{className:"tag",children:[s.fields," 字段"]}),e.jsxs("span",{className:"tag",children:[s.entries," 条目"]}),e.jsx("button",{className:"btn small ghost",onClick:()=>p(s),children:"全部信息"}),e.jsx("button",{className:"btn small ghost",onClick:()=>C(s),children:"编辑"}),e.jsx("button",{className:"btn small ghost",onClick:()=>L(s),children:"删除"})]})]},s.id)):e.jsx(T,{children:"还没有栏目。点「新建栏目」,用内置模板(求职 / 密钥)起步。"})})]}),e.jsx("div",{className:"pad hint",children:"栏目 = 一组同构条目 + 一份字段字典。字典是数据,所以这里加一个字段,CLI 与面板立刻就都能填它。"}),e.jsx(I,{module:"sections"}),c?e.jsxs(U,{title:z(c),onClose:()=>m(null),children:[c.kind==="dump"?c.body:null,c.kind==="create"?e.jsx(A,{templates:f,form:c.form,onChange:s=>m(l=>({...l,form:{...l.form,...s}})),onSubmit:()=>{if(!/^[a-z][a-z0-9_-]{0,63}$/.test(c.form.id.trim())){n("栏目 id 必须是小写字母开头,仅含 a-z 0-9 _ -","bad");return}w(c.form)}}):null,c.kind==="edit"?e.jsx(D,{modal:c,onChange:s=>m(l=>({...l,form:{...l.form,...s}})),onSave:()=>$(c),onSync:()=>F(c),onAddField:s=>S(c,s),onRemoveField:s=>E(c,s),onClose:()=>m(null)}):null]}):null]}):e.jsx("div",{className:"pad muted",children:"加载中…"})}function z(d){return d.kind==="dump"?d.title:d.kind==="create"?"新建栏目":`编辑栏目 ${d.id}`}function A({templates:d,form:n,onChange:o,onSubmit:u}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"grid",children:[e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"栏目 id"}),e.jsx("span",{className:"fkey",children:"小写字母开头,仅 a-z 0-9 _ -"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.id,onChange:t=>o({id:t.target.value}),placeholder:"例如 projects"})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"标题"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.title,onChange:t=>o({title:t.target.value}),placeholder:"例如 项目档案"})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"从模板起步"})}),e.jsx("div",{className:"fctrl",children:e.jsxs("select",{value:n.template,onChange:t=>o({template:t.target.value}),children:[e.jsx("option",{value:"",children:"(空栏目,自定义字段)"}),d.map(t=>e.jsxs("option",{value:t.id,children:[t.label,"(",t.fields," 字段)"]},t.id))]})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"排序"}),e.jsx("span",{className:"fkey",children:"越小越靠前"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{type:"number",value:n.order,onChange:t=>o({order:t.target.value})})})]})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"描述"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.description,onChange:t=>o({description:t.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:u,children:"创建"}),e.jsx("span",{className:"hint",children:"同 id 已存在会直接报冲突,不会静默覆盖。"})]})]})}function D({modal:d,onChange:n,onSave:o,onSync:u,onAddField:t,onRemoveField:b,onClose:f}){var m;const[r,x]=j.useState({key:"",label:"",type:"text",group:""}),i=d.full,c=(i==null?void 0:i.groupList)||[];return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"grid",children:[e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"标题"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.title,onChange:a=>n({title:a.target.value})})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"排序"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{type:"number",value:d.form.order,onChange:a=>n({order:a.target.value})})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"条目名字段"}),e.jsx("span",{className:"fkey",children:"列表里显示哪个字段"})]}),e.jsx("div",{className:"fctrl",children:e.jsxs("select",{value:d.form.titleField,onChange:a=>n({titleField:a.target.value}),children:[e.jsx("option",{value:"",children:"(用条目 title)"}),((i==null?void 0:i.fieldList)||[]).map(a=>e.jsxs("option",{value:a.key,children:[a.label,"(",a.key,")"]},a.key))]})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"名称标签"}),e.jsx("span",{className:"fkey",children:"新建条目时问什么"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.titleLabel,onChange:a=>n({titleLabel:a.target.value})})})]})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"描述"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.description,onChange:a=>n({description:a.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:o,children:"保存栏目属性"}),i!=null&&i.template?e.jsx("button",{className:"btn ghost",onClick:u,title:`按模板「${i.template}」补齐新增的分组与字段(只增不改不删)`,children:"同步模板字段"}):null,e.jsx("button",{className:"btn ghost",onClick:f,children:"关闭"})]}),i!=null&&i.template?e.jsxs("div",{className:"hint",children:["本栏目由模板「",i.template,"」创建。模板后来新增的字段",e.jsx("b",{children:"不会自动"}),"出现在这里, 点「同步模板字段」补齐(例如求职模板新增的「附件资料」组 / 简历、头像等 file 字段)。"]}):null,e.jsxs("div",{className:"card",style:{border:"1px solid var(--soft-2)",borderRadius:4},children:[e.jsxs("div",{className:"colhead",children:["字段字典(",((m=i==null?void 0:i.fieldList)==null?void 0:m.length)||0,")"]}),e.jsxs("div",{className:"colbody",children:[c.map(a=>e.jsxs("div",{children:[e.jsxs("div",{className:"group-title",children:[a.title,"(",a.id,")"]}),(i.fieldList||[]).filter(p=>p.group===a.id).map(p=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:p.label}),e.jsx("span",{className:"mono muted",children:p.key}),e.jsx(N,{children:p.type}),p.sensitive?e.jsx(N,{kind:"bad",children:"密文"}):null,e.jsx("span",{className:"desc",children:p.hint||""}),e.jsx("span",{className:"acts",children:e.jsx("button",{className:"btn small ghost",onClick:()=>b(p.key),children:"删除"})})]},p.key))]},a.id)),((i==null?void 0:i.fieldList)||[]).filter(a=>!c.some(p=>p.id===a.group)).map(a=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:a.label}),e.jsx("span",{className:"mono muted",children:a.key}),e.jsx(N,{children:a.type}),e.jsxs("span",{className:"desc",children:["分组 ",a.group,"(不在 groups 列表里)"]}),e.jsx("span",{className:"acts",children:e.jsx("button",{className:"btn small ghost",onClick:()=>b(a.key),children:"删除"})})]},a.key))]})]}),e.jsxs("div",{className:"card",style:{border:"1px solid var(--soft-2)",borderRadius:4},children:[e.jsx("div",{className:"colhead",children:"新增字段"}),e.jsxs("div",{className:"pad rowgap",children:[e.jsx("input",{style:{width:150},placeholder:"key(英文字母开头)",value:r.key,onChange:a=>x({...r,key:a.target.value})}),e.jsx("input",{style:{width:150},placeholder:"标签(中文名)",value:r.label,onChange:a=>x({...r,label:a.target.value})}),e.jsx("select",{style:{width:110},value:r.type,onChange:a=>x({...r,type:a.target.value}),children:P.map(a=>e.jsx("option",{value:a,children:a},a))}),e.jsxs("select",{style:{width:150},value:r.group,onChange:a=>x({...r,group:a.target.value}),children:[e.jsx("option",{value:"",children:"(首个分组)"}),c.map(a=>e.jsx("option",{value:a.id,children:a.title},a.id))]}),e.jsx("button",{className:"btn small",onClick:()=>{const a={key:r.key.trim(),label:r.label.trim()||r.key.trim(),type:r.type};r.group&&(a.group=r.group),t(a),x({key:"",label:"",type:"text",group:r.group})},children:"加入字典"}),e.jsx("span",{className:"hint",children:"加完立刻生效:CLI 与面板都能填它。"})]})]})]})}export{M as default};
@@ -0,0 +1 @@
1
+ import{u as V,r as u,a as w,j as s,b as v,E as _,C as B,T as J}from"./index-VCOWbzUk.js";function U(){var E,F,I,G,M,P,T,A;const{boot:e,reload:N,toast:x,guard:k}=V(),[f,g]=u.useState(null),[d,p]=u.useState(null),[m,h]=u.useState([]),y=typeof window>"u"?"":window.location.origin,C=typeof window>"u"?"":window.location.port||"80",b=u.useCallback(async()=>{try{const a=await w("/api/settings");g(a),p({...a.settings}),h((a.settings.navGroups||[]).map(l=>({title:l.title,sections:[...l.sections]})))}catch(a){x(a.message,"bad")}},[x]);u.useEffect(()=>{b()},[b]);const D=k(async()=>{const a={set:{exportDir:d.exportDir,exportFormat:d.exportFormat,includeSecretsInExport:!!d.includeSecretsInExport,defaultSection:d.defaultSection}},l=await w("/api/settings",{method:"PATCH",body:a});x("设置已保存"),g({...f,settings:l.set}),await N()}),R=(a,l)=>h(i=>i.map((n,t)=>t===a?{...n,...l}:n)),H=(a,l)=>h(i=>i.map((n,t)=>t===a&&!n.sections.includes(l)?{...n,sections:[...n.sections,l]}:n)),K=(a,l)=>h(i=>i.map((n,t)=>t===a?{...n,sections:n.sections.filter(c=>c!==l)}:n)),S=(a,l,i)=>h(n=>n.map((t,c)=>{if(c!==a)return t;const r=l+i;if(r<0||r>=t.sections.length)return t;const o=[...t.sections];return[o[l],o[r]]=[o[r],o[l]],{...t,sections:o}})),$=(a,l)=>h(i=>{const n=a+l;if(n<0||n>=i.length)return i;const t=[...i];return[t[a],t[n]]=[t[n],t[a]],t}),O=k(async()=>{if(m.find(n=>!String(n.title).trim())){x("每个分组都要有组名","bad");return}const l=m.find(n=>!n.sections.length);if(l){x(`分组「${l.title}」还没有勾选栏目——要么勾一个,要么删掉这个分组`,"bad");return}const i=await w("/api/settings",{method:"PATCH",body:{set:{navGroups:m}}});x(m.length?`已保存 ${m.length} 个导航分组`:"已取消分组,栏目恢复平铺"),g({...f,settings:i.set}),h((i.set.navGroups||[]).map(n=>({title:n.title,sections:[...n.sections]}))),await N()});return!f||!d?s.jsx("div",{className:"pad muted",children:"加载中…"}):s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"toolbar rowgap pad",children:[s.jsx("strong",{children:"设置"}),s.jsx("span",{className:"muted",children:"本机配置与运行状态"}),s.jsx("span",{className:"grow"}),s.jsx("button",{className:"btn small ghost",onClick:b,children:"重新读取"})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"存储与运行"}),s.jsxs("div",{className:"colbody",children:[s.jsx(j,{label:"数据目录",value:e==null?void 0:e.home,copy:e==null?void 0:e.home}),s.jsx(j,{label:"store.json",value:e==null?void 0:e.storePath,copy:e==null?void 0:e.storePathRaw}),s.jsx(j,{label:"栏目 / 条目",value:e?`${e.counts.sections} / ${e.counts.entries}`:""}),s.jsx(j,{label:"命令条数",value:e?String(e.counts.commands):""}),s.jsx(j,{label:"app / node",value:e?`v${e.app.version} · node ${e.app.node} · ${e.app.platform}`:""}),s.jsx(j,{label:"环境变量覆盖",value:e?Object.entries(e.envOverrides).filter(([,a])=>a).map(([a,l])=>`${a}=${l}`).join(" ")||"(无)":""})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"密钥(密文落盘)"}),s.jsxs("div",{className:"colbody",children:[s.jsx(j,{label:"密钥来源",value:((E=e==null?void 0:e.vault)==null?void 0:E.source)==="env"?"环境变量 NX_SK_PASSPHRASE(口令不落盘)":"本机密钥文件"}),s.jsx(j,{label:"密钥文件",value:((F=e==null?void 0:e.vault)==null?void 0:F.keyFile)||((I=e==null?void 0:e.vault)==null?void 0:I.saltFile)||"",copy:((G=e==null?void 0:e.vault)==null?void 0:G.keyFile)||((M=e==null?void 0:e.vault)==null?void 0:M.saltFile)}),s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc",children:(P=e==null?void 0:e.vault)==null?void 0:P.note})}),s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc bad",children:"删掉密钥文件或换掉口令后,已存的密文都解不开了——导出前先确认密钥可用。"})})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"面板与 CLI"}),s.jsxs("div",{className:"colbody",children:[s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:"面板地址"}),s.jsx("span",{className:"desc mono",children:y}),s.jsx("span",{className:"acts",children:s.jsx(v,{className:"mono",text:y,children:"复制"})})]}),s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:"重启面板"}),s.jsx("span",{className:"desc",children:"面板本身就是 serve 提供的;改代码后重启它即可"}),s.jsx("span",{className:"acts",children:s.jsx(v,{className:"mono",text:`nx-sk serve --port ${C}`,children:`nx-sk serve --port ${C}`})})]})]})]}),s.jsxs("div",{className:"card",children:[s.jsxs("div",{className:"colhead",children:["头部导航分组",s.jsx("span",{className:"right muted",children:m.length?`${m.length} 个分组`:"未分组(全部平铺)"})]}),s.jsxs("div",{className:"pad stack",children:[s.jsxs("div",{className:"hint",children:["把同域栏目收拢成一个头部入口:头部只显示组名,悬浮展开上下列表选具体栏目,",s.jsx("b",{children:"列表顺序就是这里的顺序"}),"(用上移 / 下移调)。不归组的栏目保持平铺; 组顺序 = 头部显示顺序;一个栏目只能属于一个分组。"]}),m.map((a,l)=>{const i=new Set(m.flatMap((c,r)=>r===l?[]:c.sections)),n=((e==null?void 0:e.sections)||[]).filter(c=>!a.sections.includes(c.id)&&!i.has(c.id)),t=c=>((e==null?void 0:e.sections)||[]).find(r=>r.id===c)||null;return s.jsxs("div",{className:"stack",style:{borderTop:l?"var(--border)":0,paddingTop:l?8:0},children:[s.jsxs("div",{className:"rowgap",children:[s.jsx("input",{value:a.title,placeholder:"组名,如「求职」",onChange:c=>R(l,{title:c.target.value}),style:{minWidth:160}}),s.jsx("button",{className:"btn small ghost",disabled:l===0,onClick:()=>$(l,-1),children:"上移"}),s.jsx("button",{className:"btn small ghost",disabled:l===m.length-1,onClick:()=>$(l,1),children:"下移"}),s.jsx("button",{className:"btn small ghost",onClick:()=>h(c=>c.filter((r,o)=>o!==l)),children:"删除组"}),s.jsxs("span",{className:"muted",children:["已选 ",a.sections.length," 个栏目"]})]}),s.jsx("div",{className:"colbody",children:a.sections.length?a.sections.map((c,r)=>{const o=t(c);return s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"mono muted",children:r+1}),s.jsx("span",{className:`name ${o?"":"bad"}`,children:o?o.title:`栏目已删除(${c})`}),s.jsx("span",{className:"desc",children:o?`${o.entries} 条`:"保存前请移除"}),s.jsxs("span",{className:"acts",children:[s.jsx("button",{className:"btn small ghost",disabled:r===0,onClick:()=>S(l,r,-1),children:"上移"}),s.jsx("button",{className:"btn small ghost",disabled:r===a.sections.length-1,onClick:()=>S(l,r,1),children:"下移"}),s.jsx("button",{className:"btn small ghost",onClick:()=>K(l,c),children:"移除"})]})]},c)}):s.jsx("div",{className:"muted",children:"这个分组还没有栏目——从下面点一个加进来。"})}),s.jsx("div",{className:"rowgap",children:n.length?n.map(c=>s.jsxs("button",{className:"btn small ghost",onClick:()=>H(l,c.id),children:["+ ",c.title,s.jsxs("span",{className:"count",children:[" ",c.entries]})]},c.id)):s.jsx("span",{className:"muted",children:"所有栏目都已归组"})})]},l)}),m.length?null:s.jsx("div",{className:"muted",children:"还没有分组——所有栏目都平铺在头部。"}),s.jsxs("div",{className:"rowgap",children:[s.jsx("button",{className:"btn",onClick:O,children:"保存分组"}),s.jsx("button",{className:"btn small ghost",onClick:()=>h(a=>[...a,{title:"",sections:[]}]),children:"新增分组"}),m.length?s.jsx("button",{className:"btn small ghost",onClick:()=>h([]),children:"清空(恢复平铺)"}):null]})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"默认行为"}),s.jsxs("div",{className:"pad",children:[s.jsxs("div",{className:"grid",children:[s.jsxs("div",{className:"field",children:[s.jsx("div",{className:"flabel",children:s.jsx("span",{children:"默认导出格式"})}),s.jsx("div",{className:"fctrl",children:s.jsxs("select",{value:d.exportFormat,onChange:a=>p({...d,exportFormat:a.target.value}),children:[s.jsx("option",{value:"both",children:"JSON + Markdown"}),s.jsx("option",{value:"json",children:"仅 JSON"}),s.jsx("option",{value:"md",children:"仅 Markdown"})]})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"默认导出目录"}),s.jsx("span",{className:"fkey",children:"留空 = ~/nx-sk/export"})]}),s.jsx("div",{className:"fctrl",children:s.jsx("input",{value:d.exportDir,onChange:a=>p({...d,exportDir:a.target.value})})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"默认栏目"}),s.jsx("span",{className:"fkey",children:"CLI 未指定 --section 时的首选"})]}),s.jsx("div",{className:"fctrl",children:s.jsx("input",{value:d.defaultSection,onChange:a=>p({...d,defaultSection:a.target.value})})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"导出默认带明文密钥"}),s.jsx("span",{className:"fkey",children:"默认开;要分享出去时关掉"})]}),s.jsx("div",{className:"fctrl",children:s.jsxs("label",{className:"inline",children:[s.jsx("input",{type:"checkbox",checked:!!d.includeSecretsInExport,onChange:a=>p({...d,includeSecretsInExport:a.target.checked})}),s.jsx("span",{className:d.includeSecretsInExport?"muted":"bad",children:d.includeSecretsInExport?"开(导出文件含真实凭据,注意别外发)":"关(凭据打码后再导出)"})]})})]})]}),s.jsx("div",{className:"rowgap",style:{marginTop:8},children:s.jsx("button",{className:"btn",onClick:D,children:"保存设置"})})]})]}),s.jsxs("div",{className:"card",children:[s.jsxs("div",{className:"colhead",children:["自动快照",s.jsx("span",{className:"right muted",children:((T=e==null?void 0:e.snapshots)==null?void 0:T.length)??0})]}),s.jsxs("div",{className:"colbody",children:[s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc",children:"每次删除条目 / 栏目、修改设置前,都会在 ~/nx-sk/backup 留一份可回滚的整库快照。"})}),(A=e==null?void 0:e.snapshots)!=null&&A.length?e.snapshots.map(a=>s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"mono name",children:a.name}),s.jsx("span",{className:"desc",children:a.at}),s.jsx("span",{className:"acts",children:s.jsxs("span",{className:"tag",children:[Math.ceil(a.bytes/1024)," KB"]})})]},a.name)):s.jsx(_,{children:"还没有快照。"})]})]}),e!=null&&e.migration?s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"数据迁移"}),s.jsxs("div",{className:"colbody",children:[s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc",children:["store 已从 v",e.migration.from," 迁到 v",e.migration.to,":密钥栏目字段 ",e.migration.fieldsBefore," → ",e.migration.fieldsAfter,",",e.migration.entries," 条条目"]})}),s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"desc",children:"迁移前的原始文件"}),s.jsx("span",{className:"acts",children:s.jsx(v,{className:"mono",text:e.migration.snapshotRaw||"",children:e.migration.snapshot})})]}),e.migration.legacyValuesKept?s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc",children:["另保留了 ",e.migration.legacyValuesKept," 个旧字段的值(不再显示,但没删)"]})}):null]})]}):null,e!=null&&e.corrupt?s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"上次读取异常"}),s.jsx("div",{className:"colbody",children:s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc bad",children:["store.json 解析失败,已挪到 ",e.corrupt.to,"(原文件没有被覆盖)"]})})})]}):null,s.jsx(B,{module:"settings"})]})}function j({label:e,value:N,copy:x}){return s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:e}),s.jsx("span",{className:"desc mono",children:N||"(空)"}),x?s.jsx("span",{className:"acts",children:s.jsx(v,{className:"mono",text:x,children:"复制"})}):s.jsx(J,{children:" "})]})}export{U as default};
@@ -1,4 +1,4 @@
1
- import{j as e,u as _,r as h,a as w,b as H,E as L,C as q,T as B,M as z}from"./index-Dxf_b602.js";import{l as V,f as Y,r as G}from"./files-I6hzWb3h.js";function Q({value:c,onChange:g,minRows:m=2,placeholder:x,disabled:l,maxLength:d}){const v=h.useRef(null);return h.useLayoutEffect(()=>{const s=v.current;s&&(s.style.height="auto",s.style.height=`${s.scrollHeight}px`)},[c]),e.jsx("textarea",{ref:v,value:String(c??""),onChange:s=>g(s.target.value),rows:m,placeholder:x,disabled:l,maxLength:d,style:{resize:"none",overflow:"hidden",width:"100%"}})}function W(c){const g=c==null?[]:Array.isArray(c)?c:[c],m=[];for(const x of g)for(const l of String(x??"").split(/[,,、;;]+/)){const d=l.trim();d&&!m.includes(d)&&m.push(d)}return m}function J({def:c,value:g,onChange:m}){const{toast:x,guard:l,patchUi:d}=_(),[v,s]=h.useState(null),[p,j]=h.useState(!1),S=h.useRef(null),C=h.useRef(0),f=W(g),r=Number(c.maxItems)||0,b=r>0&&f.length>=r,N=h.useCallback(async()=>{try{s(await V())}catch{}},[]);h.useEffect(()=>{N()},[N]);const I=(v==null?void 0:v.files)||[],T=i=>I.find(o=>o.id===i)||null,E=I.filter(i=>!f.includes(i.id)),D=l(async i=>{const o=[...i||[]];if(!o.length)return;if(b){x(`「${c.label}」最多 ${r} 个附件,先移除一个`,"bad");return}const U=[...f];let A=0;for(const F of o){const K=await Y(F),R=await w("/api/files",{method:"POST",body:{content:K,name:F.name}});if(U.includes(R.id)){A+=1;continue}U.push(R.id)}m(U);const O=o.length-A;x(A?`已上传并关联 ${O} 个;${A} 个库里已有,直接引用`:`已上传并关联 ${o.length} 个`),s(await G())}),a=i=>{i&&!f.includes(i)&&m([...f,i])},t=i=>m(f.filter(o=>o!==i)),y=l(async i=>{var o;C.current=0,j(!1),await D((o=i.dataTransfer)==null?void 0:o.files)});return e.jsxs("div",{className:`file-picker ${p?"drag":""}`,onDragEnter:i=>{i.preventDefault(),C.current+=1,j(!0)},onDragOver:i=>i.preventDefault(),onDragLeave:()=>{C.current-=1,C.current<=0&&(C.current=0,j(!1))},onDrop:y,children:[f.map(i=>{const o=T(i);return o?e.jsxs("div",{className:"file-preview",children:[o.isImage?e.jsx("img",{src:o.url,alt:o.name}):e.jsx("span",{className:"ext-badge",style:{fontSize:13,width:40,textAlign:"center"},children:o.ext?o.ext.slice(1).toUpperCase():"FILE"}),e.jsx("a",{className:"file-preview-name grow",href:o.url,target:"_blank",rel:"noreferrer",title:o.displayPath,children:o.name}),e.jsx("a",{className:"btn small ghost",href:o.downloadUrl,children:"下载"}),e.jsx(H,{className:"hint",text:o.path,title:"点击复制物理路径",children:"路径"}),e.jsx("button",{type:"button",className:"btn small ghost",onClick:()=>t(i),children:"移除"})]},i):e.jsxs("div",{className:"file-preview",children:[e.jsxs("span",{className:"hint bad grow",children:["引用的附件已不存在(",i,")—— 重新上传,或移除。"]}),e.jsx("button",{type:"button",className:"btn small ghost",onClick:()=>t(i),children:"移除"})]},i)}),f.length?null:e.jsx("span",{className:"hint",children:"还没有附件。"}),e.jsxs("div",{className:"file-pick-row",children:[e.jsxs("select",{value:"",disabled:b||!E.length,onChange:i=>a(i.target.value),children:[e.jsx("option",{value:"",children:E.length?"(从附件库选一个,选中即追加)":"(库里没有可再选的附件)"}),E.map(i=>e.jsx("option",{value:i.id,children:i.name},i.id))]}),e.jsx("button",{type:"button",className:"btn small",disabled:b,onClick:()=>{var i;return(i=S.current)==null?void 0:i.click()},children:"上传文件"}),e.jsx("button",{type:"button",className:"btn small ghost",onClick:()=>d({view:"files"}),children:"附件中心"}),e.jsx("input",{ref:S,type:"file",multiple:!0,style:{display:"none"},onChange:i=>{D(i.target.files),i.target.value=""}})]}),b?e.jsxs("span",{className:"hint",children:["已达上限(",r," 个):先移除一个,或把字段的 maxItems 调大。"]}):null,c.hint?e.jsx("span",{className:"hint",children:c.hint}):null]})}function X({def:c,value:g,onChange:m,disabled:x}){const l=c.type||"text",d=g??"";if(l==="file")return e.jsx(J,{def:c,value:d,onChange:m});if(l==="bool"){const s=d===!0||d==="true"||d==="是";return e.jsxs("label",{className:"inline",children:[e.jsx("input",{type:"checkbox",checked:s,onChange:p=>m(p.target.checked)}),e.jsx("span",{className:"muted",children:s?"是":"否"})]})}if(l==="select"){const s=c.options||[],p=d!==""&&!s.includes(d);return e.jsxs("select",{value:p?"__custom":String(d),onChange:j=>m(j.target.value==="__custom"?"":j.target.value),children:[e.jsx("option",{value:"",children:"(未填写)"}),s.map(j=>e.jsx("option",{value:j,children:j},j)),p?e.jsxs("option",{value:"__custom",children:[String(d),"(当前值,不在候选里)"]}):null]})}if(l==="textarea")return e.jsx(Q,{value:d,onChange:m});if(l==="tags"){const s=Array.isArray(d)?d.join("、"):String(d);return e.jsx("input",{value:s,placeholder:c.maxItems?`多个用「、」分隔,最多 ${c.maxItems} 个`:"多个用「、」分隔",onChange:p=>m(p.target.value.split(/[,,、;;]+/).map(j=>j.trim()).filter(Boolean))})}const v=l==="number"?"number":l==="date"?"date":l==="month"?"month":"text";return l==="secret"?e.jsx("input",{type:"text",value:String(d),placeholder:"密文落盘;原样保留占位则视为不修改",disabled:x,onChange:s=>m(s.target.value)}):e.jsx("input",{type:v,value:String(d),onChange:s=>m(s.target.value)})}function Z({title:c,fields:g,draft:m,onPatch:x}){return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"group-title",children:c}),e.jsx("div",{className:"pad",children:e.jsx("div",{className:"grid",children:g.map(l=>e.jsxs("div",{className:`field ${l.type==="textarea"||l.type==="file"?"wide":""}`,children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:l.label}),l.sensitive?e.jsx("span",{className:"tag bad",children:"密文"}):null,l.fill&&l.fill!=="normal"?e.jsx("span",{className:"tag",children:l.fillLabel||l.fill}):null,e.jsx("span",{className:"fkey",children:l.key})]}),e.jsx("div",{className:"fctrl",children:e.jsx(X,{def:l,value:m[l.key],onChange:d=>x(l.key,d)})})]},l.key))})})]})}function ee({sectionId:c,sectionTitle:g}){const{ui:m,patchUi:x,toast:l,guard:d,dialog:v,reload:s}=_(),[p,j]=h.useState(null),[S,C]=h.useState({}),[f,r]=h.useState({name:"",value:""}),b=!!m.mask,N=h.useCallback(async()=>{try{const a=`section=${encodeURIComponent(c)}${b?"&mask=1":""}`,t=await w(`/api/keys?${a}`);j(t);const y={};for(const i of t.keys)y[i.name]=i.value??"";C(y)}catch(a){l(a.message,"bad"),j(null)}},[c,b,l]);h.useEffect(()=>{N()},[N]);const I=d(async a=>{const t=S[a.name];(a.value??"")!==t&&(await w(`/api/keys/${encodeURIComponent(a.name)}`,{method:"PUT",body:{value:t,section:c}}),l(`已保存 ${a.name}`),await N())}),T=d(async(a,t)=>{const y=String(t||"").trim();if(!(!y||y===a.name)){if(p.keys.some(i=>i.name===y)){l(`已经有叫「${y}」的键了`,"bad"),await N();return}await w(`/api/entries/${encodeURIComponent(a.id)}`,{method:"PATCH",body:{title:y}}),C(i=>{const o={};for(const[U,A]of Object.entries(i))o[U===a.name?y:U]=A;return o}),l(`已重命名为 ${y}`),await N(),await s()}}),E=d(async()=>{const a=f.name.trim();if(!a){l("先填键名,例如 OPENAI_KEY","bad");return}if(p.keys.some(t=>t.name===a)){l(`已经有叫「${a}」的键了——直接在上面那行改值即可`,"bad");return}await w(`/api/keys/${encodeURIComponent(a)}`,{method:"PUT",body:{value:f.value,section:c}}),r({name:"",value:""}),l(`已新增 ${a}`),await N(),await s()}),D=d(async a=>{await v({message:`删除「${a.name}」?删除前会自动留一份整库快照在 ~/nx-sk/backup。`,danger:!0,confirmText:"删除"})&&(await w(`/api/keys/${encodeURIComponent(a.name)}`,{method:"DELETE",body:{section:c}}),l(`已删除 ${a.name}`),await N(),await s())});return p?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:g}),e.jsxs("span",{className:"muted",children:[p.count," 个键 · 名称 → 值"]}),e.jsx("span",{className:"grow"}),e.jsxs("label",{className:"inline hint",children:[e.jsx("input",{type:"checkbox",checked:b,onChange:()=>x({mask:!b})}),"打码显示(投屏/截图时用)"]}),e.jsx("button",{className:"btn small ghost",onClick:N,children:"刷新"})]}),e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"colhead",children:[e.jsx("span",{style:{width:200},children:"名称"}),e.jsx("span",{className:"grow",children:"值"})]}),e.jsxs("div",{className:"colbody",children:[p.keys.length?p.keys.map(a=>e.jsxs("div",{className:"row",children:[e.jsx("input",{className:"mono",style:{width:200},defaultValue:a.name,title:"改完按回车或点开别处即重命名",onBlur:t=>T(a,t.target.value),onKeyDown:t=>{t.key==="Enter"&&t.currentTarget.blur()}}),e.jsx("input",{className:"mono grow",value:S[a.name]??"",onChange:t=>C(y=>({...y,[a.name]:t.target.value})),onBlur:()=>I(a),onKeyDown:t=>{t.key==="Enter"&&t.currentTarget.blur()}}),e.jsxs("span",{className:"acts",children:[e.jsx(H,{className:"tag",text:S[a.name]??"",title:"点击复制值",children:"复制"}),e.jsx("button",{className:"btn small ghost",onClick:()=>D(a),children:"删除"})]})]},a.id)):e.jsx(L,{children:"还没有密钥。在下面一行填「名称 / 值」按添加,或 CLI:nx-sk key set OPENAI_KEY sk-xxx"}),e.jsxs("div",{className:"row",children:[e.jsx("input",{className:"mono",style:{width:200},placeholder:"新键名,例如 OPENAI_KEY",value:f.name,onChange:a=>r(t=>({...t,name:a.target.value})),onKeyDown:a=>{a.key==="Enter"&&E()}}),e.jsx("input",{className:"mono grow",placeholder:"值(留空也行,之后再填)",value:f.value,onChange:a=>r(t=>({...t,value:a.target.value})),onKeyDown:a=>{a.key==="Enter"&&E()}}),e.jsx("span",{className:"acts",children:e.jsx("button",{className:"btn small",onClick:E,children:"添加"})})]})]})]}),e.jsx("div",{className:"pad hint",children:"改值:直接在「值」列改完按回车(或点开别处)即保存。改键名同理。 值以密文落盘,这里显示的是原文;要打码勾上方那个框。删除前自动整库快照。"}),e.jsx(q,{module:"entries"})]}):e.jsx("div",{className:"pad muted",children:"加载中…"})}const M=c=>c==null?"":Array.isArray(c)?c.join("、"):String(c);function ne({sectionId:c}){var R;const{ui:g,patchUi:m,toast:x,guard:l,dialog:d,reload:v}=_(),[s,p]=h.useState(null),[j,S]=h.useState(null),[C,f]=h.useState(!1),[r,b]=h.useState(null),[N,I]=h.useState(null),[T,E]=h.useState(""),D=!!g.mask,a=h.useCallback(async n=>{f(!0),S(null);try{const u=await w(`/api/sections/${encodeURIComponent(c)}/dump${D?"?mask=1":""}`);p(u);const $=n||g.entry,k=u.entries.find(P=>P.id===$)||u.entries[0]||null;b(k?{id:k.id,title:k.title,values:{...k.values}}:null),k&&k.id!==g.entry&&m({entry:k.id})}catch(u){S(u.message),p(null),b(null)}finally{f(!1)}},[c,D]);h.useEffect(()=>{a()},[a]);const t=h.useMemo(()=>(s==null?void 0:s.entries.find(n=>n.id===(r==null?void 0:r.id)))||null,[s,r]),y=h.useMemo(()=>s?s.groups.map(n=>({...n,fields:s.fields.filter(u=>u.group===n.id)})).filter(n=>n.fields.length):[],[s]),i=(n,u)=>b($=>$&&{...$,values:{...$.values,[n]:u}}),o=h.useMemo(()=>{if(!r||!t)return{set:{},titleChanged:!1,count:0};const n={};for(const u of s.fields)M(t.values[u.key])!==M(r.values[u.key])&&(n[u.key]=r.values[u.key]);return{set:n,titleChanged:r.title!==t.title,count:Object.keys(n).length+(r.title!==t.title?1:0)}},[r,t,s]),U=l(async()=>{if(!r||!t)return;if(!o.count){x("没有改动");return}const n={set:o.set};o.titleChanged&&(n.title=r.title),await w(`/api/entries/${encodeURIComponent(r.id)}`,{method:"PATCH",body:n}),x(`已保存 ${o.count} 处改动`),await a(r.id),await v()}),A=l(async()=>{var P;if(!r){x("先新建一条条目","bad");return}const n=T.indexOf("=");if(n<=0){x("用 字段=值 的格式,例如 微信=zhangsan","bad");return}const u=T.slice(0,n).trim(),$=T.slice(n+1),k=await w(`/api/entries/${encodeURIComponent(r.id)}`,{method:"PATCH",body:{set:{[u]:$}}});E(""),x(`已写入 ${u}${(P=k.newFields)!=null&&P.length?"(新字段已登记)":""}`),await a(r.id),await v()}),O=l(async()=>{const n=(s==null?void 0:s.section.titleLabel)||"名称",u=await d({message:`新建「${s==null?void 0:s.section.title}」条目,${n}是?`,input:{placeholder:`例如 ${(s==null?void 0:s.section.titleField)||"名称"}`}});if(!u)return;const $={section:c,title:u};s!=null&&s.section.titleField&&($.set={[s.section.titleField]:u});const k=await w("/api/entries",{method:"POST",body:$});x(`已新建:${k.title}`),await a(k.id),await v()}),F=l(async()=>{!t||!await d({message:`删除条目「${t.title}」?删除前会自动留一份快照在 ~/nx-sk/backup。`,danger:!0,confirmText:"删除"})||(await w(`/api/entries/${encodeURIComponent(t.id)}`,{method:"DELETE"}),x("已删除"),await a(""),await v())}),K=l(async()=>{const n=await w("/api/export",{method:"POST",body:{section:c,format:"both"}});I({title:"导出结果",body:e.jsx("pre",{className:"block",children:`目录: ${n.dir}
1
+ import{j as e,u as _,r as h,a as w,b as H,E as L,C as q,T as B,M as z}from"./index-VCOWbzUk.js";import{l as V,f as Y,r as G}from"./files-CJbQeKB4.js";function Q({value:c,onChange:g,minRows:m=2,placeholder:x,disabled:l,maxLength:d}){const v=h.useRef(null);return h.useLayoutEffect(()=>{const s=v.current;s&&(s.style.height="auto",s.style.height=`${s.scrollHeight}px`)},[c]),e.jsx("textarea",{ref:v,value:String(c??""),onChange:s=>g(s.target.value),rows:m,placeholder:x,disabled:l,maxLength:d,style:{resize:"none",overflow:"hidden",width:"100%"}})}function W(c){const g=c==null?[]:Array.isArray(c)?c:[c],m=[];for(const x of g)for(const l of String(x??"").split(/[,,、;;]+/)){const d=l.trim();d&&!m.includes(d)&&m.push(d)}return m}function J({def:c,value:g,onChange:m}){const{toast:x,guard:l,patchUi:d}=_(),[v,s]=h.useState(null),[p,j]=h.useState(!1),S=h.useRef(null),C=h.useRef(0),f=W(g),r=Number(c.maxItems)||0,b=r>0&&f.length>=r,N=h.useCallback(async()=>{try{s(await V())}catch{}},[]);h.useEffect(()=>{N()},[N]);const I=(v==null?void 0:v.files)||[],T=i=>I.find(o=>o.id===i)||null,E=I.filter(i=>!f.includes(i.id)),D=l(async i=>{const o=[...i||[]];if(!o.length)return;if(b){x(`「${c.label}」最多 ${r} 个附件,先移除一个`,"bad");return}const U=[...f];let A=0;for(const F of o){const K=await Y(F),R=await w("/api/files",{method:"POST",body:{content:K,name:F.name}});if(U.includes(R.id)){A+=1;continue}U.push(R.id)}m(U);const O=o.length-A;x(A?`已上传并关联 ${O} 个;${A} 个库里已有,直接引用`:`已上传并关联 ${o.length} 个`),s(await G())}),a=i=>{i&&!f.includes(i)&&m([...f,i])},t=i=>m(f.filter(o=>o!==i)),y=l(async i=>{var o;C.current=0,j(!1),await D((o=i.dataTransfer)==null?void 0:o.files)});return e.jsxs("div",{className:`file-picker ${p?"drag":""}`,onDragEnter:i=>{i.preventDefault(),C.current+=1,j(!0)},onDragOver:i=>i.preventDefault(),onDragLeave:()=>{C.current-=1,C.current<=0&&(C.current=0,j(!1))},onDrop:y,children:[f.map(i=>{const o=T(i);return o?e.jsxs("div",{className:"file-preview",children:[o.isImage?e.jsx("img",{src:o.url,alt:o.name}):e.jsx("span",{className:"ext-badge",style:{fontSize:13,width:40,textAlign:"center"},children:o.ext?o.ext.slice(1).toUpperCase():"FILE"}),e.jsx("a",{className:"file-preview-name grow",href:o.url,target:"_blank",rel:"noreferrer",title:o.displayPath,children:o.name}),e.jsx("a",{className:"btn small ghost",href:o.downloadUrl,children:"下载"}),e.jsx(H,{className:"hint",text:o.path,title:"点击复制物理路径",children:"路径"}),e.jsx("button",{type:"button",className:"btn small ghost",onClick:()=>t(i),children:"移除"})]},i):e.jsxs("div",{className:"file-preview",children:[e.jsxs("span",{className:"hint bad grow",children:["引用的附件已不存在(",i,")—— 重新上传,或移除。"]}),e.jsx("button",{type:"button",className:"btn small ghost",onClick:()=>t(i),children:"移除"})]},i)}),f.length?null:e.jsx("span",{className:"hint",children:"还没有附件。"}),e.jsxs("div",{className:"file-pick-row",children:[e.jsxs("select",{value:"",disabled:b||!E.length,onChange:i=>a(i.target.value),children:[e.jsx("option",{value:"",children:E.length?"(从附件库选一个,选中即追加)":"(库里没有可再选的附件)"}),E.map(i=>e.jsx("option",{value:i.id,children:i.name},i.id))]}),e.jsx("button",{type:"button",className:"btn small",disabled:b,onClick:()=>{var i;return(i=S.current)==null?void 0:i.click()},children:"上传文件"}),e.jsx("button",{type:"button",className:"btn small ghost",onClick:()=>d({view:"files"}),children:"附件中心"}),e.jsx("input",{ref:S,type:"file",multiple:!0,style:{display:"none"},onChange:i=>{D(i.target.files),i.target.value=""}})]}),b?e.jsxs("span",{className:"hint",children:["已达上限(",r," 个):先移除一个,或把字段的 maxItems 调大。"]}):null,c.hint?e.jsx("span",{className:"hint",children:c.hint}):null]})}function X({def:c,value:g,onChange:m,disabled:x}){const l=c.type||"text",d=g??"";if(l==="file")return e.jsx(J,{def:c,value:d,onChange:m});if(l==="bool"){const s=d===!0||d==="true"||d==="是";return e.jsxs("label",{className:"inline",children:[e.jsx("input",{type:"checkbox",checked:s,onChange:p=>m(p.target.checked)}),e.jsx("span",{className:"muted",children:s?"是":"否"})]})}if(l==="select"){const s=c.options||[],p=d!==""&&!s.includes(d);return e.jsxs("select",{value:p?"__custom":String(d),onChange:j=>m(j.target.value==="__custom"?"":j.target.value),children:[e.jsx("option",{value:"",children:"(未填写)"}),s.map(j=>e.jsx("option",{value:j,children:j},j)),p?e.jsxs("option",{value:"__custom",children:[String(d),"(当前值,不在候选里)"]}):null]})}if(l==="textarea")return e.jsx(Q,{value:d,onChange:m});if(l==="tags"){const s=Array.isArray(d)?d.join("、"):String(d);return e.jsx("input",{value:s,placeholder:c.maxItems?`多个用「、」分隔,最多 ${c.maxItems} 个`:"多个用「、」分隔",onChange:p=>m(p.target.value.split(/[,,、;;]+/).map(j=>j.trim()).filter(Boolean))})}const v=l==="number"?"number":l==="date"?"date":l==="month"?"month":"text";return l==="secret"?e.jsx("input",{type:"text",value:String(d),placeholder:"密文落盘;原样保留占位则视为不修改",disabled:x,onChange:s=>m(s.target.value)}):e.jsx("input",{type:v,value:String(d),onChange:s=>m(s.target.value)})}function Z({title:c,fields:g,draft:m,onPatch:x}){return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"group-title",children:c}),e.jsx("div",{className:"pad",children:e.jsx("div",{className:"grid",children:g.map(l=>e.jsxs("div",{className:`field ${l.type==="textarea"||l.type==="file"?"wide":""}`,children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:l.label}),l.sensitive?e.jsx("span",{className:"tag bad",children:"密文"}):null,l.fill&&l.fill!=="normal"?e.jsx("span",{className:"tag",children:l.fillLabel||l.fill}):null,e.jsx("span",{className:"fkey",children:l.key})]}),e.jsx("div",{className:"fctrl",children:e.jsx(X,{def:l,value:m[l.key],onChange:d=>x(l.key,d)})})]},l.key))})})]})}function ee({sectionId:c,sectionTitle:g}){const{ui:m,patchUi:x,toast:l,guard:d,dialog:v,reload:s}=_(),[p,j]=h.useState(null),[S,C]=h.useState({}),[f,r]=h.useState({name:"",value:""}),b=!!m.mask,N=h.useCallback(async()=>{try{const a=`section=${encodeURIComponent(c)}${b?"&mask=1":""}`,t=await w(`/api/keys?${a}`);j(t);const y={};for(const i of t.keys)y[i.name]=i.value??"";C(y)}catch(a){l(a.message,"bad"),j(null)}},[c,b,l]);h.useEffect(()=>{N()},[N]);const I=d(async a=>{const t=S[a.name];(a.value??"")!==t&&(await w(`/api/keys/${encodeURIComponent(a.name)}`,{method:"PUT",body:{value:t,section:c}}),l(`已保存 ${a.name}`),await N())}),T=d(async(a,t)=>{const y=String(t||"").trim();if(!(!y||y===a.name)){if(p.keys.some(i=>i.name===y)){l(`已经有叫「${y}」的键了`,"bad"),await N();return}await w(`/api/entries/${encodeURIComponent(a.id)}`,{method:"PATCH",body:{title:y}}),C(i=>{const o={};for(const[U,A]of Object.entries(i))o[U===a.name?y:U]=A;return o}),l(`已重命名为 ${y}`),await N(),await s()}}),E=d(async()=>{const a=f.name.trim();if(!a){l("先填键名,例如 OPENAI_KEY","bad");return}if(p.keys.some(t=>t.name===a)){l(`已经有叫「${a}」的键了——直接在上面那行改值即可`,"bad");return}await w(`/api/keys/${encodeURIComponent(a)}`,{method:"PUT",body:{value:f.value,section:c}}),r({name:"",value:""}),l(`已新增 ${a}`),await N(),await s()}),D=d(async a=>{await v({message:`删除「${a.name}」?删除前会自动留一份整库快照在 ~/nx-sk/backup。`,danger:!0,confirmText:"删除"})&&(await w(`/api/keys/${encodeURIComponent(a.name)}`,{method:"DELETE",body:{section:c}}),l(`已删除 ${a.name}`),await N(),await s())});return p?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:g}),e.jsxs("span",{className:"muted",children:[p.count," 个键 · 名称 → 值"]}),e.jsx("span",{className:"grow"}),e.jsxs("label",{className:"inline hint",children:[e.jsx("input",{type:"checkbox",checked:b,onChange:()=>x({mask:!b})}),"打码显示(投屏/截图时用)"]}),e.jsx("button",{className:"btn small ghost",onClick:N,children:"刷新"})]}),e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"colhead",children:[e.jsx("span",{style:{width:200},children:"名称"}),e.jsx("span",{className:"grow",children:"值"})]}),e.jsxs("div",{className:"colbody",children:[p.keys.length?p.keys.map(a=>e.jsxs("div",{className:"row",children:[e.jsx("input",{className:"mono",style:{width:200},defaultValue:a.name,title:"改完按回车或点开别处即重命名",onBlur:t=>T(a,t.target.value),onKeyDown:t=>{t.key==="Enter"&&t.currentTarget.blur()}}),e.jsx("input",{className:"mono grow",value:S[a.name]??"",onChange:t=>C(y=>({...y,[a.name]:t.target.value})),onBlur:()=>I(a),onKeyDown:t=>{t.key==="Enter"&&t.currentTarget.blur()}}),e.jsxs("span",{className:"acts",children:[e.jsx(H,{className:"tag",text:S[a.name]??"",title:"点击复制值",children:"复制"}),e.jsx("button",{className:"btn small ghost",onClick:()=>D(a),children:"删除"})]})]},a.id)):e.jsx(L,{children:"还没有密钥。在下面一行填「名称 / 值」按添加,或 CLI:nx-sk key set OPENAI_KEY sk-xxx"}),e.jsxs("div",{className:"row",children:[e.jsx("input",{className:"mono",style:{width:200},placeholder:"新键名,例如 OPENAI_KEY",value:f.name,onChange:a=>r(t=>({...t,name:a.target.value})),onKeyDown:a=>{a.key==="Enter"&&E()}}),e.jsx("input",{className:"mono grow",placeholder:"值(留空也行,之后再填)",value:f.value,onChange:a=>r(t=>({...t,value:a.target.value})),onKeyDown:a=>{a.key==="Enter"&&E()}}),e.jsx("span",{className:"acts",children:e.jsx("button",{className:"btn small",onClick:E,children:"添加"})})]})]})]}),e.jsx("div",{className:"pad hint",children:"改值:直接在「值」列改完按回车(或点开别处)即保存。改键名同理。 值以密文落盘,这里显示的是原文;要打码勾上方那个框。删除前自动整库快照。"}),e.jsx(q,{module:"entries"})]}):e.jsx("div",{className:"pad muted",children:"加载中…"})}const M=c=>c==null?"":Array.isArray(c)?c.join("、"):String(c);function ne({sectionId:c}){var R;const{ui:g,patchUi:m,toast:x,guard:l,dialog:d,reload:v}=_(),[s,p]=h.useState(null),[j,S]=h.useState(null),[C,f]=h.useState(!1),[r,b]=h.useState(null),[N,I]=h.useState(null),[T,E]=h.useState(""),D=!!g.mask,a=h.useCallback(async n=>{f(!0),S(null);try{const u=await w(`/api/sections/${encodeURIComponent(c)}/dump${D?"?mask=1":""}`);p(u);const $=n||g.entry,k=u.entries.find(P=>P.id===$)||u.entries[0]||null;b(k?{id:k.id,title:k.title,values:{...k.values}}:null),k&&k.id!==g.entry&&m({entry:k.id})}catch(u){S(u.message),p(null),b(null)}finally{f(!1)}},[c,D]);h.useEffect(()=>{a()},[a]);const t=h.useMemo(()=>(s==null?void 0:s.entries.find(n=>n.id===(r==null?void 0:r.id)))||null,[s,r]),y=h.useMemo(()=>s?s.groups.map(n=>({...n,fields:s.fields.filter(u=>u.group===n.id)})).filter(n=>n.fields.length):[],[s]),i=(n,u)=>b($=>$&&{...$,values:{...$.values,[n]:u}}),o=h.useMemo(()=>{if(!r||!t)return{set:{},titleChanged:!1,count:0};const n={};for(const u of s.fields)M(t.values[u.key])!==M(r.values[u.key])&&(n[u.key]=r.values[u.key]);return{set:n,titleChanged:r.title!==t.title,count:Object.keys(n).length+(r.title!==t.title?1:0)}},[r,t,s]),U=l(async()=>{if(!r||!t)return;if(!o.count){x("没有改动");return}const n={set:o.set};o.titleChanged&&(n.title=r.title),await w(`/api/entries/${encodeURIComponent(r.id)}`,{method:"PATCH",body:n}),x(`已保存 ${o.count} 处改动`),await a(r.id),await v()}),A=l(async()=>{var P;if(!r){x("先新建一条条目","bad");return}const n=T.indexOf("=");if(n<=0){x("用 字段=值 的格式,例如 微信=zhangsan","bad");return}const u=T.slice(0,n).trim(),$=T.slice(n+1),k=await w(`/api/entries/${encodeURIComponent(r.id)}`,{method:"PATCH",body:{set:{[u]:$}}});E(""),x(`已写入 ${u}${(P=k.newFields)!=null&&P.length?"(新字段已登记)":""}`),await a(r.id),await v()}),O=l(async()=>{const n=(s==null?void 0:s.section.titleLabel)||"名称",u=await d({message:`新建「${s==null?void 0:s.section.title}」条目,${n}是?`,input:{placeholder:`例如 ${(s==null?void 0:s.section.titleField)||"名称"}`}});if(!u)return;const $={section:c,title:u};s!=null&&s.section.titleField&&($.set={[s.section.titleField]:u});const k=await w("/api/entries",{method:"POST",body:$});x(`已新建:${k.title}`),await a(k.id),await v()}),F=l(async()=>{!t||!await d({message:`删除条目「${t.title}」?删除前会自动留一份快照在 ~/nx-sk/backup。`,danger:!0,confirmText:"删除"})||(await w(`/api/entries/${encodeURIComponent(t.id)}`,{method:"DELETE"}),x("已删除"),await a(""),await v())}),K=l(async()=>{const n=await w("/api/export",{method:"POST",body:{section:c,format:"both"}});I({title:"导出结果",body:e.jsx("pre",{className:"block",children:`目录: ${n.dir}
2
2
  格式: ${n.format}
3
3
  条目: ${n.entries}
4
4
  凭据: ${n.withSecrets?"含明文":"已打码"}
@@ -1,4 +1,4 @@
1
- import{u as M,r,a as v,j as e,E as B,M as A}from"./index-Dxf_b602.js";const L=28*1024*1024,O=20;function F(){const s=new Date,t=a=>String(a).padStart(2,"0");return`${s.getFullYear()}${t(s.getMonth()+1)}${t(s.getDate())}-${t(s.getHours())}${t(s.getMinutes())}${t(s.getSeconds())}`}function I(s,t){const a=new Blob([t],{type:"application/json;charset=utf-8"}),d=URL.createObjectURL(a),l=document.createElement("a");l.href=d,l.download=s,document.body.appendChild(l),l.click(),l.remove(),setTimeout(()=>URL.revokeObjectURL(d),1e3)}function j({title:s,items:t,render:a,total:d}){if(!t||!t.length)return null;const l=d!=null&&d>t.length;return e.jsxs("div",{children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:s}),l?e.jsxs("span",{className:"fkey",children:["共 ",d," 处"]}):null]}),e.jsx("ul",{className:"stack",style:{margin:"4px 0 0",paddingLeft:18},children:t.slice(0,O).map((h,p)=>e.jsx("li",{children:a(h)},p))})]})}function U({r:s}){const t=s.files||{};return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{children:["新增栏目 ",(s.addedSections||[]).length," 个 / 新增条目 ",s.addedEntriesTotal||0," 条; 补空 ",s.filledTotal||0," 处;冲突 ",s.conflictsTotal||0," 处 (",s.counts?`栏目 ${s.counts.sectionsBefore} → ${s.counts.sectionsAfter},条目 ${s.counts.entriesBefore} → ${s.counts.entriesAfter}`:"",")"]}),e.jsx(j,{title:"补空明细(本地留空,已用备份值填上)",items:s.filled,total:s.filledTotal,render:a=>e.jsxs("span",{children:[a.section," / ",a.entry," · ",a.label," = ",a.value||"(空)"]})}),e.jsx(j,{title:"冲突(两边都有值且不同,已保留本地,请手动核对)",items:s.conflicts,total:s.conflictsTotal,render:a=>e.jsxs("span",{children:[a.section," / ",a.entry," · ",a.label,": 本地 ",a.localValue||"(空)"," | 备份 ",a.backupValue||"(空)"]})}),e.jsx(j,{title:"结构补齐(只增:备份多出的分组/字段已补进本地栏目)",items:s.structureAdded,render:a=>e.jsxs("span",{children:[a.id,": +",a.fields.length," 字段 [",a.fields.join(", "),"]",a.groups.length?`,+分组 [${a.groups.join(", ")}]`:""]})}),e.jsx(j,{title:"本地独有字段(备份里没有,已原样保留)",items:s.structureOnlyLocal,render:a=>e.jsxs("span",{children:[a.id,": [",a.fields.join(", "),"]"]})}),(s.kvMismatch||[]).length?e.jsxs("div",{className:"muted",children:["kv 标记与备份不一致、未合并结构的栏目: ",s.kvMismatch.join(", ")]}):null,(s.settingsFilled||[]).length?e.jsxs("div",{children:["设置补空: ",s.settingsFilled.join(", ")]}):null,e.jsx("div",{children:S(t)}),s.exportsSkipped?e.jsxs("div",{className:"muted",children:["备份里的导出历史 ",s.exportsSkipped," 条未并入(保留本机记录)"]}):null,s.resealed?e.jsxs("div",{className:"muted",children:["用本机密钥库重新加密的密钥字段: ",s.resealed," 个"]}):null,e.jsxs("div",{className:"muted",children:["备份生成于 ",s.backupCreatedAt||"未知"]}),e.jsxs("pre",{className:"block",children:["导入前的安全快照:",s.safetySnapshot]})]})}function S(s){const t=[];return s.toAdd!=null&&t.push(`台账新增 ${s.toAdd} 条、沿用本地 ${s.kept||0} 条`),s.written&&t.push(`落盘原件 ${s.written} 个`),s.healed&&t.push(`补齐缺失原件 ${s.healed} 个`),s.skippedExisting&&t.push(`磁盘已有但内容不同、保留磁盘 ${s.skippedExisting} 个`),s.checksumMismatch&&t.push(`校验不符被拒 ${s.checksumMismatch} 个`),s.missingBinaryTotal&&t.push(`${s.missingBinaryTotal} 条台账无原件(备份未携带)`),t.length?`附件: ${t.join(";")}${s.bytes?`(共 ${s.bytes} 字节)`:""}`:"附件: 台账已并入,原件均已就位"}function _({r:s}){const t=s.filesStats||{};return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{children:["已整份替换为备份内容:",s.sections," 个栏目 / ",s.entries," 条条目(附件台账 ",s.filesInBackup||0," 条)"]}),e.jsx("div",{children:S({...t})}),s.resealed?e.jsxs("div",{className:"muted",children:["用本机密钥库重新加密的密钥字段: ",s.resealed," 个"]}):null,e.jsxs("div",{className:"muted",children:["备份生成于 ",s.backupCreatedAt||"未知"]}),e.jsxs("pre",{className:"block",children:["恢复前的安全快照:",s.safetySnapshot]})]})}function V(){const{toast:s,guard:t,dialog:a,reload:d}=M(),[l,h]=r.useState(""),[p,b]=r.useState(""),[f,C]=r.useState(!1),[m,g]=r.useState(""),[c,N]=r.useState(null),[u,w]=r.useState(null),k=r.useRef(null),R=t(async()=>{if(!l){s("请先设置备份口令","bad");return}if(l!==p){s("两次输入的口令不一致","bad");return}const n=await v("/api/backup",{method:"POST",body:{password:l,"with-files":f}}),i=`nx-sk-backup-${F()}.nxskbak`;I(i,JSON.stringify(n.archive,null,2)+`
1
+ import{u as M,r,a as v,j as e,E as B,M as A}from"./index-VCOWbzUk.js";const L=28*1024*1024,O=20;function F(){const s=new Date,t=a=>String(a).padStart(2,"0");return`${s.getFullYear()}${t(s.getMonth()+1)}${t(s.getDate())}-${t(s.getHours())}${t(s.getMinutes())}${t(s.getSeconds())}`}function I(s,t){const a=new Blob([t],{type:"application/json;charset=utf-8"}),d=URL.createObjectURL(a),l=document.createElement("a");l.href=d,l.download=s,document.body.appendChild(l),l.click(),l.remove(),setTimeout(()=>URL.revokeObjectURL(d),1e3)}function j({title:s,items:t,render:a,total:d}){if(!t||!t.length)return null;const l=d!=null&&d>t.length;return e.jsxs("div",{children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:s}),l?e.jsxs("span",{className:"fkey",children:["共 ",d," 处"]}):null]}),e.jsx("ul",{className:"stack",style:{margin:"4px 0 0",paddingLeft:18},children:t.slice(0,O).map((h,p)=>e.jsx("li",{children:a(h)},p))})]})}function U({r:s}){const t=s.files||{};return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{children:["新增栏目 ",(s.addedSections||[]).length," 个 / 新增条目 ",s.addedEntriesTotal||0," 条; 补空 ",s.filledTotal||0," 处;冲突 ",s.conflictsTotal||0," 处 (",s.counts?`栏目 ${s.counts.sectionsBefore} → ${s.counts.sectionsAfter},条目 ${s.counts.entriesBefore} → ${s.counts.entriesAfter}`:"",")"]}),e.jsx(j,{title:"补空明细(本地留空,已用备份值填上)",items:s.filled,total:s.filledTotal,render:a=>e.jsxs("span",{children:[a.section," / ",a.entry," · ",a.label," = ",a.value||"(空)"]})}),e.jsx(j,{title:"冲突(两边都有值且不同,已保留本地,请手动核对)",items:s.conflicts,total:s.conflictsTotal,render:a=>e.jsxs("span",{children:[a.section," / ",a.entry," · ",a.label,": 本地 ",a.localValue||"(空)"," | 备份 ",a.backupValue||"(空)"]})}),e.jsx(j,{title:"结构补齐(只增:备份多出的分组/字段已补进本地栏目)",items:s.structureAdded,render:a=>e.jsxs("span",{children:[a.id,": +",a.fields.length," 字段 [",a.fields.join(", "),"]",a.groups.length?`,+分组 [${a.groups.join(", ")}]`:""]})}),e.jsx(j,{title:"本地独有字段(备份里没有,已原样保留)",items:s.structureOnlyLocal,render:a=>e.jsxs("span",{children:[a.id,": [",a.fields.join(", "),"]"]})}),(s.kvMismatch||[]).length?e.jsxs("div",{className:"muted",children:["kv 标记与备份不一致、未合并结构的栏目: ",s.kvMismatch.join(", ")]}):null,(s.settingsFilled||[]).length?e.jsxs("div",{children:["设置补空: ",s.settingsFilled.join(", ")]}):null,e.jsx("div",{children:S(t)}),s.exportsSkipped?e.jsxs("div",{className:"muted",children:["备份里的导出历史 ",s.exportsSkipped," 条未并入(保留本机记录)"]}):null,s.resealed?e.jsxs("div",{className:"muted",children:["用本机密钥库重新加密的密钥字段: ",s.resealed," 个"]}):null,e.jsxs("div",{className:"muted",children:["备份生成于 ",s.backupCreatedAt||"未知"]}),e.jsxs("pre",{className:"block",children:["导入前的安全快照:",s.safetySnapshot]})]})}function S(s){const t=[];return s.toAdd!=null&&t.push(`台账新增 ${s.toAdd} 条、沿用本地 ${s.kept||0} 条`),s.written&&t.push(`落盘原件 ${s.written} 个`),s.healed&&t.push(`补齐缺失原件 ${s.healed} 个`),s.skippedExisting&&t.push(`磁盘已有但内容不同、保留磁盘 ${s.skippedExisting} 个`),s.checksumMismatch&&t.push(`校验不符被拒 ${s.checksumMismatch} 个`),s.missingBinaryTotal&&t.push(`${s.missingBinaryTotal} 条台账无原件(备份未携带)`),t.length?`附件: ${t.join(";")}${s.bytes?`(共 ${s.bytes} 字节)`:""}`:"附件: 台账已并入,原件均已就位"}function _({r:s}){const t=s.filesStats||{};return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{children:["已整份替换为备份内容:",s.sections," 个栏目 / ",s.entries," 条条目(附件台账 ",s.filesInBackup||0," 条)"]}),e.jsx("div",{children:S({...t})}),s.resealed?e.jsxs("div",{className:"muted",children:["用本机密钥库重新加密的密钥字段: ",s.resealed," 个"]}):null,e.jsxs("div",{className:"muted",children:["备份生成于 ",s.backupCreatedAt||"未知"]}),e.jsxs("pre",{className:"block",children:["恢复前的安全快照:",s.safetySnapshot]})]})}function V(){const{toast:s,guard:t,dialog:a,reload:d}=M(),[l,h]=r.useState(""),[p,b]=r.useState(""),[f,C]=r.useState(!1),[m,g]=r.useState(""),[c,N]=r.useState(null),[u,w]=r.useState(null),k=r.useRef(null),R=t(async()=>{if(!l){s("请先设置备份口令","bad");return}if(l!==p){s("两次输入的口令不一致","bad");return}const n=await v("/api/backup",{method:"POST",body:{password:l,"with-files":f}}),i=`nx-sk-backup-${F()}.nxskbak`;I(i,JSON.stringify(n.archive,null,2)+`
2
2
  `);const o=n.counts,x=f?`;嵌入原件 ${o.filesEmbedded} 个`:`;附件台账 ${o.files||0} 条未嵌原件`;s(`已生成加密备份:${o.sections} 栏目 / ${o.entries} 条目${x}`),h(""),b("")}),T=async n=>{var x;const i=(x=n.target.files)==null?void 0:x[0];if(!i)return;const o=await i.text();N({name:i.name,bytes:i.size,text:o})},y=()=>c?m?c.bytes>L?(s("备份文件超过 28MB,可能超过接口上限;请改用 CLI 导入:nx-sk backup import","bad"),!1):!0:(s("请输入备份口令","bad"),!1):(s("请先选择备份文件","bad"),!1),$=n=>(w(n),g(""),N(null),k.current&&(k.current.value=""),d()),P=t(async()=>{if(!y()||!await a({confirmText:"解密并合并导入",message:"合并导入:只把备份里有、而当前缺失或留空的内容补进来,不会覆盖你已填过的值,也不会删改本地栏目结构。",detail:`备份文件:${c.name}
3
3
  冲突(两边都有值且不同)会保留本地、列在导入报告里。
4
4
  导入前会自动给当前数据留一份安全快照(可回滚)。`}))return;const i=await v("/api/backup/restore",{method:"POST",body:{password:m,content:c.text}});await $(i)}),E=t(async()=>{if(!y()||!await a({danger:!0,confirmText:"解密并整份替换",message:"整份替换:当前数据会被备份内容整体覆盖,包括删掉备份里没有的条目。",detail:`备份文件:${c.name}
@@ -1 +1 @@
1
- import{u as q,r,a as p,j as e,b as w,T as A,E as H,C as _,M as z}from"./index-Dxf_b602.js";import{f as K,r as f}from"./files-I6hzWb3h.js";function S(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(2)} MB`}const Q=t=>`${t.sectionTitle} / ${t.entryTitle||"(无名)"} / ${t.fieldLabel}`;function X(){const{toast:t,guard:l,dialog:m,reload:n}=q(),[i,k]=r.useState(null),[C,$]=r.useState(null),[u,N]=r.useState(""),[g,E]=r.useState(""),[D,v]=r.useState(!1),[d,h]=r.useState(null),T=r.useRef(null),x=r.useRef(0),o=r.useCallback(async()=>{try{const a=new URLSearchParams;u&&a.set("section",u),g&&a.set("q",g);const s=a.toString(),c=await p(`/api/files${s?`?${s}`:""}`);k(c),$(null)}catch(a){$(a.message),k(null)}},[u,g]);r.useEffect(()=>{o()},[o]);const b=r.useCallback(async a=>{const s=Array.from(a||[]);if(!s.length)return;let c=0;for(const j of s)try{const y=await K(j);await p("/api/files",{method:"POST",body:{content:y,name:j.name}}),c+=1}catch(y){t(`${j.name} 上传失败:${y.message}`,"bad")}c&&t(`已导入 ${c} 个附件(到栏目条目里选它即可链接)`),await f(),await o(),await n()},[o,n,t]),P=r.useCallback(a=>{a.preventDefault(),x.current=0,v(!1),b(a.dataTransfer.files)},[b]),I=l(async()=>{h({kind:"path",form:{path:"",copy:!1}})}),R=l(async a=>{if(!a.path.trim()){t("请填本机文件的绝对路径","bad");return}const s=await p("/api/files",{method:"POST",body:{path:a.path.trim(),copy:a.copy}});h(null),t(s.duplicated?"库中已有相同文件":s.moved?"已移动进附件库(可还原)":"已复制进附件库"),await f(),await o(),await n()}),B=l(async a=>{h({kind:"edit",id:a.id,form:{name:a.name,tags:a.tags.join("、"),note:a.note}})}),F=l(async(a,s)=>{await p(`/api/files/${encodeURIComponent(a)}`,{method:"PATCH",body:{name:s.name,tags:s.tags.split(/[,,、;;]+/).map(c=>c.trim()).filter(Boolean),note:s.note}}),h(null),t("附件已更新"),await f(),await o()}),U=l(async a=>{const s=await m({message:`把「${a.name}」还原到哪?`,detail:a.sourceDisplay?`默认移回原始位置并撤销台账:${a.sourceDisplay}`:"该附件没有原始位置记录,请在下一步填写目标目录。",input:a.sourceDisplay?void 0:{placeholder:"目标目录绝对路径,例如 D:\\资料"},confirmText:"移回并撤销台账",cancelText:"取消"});if(s===null)return;const c={};s&&typeof s=="string"&&(c.to=s.trim());const j=await p(`/api/files/${encodeURIComponent(a.id)}/restore`,{method:"POST",body:c});t(`已还原到 ${j.displayPath}`),await f(),await o(),await n()}),O=l(async a=>{const s=await m({message:`导出「${a.name}」的副本到哪个目录?库中原件保留。`,input:{placeholder:"目标目录绝对路径,例如 D:\\资料"},confirmText:"导出副本"});if(!s)return;const c=await p(`/api/files/${encodeURIComponent(a.id)}/restore`,{method:"POST",body:{to:String(s).trim(),copy:!0}});t(`已导出到 ${c.displayPath}`)}),L=l(async a=>{await m({message:`删除附件「${a.name}」?物理原件会一起删除,删除前自动留台账快照。`,detail:"想保留原件、只撤销台账,请改用「还原」。",danger:!0,confirmText:"删除"})&&(await p(`/api/files/${encodeURIComponent(a.id)}`,{method:"DELETE"}),t("已删除"),await f(),await o(),await n())});if(C)return e.jsxs("div",{className:"pad stack",children:[e.jsx("div",{className:"bad",children:e.jsx("strong",{children:"附件库读不到"})}),e.jsx("pre",{className:"block",children:C})]});if(!i)return e.jsx("div",{className:"pad muted",children:"加载中…"});const M=i.sections||[];return e.jsxs("div",{className:`files-page ${D?"drag-over":""}`,onDragEnter:a=>{a.preventDefault(),x.current+=1,v(!0)},onDragOver:a=>a.preventDefault(),onDragLeave:a=>{a.preventDefault(),x.current-=1,x.current<=0&&(x.current=0,v(!1))},onDrop:P,children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:"附件"}),e.jsxs("span",{className:"muted",children:[i.total," 个 · ",S(i.totalBytes)]}),e.jsxs("span",{className:"hint",title:"被栏目条目的 file 字段引用的附件数",children:["被引用 ",i.total-i.unlinked,"/",i.total]}),e.jsx(w,{className:"hint",text:i.rootRaw,title:"点击复制附件库目录",children:i.root}),e.jsx("span",{className:"grow"}),e.jsx("input",{style:{width:180},placeholder:"搜索名称 / 备注 / 标签 / 引用它的条目",value:g,onChange:a=>E(a.target.value)}),e.jsx("button",{className:"btn small ghost",onClick:o,children:"刷新"}),e.jsx("button",{className:"btn small ghost",onClick:I,children:"本机路径导入"}),e.jsx("button",{className:"btn small",onClick:()=>{var a;return(a=T.current)==null?void 0:a.click()},children:"上传文件"}),e.jsx("input",{ref:T,type:"file",multiple:!0,style:{display:"none"},onChange:a=>{b(a.target.files),a.target.value=""}})]}),e.jsxs("div",{className:"rowgap pad file-filters",children:[e.jsxs("button",{className:`tab small ${u?"":"active"}`,onClick:()=>N(""),children:["全部",e.jsx("span",{className:"count",children:i.total})]}),M.map(a=>e.jsxs("button",{className:`tab small ${u===a.id?"active":""}`,onClick:()=>N(a.id),title:`被「${a.title}」栏目引用的附件`,children:[a.title,e.jsx("span",{className:"count",children:a.count})]},a.id)),e.jsxs("button",{className:`tab small ${u==="none"?"active":""}`,onClick:()=>N("none"),children:["未引用",e.jsx("span",{className:"count",children:i.unlinked})]})]}),D?e.jsx("div",{className:"drop-hint",children:"松开鼠标,文件直接导入附件库"}):null,i.files.length?e.jsx("div",{className:"file-grid pad",children:i.files.map(a=>{var s;return e.jsxs("div",{className:"file-card",children:[e.jsx("a",{className:"file-thumb",href:a.url,target:"_blank",rel:"noreferrer",title:"在浏览器中预览",children:a.isImage?e.jsx("img",{src:a.url,alt:a.name,loading:"lazy"}):e.jsx("span",{className:"ext-badge",children:a.ext?a.ext.slice(1).toUpperCase():"FILE"})}),e.jsxs("div",{className:"file-meta",children:[e.jsx("a",{className:"file-name",href:a.url,target:"_blank",rel:"noreferrer",title:a.name,children:a.name}),e.jsxs("div",{className:"rowgap",style:{gap:4},children:[e.jsx("span",{className:"hint",children:S(a.bytes)}),a.mode==="move"?e.jsx(A,{children:"移动导入"}):null]}),(s=a.usedBy)!=null&&s.length?e.jsxs("div",{className:"hint",title:"条目的 file 字段引用了本附件",children:["被引用:",a.usedBy.map(Q).join("、")]}):e.jsx("div",{className:"hint",children:"未被任何条目引用(到栏目条目里选它即可链接)"})]}),e.jsxs("div",{className:"file-acts",children:[e.jsx("a",{className:"btn small ghost",href:a.downloadUrl,children:"下载"}),e.jsx("button",{className:"btn small ghost",onClick:()=>O(a),children:"导出副本"}),e.jsx("button",{className:"btn small ghost",onClick:()=>U(a),children:"还原"}),e.jsx("button",{className:"btn small ghost",onClick:()=>B(a),children:"编辑"}),e.jsx("button",{className:"btn small ghost danger-ghost",onClick:()=>L(a),children:"删除"}),e.jsx("span",{className:"grow"}),e.jsx(w,{className:"hint",text:a.path,title:"点击复制物理路径",children:"路径"}),e.jsx(w,{className:"hint",text:a.url,title:"点击复制链接",children:"链接"})]})]},a.id)})}):e.jsxs("div",{className:"pad",children:[e.jsx(H,{children:"还没有附件。把文件拖到这里、点「上传文件」,或用「本机路径导入」把原件移动进库;随后到栏目的条目里选它。"}),e.jsx("div",{className:"hint pad",children:'CLI:nx-sk file add --path "D:\\简历\\张三-简历.pdf"(默认移动;--copy 仅复制)'})]}),e.jsx(_,{module:"files"}),d?e.jsx(z,{title:d.kind==="edit"?"编辑附件":"从本机路径导入",onClose:()=>h(null),children:d.kind==="path"?e.jsx(V,{form:d.form,onChange:a=>h(s=>({...s,form:{...s.form,...a}})),onSubmit:()=>R(d.form)}):e.jsx(G,{form:d.form,onChange:a=>h(s=>({...s,form:{...s.form,...a}})),onSubmit:()=>F(d.id,d.form)})}):null]})}function V({form:t,onChange:l,onSubmit:m}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"本机文件的绝对路径"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.path,placeholder:"例如 D:\\简历\\张三-简历.pdf",onChange:n=>l({path:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"导入方式"})}),e.jsx("div",{className:"fctrl",children:e.jsxs("label",{className:"inline",children:[e.jsx("input",{type:"checkbox",checked:t.copy,onChange:n=>l({copy:n.target.checked})}),e.jsx("span",{className:"hint",children:"仅复制、保留原件(默认把原件移动进库,可用「还原」撤销)"})]})})]}),e.jsx("div",{className:"rowgap",children:e.jsx("button",{className:"btn",onClick:m,children:"导入"})})]})}function G({form:t,onChange:l,onSubmit:m}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"文件名"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.name,onChange:n=>l({name:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"标签"}),e.jsx("span",{className:"fkey",children:"多个用「、」分隔"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.tags,onChange:n=>l({tags:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"备注"})}),e.jsx("div",{className:"fctrl",children:e.jsx("textarea",{rows:2,value:t.note,onChange:n=>l({note:n.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:m,children:"保存"}),e.jsx("span",{className:"hint",children:"改文件名时,物理原件会随之移动。要链接到条目,请在栏目条目的 file 字段里选本附件。"})]})]})}export{X as default};
1
+ import{u as q,r,a as p,j as e,b as w,T as A,E as H,C as _,M as z}from"./index-VCOWbzUk.js";import{f as K,r as f}from"./files-CJbQeKB4.js";function S(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(2)} MB`}const Q=t=>`${t.sectionTitle} / ${t.entryTitle||"(无名)"} / ${t.fieldLabel}`;function X(){const{toast:t,guard:l,dialog:m,reload:n}=q(),[i,k]=r.useState(null),[C,$]=r.useState(null),[u,N]=r.useState(""),[g,E]=r.useState(""),[D,v]=r.useState(!1),[d,h]=r.useState(null),T=r.useRef(null),x=r.useRef(0),o=r.useCallback(async()=>{try{const a=new URLSearchParams;u&&a.set("section",u),g&&a.set("q",g);const s=a.toString(),c=await p(`/api/files${s?`?${s}`:""}`);k(c),$(null)}catch(a){$(a.message),k(null)}},[u,g]);r.useEffect(()=>{o()},[o]);const b=r.useCallback(async a=>{const s=Array.from(a||[]);if(!s.length)return;let c=0;for(const j of s)try{const y=await K(j);await p("/api/files",{method:"POST",body:{content:y,name:j.name}}),c+=1}catch(y){t(`${j.name} 上传失败:${y.message}`,"bad")}c&&t(`已导入 ${c} 个附件(到栏目条目里选它即可链接)`),await f(),await o(),await n()},[o,n,t]),P=r.useCallback(a=>{a.preventDefault(),x.current=0,v(!1),b(a.dataTransfer.files)},[b]),I=l(async()=>{h({kind:"path",form:{path:"",copy:!1}})}),R=l(async a=>{if(!a.path.trim()){t("请填本机文件的绝对路径","bad");return}const s=await p("/api/files",{method:"POST",body:{path:a.path.trim(),copy:a.copy}});h(null),t(s.duplicated?"库中已有相同文件":s.moved?"已移动进附件库(可还原)":"已复制进附件库"),await f(),await o(),await n()}),B=l(async a=>{h({kind:"edit",id:a.id,form:{name:a.name,tags:a.tags.join("、"),note:a.note}})}),F=l(async(a,s)=>{await p(`/api/files/${encodeURIComponent(a)}`,{method:"PATCH",body:{name:s.name,tags:s.tags.split(/[,,、;;]+/).map(c=>c.trim()).filter(Boolean),note:s.note}}),h(null),t("附件已更新"),await f(),await o()}),U=l(async a=>{const s=await m({message:`把「${a.name}」还原到哪?`,detail:a.sourceDisplay?`默认移回原始位置并撤销台账:${a.sourceDisplay}`:"该附件没有原始位置记录,请在下一步填写目标目录。",input:a.sourceDisplay?void 0:{placeholder:"目标目录绝对路径,例如 D:\\资料"},confirmText:"移回并撤销台账",cancelText:"取消"});if(s===null)return;const c={};s&&typeof s=="string"&&(c.to=s.trim());const j=await p(`/api/files/${encodeURIComponent(a.id)}/restore`,{method:"POST",body:c});t(`已还原到 ${j.displayPath}`),await f(),await o(),await n()}),O=l(async a=>{const s=await m({message:`导出「${a.name}」的副本到哪个目录?库中原件保留。`,input:{placeholder:"目标目录绝对路径,例如 D:\\资料"},confirmText:"导出副本"});if(!s)return;const c=await p(`/api/files/${encodeURIComponent(a.id)}/restore`,{method:"POST",body:{to:String(s).trim(),copy:!0}});t(`已导出到 ${c.displayPath}`)}),L=l(async a=>{await m({message:`删除附件「${a.name}」?物理原件会一起删除,删除前自动留台账快照。`,detail:"想保留原件、只撤销台账,请改用「还原」。",danger:!0,confirmText:"删除"})&&(await p(`/api/files/${encodeURIComponent(a.id)}`,{method:"DELETE"}),t("已删除"),await f(),await o(),await n())});if(C)return e.jsxs("div",{className:"pad stack",children:[e.jsx("div",{className:"bad",children:e.jsx("strong",{children:"附件库读不到"})}),e.jsx("pre",{className:"block",children:C})]});if(!i)return e.jsx("div",{className:"pad muted",children:"加载中…"});const M=i.sections||[];return e.jsxs("div",{className:`files-page ${D?"drag-over":""}`,onDragEnter:a=>{a.preventDefault(),x.current+=1,v(!0)},onDragOver:a=>a.preventDefault(),onDragLeave:a=>{a.preventDefault(),x.current-=1,x.current<=0&&(x.current=0,v(!1))},onDrop:P,children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:"附件"}),e.jsxs("span",{className:"muted",children:[i.total," 个 · ",S(i.totalBytes)]}),e.jsxs("span",{className:"hint",title:"被栏目条目的 file 字段引用的附件数",children:["被引用 ",i.total-i.unlinked,"/",i.total]}),e.jsx(w,{className:"hint",text:i.rootRaw,title:"点击复制附件库目录",children:i.root}),e.jsx("span",{className:"grow"}),e.jsx("input",{style:{width:180},placeholder:"搜索名称 / 备注 / 标签 / 引用它的条目",value:g,onChange:a=>E(a.target.value)}),e.jsx("button",{className:"btn small ghost",onClick:o,children:"刷新"}),e.jsx("button",{className:"btn small ghost",onClick:I,children:"本机路径导入"}),e.jsx("button",{className:"btn small",onClick:()=>{var a;return(a=T.current)==null?void 0:a.click()},children:"上传文件"}),e.jsx("input",{ref:T,type:"file",multiple:!0,style:{display:"none"},onChange:a=>{b(a.target.files),a.target.value=""}})]}),e.jsxs("div",{className:"rowgap pad file-filters",children:[e.jsxs("button",{className:`tab small ${u?"":"active"}`,onClick:()=>N(""),children:["全部",e.jsx("span",{className:"count",children:i.total})]}),M.map(a=>e.jsxs("button",{className:`tab small ${u===a.id?"active":""}`,onClick:()=>N(a.id),title:`被「${a.title}」栏目引用的附件`,children:[a.title,e.jsx("span",{className:"count",children:a.count})]},a.id)),e.jsxs("button",{className:`tab small ${u==="none"?"active":""}`,onClick:()=>N("none"),children:["未引用",e.jsx("span",{className:"count",children:i.unlinked})]})]}),D?e.jsx("div",{className:"drop-hint",children:"松开鼠标,文件直接导入附件库"}):null,i.files.length?e.jsx("div",{className:"file-grid pad",children:i.files.map(a=>{var s;return e.jsxs("div",{className:"file-card",children:[e.jsx("a",{className:"file-thumb",href:a.url,target:"_blank",rel:"noreferrer",title:"在浏览器中预览",children:a.isImage?e.jsx("img",{src:a.url,alt:a.name,loading:"lazy"}):e.jsx("span",{className:"ext-badge",children:a.ext?a.ext.slice(1).toUpperCase():"FILE"})}),e.jsxs("div",{className:"file-meta",children:[e.jsx("a",{className:"file-name",href:a.url,target:"_blank",rel:"noreferrer",title:a.name,children:a.name}),e.jsxs("div",{className:"rowgap",style:{gap:4},children:[e.jsx("span",{className:"hint",children:S(a.bytes)}),a.mode==="move"?e.jsx(A,{children:"移动导入"}):null]}),(s=a.usedBy)!=null&&s.length?e.jsxs("div",{className:"hint",title:"条目的 file 字段引用了本附件",children:["被引用:",a.usedBy.map(Q).join("、")]}):e.jsx("div",{className:"hint",children:"未被任何条目引用(到栏目条目里选它即可链接)"})]}),e.jsxs("div",{className:"file-acts",children:[e.jsx("a",{className:"btn small ghost",href:a.downloadUrl,children:"下载"}),e.jsx("button",{className:"btn small ghost",onClick:()=>O(a),children:"导出副本"}),e.jsx("button",{className:"btn small ghost",onClick:()=>U(a),children:"还原"}),e.jsx("button",{className:"btn small ghost",onClick:()=>B(a),children:"编辑"}),e.jsx("button",{className:"btn small ghost danger-ghost",onClick:()=>L(a),children:"删除"}),e.jsx("span",{className:"grow"}),e.jsx(w,{className:"hint",text:a.path,title:"点击复制物理路径",children:"路径"}),e.jsx(w,{className:"hint",text:a.url,title:"点击复制链接",children:"链接"})]})]},a.id)})}):e.jsxs("div",{className:"pad",children:[e.jsx(H,{children:"还没有附件。把文件拖到这里、点「上传文件」,或用「本机路径导入」把原件移动进库;随后到栏目的条目里选它。"}),e.jsx("div",{className:"hint pad",children:'CLI:nx-sk file add --path "D:\\简历\\张三-简历.pdf"(默认移动;--copy 仅复制)'})]}),e.jsx(_,{module:"files"}),d?e.jsx(z,{title:d.kind==="edit"?"编辑附件":"从本机路径导入",onClose:()=>h(null),children:d.kind==="path"?e.jsx(V,{form:d.form,onChange:a=>h(s=>({...s,form:{...s.form,...a}})),onSubmit:()=>R(d.form)}):e.jsx(G,{form:d.form,onChange:a=>h(s=>({...s,form:{...s.form,...a}})),onSubmit:()=>F(d.id,d.form)})}):null]})}function V({form:t,onChange:l,onSubmit:m}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"本机文件的绝对路径"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.path,placeholder:"例如 D:\\简历\\张三-简历.pdf",onChange:n=>l({path:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"导入方式"})}),e.jsx("div",{className:"fctrl",children:e.jsxs("label",{className:"inline",children:[e.jsx("input",{type:"checkbox",checked:t.copy,onChange:n=>l({copy:n.target.checked})}),e.jsx("span",{className:"hint",children:"仅复制、保留原件(默认把原件移动进库,可用「还原」撤销)"})]})})]}),e.jsx("div",{className:"rowgap",children:e.jsx("button",{className:"btn",onClick:m,children:"导入"})})]})}function G({form:t,onChange:l,onSubmit:m}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"文件名"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.name,onChange:n=>l({name:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"标签"}),e.jsx("span",{className:"fkey",children:"多个用「、」分隔"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.tags,onChange:n=>l({tags:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"备注"})}),e.jsx("div",{className:"fctrl",children:e.jsx("textarea",{rows:2,value:t.note,onChange:n=>l({note:n.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:m,children:"保存"}),e.jsx("span",{className:"hint",children:"改文件名时,物理原件会随之移动。要链接到条目,请在栏目条目的 file 字段里选本附件。"})]})]})}export{X as default};
@@ -8,8 +8,8 @@
8
8
  <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png" />
9
9
  <link rel="icon" type="image/x-icon" href="/favicon.ico" />
10
10
  <link rel="apple-touch-icon" href="/logo-rounded.png" />
11
- <script type="module" crossorigin src="/assets/index-Dxf_b602.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-Cwk9VSKZ.css">
11
+ <script type="module" crossorigin src="/assets/index-VCOWbzUk.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-lmztA3cP.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -1 +0,0 @@
1
- :root{--ink: #14161a;--paper: #fff;--soft: #f5f6f7;--soft-2: #ebedf0;--mid: #8b9096;--danger: #b3261e;--border: 1px solid var(--soft-2);--row-h: 28px;--btn-h: 32px;--fs-core: 12px;--radius-btn: 8px;--shadow-relief: -1px -1px 2px rgba(255, 255, 255, .85), 2px 2px 6px rgba(20, 22, 26, .07);--shadow-relief-press: inset 1px 1px 2px rgba(20, 22, 26, .1), inset -1px -1px 2px rgba(255, 255, 255, .7);--shadow-relief-inset: inset 1px 1px 2px rgba(20, 22, 26, .06), inset -1px -1px 2px rgba(255, 255, 255, .9)}*{box-sizing:border-box}html,body,#root{height:100%}body{margin:0;background:var(--paper);color:var(--ink);font-family:system-ui,-apple-system,Segoe UI,Microsoft YaHei,sans-serif;font-size:var(--fs-core);line-height:1.5;-webkit-font-smoothing:antialiased}.mono,code,.path,.cli-cmd{font-family:ui-monospace,Consolas,Courier New,monospace}.muted{color:var(--mid)}.bad{color:var(--danger)}.nowrap{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pad{padding:10px 14px}.rowgap{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.grow{flex:1;min-width:0}.right{margin-left:auto}.app{display:flex;flex-direction:column;height:100%;min-height:0}header.top{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:10px 14px;border-bottom:var(--border);flex-shrink:0}header.top .brand{font-size:15px;font-weight:700;letter-spacing:.2px;line-height:1;display:inline-flex;align-items:center;gap:8px}header.top .brand img{width:22px;height:22px;border-radius:6px;display:block}header.top .tagline{color:var(--mid);font-size:11px}header.top .ver{font-size:10px;color:var(--mid);background:var(--soft);border:var(--border);border-radius:999px;padding:1px 8px;line-height:1.7}header.top .store{display:inline-flex;align-items:center;gap:6px;min-width:0;font-size:11px;color:var(--mid)}header.top .store .store-label{color:var(--mid)}header.top .store .copyable{max-width:30ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}nav.tabs{display:flex;flex-wrap:wrap;align-items:center;gap:6px;padding:6px 14px;border-bottom:var(--border);flex-shrink:0}.tabs-sep{width:1px;height:18px;background:var(--soft-2);margin:0 4px}.tab-group{position:relative;display:inline-flex}.tab-group .caret{width:0;height:0;margin-left:1px;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid var(--mid)}.tab-group .tab.active .caret{border-top-color:var(--ink)}.tab-menu{position:absolute;top:calc(100% + 4px);left:0;z-index:40;min-width:172px;padding:4px;display:flex;flex-direction:column;gap:2px;background:var(--paper);border:var(--border);border-radius:var(--radius-btn);box-shadow:var(--shadow-relief)}.tab-menu-row{display:flex;align-items:center;gap:10px;width:100%;height:var(--row-h);padding:0 8px;border:0;border-radius:6px;background:none;color:var(--ink);font:inherit;font-size:var(--fs-core);text-align:left;cursor:pointer}.tab-menu-row:hover{background:var(--soft)}.tab-menu-row.current{font-weight:600}.tab-menu-row .count{color:var(--mid);font-size:10px;font-variant-numeric:tabular-nums}main.body{flex:1;min-height:0;overflow:auto}.tab{font:inherit;font-size:var(--fs-core);height:var(--btn-h);padding:0 12px;border:0;border-radius:var(--radius-btn);background:var(--soft);color:var(--ink);cursor:pointer;display:inline-flex;align-items:center;gap:7px}.tab .count{color:var(--mid);font-size:10px;font-variant-numeric:tabular-nums}.tab.active{background:var(--paper);box-shadow:var(--shadow-relief);font-weight:600}.btn{font:inherit;font-size:var(--fs-core);cursor:pointer;height:var(--btn-h);padding:0 12px;border:0;border-radius:var(--radius-btn);background:var(--ink);color:var(--paper);box-shadow:var(--shadow-relief);display:inline-flex;align-items:center;gap:4px;transition:box-shadow .12s,transform .08s}.btn:hover:not(:disabled){box-shadow:var(--shadow-relief),0 0 0 3px #14161a0d}.btn:active:not(:disabled){box-shadow:var(--shadow-relief-press);transform:translateY(1px)}.btn:disabled{background:var(--soft);color:var(--mid);box-shadow:var(--shadow-relief-inset);cursor:not-allowed}.btn.ghost{background:var(--soft);color:var(--ink)}.btn.danger{background:var(--danger);color:#fff}.btn.small{height:22px;padding:0 8px;font-size:11px;border-radius:6px}input,select,textarea{font:inherit;font-size:var(--fs-core);color:var(--ink);background:var(--paper);border:var(--border);border-radius:6px;padding:0 8px}input,select{height:var(--row-h)}textarea{padding:5px 8px;resize:vertical;min-height:56px;line-height:1.5}input:focus,select:focus,textarea:focus{outline:none;border-color:var(--soft-2);box-shadow:var(--shadow-relief-inset)}label.inline{display:inline-flex;align-items:center;gap:6px}.card{border-bottom:var(--border)}.card:last-child{border-bottom:0}.card>.colhead{display:flex;align-items:center;gap:8px;height:var(--row-h);padding:0 14px;border-bottom:var(--border);font-size:13px;font-weight:600}.card>.colbody{padding:0}.row{display:flex;align-items:center;gap:8px;min-height:var(--row-h);padding:0 14px;border-bottom:var(--border);font-size:var(--fs-core);transition:background .12s}.row:hover{background:var(--soft)}.row:last-child{border-bottom:0}.row .name{font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row .desc{color:var(--mid);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row .acts{margin-left:auto;display:flex;align-items:center;gap:6px;flex-shrink:0}.row.selectable{cursor:pointer}.row.selected{background:var(--soft);box-shadow:inset 2px 0 0 var(--ink)}.tag{display:inline-flex;align-items:center;height:22px;padding:0 7px;border-radius:999px;background:var(--soft);color:var(--mid);font-size:11px;white-space:nowrap}.tag.strong{background:var(--ink);color:var(--paper)}.tag.bad{background:#fbeae9;color:var(--danger)}.copyable{cursor:copy;border-radius:4px;padding:0 3px}.copyable:hover{background:var(--soft)}.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:0 14px}.field{border-bottom:var(--border);padding:5px 0;min-width:0}.field>.flabel{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--mid)}.field>.flabel .fkey{font-family:ui-monospace,Consolas,monospace;font-size:10px;color:var(--soft-2)}.field>.fctrl{margin-top:3px}.field input,.field select,.field textarea{width:100%}.field.wide{grid-column:1 / -1}.group-title{display:flex;align-items:center;gap:8px;height:var(--row-h);padding:0 14px;border-bottom:var(--border);background:var(--soft);font-size:11px;color:var(--mid);letter-spacing:.5px}.cli-hint{display:flex;flex-wrap:wrap;align-items:center;gap:2px;padding:6px 14px;color:var(--mid);font-size:11px;border-top:var(--border)}.cli-hint-label{margin-right:4px}.cli-hint-sep{color:var(--soft-2)}.cli-cmd{color:var(--ink)}.toasts{position:fixed;right:14px;bottom:14px;display:flex;flex-direction:column;gap:6px;z-index:40}.toast{background:var(--ink);color:var(--paper);padding:7px 12px;border-radius:8px;font-size:var(--fs-core);box-shadow:var(--shadow-relief);max-width:420px}.toast.bad{background:var(--danger)}.mask{position:fixed;top:0;right:0;bottom:0;left:0;background:#14161a47;display:flex;align-items:center;justify-content:center;z-index:50;padding:20px}.dialog{background:var(--paper);border-radius:8px;min-width:320px;max-width:min(880px,92vw);max-height:86vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 10px 40px #14161a2e}.dialog-title{display:flex;align-items:center;gap:8px;height:var(--row-h);padding:0 14px;border-bottom:var(--border);font-size:13px;font-weight:600;flex-shrink:0}.dialog-body{padding:10px 14px;overflow:auto;min-height:0}.dialog-foot{display:flex;gap:8px;justify-content:flex-end;padding:8px 14px;border-top:var(--border);flex-shrink:0}pre.block{margin:0;padding:10px 14px;background:var(--soft);border-radius:8px;font-family:ui-monospace,Consolas,monospace;font-size:11px;line-height:1.55;white-space:pre-wrap;word-break:break-word;max-height:60vh;overflow:auto}.hint{color:var(--mid);font-size:11px}.stack{display:flex;flex-direction:column;gap:10px}.toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.cols{display:flex;gap:14px;align-items:flex-start;padding:0 14px 14px}.col{flex:1;min-width:0}.desc{color:var(--mid)}.cols>.col>.card{border:var(--border);border-radius:4px}.files-page{min-height:100%;position:relative}.files-page.drag-over{outline:2px dashed var(--ink);outline-offset:-8px;background:var(--soft)}.tab.small{height:22px;padding:0 8px;font-size:11px;border-radius:6px}.file-filters{padding-top:4px;padding-bottom:4px}.drop-hint{margin:0 14px;padding:18px;text-align:center;border:1px dashed var(--ink);border-radius:8px;background:var(--paper);font-weight:600}.file-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:10px}.file-card{display:flex;flex-direction:column;border:var(--border);border-radius:8px;overflow:hidden;background:var(--paper)}.file-thumb{display:flex;align-items:center;justify-content:center;height:120px;background:var(--soft);border-bottom:var(--border)}.file-thumb img{max-width:100%;max-height:100%;object-fit:contain}.ext-badge{font-size:22px;font-weight:700;letter-spacing:1px;color:var(--mid);font-family:ui-monospace,Consolas,monospace}.file-meta{padding:8px 10px 4px;display:flex;flex-direction:column;gap:4px}a.file-name{color:var(--ink);font-weight:600;text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}a.file-name:hover{text-decoration:underline}.file-acts{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:6px 10px 8px;margin-top:auto}.btn.ghost.danger-ghost{color:var(--danger)}.file-picker{display:flex;flex-direction:column;gap:4px}.file-picker.drag{outline:2px dashed var(--ink);outline-offset:-4px;background:var(--soft)}.file-picker .file-pick-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.file-picker select{flex:1;min-width:120px}.file-preview{display:flex;align-items:center;gap:8px;border:var(--border);border-radius:6px;padding:4px 8px;background:var(--soft)}.file-preview img{width:40px;height:40px;object-fit:cover;border-radius:4px;border:var(--border);background:#fff}.file-preview .file-preview-name{color:var(--ink);text-decoration:none;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-preview .file-preview-name:hover{text-decoration:underline}
@@ -1 +0,0 @@
1
- import{u as K,r as x,a as w,j as s,b as p,E as M,C as O,T as V}from"./index-Dxf_b602.js";function B(){var C,E,$,F,I,G,P,T;const{boot:e,reload:j,toast:r,guard:y}=K(),[u,N]=x.useState(null),[i,h]=x.useState(null),[d,o]=x.useState([]),b=typeof window>"u"?"":window.location.origin,k=typeof window>"u"?"":window.location.port||"80",v=x.useCallback(async()=>{try{const a=await w("/api/settings");N(a),h({...a.settings}),o((a.settings.navGroups||[]).map(l=>({title:l.title,sections:[...l.sections]})))}catch(a){r(a.message,"bad")}},[r]);x.useEffect(()=>{v()},[v]);const A=y(async()=>{const a={exportDir:i.exportDir,exportFormat:i.exportFormat,includeSecretsInExport:!!i.includeSecretsInExport,defaultSection:i.defaultSection},l=await w("/api/settings",{method:"PATCH",body:a});r("设置已保存"),N({...u,settings:l.set}),await j()}),D=(a,l)=>o(c=>c.map((n,t)=>t===a?{...n,...l}:n)),R=(a,l)=>o(c=>c.map((n,t)=>{if(t!==a)return n;const g=n.sections.includes(l);return{...n,sections:g?n.sections.filter(f=>f!==l):[...n.sections,l]}})),S=(a,l)=>o(c=>{const n=a+l;if(n<0||n>=c.length)return c;const t=[...c];return[t[a],t[n]]=[t[n],t[a]],t}),H=y(async()=>{if(d.find(n=>!String(n.title).trim())){r("每个分组都要有组名","bad");return}const l=d.find(n=>!n.sections.length);if(l){r(`分组「${l.title}」还没有勾选栏目——要么勾一个,要么删掉这个分组`,"bad");return}const c=await w("/api/settings",{method:"PATCH",body:{navGroups:d}});r(d.length?`已保存 ${d.length} 个导航分组`:"已取消分组,栏目恢复平铺"),N({...u,settings:c.set}),o((c.set.navGroups||[]).map(n=>({title:n.title,sections:[...n.sections]}))),await j()});return!u||!i?s.jsx("div",{className:"pad muted",children:"加载中…"}):s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"toolbar rowgap pad",children:[s.jsx("strong",{children:"设置"}),s.jsx("span",{className:"muted",children:"本机配置与运行状态"}),s.jsx("span",{className:"grow"}),s.jsx("button",{className:"btn small ghost",onClick:v,children:"重新读取"})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"存储与运行"}),s.jsxs("div",{className:"colbody",children:[s.jsx(m,{label:"数据目录",value:e==null?void 0:e.home,copy:e==null?void 0:e.home}),s.jsx(m,{label:"store.json",value:e==null?void 0:e.storePath,copy:e==null?void 0:e.storePathRaw}),s.jsx(m,{label:"栏目 / 条目",value:e?`${e.counts.sections} / ${e.counts.entries}`:""}),s.jsx(m,{label:"命令条数",value:e?String(e.counts.commands):""}),s.jsx(m,{label:"app / node",value:e?`v${e.app.version} · node ${e.app.node} · ${e.app.platform}`:""}),s.jsx(m,{label:"环境变量覆盖",value:e?Object.entries(e.envOverrides).filter(([,a])=>a).map(([a,l])=>`${a}=${l}`).join(" ")||"(无)":""})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"密钥(密文落盘)"}),s.jsxs("div",{className:"colbody",children:[s.jsx(m,{label:"密钥来源",value:((C=e==null?void 0:e.vault)==null?void 0:C.source)==="env"?"环境变量 NX_SK_PASSPHRASE(口令不落盘)":"本机密钥文件"}),s.jsx(m,{label:"密钥文件",value:((E=e==null?void 0:e.vault)==null?void 0:E.keyFile)||(($=e==null?void 0:e.vault)==null?void 0:$.saltFile)||"",copy:((F=e==null?void 0:e.vault)==null?void 0:F.keyFile)||((I=e==null?void 0:e.vault)==null?void 0:I.saltFile)}),s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc",children:(G=e==null?void 0:e.vault)==null?void 0:G.note})}),s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc bad",children:"删掉密钥文件或换掉口令后,已存的密文都解不开了——导出前先确认密钥可用。"})})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"面板与 CLI"}),s.jsxs("div",{className:"colbody",children:[s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:"面板地址"}),s.jsx("span",{className:"desc mono",children:b}),s.jsx("span",{className:"acts",children:s.jsx(p,{className:"mono",text:b,children:"复制"})})]}),s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:"重启面板"}),s.jsx("span",{className:"desc",children:"面板本身就是 serve 提供的;改代码后重启它即可"}),s.jsx("span",{className:"acts",children:s.jsx(p,{className:"mono",text:`nx-sk serve --port ${k}`,children:`nx-sk serve --port ${k}`})})]})]})]}),s.jsxs("div",{className:"card",children:[s.jsxs("div",{className:"colhead",children:["头部导航分组",s.jsx("span",{className:"right muted",children:d.length?`${d.length} 个分组`:"未分组(全部平铺)"})]}),s.jsxs("div",{className:"pad stack",children:[s.jsx("div",{className:"hint",children:"把同域栏目收拢成一个头部入口:头部只显示组名,**悬浮展开上下列表**选具体栏目。 不勾进任何分组的栏目保持平铺;组顺序 = 头部显示顺序;一个栏目只能属于一个分组。"}),d.map((a,l)=>s.jsxs("div",{className:"stack",style:{borderTop:l?"var(--border)":0,paddingTop:l?8:0},children:[s.jsxs("div",{className:"rowgap",children:[s.jsx("input",{value:a.title,placeholder:"组名,如「求职」",onChange:c=>D(l,{title:c.target.value}),style:{minWidth:160}}),s.jsx("button",{className:"btn small ghost",disabled:l===0,onClick:()=>S(l,-1),children:"上移"}),s.jsx("button",{className:"btn small ghost",disabled:l===d.length-1,onClick:()=>S(l,1),children:"下移"}),s.jsx("button",{className:"btn small ghost",onClick:()=>o(c=>c.filter((n,t)=>t!==l)),children:"删除组"}),s.jsxs("span",{className:"muted",children:["已选 ",a.sections.length," 个栏目"]})]}),s.jsx("div",{className:"rowgap",children:((e==null?void 0:e.sections)||[]).map(c=>{const n=a.sections.includes(c.id),t=d.some((g,f)=>f!==l&&g.sections.includes(c.id));return s.jsxs("label",{className:"inline",title:t?"已归入其它分组":"",children:[s.jsx("input",{type:"checkbox",checked:n,disabled:t,onChange:()=>R(l,c.id)}),s.jsxs("span",{className:t?"muted":"",children:[c.title,s.jsxs("span",{className:"count",children:[" ",c.entries]})]})]},c.id)})})]},l)),d.length?null:s.jsx("div",{className:"muted",children:"还没有分组——所有栏目都平铺在头部。"}),s.jsxs("div",{className:"rowgap",children:[s.jsx("button",{className:"btn",onClick:H,children:"保存分组"}),s.jsx("button",{className:"btn small ghost",onClick:()=>o(a=>[...a,{title:"",sections:[]}]),children:"新增分组"}),d.length?s.jsx("button",{className:"btn small ghost",onClick:()=>o([]),children:"清空(恢复平铺)"}):null]})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"默认行为"}),s.jsxs("div",{className:"pad",children:[s.jsxs("div",{className:"grid",children:[s.jsxs("div",{className:"field",children:[s.jsx("div",{className:"flabel",children:s.jsx("span",{children:"默认导出格式"})}),s.jsx("div",{className:"fctrl",children:s.jsxs("select",{value:i.exportFormat,onChange:a=>h({...i,exportFormat:a.target.value}),children:[s.jsx("option",{value:"both",children:"JSON + Markdown"}),s.jsx("option",{value:"json",children:"仅 JSON"}),s.jsx("option",{value:"md",children:"仅 Markdown"})]})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"默认导出目录"}),s.jsx("span",{className:"fkey",children:"留空 = ~/nx-sk/export"})]}),s.jsx("div",{className:"fctrl",children:s.jsx("input",{value:i.exportDir,onChange:a=>h({...i,exportDir:a.target.value})})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"默认栏目"}),s.jsx("span",{className:"fkey",children:"CLI 未指定 --section 时的首选"})]}),s.jsx("div",{className:"fctrl",children:s.jsx("input",{value:i.defaultSection,onChange:a=>h({...i,defaultSection:a.target.value})})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"导出默认带明文密钥"}),s.jsx("span",{className:"fkey",children:"默认开;要分享出去时关掉"})]}),s.jsx("div",{className:"fctrl",children:s.jsxs("label",{className:"inline",children:[s.jsx("input",{type:"checkbox",checked:!!i.includeSecretsInExport,onChange:a=>h({...i,includeSecretsInExport:a.target.checked})}),s.jsx("span",{className:i.includeSecretsInExport?"muted":"bad",children:i.includeSecretsInExport?"开(导出文件含真实凭据,注意别外发)":"关(凭据打码后再导出)"})]})})]})]}),s.jsx("div",{className:"rowgap",style:{marginTop:8},children:s.jsx("button",{className:"btn",onClick:A,children:"保存设置"})})]})]}),s.jsxs("div",{className:"card",children:[s.jsxs("div",{className:"colhead",children:["自动快照",s.jsx("span",{className:"right muted",children:((P=e==null?void 0:e.snapshots)==null?void 0:P.length)??0})]}),s.jsxs("div",{className:"colbody",children:[s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc",children:"每次删除条目 / 栏目、修改设置前,都会在 ~/nx-sk/backup 留一份可回滚的整库快照。"})}),(T=e==null?void 0:e.snapshots)!=null&&T.length?e.snapshots.map(a=>s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"mono name",children:a.name}),s.jsx("span",{className:"desc",children:a.at}),s.jsx("span",{className:"acts",children:s.jsxs("span",{className:"tag",children:[Math.ceil(a.bytes/1024)," KB"]})})]},a.name)):s.jsx(M,{children:"还没有快照。"})]})]}),e!=null&&e.migration?s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"数据迁移"}),s.jsxs("div",{className:"colbody",children:[s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc",children:["store 已从 v",e.migration.from," 迁到 v",e.migration.to,":密钥栏目字段 ",e.migration.fieldsBefore," → ",e.migration.fieldsAfter,",",e.migration.entries," 条条目"]})}),s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"desc",children:"迁移前的原始文件"}),s.jsx("span",{className:"acts",children:s.jsx(p,{className:"mono",text:e.migration.snapshotRaw||"",children:e.migration.snapshot})})]}),e.migration.legacyValuesKept?s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc",children:["另保留了 ",e.migration.legacyValuesKept," 个旧字段的值(不再显示,但没删)"]})}):null]})]}):null,e!=null&&e.corrupt?s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"上次读取异常"}),s.jsx("div",{className:"colbody",children:s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc bad",children:["store.json 解析失败,已挪到 ",e.corrupt.to,"(原文件没有被覆盖)"]})})})]}):null,s.jsx(O,{module:"settings"})]})}function m({label:e,value:j,copy:r}){return s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:e}),s.jsx("span",{className:"desc mono",children:j||"(空)"}),r?s.jsx("span",{className:"acts",children:s.jsx(p,{className:"mono",text:r,children:"复制"})}):s.jsx(V,{children:" "})]})}export{B as default};