kingkont 0.7.38 → 0.7.40

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.
@@ -1,149 +1,48 @@
1
- # Структура проекта KingKont
1
+ # Это проект KingKont
2
2
 
3
- Эта папка — **проект** (фильм/сериал) редактора **KingKont · Chatium**. Файлы здесь
4
- создаются и редактируются GUI-приложением, но всё хранится в plain-формате —
5
- любой инструмент (включая Claude Code) может читать/изменять напрямую.
3
+ Эта папка — проект редактора **KingKont · Chatium** (нод-редактор сцен фильма
4
+ с AI-генерацией картинок/видео/голоса/SFX/музыки/текста).
6
5
 
7
- ## Что лежит в корне
6
+ ## Для Claude
8
7
 
9
- ```
10
- .
11
- ├── CLAUDE.md ← этот файл
12
- ├── _characters/ ← персонажи
13
- │ └── <имя>/ ← одна доска на персонажа
14
- ├── _locations/ ← локации
15
- │ └── <имя>/ ← одна доска на локацию
16
- ├── <имя сцены>/ ← сцены лежат прямо в корне как папки
17
- │ └── … ← (любое имя кроме начинающихся с `_` и `.`)
18
- ├── _deleted/ ← корзина: удалённые файлы попадают сюда
19
- │ (восстанавливаются через Cmd+Z в редакторе)
20
- └── (любые другие файлы) ← редактор их не трогает
21
- ```
8
+ **Используй скилл `kingkont`.** Он знает формат `scene.json`, расположение
9
+ досок (`_characters/`, `_locations/`, `<сцена>/`) и умеет управлять нодами +
10
+ запускать генерации через CLI без открытия UI приложения. API-ключи скилл
11
+ берёт автоматически из настроек установленного приложения KingKont.
22
12
 
23
- ## Доска (board) — папка персонажа / локации / сцены
13
+ ### Установка скилла
24
14
 
25
- Любая доска внутри (`_characters/<имя>/`, `_locations/<имя>/`, `<сцена>/`)
26
- имеет одинаковую структуру:
15
+ Если приложение `kingkont` ещё не установлено:
27
16
 
28
- ```
29
- <board>/
30
- ├── scene.json ← вся метаинформация (см. ниже)
31
- ├── clips/ ← видео-файлы (mp4, mov, webm…)
32
- ├── images/ ← картинки (jpg, png, webp…)
33
- ├── audio/ ← аудио (mp3, wav, m4a…)
34
- └── texts/ ← .md-файлы текстовых нод
17
+ ```bash
18
+ npm i -g kingkont
35
19
  ```
36
20
 
37
- ## Формат `scene.json`
21
+ Установить скилл (положит `~/.claude/skills/kingkont/SKILL.md`):
38
22
 
39
- Главный файл доски. Редактор читает его при открытии и пишет debounced при
40
- любом изменении. Пример:
41
-
42
- ```json
43
- {
44
- "nodes": [
45
- {
46
- "id": "uuid-here",
47
- "type": "image" | "video" | "audio" | "text",
48
- "name": "опциональное имя для @-ссылок",
49
- "file": "images/foo.jpg", // относительный путь в папке доски
50
- "x": 100, "y": 200, // позиция на холсте (логические px)
51
- "width": 280, "height": 320, // опц., если юзер ресайзил ноду
52
- "generated": { // опц., если нода создана генерацией
53
- "kind": "image" | "video" | "audio" | "text",
54
- "prompt": "финальный промпт после resolve mentions",
55
- "rawPrompt": "исходный промпт с [@ref]",
56
- "model": "nano-banana-2" | "bytedance/seedance-2" | "eleven_v3" | "anthropic/claude-sonnet-4" | …,
57
- "modelKey": "ключ модели для KIE",
58
- "voiceId": "...", // для audio
59
- "tones": ["шепот", …], // для audio
60
- "refs": [{name, type, file}],
61
- "state": "submitting" | "queued" | "generating" | "success" | "error",
62
- "taskId": "..."
63
- },
64
- "status": "generating" | "draft" | "error" | undefined,
65
- "error": "текст ошибки если status=error",
66
- "history": [...], // history правок (через ⇄ Заменить)
67
- "historyIndex": 0
68
- }
69
- // ... text-нода: поле "text" в JSON НЕ хранится; контент лежит в texts/<file>.md
70
- ],
71
- "connections": [
72
- { "from": "node-id", "to": "node-id" }
73
- ],
74
- "view": { "scrollLeft": 0, "scrollTop": 0, "zoom": 1.0 },
75
- "character": { // только для досок-персонажей
76
- "characterSheet": "images/sheet.jpg",
77
- "voice": "elevenlabs-voice-id",
78
- "tone": "по умолчанию",
79
- "commonTones": ["шепот", "крик", …],
80
- "lastTones": [],
81
- "replicas": [...]
82
- },
83
- "location": { // только для досок-локаций
84
- "sheet": "images/sheet.jpg"
85
- },
86
- "timeline": { // монтажный лист сцены
87
- "tracks": [
88
- {
89
- "id": "tv", "name": "Видео", "kind": "video" | "audio",
90
- "clips": [
91
- {
92
- "id": "uuid",
93
- "nodeId": "node-id или null", // привязка к ноде на холсте
94
- "type": "video" | "image" | "audio",
95
- "file": "clips/foo.mp4",
96
- "name": "Foo",
97
- "duration": 5.0, // в секундах
98
- "start": 0, // позиция начала на дорожке
99
- "trimStart": 0, // обрезка спереди
100
- "sourceDuration": 5.0, // полная длительность файла
101
- "_durationLoaded": true,
102
- "groupId": "...", // опц., группа клипов
103
- "disabled": false // skip при playback
104
- }
105
- ]
106
- }
107
- ],
108
- "playhead": 0.27
109
- },
110
- "history": { // undo/redo (Cmd+Z / Cmd+Shift+Z)
111
- "past": [{ "ts": 1234567890, "label": "…", "snap": "JSON-string", "movedFiles": [...] }],
112
- "future": [...]
113
- }
114
- }
23
+ ```bash
24
+ kingkont install-skill
115
25
  ```
116
26
 
117
- ## Текстовые ноды
118
-
119
- Текст ноды хранится не в `scene.json`, а в `texts/<имя>.md`. Поле `node.file`
120
- указывает путь, поле `node.text` в JSON отсутствует (грузится при загрузке
121
- доски). Это удобно для редактирования: можно править .md руками, и редактор
122
- подхватит изменения при следующем открытии.
27
+ После `/reload` в Claude Code скилл `kingkont` станет доступен.
123
28
 
124
- ## Файловые операции редактора
29
+ ### Если скилл недоступен
125
30
 
126
- - **Удаление ноды**: файл (если был) переезжает в `<root>/_deleted/<уникальное-имя>`.
127
- Cmd+Z восстанавливает обратно (movedFiles в history).
128
- - **Замена/перегенерация**: новый файл записывается в соответствующую
129
- подпапку, старый сохраняется в `node.history[]` для отката.
130
- - **Импорт через drag-drop**: файлы копируются в правильную подпапку
131
- (clips/images/audio) с уникальным именем.
31
+ Краткая шпаргалка (полный API в `kingkont --help` и в самом скилле):
132
32
 
133
- ## Что НЕ трогать руками (если хочешь долго жить)
33
+ ```bash
34
+ kingkont open <project> # JSON со структурой проекта
35
+ kingkont board <project> <board> # одна доска (--json)
36
+ kingkont add-node <project> <board> --kind=image|video|audio|text [--name=...]
37
+ kingkont gen <project> <board> --kind=... --prompt="..." [--model=...] [--refs=@a,@b]
38
+ kingkont connect <project> <board> <fromId> <toId>
39
+ kingkont rm-node <project> <board> <nodeId>
40
+ ```
134
41
 
135
- - Поля с `id` в формате UUID на них завязаны connections, timeline-clips,
136
- history. Меняй name, prompt, x/y свободно.
137
- - `history.past`/`future` — снимки всей сцены. Лучше не редактировать, иначе
138
- Cmd+Z восстановит мусор.
42
+ Где `<board>`это `Scene1`, `_characters/Anna` или `_locations/Office`.
139
43
 
140
- ## Совет для AI-агентов
44
+ ---
141
45
 
142
- При работе с этой папкой:
143
- 1. Читай `scene.json` чтобы понять структуру нод и связей.
144
- 2. Текст текстовых нод в `texts/`. Читай и пиши .md напрямую.
145
- 3. Если меняешь `scene.json` — сохраняй валидный JSON и не трогай поля
146
- которые не понимаешь (особенно `history`).
147
- 4. Бинарные ресурсы (клипы/картинки/аудио) лежат в подпапках по типу.
148
- Импорт через drag-drop в редакторе автоматически кладёт в правильное
149
- место и регистрирует ноду на холсте.
46
+ *Этот файл создан KingKont автоматически и обновляется при каждом открытии
47
+ проекта. Безопасно удалить приложение положит его обратно. Если хочешь
48
+ свою документацию переименуй файл (например в `NOTES.md`).*
package/bin/kingkont.js CHANGED
@@ -1,26 +1,59 @@
1
1
  #!/usr/bin/env node
2
- // CLI launcher для KingKont. Без аргументов — запускает Electron-приложение.
3
- // С `serve [path]` поднимает только HTTP-сервер для запуска редактора в браузере.
2
+ // CLI launcher для KingKont.
3
+ // `kingkont` запустить Electron-приложение
4
+ // `kingkont serve` — поднять только HTTP-сервер для Chrome
5
+ // `kingkont install-skill`— положить SKILL.md для Claude Code
6
+ // `kingkont <node-cmd>` — управление нодами проекта без UI
7
+ // (open / list / board / add-node / gen / generate /
8
+ // status / connect / rm-node / voices / balance / upload)
9
+ //
10
+ // Полный help: `kingkont --help`.
11
+ // Подробная документация по нод-командам — в SKILL.md.
12
+
13
+ 'use strict';
14
+
4
15
  const path = require('node:path');
5
16
  const { spawn } = require('node:child_process');
6
17
 
7
18
  const root = path.resolve(__dirname, '..');
8
19
  const args = process.argv.slice(2);
9
20
 
21
+ const settingsLib = require(path.join(root, 'lib', 'settings.js'));
22
+
10
23
  function showHelp() {
11
24
  console.log(`
12
25
  KingKont · Chatium — видео-редактор сериала
13
26
 
14
- Запуск:
15
- npx kingkont запустить Electron-приложение (рекомендуется)
16
- npx kingkont serve запустить только HTTP-сервер; URL для Chrome
17
- npx kingkont serve <path> то же, путь к проекту/cwd по умолчанию
18
- npx kingkont install-skill установить Skill для Claude Code (~/.claude/skills/kingkont)
27
+ Запуск редактора:
28
+ npx kingkont запустить Electron-приложение (рекомендуется)
29
+ npx kingkont serve запустить HTTP-сервер; URL для Chrome
30
+ npx kingkont serve <path> то же, путь к проекту/cwd по умолчанию
31
+ npx kingkont install-skill установить Skill для Claude Code
32
+
33
+ Управление нодами без UI (правит scene.json напрямую):
34
+ kingkont open <project> структура проекта (JSON с --json)
35
+ kingkont list <project> список досок
36
+ kingkont board <project> <board> одна доска (--json для машины)
37
+ kingkont add-node <project> <board> --kind=image|video|audio|text [--name=...]
38
+ kingkont gen <project> <board> --kind=... --prompt="..." [--model=...] [--refs=@a,@b]
39
+ kingkont generate <project> <board> <nodeId> перезапустить draft-ноду
40
+ kingkont status <project> <board> <nodeId> прогресс in-flight задачи
41
+ kingkont connect <project> <board> <fromId> <toId>
42
+ kingkont rm-node <project> <board> <nodeId>
43
+
44
+ Утилиты:
45
+ kingkont voices список голосов ElevenLabs (нужен ключ)
46
+ kingkont balance балансы всех включённых провайдеров
47
+ kingkont upload <file> загрузить файл в storage, печатает URL
19
48
 
20
49
  Опции:
21
- --port <N> порт сервера (по умолчанию 17893)
22
- --project install-skill: положить в .claude/skills/ текущей папки
23
- --help, -h эта справка
50
+ --port <N> порт HTTP-сервера (по умолчанию 17893)
51
+ --json машино-читаемый вывод (для cli/Claude)
52
+ --project install-skill: положить в .claude/skills/ текущей папки
53
+ --help, -h эта справка
54
+
55
+ Где CLI берёт API-ключи: ~/Library/Application Support/KingKont/settings.json
56
+ (те же ключи, что подключены через UI приложения).
24
57
  `);
25
58
  }
26
59
 
@@ -51,21 +84,27 @@ if (args.includes('--help') || args.includes('-h')) {
51
84
  const portIdx = args.indexOf('--port');
52
85
  const port = portIdx >= 0 ? parseInt(args[portIdx + 1], 10) : 17893;
53
86
 
54
- if (args[0] === 'install-skill') {
87
+ // Подгружаем ключи из settings.json в env (для всех режимов).
88
+ settingsLib.applySettingsToEnv(settingsLib.loadSettings());
89
+
90
+ const cmd = args[0];
91
+
92
+ if (cmd === 'install-skill') {
55
93
  installSkill();
56
94
  process.exit(0);
57
95
  }
58
96
 
59
- if (args[0] === 'serve') {
60
- // Только Node-сервер. FSAH-API в Chrome требует user-gesture для
61
- // showDirectoryPicker юзер сам выбирает папку через UI.
97
+ // Нод-команды делегируем в lib/cli.
98
+ const NODE_COMMANDS = require(path.join(root, 'lib', 'cli.js')).COMMANDS;
99
+ if (cmd && NODE_COMMANDS[cmd]) {
100
+ const cliLib = require(path.join(root, 'lib', 'cli.js'));
101
+ cliLib.run(args).then(
102
+ () => process.exit(0),
103
+ err => { console.error('Ошибка:', err.message); process.exit(1); }
104
+ );
105
+ } else if (cmd === 'serve') {
62
106
  process.env.PORT = String(port);
63
- // Если задан путь — на старте просто печатаем подсказку, открыть его
64
- // через FSAH юзер должен руками (нельзя без gesture).
65
107
  const targetPath = args[1] && !args[1].startsWith('--') ? path.resolve(args[1]) : null;
66
- // Подгружаем сервер. Если ключи не в env — попробуем взять из userData
67
- // (так же как делает main.js).
68
- applySettingsToEnv();
69
108
  const { start } = require(path.join(root, 'server.js'));
70
109
  start(port).then(actualPort => {
71
110
  const url = `http://localhost:${actualPort}/`;
@@ -77,13 +116,7 @@ if (args[0] === 'serve') {
77
116
  process.exit(1);
78
117
  });
79
118
  } else {
80
- // Electron-приложение.
81
- applySettingsToEnv();
82
- // Self-heal: если postinstall не отработал (npm 9+ иногда тихо пропускает
83
- // scripts при глобал-install под некоторыми node-prefix'ами), Electron.app
84
- // в node_modules останется не переименованным → в menubar macOS «Electron»
85
- // вместо «KingKont». Запустим патч-скрипт прямо сейчас, idempotent — если
86
- // уже переименовано, выйдет no-op. Ошибку проглатываем — это cosmetic.
119
+ // Electron-приложение (default).
87
120
  if (process.platform === 'darwin') {
88
121
  try {
89
122
  const patch = require(path.join(root, 'scripts', 'patch-electron-name.js'));
@@ -92,7 +125,6 @@ if (args[0] === 'serve') {
92
125
  }
93
126
  const electronPath = (() => {
94
127
  try {
95
- // Сбрасываем кэш — postinstall мог только что переписать path.txt.
96
128
  delete require.cache[require.resolve('electron')];
97
129
  return require('electron');
98
130
  }
@@ -105,25 +137,3 @@ if (args[0] === 'serve') {
105
137
  const proc = spawn(electronPath, [root], { stdio: 'inherit', env: process.env });
106
138
  proc.on('exit', code => process.exit(code || 0));
107
139
  }
108
-
109
- // Прокидываем сохранённые ключи из userData/settings.json в process.env,
110
- // чтобы server.js (handleGenerate/Tts/Sfx/Music/Text) их подхватил.
111
- function applySettingsToEnv() {
112
- try {
113
- const fs = require('node:fs');
114
- const os = require('node:os');
115
- // На macOS userData = ~/Library/Application Support/KingKont
116
- const candidates = [
117
- path.join(os.homedir(), 'Library', 'Application Support', 'KingKont', 'settings.json'),
118
- path.join(os.homedir(), '.config', 'KingKont', 'settings.json'),
119
- ];
120
- for (const p of candidates) {
121
- if (!fs.existsSync(p)) continue;
122
- const s = JSON.parse(fs.readFileSync(p, 'utf-8'));
123
- if (s.kieKey) process.env.KIE_API_KEY = s.kieKey;
124
- if (s.elevenKey) process.env.ELEVENLABS_API_KEY = s.elevenKey;
125
- if (s.openrouterKey) process.env.OPENROUTER_API_KEY = s.openrouterKey;
126
- break;
127
- }
128
- } catch {}
129
- }