kingkont 0.7.58 → 0.7.60

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.7.58",
3
+ "version": "0.7.60",
4
4
  "description": "KingKont · Chatium — нод-редактор сцен с AI-генерацией (картинки/видео/голос/SFX/музыка/текст)",
5
5
  "main": "main.js",
6
6
  "bin": {
package/renderer/media.js CHANGED
@@ -886,6 +886,20 @@ function getMentionSuggestions(kind) {
886
886
  });
887
887
  }
888
888
  }
889
+ // Локации: @<location> → location.sheet; @<location>.<имя> → именованная картинка локации.
890
+ for (const l of state.locationsInfo) {
891
+ if (state.currentBoard.kind === 'location' && state.currentBoard.name === l.name) continue;
892
+ if (l.sheet) {
893
+ out.push({ key: l.name, label: l.name + ' · sheet', type: 'image', scope: 'loc-sheet' });
894
+ }
895
+ for (const img of (l.imageNodes || [])) {
896
+ out.push({
897
+ key: `${l.name}.${img.name}`,
898
+ label: `${l.name}.${img.name}`,
899
+ type: 'image', scope: 'loc-image',
900
+ });
901
+ }
902
+ }
889
903
  }
890
904
  return out;
891
905
  }
@@ -926,6 +940,21 @@ function gatherMediaRefs(rawPrompt) {
926
940
  });
927
941
  }
928
942
  }
943
+ for (const l of state.locationsInfo) {
944
+ if (state.currentBoard?.kind === 'location' && state.currentBoard.name === l.name) continue;
945
+ if (l.sheet) {
946
+ candidates.push({
947
+ key: l.name, name: l.name, type: 'image',
948
+ file: l.sheet, boardHandle: l.handle, locName: l.name,
949
+ });
950
+ }
951
+ for (const img of (l.imageNodes || [])) {
952
+ candidates.push({
953
+ key: `${l.name}.${img.name}`, name: img.name, type: 'image',
954
+ file: img.file, boardHandle: l.handle, locName: l.name,
955
+ });
956
+ }
957
+ }
929
958
  // Длинные ключи раньше — на этапе матчинга
930
959
  candidates.sort((a, b) => b.key.length - a.key.length);
931
960
  // Маскируем уже найденные позиции, чтобы @алиса.happy не «находил» @алиса повторно
@@ -1080,10 +1109,10 @@ function updateMentionPopup() {
1080
1109
  let items = [];
1081
1110
 
1082
1111
  if (dotIdx >= 0) {
1083
- // Уже выбран персонаж — показываем только его картинки
1084
- const charName = query.slice(0, dotIdx);
1112
+ // Уже выбран character ИЛИ location — показываем его картинки.
1113
+ const ownerName = query.slice(0, dotIdx);
1085
1114
  const imgQuery = query.slice(dotIdx + 1);
1086
- const c = state.charactersInfo.find(x => x.name.toLowerCase() === charName);
1115
+ const c = state.charactersInfo.find(x => x.name.toLowerCase() === ownerName);
1087
1116
  if (c) {
1088
1117
  for (const img of (c.imageNodes || [])) {
1089
1118
  if (!imgQuery || img.name.toLowerCase().includes(imgQuery)) {
@@ -1096,6 +1125,21 @@ function updateMentionPopup() {
1096
1125
  });
1097
1126
  }
1098
1127
  }
1128
+ } else {
1129
+ const l = state.locationsInfo.find(x => x.name.toLowerCase() === ownerName);
1130
+ if (l) {
1131
+ for (const img of (l.imageNodes || [])) {
1132
+ if (!imgQuery || img.name.toLowerCase().includes(imgQuery)) {
1133
+ items.push({
1134
+ key: `${l.name}.${img.name}`,
1135
+ label: img.name,
1136
+ type: 'image',
1137
+ scope: 'loc-image',
1138
+ locName: l.name,
1139
+ });
1140
+ }
1141
+ }
1142
+ }
1099
1143
  }
1100
1144
  } else if (isCharOnly) {
1101
1145
  // @@ — только персонажи
@@ -1136,6 +1180,19 @@ function updateMentionPopup() {
1136
1180
  });
1137
1181
  }
1138
1182
  }
1183
+ for (const l of state.locationsInfo) {
1184
+ if (state.currentBoard?.kind === 'location' && state.currentBoard.name === l.name) continue;
1185
+ const hasImages = (l.imageNodes || []).length > 0;
1186
+ if (l.sheet || hasImages) {
1187
+ items.push({
1188
+ key: l.name,
1189
+ label: l.name + (hasImages && l.sheet ? '' : (hasImages ? ' …' : ' · sheet')),
1190
+ type: 'image',
1191
+ scope: 'loc',
1192
+ hasImages, hasSheet: !!l.sheet,
1193
+ });
1194
+ }
1195
+ }
1139
1196
  }
1140
1197
  items = items.filter(s => s.key.toLowerCase().includes(query)).slice(0, 16);
1141
1198
  }
@@ -1159,6 +1216,8 @@ function updateMentionPopup() {
1159
1216
  t.className = `mtype ${s.type}`;
1160
1217
  t.textContent = s.scope === 'char' ? 'персонаж'
1161
1218
  : s.scope === 'char-image' ? `персонаж·${s.charName}`
1219
+ : s.scope === 'loc' ? 'локация'
1220
+ : s.scope === 'loc-image' ? `локация·${s.locName}`
1162
1221
  : s.type;
1163
1222
  const nm = document.createElement('span');
1164
1223
  nm.textContent = s.label;
@@ -1183,10 +1242,11 @@ function selectMention(idx = mentionState.selected) {
1183
1242
  const cursor = ta.selectionStart;
1184
1243
  const before = ta.value.slice(0, mentionState.anchor);
1185
1244
  const after = ta.value.slice(cursor);
1186
- // Если выбрали персонажа и у него есть картинки — авто-drill: оставляем @name. для дальнейшего ввода
1245
+ // Если выбрали персонажа/локацию и у них есть картинки — авто-drill:
1246
+ // оставляем @name. для дальнейшего ввода имени картинки.
1187
1247
  let insert;
1188
1248
  let reopen = false;
1189
- if (item.scope === 'char' && item.hasImages) {
1249
+ if ((item.scope === 'char' || item.scope === 'loc') && item.hasImages) {
1190
1250
  insert = '[@' + item.key + '.';
1191
1251
  reopen = true;
1192
1252
  } else {
@@ -477,6 +477,15 @@ async function openGenerateForRef(fromNode, clientX, clientY, forceKind) {
477
477
  }
478
478
  syncSourceRefRow();
479
479
 
480
+ // Если генерим image из существующей картинки-ноды — дефолт «↺ Как у
481
+ // исходной» (юзер обычно хочет такой же формат как у источника).
482
+ // Та же логика что в openGenModal, для anchor-drag-to-generate flow.
483
+ if (forceKind === 'image'
484
+ && state.sourceRef && state.sourceRef.type === 'image' && state.sourceRef.use) {
485
+ state.imageAspect = 'source';
486
+ syncImageAspectActive();
487
+ }
488
+
480
489
  closeMentionPopup();
481
490
  $('genModal').classList.remove('hidden');
482
491
  setTimeout(() => {