@xia-sc/dsh-cc-studio 0.4.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -295
- package/lib/agent.js +1402 -159
- package/lib/client.js +327 -8
- package/lib/index.js +647 -25
- package/package.json +2 -2
- package/presets/cc/agent.cordis.yml +6 -4
- package/presets/cc.patch.yml +6 -4
package/lib/client.js
CHANGED
|
@@ -132,6 +132,50 @@ window.__ModuleLoader__.load({
|
|
|
132
132
|
"workshop.library.save":"★ 保存当前",
|
|
133
133
|
"workshop.library.saveAs":"+ 另存为新",
|
|
134
134
|
"workshop.library.rename":"✎ 重命名",
|
|
135
|
+
// —— 0.8.0 人物关系图谱 / 群聊分组 ——
|
|
136
|
+
"workshop.group.all":"全部",
|
|
137
|
+
"workshop.group.none":"未分组",
|
|
138
|
+
"workshop.group.new":"+ 组",
|
|
139
|
+
"workshop.group.summary":"{n} 卡 / {m} 关系",
|
|
140
|
+
"workshop.group.prompt":"新分组名称(同一世界观的角色放一组)",
|
|
141
|
+
"workshop.group.assign":"组",
|
|
142
|
+
"workshop.group.set":"已归组",
|
|
143
|
+
"workshop.rel.title":"人物关系",
|
|
144
|
+
"workshop.rel.open":"关系",
|
|
145
|
+
"workshop.rel.pickGroup":"先在左侧选一个具体分组(「全部 / 未分组」下没有可编辑的关系)。",
|
|
146
|
+
"workshop.rel.noGroup":"未选分组",
|
|
147
|
+
"workshop.rel.members":"成员卡",
|
|
148
|
+
"workshop.rel.noMember":"(还没有卡入组 —— 用卡片行上的「组」按钮把同一世界观的卡拉进来)",
|
|
149
|
+
"workshop.rel.edges":"关系",
|
|
150
|
+
"workshop.rel.noEdge":"(还没有关系:用下面的表单加一条,或让模型用 cc_patch_relations 批量建)",
|
|
151
|
+
"workshop.rel.add":"添加关系",
|
|
152
|
+
"workshop.rel.addNode":"+ NPC",
|
|
153
|
+
"workshop.rel.nodePrompt":"NPC 名字(没有独立角色卡的人物,例如「柳长老」)",
|
|
154
|
+
"workshop.rel.from":"从",
|
|
155
|
+
"workshop.rel.to":"到",
|
|
156
|
+
"workshop.rel.strength":"强度",
|
|
157
|
+
"workshop.rel.direction":"方向",
|
|
158
|
+
"workshop.rel.dirMutual":"对等",
|
|
159
|
+
"workshop.rel.dirOneWay":"单向",
|
|
160
|
+
"workshop.rel.note":"备注",
|
|
161
|
+
"workshop.rel.check":"体检",
|
|
162
|
+
"workshop.rel.render":"渲染到当前卡",
|
|
163
|
+
"workshop.rel.rendered":"已把 {n} 条关系写进当前卡(记得「保存当前」才会落库)",
|
|
164
|
+
"workshop.rel.checkResult":"体检:{n} 个问题",
|
|
165
|
+
"workshop.rel.checkOk":"体检通过:没有结构性问题",
|
|
166
|
+
"workshop.rel.saved":"关系已更新(rev {rev})",
|
|
167
|
+
"workshop.rel.needLoad":"先在左侧「载入」一张卡:渲染要知道写进哪张卡",
|
|
168
|
+
"workshop.rel.hint":"关系只存在插件侧,不写进 CCv3 字段;「渲染到当前卡」会把它们写进 description 的【关系】段,让分享出去的卡自包含。",
|
|
169
|
+
"rel.type.family":"亲属",
|
|
170
|
+
"rel.type.romance":"恋慕",
|
|
171
|
+
"rel.type.mentor":"师徒",
|
|
172
|
+
"rel.type.friend":"挚友",
|
|
173
|
+
"rel.type.colleague":"同门",
|
|
174
|
+
"rel.type.rival":"竞争",
|
|
175
|
+
"rel.type.enemy":"敌对",
|
|
176
|
+
"rel.type.debt":"恩义",
|
|
177
|
+
"rel.type.superior":"上下级",
|
|
178
|
+
"rel.type.other":"其他",
|
|
135
179
|
"workshop.library.new":"+ 新建",
|
|
136
180
|
"workshop.library.searchPlaceholder":"搜索 名称/标签…",
|
|
137
181
|
"workshop.library.empty":"还没有已存角色\n去“世界观/角色细化”填好后点“保存当前”",
|
|
@@ -216,6 +260,7 @@ window.__ModuleLoader__.load({
|
|
|
216
260
|
"workshop.greeting.groupHint":"建议 ≥1 条",
|
|
217
261
|
"workshop.field.nickname":"nickname({{char}} 用)",
|
|
218
262
|
"workshop.field.tags":"tags(逗号分隔)",
|
|
263
|
+
"workshop.field.creator":"作者(creator)",
|
|
219
264
|
"workshop.field.mesExample":"mes_example({{user}}/{{char}})",
|
|
220
265
|
"workshop.edit.title":"{title} · 大框编辑",
|
|
221
266
|
"workshop.edit.fallbackTitle":"编辑",
|
|
@@ -344,6 +389,50 @@ window.__ModuleLoader__.load({
|
|
|
344
389
|
"workshop.library.save":"★ Save Current",
|
|
345
390
|
"workshop.library.saveAs":"+ Save as New",
|
|
346
391
|
"workshop.library.rename":"✎ Rename",
|
|
392
|
+
// —— 0.8.0 relationship graph / group chat ——
|
|
393
|
+
"workshop.group.all":"All",
|
|
394
|
+
"workshop.group.none":"Ungrouped",
|
|
395
|
+
"workshop.group.new":"+ Group",
|
|
396
|
+
"workshop.group.summary":"{n} cards / {m} relations",
|
|
397
|
+
"workshop.group.prompt":"New group name (characters of one world go together)",
|
|
398
|
+
"workshop.group.assign":"Group",
|
|
399
|
+
"workshop.group.set":"Grouped",
|
|
400
|
+
"workshop.rel.title":"Relationships",
|
|
401
|
+
"workshop.rel.open":"Relations",
|
|
402
|
+
"workshop.rel.pickGroup":"Pick a specific group on the left (no editable relations under All / Ungrouped).",
|
|
403
|
+
"workshop.rel.noGroup":"No group",
|
|
404
|
+
"workshop.rel.members":"Member cards",
|
|
405
|
+
"workshop.rel.noMember":"(no cards yet — use the “Group” button on a card row to pull in cards of the same world)",
|
|
406
|
+
"workshop.rel.edges":"Relations",
|
|
407
|
+
"workshop.rel.noEdge":"(none yet: add one with the form below, or let the model build them with cc_patch_relations)",
|
|
408
|
+
"workshop.rel.add":"Add relation",
|
|
409
|
+
"workshop.rel.addNode":"+ NPC",
|
|
410
|
+
"workshop.rel.nodePrompt":"NPC name (someone without their own card, e.g. an elder)",
|
|
411
|
+
"workshop.rel.from":"From",
|
|
412
|
+
"workshop.rel.to":"To",
|
|
413
|
+
"workshop.rel.strength":"Strength",
|
|
414
|
+
"workshop.rel.direction":"Direction",
|
|
415
|
+
"workshop.rel.dirMutual":"Mutual",
|
|
416
|
+
"workshop.rel.dirOneWay":"One-way",
|
|
417
|
+
"workshop.rel.note":"Note",
|
|
418
|
+
"workshop.rel.check":"Check",
|
|
419
|
+
"workshop.rel.render":"Render into current card",
|
|
420
|
+
"workshop.rel.rendered":"Wrote {n} relations into the current card (remember to “Save current”)",
|
|
421
|
+
"workshop.rel.checkResult":"Check: {n} issue(s)",
|
|
422
|
+
"workshop.rel.checkOk":"Check passed: no structural issues",
|
|
423
|
+
"workshop.rel.saved":"Relations updated (rev {rev})",
|
|
424
|
+
"workshop.rel.needLoad":"Load a card on the left first: rendering needs to know which card to write into",
|
|
425
|
+
"workshop.rel.hint":"Relations live on the plugin side only and are never written into CCv3 fields; “Render into current card” writes them into the 【关系】 block of description so a shared card stays self-contained.",
|
|
426
|
+
"rel.type.family":"Family",
|
|
427
|
+
"rel.type.romance":"Romance",
|
|
428
|
+
"rel.type.mentor":"Mentor",
|
|
429
|
+
"rel.type.friend":"Close friend",
|
|
430
|
+
"rel.type.colleague":"Colleague",
|
|
431
|
+
"rel.type.rival":"Rival",
|
|
432
|
+
"rel.type.enemy":"Enemy",
|
|
433
|
+
"rel.type.debt":"Obligation",
|
|
434
|
+
"rel.type.superior":"Superior",
|
|
435
|
+
"rel.type.other":"Other",
|
|
347
436
|
"workshop.library.new":"+ New",
|
|
348
437
|
"workshop.library.searchPlaceholder":"Search name/tags…",
|
|
349
438
|
"workshop.library.empty":"No saved characters\nFill World/Character then ★ Save Current",
|
|
@@ -428,6 +517,7 @@ window.__ModuleLoader__.load({
|
|
|
428
517
|
"workshop.greeting.groupHint":"≥1 recommended",
|
|
429
518
|
"workshop.field.nickname":"nickname (used by {{char}})",
|
|
430
519
|
"workshop.field.tags":"tags (comma separated)",
|
|
520
|
+
"workshop.field.creator":"creator / author",
|
|
431
521
|
"workshop.field.mesExample":"mes_example ({{user}}/{{char}})",
|
|
432
522
|
"workshop.edit.title":"{title} · Large editor",
|
|
433
523
|
"workshop.edit.fallbackTitle":"Edit",
|
|
@@ -485,7 +575,7 @@ window.__ModuleLoader__.load({
|
|
|
485
575
|
var _saved = loadSettings();
|
|
486
576
|
// 兼容旧开关:poll=false 视为 0 秒
|
|
487
577
|
if(_saved.poll===false) _saved.pollInterval = 0;
|
|
488
|
-
var state={ draft: draft, draftStatus: null, cur:0, panelOpen:false, sideExpanded:false, isCcMode:false, currentSessionId:null, slotWarn:null, alternateSlots:null, world:{ timeline:"", factions:"", geo:"", power:"", daily:"" }, busy:null, valid:{valid:true,errors:[],warnings:[]}, lastMsg:null, library:[], libraryQuery:"", libraryCollapsed:false, libraryBusy:null, currentLibraryId:null, editingWorld:null, editingWorldRevert:null, editingText:null, pollEnabled:_saved.poll, pollInterval:_saved.pollInterval, autoValidate:_saved.autoValidate, autoSaveOnImport:_saved.autoSaveOnImport };
|
|
578
|
+
var state={ draft: draft, draftStatus: null, cur:0, panelOpen:false, sideExpanded:false, isCcMode:false, currentSessionId:null, slotWarn:null, alternateSlots:null, world:{ timeline:"", factions:"", geo:"", power:"", daily:"" }, busy:null, valid:{valid:true,errors:[],warnings:[]}, lastMsg:null, library:[], libraryQuery:"", libraryCollapsed:false, libraryBusy:null, groups:[], currentGroupId:"", relations:null, relBusy:null, currentLibraryId:null, editingWorld:null, editingWorldRevert:null, editingText:null, pollEnabled:_saved.poll, pollInterval:_saved.pollInterval, autoValidate:_saved.autoValidate, autoSaveOnImport:_saved.autoSaveOnImport };
|
|
489
579
|
var listeners=new Set();
|
|
490
580
|
function emit(){ listeners.forEach(function(fn){ try{fn();}catch(e){console.error(e);} }); }
|
|
491
581
|
function getSnapshot(){ return state; }
|
|
@@ -789,6 +879,121 @@ window.__ModuleLoader__.load({
|
|
|
789
879
|
});
|
|
790
880
|
}
|
|
791
881
|
function setLibraryQuery(v){ set({libraryQuery: String(v).slice(0,40)}); }
|
|
882
|
+
// ———————————————— 0.8.0 人物关系图谱 / 群聊分组 ————————————————
|
|
883
|
+
// 关系数据活在宿主侧(<DSH_HOME>/cc-groups/),前端只通过 RPC 读写;「卡属于哪个组」由库条目
|
|
884
|
+
// 的 meta.groupId 决定(分组文件里不存成员),所以这里每次入组/移组后都要 refreshLibrary。
|
|
885
|
+
function setCurrentGroupId(id){
|
|
886
|
+
var gid = String(id || "");
|
|
887
|
+
set({currentGroupId: gid});
|
|
888
|
+
// "" = 全部(不过滤)、"__none" = 只看未分组:这两个都不是真分组,没有图谱可载
|
|
889
|
+
if(gid && gid !== "__none") loadGroup(gid); else set({relations:null});
|
|
890
|
+
}
|
|
891
|
+
async function refreshGroups(){
|
|
892
|
+
try{
|
|
893
|
+
var res = await rpc("cc_listGroups", {});
|
|
894
|
+
if(res && res.ok && res.value) set({groups: res.value.groups||[]});
|
|
895
|
+
}catch(e){ set({lastMsg:String(e && e.message || e)}); }
|
|
896
|
+
}
|
|
897
|
+
async function loadGroup(id){
|
|
898
|
+
var gid = String(id || state.currentGroupId || "");
|
|
899
|
+
if(!gid){ set({relations:null}); return; }
|
|
900
|
+
set({relBusy:"load"});
|
|
901
|
+
try{
|
|
902
|
+
var res = await rpc("cc_getGroup", {id: gid});
|
|
903
|
+
if(res && res.ok && res.value) set({relations: res.value});
|
|
904
|
+
else set({relations:null, lastMsg:String((res && res.error && res.error.message) || "")});
|
|
905
|
+
}catch(e){ set({lastMsg:String(e && e.message || e)}); }
|
|
906
|
+
set({relBusy:null});
|
|
907
|
+
}
|
|
908
|
+
async function createGroup(){
|
|
909
|
+
var name = "";
|
|
910
|
+
try{ if(typeof window!=="undefined" && window.prompt) name = String(window.prompt(t("workshop.group.prompt"), "")||"").trim(); }catch(e){}
|
|
911
|
+
if(!name) return;
|
|
912
|
+
set({relBusy:"new"});
|
|
913
|
+
try{
|
|
914
|
+
var res = await rpc("cc_createGroup", {name: name});
|
|
915
|
+
if(res && res.ok && res.value){
|
|
916
|
+
await refreshGroups();
|
|
917
|
+
set({currentGroupId: res.value.group.id});
|
|
918
|
+
await loadGroup(res.value.group.id);
|
|
919
|
+
} else set({lastMsg:String((res && res.error && res.error.message) || "")});
|
|
920
|
+
}catch(e){ set({lastMsg:String(e && e.message || e)}); }
|
|
921
|
+
set({relBusy:null});
|
|
922
|
+
}
|
|
923
|
+
async function deleteGroup(id){
|
|
924
|
+
try{ if(typeof window!=="undefined" && window.confirm && !window.confirm(t("store.confirmDelete"))) return; }catch(e){}
|
|
925
|
+
set({relBusy:"del"});
|
|
926
|
+
try{
|
|
927
|
+
var res = await rpc("cc_deleteGroup", {id: id});
|
|
928
|
+
if(res && res.ok){ set({currentGroupId:"", relations:null}); await refreshGroups(); await refreshLibrary(); }
|
|
929
|
+
else set({lastMsg:String((res && res.error && res.error.message) || "")});
|
|
930
|
+
}catch(e){ set({lastMsg:String(e && e.message || e)}); }
|
|
931
|
+
set({relBusy:null});
|
|
932
|
+
}
|
|
933
|
+
async function assignGroup(cardId, groupId){
|
|
934
|
+
if(!cardId) return;
|
|
935
|
+
set({libraryBusy:"group:"+cardId});
|
|
936
|
+
try{
|
|
937
|
+
var res = await rpc("cc_setCardGroup", {cardId: cardId, groupId: groupId || null});
|
|
938
|
+
if(res && res.ok){
|
|
939
|
+
await refreshLibrary();
|
|
940
|
+
await refreshGroups();
|
|
941
|
+
if(state.currentGroupId) await loadGroup(state.currentGroupId);
|
|
942
|
+
} else set({lastMsg:String((res && res.error && res.error.message) || "")});
|
|
943
|
+
}catch(e){ set({lastMsg:String(e && e.message || e)}); }
|
|
944
|
+
set({libraryBusy:null});
|
|
945
|
+
}
|
|
946
|
+
/** 改图谱:ops 与 cc_patch_relations 同构(addNode/updateNode/removeNode/addEdge/updateEdge/removeEdge)。 */
|
|
947
|
+
async function patchRelations(ops){
|
|
948
|
+
var gid = state.currentGroupId;
|
|
949
|
+
if(!gid){ set({lastMsg:t("workshop.rel.pickGroup")}); return; }
|
|
950
|
+
set({relBusy:"patch"});
|
|
951
|
+
try{
|
|
952
|
+
var args = { id: gid, ops: ops };
|
|
953
|
+
var rev = state.relations && state.relations.group ? state.relations.group.rev : null;
|
|
954
|
+
if(typeof rev === "number") args.expectedRev = rev;
|
|
955
|
+
var res = await rpc("cc_patchGroup", args);
|
|
956
|
+
if(res && res.ok && res.value){
|
|
957
|
+
set({lastMsg: t("workshop.rel.saved", {rev: String(res.value.group.rev)})});
|
|
958
|
+
await loadGroup(gid);
|
|
959
|
+
} else set({lastMsg:String((res && res.error && res.error.message) || "")});
|
|
960
|
+
}catch(e){ set({lastMsg:String(e && e.message || e)}); }
|
|
961
|
+
set({relBusy:null});
|
|
962
|
+
}
|
|
963
|
+
async function checkGroupNow(){
|
|
964
|
+
var gid = state.currentGroupId;
|
|
965
|
+
if(!gid){ set({lastMsg:t("workshop.rel.pickGroup")}); return; }
|
|
966
|
+
set({relBusy:"check"});
|
|
967
|
+
try{
|
|
968
|
+
var res = await rpc("cc_groupCheck", {id: gid});
|
|
969
|
+
if(res && res.ok && res.value){
|
|
970
|
+
var issues = res.value.issues || [];
|
|
971
|
+
set({lastMsg: issues.length
|
|
972
|
+
? t("workshop.rel.checkResult", {n: String(issues.length)}) + " — " + issues.map(function(i){ return i.message; }).join(";")
|
|
973
|
+
: t("workshop.rel.checkOk")});
|
|
974
|
+
} else set({lastMsg:String((res && res.error && res.error.message) || "")});
|
|
975
|
+
}catch(e){ set({lastMsg:String(e && e.message || e)}); }
|
|
976
|
+
set({relBusy:null});
|
|
977
|
+
}
|
|
978
|
+
/** 把图谱里「我对别人的看法」渲染进当前卡的 description(固定标记区,重复渲染是替换)。 */
|
|
979
|
+
async function renderRelationsToCard(){
|
|
980
|
+
if(!state.currentLibraryId){ set({lastMsg:t("workshop.rel.needLoad")}); return; }
|
|
981
|
+
set({relBusy:"render"});
|
|
982
|
+
try{
|
|
983
|
+
var sid = state.currentSessionId || null;
|
|
984
|
+
var args = { cardId: state.currentLibraryId };
|
|
985
|
+
if(state.currentGroupId) args.groupId = state.currentGroupId;
|
|
986
|
+
if(sid) args.sessionId = sid;
|
|
987
|
+
var res = await rpc("cc_renderRelations", args);
|
|
988
|
+
if(res && res.ok && res.value){
|
|
989
|
+
// 宿主改了草稿文件 → 重新拉一次,否则工坊里看到的还是旧 description
|
|
990
|
+
var dres = await rpc("cc_getDraft", sid ? {sessionId: sid} : {});
|
|
991
|
+
if(dres && dres.ok && dres.value && dres.value.draft) setDraftFromHost(dres.value.draft, dres.value.draftStatus || null);
|
|
992
|
+
set({lastMsg: t("workshop.rel.rendered", {n: String(res.value.outgoing || 0)})});
|
|
993
|
+
} else set({lastMsg:String((res && res.error && res.error.message) || "")});
|
|
994
|
+
}catch(e){ set({lastMsg:String(e && e.message || e)}); }
|
|
995
|
+
set({relBusy:null});
|
|
996
|
+
}
|
|
792
997
|
function toggleLibraryCollapsed(){ set({libraryCollapsed: !state.libraryCollapsed}); }
|
|
793
998
|
async function refreshLibrary(){
|
|
794
999
|
set({libraryBusy:"refresh"});
|
|
@@ -838,7 +1043,11 @@ window.__ModuleLoader__.load({
|
|
|
838
1043
|
}catch(e){ set({lastMsg: String(e && e.message || e)}); }
|
|
839
1044
|
set({libraryBusy:null});
|
|
840
1045
|
}
|
|
841
|
-
function clearCurrentLibraryId(){
|
|
1046
|
+
function clearCurrentLibraryId(){
|
|
1047
|
+
set({currentLibraryId:null, lastMsg:t("store.switchedNew")});
|
|
1048
|
+
// 同步清掉宿主侧的来源 id(sidecar):否则下次保存又会按旧来源「更新」那一张
|
|
1049
|
+
try{ rpc("cc_setOrigin", { sessionId: state.currentSessionId||null, id: null }); }catch(e){}
|
|
1050
|
+
}
|
|
842
1051
|
// 清空当前角色卡:整份草稿换回空卡(makeDraft),并解除「跟已存角色的关联」——
|
|
843
1052
|
// 否则清空后点「保存当前」会把库里那张卡覆盖成空卡(要删卡请用右侧列表的删除)。
|
|
844
1053
|
// 落盘与其它写入走同一条路(pushDraftToHost):刷新页面、模型侧读到的都是空卡。
|
|
@@ -852,6 +1061,8 @@ window.__ModuleLoader__.load({
|
|
|
852
1061
|
currentLibraryId:null,
|
|
853
1062
|
lastMsg: t("store.cleared")
|
|
854
1063
|
});
|
|
1064
|
+
// 清空 = 与已存卡脱钩:宿主侧的来源 id(sidecar)也要清,否则下次保存会「更新」原来那张卡(把它覆盖成空卡)
|
|
1065
|
+
try{ rpc("cc_setOrigin", { sessionId: state.currentSessionId||null, id: null }); }catch(e){}
|
|
855
1066
|
setTimeout(pushDraftToHost, 80);
|
|
856
1067
|
}
|
|
857
1068
|
async function loadFromLibrary(id){
|
|
@@ -911,7 +1122,7 @@ window.__ModuleLoader__.load({
|
|
|
911
1122
|
// 0.4.1:这里原本还有一个 exportLibraryEntry —— 直接下载某张已存角色的 JSON。
|
|
912
1123
|
// 三个导出入口归一之后,卡片上的「导出」改成切到导出页,导出动作只剩导出页那一套实现,
|
|
913
1124
|
// 这条旁路就没有调用者了(留着它等于「同一次导出有两套行为」),故删除。
|
|
914
|
-
return { getSnapshot:getSnapshot, subscribe:subscribe, setCur:setCur, setPanel:setPanel, setSideExpanded:setSideExpanded, setIsCcMode:setIsCcMode, setDraftFromHost:setDraftFromHost, setSessionId:setSessionId, setSlotWarn:setSlotWarn, setAlternateSlots:setAlternateSlots, migrateSlot:migrateSlot, checkIsCcMode:checkIsCcMode, setPollEnabled:setPollEnabled, setPollInterval:setPollInterval, setAutoValidate:setAutoValidate, setAutoSaveOnImport:setAutoSaveOnImport, updateDraft:updateDraft, validate:validate, exportJSON:exportJSON, exportPNG:exportPNG, embedIntoPng:embedIntoPng, exportCharx:exportCharx, importJSON:importJSON, importAny:importAny, setWorldPatch:setWorldPatch, setEditingWorld:setEditingWorld, cancelEditingWorld:cancelEditingWorld, setEditingText:setEditingText, clearEditingText:clearEditingText, cancelEditingText:cancelEditingText, updateEditingText:updateEditingText, addEntry:addEntry, setLibraryQuery:setLibraryQuery, toggleLibraryCollapsed:toggleLibraryCollapsed, refreshLibrary:refreshLibrary, saveCurrentToLibrary:saveCurrentToLibrary, saveAsNewToLibrary:saveAsNewToLibrary, clearCurrentLibraryId:clearCurrentLibraryId, clearDraft:clearDraft, loadFromLibrary:loadFromLibrary, deleteFromLibrary:deleteFromLibrary, renameCurrentInLibrary:renameCurrentInLibrary };
|
|
1125
|
+
return { getSnapshot:getSnapshot, subscribe:subscribe, setCur:setCur, setPanel:setPanel, setSideExpanded:setSideExpanded, setIsCcMode:setIsCcMode, setDraftFromHost:setDraftFromHost, setSessionId:setSessionId, setSlotWarn:setSlotWarn, setAlternateSlots:setAlternateSlots, migrateSlot:migrateSlot, checkIsCcMode:checkIsCcMode, setPollEnabled:setPollEnabled, setPollInterval:setPollInterval, setAutoValidate:setAutoValidate, setAutoSaveOnImport:setAutoSaveOnImport, updateDraft:updateDraft, validate:validate, exportJSON:exportJSON, exportPNG:exportPNG, embedIntoPng:embedIntoPng, exportCharx:exportCharx, importJSON:importJSON, importAny:importAny, setWorldPatch:setWorldPatch, setEditingWorld:setEditingWorld, cancelEditingWorld:cancelEditingWorld, setEditingText:setEditingText, clearEditingText:clearEditingText, cancelEditingText:cancelEditingText, updateEditingText:updateEditingText, addEntry:addEntry, setLibraryQuery:setLibraryQuery, toggleLibraryCollapsed:toggleLibraryCollapsed, refreshLibrary:refreshLibrary, saveCurrentToLibrary:saveCurrentToLibrary, saveAsNewToLibrary:saveAsNewToLibrary, clearCurrentLibraryId:clearCurrentLibraryId, clearDraft:clearDraft, loadFromLibrary:loadFromLibrary, deleteFromLibrary:deleteFromLibrary, renameCurrentInLibrary:renameCurrentInLibrary, setCurrentGroupId:setCurrentGroupId, refreshGroups:refreshGroups, createGroup:createGroup, loadGroup:loadGroup, assignGroup:assignGroup, deleteGroup:deleteGroup, patchRelations:patchRelations, checkGroupNow:checkGroupNow, renderRelationsToCard:renderRelationsToCard };
|
|
915
1126
|
}
|
|
916
1127
|
|
|
917
1128
|
var S={
|
|
@@ -1150,6 +1361,8 @@ window.__ModuleLoader__.load({
|
|
|
1150
1361
|
// 从「已存角色」卡片跳进导出页时的来源卡(几秒后自动清掉):用来给那枚按钮一个即时反馈,
|
|
1151
1362
|
// 并在导出页把「导出的仍是工坊里这张、你点的那张还没载入」说清楚。清空后回到常态提示。
|
|
1152
1363
|
var jumpRef=React.useState(null); var jumpFrom=jumpRef[0], setJumpFrom=jumpRef[1];
|
|
1364
|
+
// 0.8.0 关系视图的「添加关系」表单(局部状态,不进 store:它只是草稿输入)
|
|
1365
|
+
var relFormRef=React.useState({from:"", to:"", type:"friend", strength:3, direction:"one-way", note:""}); var relForm=relFormRef[0], setRelForm=relFormRef[1];
|
|
1153
1366
|
React.useEffect(function(){ return store.subscribe(function(){ setS(store.getSnapshot()); }); }, [store]);
|
|
1154
1367
|
React.useEffect(function(){
|
|
1155
1368
|
if(s.panelOpen) store.refreshLibrary();
|
|
@@ -1295,6 +1508,9 @@ window.__ModuleLoader__.load({
|
|
|
1295
1508
|
field(t("workshop.field.nickname"),"nickname",{rowKey:"nickname", input:true})
|
|
1296
1509
|
),
|
|
1297
1510
|
field(t("workshop.field.tags"),"tags",{rowKey:"tags", input:true, bigBox:false, value:s.draft.data.tags.join(", "), onChange:function(v){ store.updateDraft(function(d){ d.tags=v.split(",").map(function(x){return x.trim();}).filter(Boolean); }); }}),
|
|
1511
|
+
// N1(0.7.0):data.creator 此前**没有任何写入路径**(工具侧与工坊都改不了),
|
|
1512
|
+
// 而 cc_validate 每次都警告它为空。这里补上人工入口,工具侧对应 cc_patch_character 的 creator。
|
|
1513
|
+
field(t("workshop.field.creator"),"creator",{rowKey:"creator", input:true, bigBox:false}),
|
|
1298
1514
|
field("description","description",{rowKey:"desc"}),
|
|
1299
1515
|
field("personality","personality",{rowKey:"pers"}),
|
|
1300
1516
|
field("scenario","scenario",{rowKey:"scen"}),
|
|
@@ -1336,7 +1552,7 @@ window.__ModuleLoader__.load({
|
|
|
1336
1552
|
s.lastMsg? h("div",{style:{marginTop:8, fontSize:12, color:"var(--dsw-alias-label-secondary)"}}, s.lastMsg):null
|
|
1337
1553
|
)
|
|
1338
1554
|
);
|
|
1339
|
-
} else {
|
|
1555
|
+
} else if(s.cur===3){
|
|
1340
1556
|
var v=s.valid;
|
|
1341
1557
|
main=h("div",null,
|
|
1342
1558
|
h("div",{style:S.readCard},
|
|
@@ -1374,6 +1590,83 @@ window.__ModuleLoader__.load({
|
|
|
1374
1590
|
s.lastMsg? h("div",{style:{marginTop:8, fontSize:12, color:"var(--dsw-alias-label-secondary)"}}, s.lastMsg):null
|
|
1375
1591
|
)
|
|
1376
1592
|
);
|
|
1593
|
+
} else {
|
|
1594
|
+
// ── 关系视图(s.cur === 4)──
|
|
1595
|
+
// 与导出页同属「不在左侧导航里的视图」:入口是右侧分组选择器旁的「关系」按钮。
|
|
1596
|
+
// 这里编辑的是**插件侧图谱**(<DSH_HOME>/cc-groups/);只有「渲染到当前卡」才会把关系写进 description。
|
|
1597
|
+
var relNodes = (s.relations && s.relations.nodes) || [];
|
|
1598
|
+
var relEdges = (s.relations && s.relations.edges) || [];
|
|
1599
|
+
var relMembers = (s.relations && s.relations.members) || [];
|
|
1600
|
+
var relGroup = s.relations && s.relations.group;
|
|
1601
|
+
var gid = String(s.currentGroupId || "");
|
|
1602
|
+
var canEdit = !!gid && gid !== "__none";
|
|
1603
|
+
var relTypes = ["family","romance","mentor","friend","colleague","rival","enemy","debt","superior","other"];
|
|
1604
|
+
var nodeName = function(id){ var hit = relNodes.filter(function(x){ return x.id === id; })[0]; return hit ? hit.name : id; };
|
|
1605
|
+
var setF = function(patch){ setRelForm(Object.assign({}, relForm, patch)); };
|
|
1606
|
+
var smallSel = {fontSize:11, background:"transparent", border:"1px solid var(--dsw-alias-border-l1)", borderRadius:6, color:"var(--dsw-alias-label-primary)", padding:"1px 4px"};
|
|
1607
|
+
main = h("div",null,
|
|
1608
|
+
h("div",{style:S.readCard},
|
|
1609
|
+
h("h3",{style:S.readTitle}, t("workshop.rel.title"),
|
|
1610
|
+
h("span",{style:{marginLeft:8, fontSize:12, fontWeight:400, color:"var(--dsw-alias-label-secondary)"}}, "· " + ((relGroup && relGroup.name) || t("workshop.rel.noGroup"))),
|
|
1611
|
+
relGroup ? h("span",{style:Object.assign({}, S.badge, {marginLeft:8})}, "rev " + relGroup.rev) : null),
|
|
1612
|
+
h("div",{style:{fontSize:11, color:"var(--dsw-alias-label-secondary)", marginBottom:10, lineHeight:1.7}}, t("workshop.rel.hint")),
|
|
1613
|
+
!canEdit ? h("div",{style:{fontSize:12, color:"var(--dsw-alias-label-secondary)"}}, t("workshop.rel.pickGroup")) : h("div",null,
|
|
1614
|
+
h("div",{style:{fontSize:12, fontWeight:600, marginBottom:4}}, t("workshop.rel.members") + " · " + relMembers.length),
|
|
1615
|
+
h("div",{style:{display:"flex", flexWrap:"wrap", gap:6, marginBottom:14, alignItems:"center"}},
|
|
1616
|
+
relMembers.map(function(m){ return h("span",{key:m.id, style:S.badge}, m.name); }),
|
|
1617
|
+
relMembers.length===0 ? h("span",{style:{fontSize:11, color:"var(--dsw-alias-label-secondary)"}}, t("workshop.rel.noMember")) : null),
|
|
1618
|
+
h("div",{style:{display:"flex", flexWrap:"wrap", gap:6, marginBottom:14, alignItems:"center"}},
|
|
1619
|
+
relNodes.filter(function(n){ return n.kind === "npc"; }).map(function(n){ return h("span",{key:n.id, style:S.badge}, n.name); }),
|
|
1620
|
+
h("button",{type:"button", style:Object.assign({}, S.btnGhost, {border:"1px solid var(--dsw-alias-border-l1)", background:"transparent", color:"var(--dsw-alias-label-secondary)", borderRadius:8, padding:"2px 8px", fontSize:11}), onClick:function(){
|
|
1621
|
+
var name = "";
|
|
1622
|
+
try{ if(typeof window!=="undefined" && window.prompt) name = String(window.prompt(t("workshop.rel.nodePrompt"), "")||"").trim(); }catch(e){}
|
|
1623
|
+
if(name) store.patchRelations([{op:"addNode", kind:"npc", name:name}]);
|
|
1624
|
+
}}, t("workshop.rel.addNode"))),
|
|
1625
|
+
h("div",{style:{fontSize:12, fontWeight:600, marginBottom:4}}, t("workshop.rel.edges") + " · " + relEdges.length),
|
|
1626
|
+
relEdges.length===0 ? h("div",{style:{fontSize:11, color:"var(--dsw-alias-label-secondary)", marginBottom:10}}, t("workshop.rel.noEdge")) :
|
|
1627
|
+
h("div",null, relEdges.map(function(e){
|
|
1628
|
+
return h("div",{key:e.id, style:{borderBottom:"1px solid var(--dsw-alias-border-l1)", padding:"6px 0"}},
|
|
1629
|
+
h("div",{style:{display:"flex", alignItems:"center", gap:6, flexWrap:"wrap"}},
|
|
1630
|
+
h("span",{style:{fontSize:12}}, nodeName(e.from)),
|
|
1631
|
+
h("span",{style:{color:"#7c5cff"}}, e.direction === "mutual" ? "⇄" : "→"),
|
|
1632
|
+
h("span",{style:{fontSize:12}}, nodeName(e.to)),
|
|
1633
|
+
h("select",{style:smallSel, value:e.type, onChange:function(ev){ store.patchRelations([{op:"updateEdge", edgeId:e.id, type:ev.target.value}]); }},
|
|
1634
|
+
relTypes.map(function(ty){ return h("option",{key:ty, value:ty}, t("rel.type."+ty)); })),
|
|
1635
|
+
h("select",{style:smallSel, value:String(e.strength), onChange:function(ev){ store.patchRelations([{op:"updateEdge", edgeId:e.id, strength:Number(ev.target.value)}]); }},
|
|
1636
|
+
[1,2,3,4,5].map(function(n){ return h("option",{key:n, value:String(n)}, n + "/5"); })),
|
|
1637
|
+
h("select",{style:smallSel, value:e.direction, onChange:function(ev){ store.patchRelations([{op:"updateEdge", edgeId:e.id, direction:ev.target.value}]); }},
|
|
1638
|
+
h("option",{value:"one-way"}, t("workshop.rel.dirOneWay")),
|
|
1639
|
+
h("option",{value:"mutual"}, t("workshop.rel.dirMutual"))),
|
|
1640
|
+
h("button",{type:"button", style:Object.assign({}, S.btnGhost, {border:"1px solid #3a2a2a", background:"transparent", color:"#e8a6a6", borderRadius:6, padding:"1px 6px", fontSize:11}), onClick:function(){ store.patchRelations([{op:"removeEdge", edgeId:e.id}]); }}, "✕")),
|
|
1641
|
+
e.note ? h("div",{style:{fontSize:11, color:"var(--dsw-alias-label-secondary)", marginTop:2}}, e.note) : null);
|
|
1642
|
+
})),
|
|
1643
|
+
h("div",{style:{marginTop:14, paddingTop:10, borderTop:"1px dashed var(--dsw-alias-border-l1)"}},
|
|
1644
|
+
h("div",{style:{fontSize:12, fontWeight:600, marginBottom:6}}, t("workshop.rel.add")),
|
|
1645
|
+
h("div",{style:{display:"flex", gap:6, flexWrap:"wrap", alignItems:"center"}},
|
|
1646
|
+
h("span",{style:{fontSize:11, color:"var(--dsw-alias-label-secondary)"}}, t("workshop.rel.from")),
|
|
1647
|
+
h("select",{style:Object.assign({}, smallSel, {background:"var(--dsw-alias-bg-base)", maxWidth:130}), value:relForm.from, onChange:function(ev){ setF({from:ev.target.value}); }},
|
|
1648
|
+
h("option",{value:""}, "—"),
|
|
1649
|
+
relNodes.map(function(n){ return h("option",{key:n.id, value:n.id}, n.name); })),
|
|
1650
|
+
h("span",{style:{fontSize:11, color:"var(--dsw-alias-label-secondary)"}}, t("workshop.rel.to")),
|
|
1651
|
+
h("select",{style:Object.assign({}, smallSel, {background:"var(--dsw-alias-bg-base)", maxWidth:130}), value:relForm.to, onChange:function(ev){ setF({to:ev.target.value}); }},
|
|
1652
|
+
h("option",{value:""}, "—"),
|
|
1653
|
+
relNodes.map(function(n){ return h("option",{key:n.id, value:n.id}, n.name); })),
|
|
1654
|
+
h("select",{style:Object.assign({}, smallSel, {background:"var(--dsw-alias-bg-base)"}), value:relForm.type, onChange:function(ev){ setF({type:ev.target.value}); }},
|
|
1655
|
+
relTypes.map(function(ty){ return h("option",{key:ty, value:ty}, t("rel.type."+ty)); })),
|
|
1656
|
+
h("select",{style:Object.assign({}, smallSel, {background:"var(--dsw-alias-bg-base)"}), value:String(relForm.strength), onChange:function(ev){ setF({strength:Number(ev.target.value)}); }},
|
|
1657
|
+
[1,2,3,4,5].map(function(n){ return h("option",{key:n, value:String(n)}, t("workshop.rel.strength") + " " + n); })),
|
|
1658
|
+
h("select",{style:Object.assign({}, smallSel, {background:"var(--dsw-alias-bg-base)"}), value:relForm.direction, onChange:function(ev){ setF({direction:ev.target.value}); }},
|
|
1659
|
+
h("option",{value:"one-way"}, t("workshop.rel.dirOneWay")),
|
|
1660
|
+
h("option",{value:"mutual"}, t("workshop.rel.dirMutual"))),
|
|
1661
|
+
h("button",{type:"button", style:Object.assign({}, S.btn, {padding:"3px 10px", fontSize:11}), onClick:function(){ store.patchRelations([{op:"addEdge", from:relForm.from, to:relForm.to, type:relForm.type, strength:relForm.strength, direction:relForm.direction, note:relForm.note}]); }}, t("workshop.rel.add"))),
|
|
1662
|
+
h("input",{placeholder:t("workshop.rel.note"), style:{width:"100%", boxSizing:"border-box", marginTop:6, background:"var(--dsw-alias-bg-base)", border:"1px solid var(--dsw-alias-border-l1)", borderRadius:8, color:"var(--dsw-alias-label-primary)", padding:"4px 8px", fontSize:12}, value:relForm.note, onChange:function(ev){ setF({note:ev.target.value}); }})),
|
|
1663
|
+
h("div",{style:{display:"flex", gap:8, marginTop:14, flexWrap:"wrap"}},
|
|
1664
|
+
h("button",{type:"button", style:Object.assign({}, S.btn, {padding:"4px 12px", fontSize:12}), onClick:function(){ store.renderRelationsToCard(); }, disabled: s.relBusy==="render"}, s.relBusy==="render" ? "…" : t("workshop.rel.render")),
|
|
1665
|
+
h("button",{type:"button", style:Object.assign({}, S.btnGhost, {border:"1px solid var(--dsw-alias-border-l1)", background:"transparent", color:"var(--dsw-alias-label-primary)", borderRadius:8, padding:"4px 12px", fontSize:12}), onClick:function(){ store.checkGroupNow(); }, disabled: s.relBusy==="check"}, s.relBusy==="check" ? "…" : t("workshop.rel.check")),
|
|
1666
|
+
h("button",{type:"button", style:Object.assign({}, S.btnGhost, {border:"1px solid var(--dsw-alias-border-l1)", background:"transparent", color:"var(--dsw-alias-label-secondary)", borderRadius:8, padding:"4px 12px", fontSize:12}), onClick:function(){ store.loadGroup(gid); }}, t("action.refresh")),
|
|
1667
|
+
h("button",{type:"button", style:Object.assign({}, S.btnGhost, {border:"1px solid #3a2a2a", background:"transparent", color:"#e8a6a6", borderRadius:8, padding:"4px 12px", fontSize:12}), onClick:function(){ store.deleteGroup(gid); }}, t("common.delete")))),
|
|
1668
|
+
s.lastMsg ? h("div",{style:{marginTop:10, fontSize:12, color:"var(--dsw-alias-label-secondary)", lineHeight:1.7}}, s.lastMsg) : null)
|
|
1669
|
+
);
|
|
1377
1670
|
}
|
|
1378
1671
|
var stepsEls=steps.map(function(label, i){
|
|
1379
1672
|
var active=s.cur===i;
|
|
@@ -1384,9 +1677,14 @@ window.__ModuleLoader__.load({
|
|
|
1384
1677
|
onClick:function(){ store.setCur(i); }}, (i+1)+". "+label);
|
|
1385
1678
|
});
|
|
1386
1679
|
var filteredLibrary = (function(){
|
|
1680
|
+
var gid = String(s.currentGroupId||"");
|
|
1681
|
+
var base = s.library;
|
|
1682
|
+
// 0.8.0:分组筛选("" = 全部;"__none" = 只看未分组)
|
|
1683
|
+
if(gid === "__none") base = base.filter(function(e){ return !e.groupId; });
|
|
1684
|
+
else if(gid) base = base.filter(function(e){ return String(e.groupId||"")===gid; });
|
|
1387
1685
|
var q = String(s.libraryQuery||"").trim().toLowerCase();
|
|
1388
|
-
if(!q) return
|
|
1389
|
-
return
|
|
1686
|
+
if(!q) return base;
|
|
1687
|
+
return base.filter(function(e){
|
|
1390
1688
|
var nameMatch = String(e.name||"").toLowerCase().indexOf(q)>=0;
|
|
1391
1689
|
var tagMatch = Array.isArray(e.tags) && e.tags.some(function(t){ return String(t).toLowerCase().indexOf(q)>=0; });
|
|
1392
1690
|
var loreMatch = String(e.loreCount||"").indexOf(q)>=0;
|
|
@@ -1432,6 +1730,14 @@ window.__ModuleLoader__.load({
|
|
|
1432
1730
|
h("span",{style:{fontSize:10, color:"var(--dsw-alias-label-secondary)", alignSelf:"center", overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap", flex:1, textAlign:"right"}}, s.draft.data.name ? s.draft.data.name.slice(0,8) : t("workshop.library.unnamed"))
|
|
1433
1731
|
),
|
|
1434
1732
|
h("input",{placeholder:t("workshop.library.searchPlaceholder"), style:{width:"100%", boxSizing:"border-box", maxWidth:"100%", background:"var(--dsw-alias-bg-base)", border:"1px solid var(--dsw-alias-border-l1)", borderRadius:8, color:"var(--dsw-alias-label-primary)", padding:"6px 8px", fontSize:12}, value:s.libraryQuery, onChange:function(e){ store.setLibraryQuery(e.target.value); }}),
|
|
1733
|
+
// 0.8.0 分组:同一世界观的角色拉进一组,关系网按组维护(「关系」按钮进关系视图,s.cur===4)
|
|
1734
|
+
h("div",{style:{display:"flex", gap:6, marginTop:6}},
|
|
1735
|
+
h("select",{style:{flex:1, minWidth:0, background:"var(--dsw-alias-bg-base)", border:"1px solid var(--dsw-alias-border-l1)", borderRadius:8, color:"var(--dsw-alias-label-primary)", padding:"4px 6px", fontSize:12}, value:s.currentGroupId||"", onChange:function(e){ store.setCurrentGroupId(e.target.value); }},
|
|
1736
|
+
h("option",{value:""}, t("workshop.group.all")),
|
|
1737
|
+
h("option",{value:"__none"}, t("workshop.group.none")),
|
|
1738
|
+
(s.groups||[]).map(function(g){ return h("option",{key:g.id, value:g.id}, g.name + " · " + t("workshop.group.summary",{n:String(g.memberCount), m:String(g.edgeCount)})); })),
|
|
1739
|
+
h("button",{type:"button", style:Object.assign({}, S.btnGhost, {border:"1px solid var(--dsw-alias-border-l1)", background:"transparent", color:"var(--dsw-alias-label-secondary)", borderRadius:8, padding:"4px 8px", fontSize:11, flexShrink:0}), onClick:function(){ store.createGroup(); }}, t("workshop.group.new")),
|
|
1740
|
+
h("button",{type:"button", style:Object.assign({}, S.btnGhost, {border:"1px solid #7c5cff", background:"transparent", color:"#7c5cff", borderRadius:8, padding:"4px 8px", fontSize:11, flexShrink:0}), disabled: !s.currentGroupId || s.currentGroupId==="__none", onClick:function(){ store.setCur(4); }}, t("workshop.rel.open"))),
|
|
1435
1741
|
s.lastMsg ? h("div",{style:{fontSize:11, color:"var(--dsw-alias-label-secondary)", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis"}}, s.lastMsg) : null
|
|
1436
1742
|
),
|
|
1437
1743
|
h("div",{style:S.libraryList},
|
|
@@ -1439,6 +1745,10 @@ window.__ModuleLoader__.load({
|
|
|
1439
1745
|
filteredLibrary.map(function(entry){
|
|
1440
1746
|
var isBusy = s.libraryBusy && String(s.libraryBusy).indexOf(entry.id)>=0;
|
|
1441
1747
|
var isCurrent = s.currentLibraryId===entry.id;
|
|
1748
|
+
// 列表展示前 4 个标签,但「还有几个没显示」必须用 meta.tagsTotal 算:meta.tags 本身截断到 6 个,
|
|
1749
|
+
// 拿它的长度当总数会偏小(卡里 7 个、列表只显示 6 个,用户会以为标签没存上 —— LLM 侧回测报的 P1-11)
|
|
1750
|
+
var tagTotal = (typeof entry.tagsTotal==="number") ? entry.tagsTotal : (entry.tags||[]).length;
|
|
1751
|
+
var tagShown = Math.min((entry.tags||[]).length, 4);
|
|
1442
1752
|
var itemStyle = isCurrent ? Object.assign({}, S.libraryItem, {borderColor:"#7c5cff", background:"color-mix(in srgb, #7c5cff 8%, var(--dsw-alias-bg-layer-2))"}) : S.libraryItem;
|
|
1443
1753
|
return h("div",{key:entry.id, style:itemStyle},
|
|
1444
1754
|
h("div",{style:{display:"flex", alignItems:"center", justifyContent:"space-between", gap:6}},
|
|
@@ -1449,7 +1759,7 @@ window.__ModuleLoader__.load({
|
|
|
1449
1759
|
h("div",{style:{fontSize:11, color:"var(--dsw-alias-label-secondary)", marginTop:2, whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis"}}, entry.descriptionSnippet || entry.tags.join(" · ") || "—"),
|
|
1450
1760
|
h("div",{style:{display:"flex", flexWrap:"wrap", gap:4, marginTop:6}},
|
|
1451
1761
|
(entry.tags||[]).slice(0,4).map(function(t){ return h("span",{key:t, style:Object.assign({}, S.badge, {fontSize:10, padding:"1px 5px"})}, t); }),
|
|
1452
|
-
|
|
1762
|
+
tagTotal > tagShown ? h("span",{style:{fontSize:10, color:"var(--dsw-alias-label-secondary)"}}, "+"+(tagTotal-tagShown)) : null
|
|
1453
1763
|
),
|
|
1454
1764
|
h("div",{style:{fontSize:10, color:"var(--dsw-alias-label-secondary)", marginTop:4}}, new Date(entry.updatedAt*1000).toLocaleString()),
|
|
1455
1765
|
h("div",{style:{display:"flex", gap:6, marginTop:8}},
|
|
@@ -1458,7 +1768,11 @@ window.__ModuleLoader__.load({
|
|
|
1458
1768
|
// 想导这张已存角色先点「载入」—— 免得这里悄悄换掉用户正在编辑的草稿。
|
|
1459
1769
|
// 点下去按钮自己先变(「已切到 ✓」),不然切页发生在中栏、用户视线却在右侧,会以为没反应。
|
|
1460
1770
|
h("button",{style:Object.assign({}, S.btnGhost, {border:"1px solid var(--dsw-alias-border-l1)", background:"transparent", color:(jumpFrom && jumpFrom.id===entry.id)? "#a6e8c8" : "var(--dsw-alias-label-secondary)", borderRadius:8, padding:"4px 8px", fontSize:11}), title:t("workshop.library.exportTip"), onClick:function(){ jumpToExport(entry); }}, (jumpFrom && jumpFrom.id===entry.id)? t("workshop.library.exportJumped"): t("workshop.library.export")),
|
|
1461
|
-
h("button",{style:Object.assign({}, S.btnGhost, {border:"1px solid #3a2a2a", background:"transparent", color:"#e8a6a6", borderRadius:8, padding:"4px 8px", fontSize:11}), onClick:function(){ store.deleteFromLibrary(entry.id); }, disabled: !!isBusy}, t("common.delete"))
|
|
1771
|
+
h("button",{style:Object.assign({}, S.btnGhost, {border:"1px solid #3a2a2a", background:"transparent", color:"#e8a6a6", borderRadius:8, padding:"4px 8px", fontSize:11}), onClick:function(){ store.deleteFromLibrary(entry.id); }, disabled: !!isBusy}, t("common.delete")),
|
|
1772
|
+
// 0.8.0:直接把卡归到某个分组(空 = 未分组)。分组是关系图谱的载体。
|
|
1773
|
+
h("select",{style:{background:"transparent", border:"1px solid var(--dsw-alias-border-l1)", borderRadius:8, color:(entry.groupId?"#7c5cff":"var(--dsw-alias-label-secondary)"), fontSize:11, padding:"2px 4px", maxWidth:92, flexShrink:0}, value:String(entry.groupId||""), disabled: !!isBusy, title:t("workshop.group.assign"), onChange:function(ev){ store.assignGroup(entry.id, ev.target.value||null); }},
|
|
1774
|
+
h("option",{value:""}, t("workshop.group.none")),
|
|
1775
|
+
(s.groups||[]).map(function(g){ return h("option",{key:g.id, value:g.id}, g.name); }))
|
|
1462
1776
|
)
|
|
1463
1777
|
);
|
|
1464
1778
|
})
|
|
@@ -1702,6 +2016,11 @@ window.__ModuleLoader__.load({
|
|
|
1702
2016
|
try{ if(localStorage.getItem("dsh-cc-studio-legacy-notice")==="1") alts = null; }catch(e){}
|
|
1703
2017
|
}
|
|
1704
2018
|
store.setAlternateSlots(alts);
|
|
2019
|
+
// 载入来源 id(0.5.0):宿主把「这份草稿是从哪张已存卡载入的」落在盘上(sidecar),
|
|
2020
|
+
// 刷新页面后据此恢复「已载入 ID」—— 否则内存里的 currentLibraryId 一丢,
|
|
2021
|
+
// 再点保存就会新建一张同名重复卡(LLM 侧回环测试抓到的 P0-4)。
|
|
2022
|
+
var originId = (typeof res.value.originId==="string" && res.value.originId) ? res.value.originId : null;
|
|
2023
|
+
if(originId !== store.getSnapshot().currentLibraryId) set({ currentLibraryId: originId });
|
|
1705
2024
|
var draft=res.value.draft;
|
|
1706
2025
|
var draftStatus=res.value.draftStatus||null;
|
|
1707
2026
|
try{
|