kingkont 0.7.10 → 0.7.12

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/bin/kingkont.js CHANGED
@@ -79,8 +79,23 @@ if (args[0] === 'serve') {
79
79
  } else {
80
80
  // Electron-приложение.
81
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.
87
+ if (process.platform === 'darwin') {
88
+ try {
89
+ const patch = require(path.join(root, 'scripts', 'patch-electron-name.js'));
90
+ patch();
91
+ } catch (e) { console.warn('[kingkont] electron-name patch failed:', e.message); }
92
+ }
82
93
  const electronPath = (() => {
83
- try { return require('electron'); }
94
+ try {
95
+ // Сбрасываем кэш — postinstall мог только что переписать path.txt.
96
+ delete require.cache[require.resolve('electron')];
97
+ return require('electron');
98
+ }
84
99
  catch { return null; }
85
100
  })();
86
101
  if (!electronPath) {
package/index.html CHANGED
@@ -150,6 +150,23 @@
150
150
  width: 240px; flex-shrink: 0;
151
151
  scroll-snap-align: start;
152
152
  }
153
+ /* Карточка «Открыть проект» — стилизована под обычную recent-карточку,
154
+ но контент не превью, а большая иконка + надпись. */
155
+ .welcome-card.open-card {
156
+ background: linear-gradient(135deg, #2a3e5c, #344a6e);
157
+ border-color: #4a6a9a;
158
+ }
159
+ .welcome-card.open-card:hover {
160
+ border-color: #6a8aba;
161
+ background: linear-gradient(135deg, #344a6e, #4a6a9a);
162
+ }
163
+ .welcome-card.open-card .welcome-card-thumb {
164
+ background: transparent; color: #c4d4ec; font-size: 56px; line-height: 1;
165
+ }
166
+ .welcome-card.open-card .welcome-card-name {
167
+ color: #e0e8f4; font-weight: 600;
168
+ }
169
+ .welcome-card.open-card .welcome-card-ts { color: #aac0d8; }
153
170
  .welcome-card:hover { border-color: #4a6a9a; transform: translateY(-2px); }
154
171
  .welcome-card-thumb {
155
172
  width: 100%; aspect-ratio: 16 / 9; background: #1a1a1a;
@@ -1017,12 +1034,13 @@
1017
1034
  <img class="welcome-logo" src="assets/logo-square.svg" alt="" draggable="false" id="welcomeLogo" title="Дабл-клик — настройки" style="cursor:pointer;">
1018
1035
  <h1 class="welcome-title">KingKont</h1>
1019
1036
  <div class="welcome-sub">Видео-редактор</div>
1020
- <button id="welcomeOpen" class="welcome-open primary">Открыть проект</button>
1021
1037
  </div>
1022
- <div class="welcome-recent" id="welcomeRecent" style="display:none;">
1023
- <div class="welcome-recent-title">Недавние проекты</div>
1038
+ <div class="welcome-recent" id="welcomeRecent">
1039
+ <div class="welcome-recent-title" id="welcomeRecentTitle">Открыть проект</div>
1024
1040
  <div class="welcome-recent-grid" id="welcomeRecentGrid"></div>
1025
1041
  </div>
1042
+ <!-- Скрытая кнопка-shim — на неё дёргают .click() из app menu и dropzone'ов. -->
1043
+ <button id="welcomeOpen" style="display:none;"></button>
1026
1044
  </div>
1027
1045
  </div>
1028
1046
 
@@ -2286,11 +2304,35 @@ function fmtRelativeTime(ts) {
2286
2304
  async function renderWelcomeRecents() {
2287
2305
  const grid = $('welcomeRecentGrid');
2288
2306
  const wrap = $('welcomeRecent');
2307
+ const titleEl = $('welcomeRecentTitle');
2289
2308
  if (!grid || !wrap) return;
2290
2309
  grid.innerHTML = '';
2310
+ wrap.style.display = '';
2291
2311
  const list = await getRecents();
2292
- if (!list.length) { wrap.style.display = 'none'; return; }
2293
- wrap.style.display = 'block';
2312
+
2313
+ // Первой картой — «Открыть проект». Кликается → дёргает скрытый
2314
+ // #pickRoot button (тот же, что использует app menu).
2315
+ const openCard = document.createElement('div');
2316
+ openCard.className = 'welcome-card open-card';
2317
+ const openThumb = document.createElement('div');
2318
+ openThumb.className = 'welcome-card-thumb';
2319
+ openThumb.textContent = '+';
2320
+ const openMeta = document.createElement('div');
2321
+ openMeta.className = 'welcome-card-meta';
2322
+ const openName = document.createElement('div');
2323
+ openName.className = 'welcome-card-name';
2324
+ openName.textContent = 'Открыть проект';
2325
+ const openSub = document.createElement('div');
2326
+ openSub.className = 'welcome-card-ts';
2327
+ openSub.textContent = 'выбрать папку…';
2328
+ openMeta.append(openName, openSub);
2329
+ openCard.append(openThumb, openMeta);
2330
+ openCard.addEventListener('click', () => $('pickRoot').click());
2331
+ grid.appendChild(openCard);
2332
+
2333
+ // Title меняем в зависимости от наличия recents.
2334
+ if (titleEl) titleEl.textContent = list.length ? 'Открыть проект · недавние' : 'Открыть проект';
2335
+
2294
2336
  for (const r of list) {
2295
2337
  const card = document.createElement('div');
2296
2338
  card.className = 'welcome-card';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.7.10",
3
+ "version": "0.7.12",
4
4
  "description": "KingKont \u00b7 Chatium \u2014 \u043d\u043e\u0434-\u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0441\u0446\u0435\u043d \u0441 AI-\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 (\u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438/\u0432\u0438\u0434\u0435\u043e/\u0433\u043e\u043b\u043e\u0441/SFX/\u043c\u0443\u0437\u044b\u043a\u0430/\u0442\u0435\u043a\u0441\u0442)",
5
5
  "main": "main.js",
6
6
  "bin": {
@@ -1,108 +1,92 @@
1
1
  #!/usr/bin/env node
2
2
  // Переименовывает dev-Electron бандл в «KingKont» — в menubar macOS,
3
- // Activity Monitor, Dock-tooltip и пр. Запускается через postinstall.
4
- // Идемпотентно. Молча выходит если что-то не так — это не критическая
5
- // часть установки.
3
+ // Activity Monitor, Dock-tooltip и пр. Запускается через postinstall
4
+ // или (как self-heal) из bin/kingkont.js при каждом старте.
5
+ // Идемпотентно. Молча выходит если что-то не так — не критическая часть.
6
6
  'use strict';
7
7
  const fs = require('node:fs');
8
8
  const path = require('node:path');
9
9
  const { execFileSync } = require('node:child_process');
10
10
 
11
- if (process.platform !== 'darwin') process.exit(0);
11
+ function patchElectronName() {
12
+ if (process.platform !== 'darwin') return false;
12
13
 
13
- let electronExe;
14
- try { electronExe = require('electron'); }
15
- catch (e) {
16
- console.warn('[kingkont postinstall] electron not found, skip rename:', e.message);
17
- process.exit(0);
18
- }
19
- // require('electron') возвращает путь к бинарю. На macOS это:
20
- // node_modules/electron/dist/Electron.app/Contents/MacOS/Electron
21
- // Если уже переименовано — KingKont.app/Contents/MacOS/KingKont
22
- const macOsDir = path.dirname(electronExe); // .../MacOS
23
- const contentsDir = path.dirname(macOsDir); // .../Contents
24
- const appBundle = path.dirname(contentsDir); // .../Electron.app или KingKont.app
25
- const dist = path.dirname(appBundle); // .../dist
26
- const electronPkg = path.dirname(dist); // .../electron
27
-
28
- const oldApp = path.join(dist, 'Electron.app');
29
- const newApp = path.join(dist, 'KingKont.app');
30
-
31
- // Уже переименовано?
32
- if (fs.existsSync(newApp) && !fs.existsSync(oldApp)) {
33
- // Убедимся что иконка из нашего пакета актуальна (на случай обновления).
34
- ensureIcon(newApp);
35
- process.exit(0);
36
- }
37
- if (!fs.existsSync(oldApp)) {
38
- console.warn('[kingkont postinstall] Electron.app not found at', oldApp);
39
- process.exit(0);
40
- }
14
+ let electronExe;
15
+ try { electronExe = require('electron'); }
16
+ catch (e) { return false; }
41
17
 
42
- console.log('[kingkont postinstall] Electron KingKont…');
43
-
44
- // 1) main bundle + бинарь
45
- fs.renameSync(oldApp, newApp);
46
- fs.renameSync(path.join(newApp, 'Contents', 'MacOS', 'Electron'),
47
- path.join(newApp, 'Contents', 'MacOS', 'KingKont'));
48
-
49
- // 2) Helpers
50
- const fwDir = path.join(newApp, 'Contents', 'Frameworks');
51
- const helpers = [
52
- ['Electron Helper.app', 'KingKont Helper.app'],
53
- ['Electron Helper (GPU).app', 'KingKont Helper (GPU).app'],
54
- ['Electron Helper (Plugin).app', 'KingKont Helper (Plugin).app'],
55
- ['Electron Helper (Renderer).app','KingKont Helper (Renderer).app'],
56
- ];
57
- for (const [oldH, newH] of helpers) {
58
- const o = path.join(fwDir, oldH);
59
- const n = path.join(fwDir, newH);
60
- if (!fs.existsSync(o)) continue;
61
- fs.renameSync(o, n);
62
- const oldBin = oldH.replace(/\.app$/, '');
63
- const newBin = newH.replace(/\.app$/, '');
64
- fs.renameSync(path.join(n, 'Contents', 'MacOS', oldBin),
65
- path.join(n, 'Contents', 'MacOS', newBin));
66
- patchPlist(path.join(n, 'Contents', 'Info.plist'), {
67
- CFBundleExecutable: newBin,
68
- CFBundleName: newBin,
69
- CFBundleDisplayName: newBin,
70
- });
71
- }
18
+ // require('electron') возвращает путь к бинарю.
19
+ const macOsDir = path.dirname(electronExe);
20
+ const contentsDir = path.dirname(macOsDir);
21
+ const appBundle = path.dirname(contentsDir);
22
+ const dist = path.dirname(appBundle);
23
+ const electronPkg = path.dirname(dist);
72
24
 
73
- // 3) Info.plist главного app
74
- patchPlist(path.join(newApp, 'Contents', 'Info.plist'), {
75
- CFBundleExecutable: 'KingKont',
76
- CFBundleName: 'KingKont',
77
- CFBundleDisplayName: 'KingKont',
78
- });
25
+ const oldApp = path.join(dist, 'Electron.app');
26
+ const newApp = path.join(dist, 'KingKont.app');
79
27
 
80
- // 4) electron/path.txt — npm-обёртка читает его при require('electron')
81
- fs.writeFileSync(path.join(electronPkg, 'path.txt'),
82
- 'KingKont.app/Contents/MacOS/KingKont');
28
+ // Уже переименовано?
29
+ if (fs.existsSync(newApp) && !fs.existsSync(oldApp)) {
30
+ ensureIcon(newApp);
31
+ return false;
32
+ }
33
+ if (!fs.existsSync(oldApp)) return false;
34
+
35
+ console.log('[kingkont] Electron → KingKont…');
36
+
37
+ fs.renameSync(oldApp, newApp);
38
+ fs.renameSync(path.join(newApp, 'Contents', 'MacOS', 'Electron'),
39
+ path.join(newApp, 'Contents', 'MacOS', 'KingKont'));
40
+
41
+ const fwDir = path.join(newApp, 'Contents', 'Frameworks');
42
+ const helpers = [
43
+ ['Electron Helper.app', 'KingKont Helper.app'],
44
+ ['Electron Helper (GPU).app', 'KingKont Helper (GPU).app'],
45
+ ['Electron Helper (Plugin).app', 'KingKont Helper (Plugin).app'],
46
+ ['Electron Helper (Renderer).app','KingKont Helper (Renderer).app'],
47
+ ];
48
+ for (const [oldH, newH] of helpers) {
49
+ const o = path.join(fwDir, oldH);
50
+ const n = path.join(fwDir, newH);
51
+ if (!fs.existsSync(o)) continue;
52
+ fs.renameSync(o, n);
53
+ const oldBin = oldH.replace(/\.app$/, '');
54
+ const newBin = newH.replace(/\.app$/, '');
55
+ fs.renameSync(path.join(n, 'Contents', 'MacOS', oldBin),
56
+ path.join(n, 'Contents', 'MacOS', newBin));
57
+ patchPlist(path.join(n, 'Contents', 'Info.plist'), {
58
+ CFBundleExecutable: newBin,
59
+ CFBundleName: newBin,
60
+ CFBundleDisplayName: newBin,
61
+ });
62
+ }
83
63
 
84
- // 5) Иконка
85
- ensureIcon(newApp);
64
+ patchPlist(path.join(newApp, 'Contents', 'Info.plist'), {
65
+ CFBundleExecutable: 'KingKont',
66
+ CFBundleName: 'KingKont',
67
+ CFBundleDisplayName: 'KingKont',
68
+ });
86
69
 
87
- // 6) Ad-hoc resign — без неё macOS Sequoia может убить процесс при старте.
88
- try {
89
- execFileSync('codesign', ['--force', '--deep', '--sign', '-', newApp], { stdio: 'ignore' });
90
- } catch {}
70
+ fs.writeFileSync(path.join(electronPkg, 'path.txt'),
71
+ 'KingKont.app/Contents/MacOS/KingKont');
91
72
 
92
- // 7) Refresh LaunchServices
93
- try {
94
- execFileSync('/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister',
95
- ['-f', newApp], { stdio: 'ignore' });
96
- } catch {}
73
+ ensureIcon(newApp);
97
74
 
98
- console.log('[kingkont postinstall] KingKont готов');
75
+ // Ad-hoc resign без неё macOS Sequoia может убить процесс при старте.
76
+ try { execFileSync('codesign', ['--force', '--deep', '--sign', '-', newApp], { stdio: 'ignore' }); } catch {}
77
+ // Refresh LaunchServices.
78
+ try {
79
+ execFileSync('/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister',
80
+ ['-f', newApp], { stdio: 'ignore' });
81
+ } catch {}
99
82
 
100
- // === helpers ===
83
+ console.log('[kingkont] KingKont готов');
84
+ return true;
85
+ }
101
86
 
102
87
  function patchPlist(plistPath, fields) {
103
88
  if (!fs.existsSync(plistPath)) return;
104
89
  for (const [k, v] of Object.entries(fields)) {
105
- // Сначала Set; если ключа нет — Add.
106
90
  try {
107
91
  execFileSync('/usr/libexec/PlistBuddy', ['-c', `Set :${k} ${v}`, plistPath], { stdio: 'ignore' });
108
92
  } catch {
@@ -114,11 +98,17 @@ function patchPlist(plistPath, fields) {
114
98
  }
115
99
 
116
100
  function ensureIcon(bundle) {
117
- // Этот скрипт лежит в <pkg>/scripts/, иконка в <pkg>/assets/logo.icns.
118
101
  const src = path.join(__dirname, '..', 'assets', 'logo.icns');
119
102
  const dst = path.join(bundle, 'Contents', 'Resources', 'electron.icns');
120
103
  if (!fs.existsSync(src)) return;
121
- try {
122
- fs.copyFileSync(src, dst);
123
- } catch {}
104
+ try { fs.copyFileSync(src, dst); } catch {}
105
+ }
106
+
107
+ module.exports = patchElectronName;
108
+
109
+ // Если запущен как entry-point (postinstall) — выполняем сразу.
110
+ if (require.main === module) {
111
+ try { patchElectronName(); } catch (e) {
112
+ console.warn('[kingkont postinstall] failed:', e.message);
113
+ }
124
114
  }