akari-video 0.1.87 → 0.1.89
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 +1 -1
- package/vendor/packages/akari-launcher/package.json +1 -1
- package/vendor/packages/edit-store/lib/caption-runs.js +18 -6
- package/vendor/packages/edit-store/lib/index.d.ts +1 -0
- package/vendor/packages/edit-store/lib/index.js +1 -0
- package/vendor/packages/edit-store/lib/media-planes.d.ts +2 -0
- package/vendor/packages/edit-store/lib/media-planes.js +70 -0
- package/vendor/packages/overlay-runtime/src/interaction.js +7 -3
- package/vendor/packages/project-scaffold/src/index.mjs +2 -2
- package/vendor/packages/project-scaffold/test/hidden-adapters.test.mjs +16 -0
- package/vendor/packages/project-scaffold/test/kit-skill-adapter.test.mjs +2 -2
- package/vendor/packages/project-scaffold/test/skill-adapter-link.test.mjs +3 -1
- package/vendor/skills/verify/SKILL.md +10 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.89",
|
|
4
4
|
"description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.89",
|
|
4
4
|
"description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。 [akari-video npm vendor: bin/akari.mjs is reference-only. These CLI entrypoints are not included in the akari-video npm package. Use `akari doctor --json` and run the path reported in `render_cut.path`. Full installations provide it in a monorepo checkout, ~/.akari/app, /Applications/AKARI Video.app/Contents/Resources/packages, or %LOCALAPPDATA%\\Programs\\@akari-videoshell\\resources\\packages.]",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -249,6 +249,7 @@ function applyCaptionRunsToHtml(html, displayText, runs) {
|
|
|
249
249
|
const Segmenter = Intl.Segmenter;
|
|
250
250
|
const segment = (value) => Array.from(new Segmenter(undefined, { granularity: 'grapheme' }).segment(value), item => item.segment);
|
|
251
251
|
const chars = segment(displayText);
|
|
252
|
+
const defaultStroke = html.includes('akari-caption--single-line') ? '0 transparent' : '0.14em rgba(0,0,0,.9)';
|
|
252
253
|
if (!runs.some(run => Number.isInteger(run?.from) && Number.isInteger(run?.to)
|
|
253
254
|
&& run.from >= 0 && run.to <= chars.length && run.from < run.to))
|
|
254
255
|
return html;
|
|
@@ -277,12 +278,23 @@ function applyCaptionRunsToHtml(html, displayText, runs) {
|
|
|
277
278
|
}
|
|
278
279
|
const style = item.style;
|
|
279
280
|
const css = ['display:inline-block', 'vertical-align:baseline', 'line-height:1'];
|
|
281
|
+
const scale = typeof style.scale === 'number' && Number.isFinite(style.scale) && style.scale > 0
|
|
282
|
+
? style.scale : 1;
|
|
283
|
+
if (scale !== 1)
|
|
284
|
+
css.push(`font-size:${scale}em`);
|
|
285
|
+
// Re-resolve em values on the run: inherited computed lengths stay at the parent's px size.
|
|
286
|
+
if (typeof style.letter_spacing_em === 'number' && Number.isFinite(style.letter_spacing_em)) {
|
|
287
|
+
css.push(`letter-spacing:${style.letter_spacing_em}em`);
|
|
288
|
+
}
|
|
289
|
+
else if (scale !== 1) {
|
|
290
|
+
css.push('letter-spacing:var(--caption-letter-spacing,normal)');
|
|
291
|
+
}
|
|
292
|
+
if (scale !== 1)
|
|
293
|
+
css.push(`-webkit-text-stroke:var(--caption-webkit-text-stroke,var(--caption-stroke,${defaultStroke}))`);
|
|
280
294
|
if (typeof style.color === 'string' && /^#(?:[\da-fA-F]{3}|[\da-fA-F]{6}|[\da-fA-F]{8})$/.test(style.color))
|
|
281
295
|
css.push(`color:${style.color}`);
|
|
282
296
|
if (Number.isInteger(style.font_weight) && style.font_weight >= 1 && style.font_weight <= 1000)
|
|
283
297
|
css.push(`font-weight:${style.font_weight}`);
|
|
284
|
-
if (typeof style.letter_spacing_em === 'number' && Number.isFinite(style.letter_spacing_em))
|
|
285
|
-
css.push(`letter-spacing:${style.letter_spacing_em}em`);
|
|
286
298
|
if (style.italic === true)
|
|
287
299
|
css.push('font-style:italic');
|
|
288
300
|
if (style.underline === true)
|
|
@@ -296,10 +308,10 @@ function applyCaptionRunsToHtml(html, displayText, runs) {
|
|
|
296
308
|
? style.baseline_shift_em : 0;
|
|
297
309
|
const rotate = typeof style.rotate_deg === 'number' && Number.isFinite(style.rotate_deg)
|
|
298
310
|
? style.rotate_deg : 0;
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
311
|
+
if (shift || rotate)
|
|
312
|
+
css.push(scale === 1
|
|
313
|
+
? `transform:translateY(${shift}em) rotate(${rotate}deg) scale(1)`
|
|
314
|
+
: `transform:translateY(${shift / scale}em) rotate(${rotate}deg)`);
|
|
303
315
|
return `<span class="akari-caption__run"${item.role ? ` data-role="${escape(item.role)}"` : ''} style="${css.join(';')}">${escape(character)}</span>`;
|
|
304
316
|
}).join('');
|
|
305
317
|
};
|
|
@@ -21,6 +21,7 @@ export * from './legacy-audio-view';
|
|
|
21
21
|
export * from './retime';
|
|
22
22
|
export * from './track-order';
|
|
23
23
|
export * from './track-z';
|
|
24
|
+
export * from './media-planes';
|
|
24
25
|
export * from './track-transition-compatibility';
|
|
25
26
|
export * from './cut-adjacency';
|
|
26
27
|
export * from './transition-vocabulary';
|
|
@@ -45,6 +45,7 @@ __exportStar(require("./legacy-audio-view"), exports);
|
|
|
45
45
|
__exportStar(require("./retime"), exports);
|
|
46
46
|
__exportStar(require("./track-order"), exports);
|
|
47
47
|
__exportStar(require("./track-z"), exports);
|
|
48
|
+
__exportStar(require("./media-planes"), exports);
|
|
48
49
|
__exportStar(require("./track-transition-compatibility"), exports);
|
|
49
50
|
__exportStar(require("./cut-adjacency"), exports);
|
|
50
51
|
__exportStar(require("./transition-vocabulary"), exports);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.partitionPreviewMediaPlanes = partitionPreviewMediaPlanes;
|
|
4
|
+
/** Partition decoded media around DOM stacking barriers. Browser safe: the preview embeds this function. */
|
|
5
|
+
function partitionPreviewMediaPlanes(plan, summary) {
|
|
6
|
+
const tracks = Array.isArray(summary.timelineTracks) ? summary.timelineTracks : [];
|
|
7
|
+
const zOfTrack = (id) => tracks.findIndex((track) => track.id === id);
|
|
8
|
+
const zOfItem = (id, trackId, renderTrack) => Number.isInteger(summary.itemStackZ?.[String(id)]) ? summary.itemStackZ[String(id)]
|
|
9
|
+
: Number.isInteger(summary.trackStackZ?.[String(trackId)]) ? summary.trackStackZ[String(trackId)]
|
|
10
|
+
: Number.isInteger(renderTrack) ? renderTrack : zOfTrack(trackId);
|
|
11
|
+
const barriers = [...new Set([
|
|
12
|
+
...(Array.isArray(summary.overlays) ? summary.overlays : [])
|
|
13
|
+
.map((item) => zOfItem(item.id, item.trackId, item.renderTrack)),
|
|
14
|
+
zOfItem(undefined, summary.captionTrackId),
|
|
15
|
+
...Object.values(summary.itemStackZ ?? {}),
|
|
16
|
+
...(Array.isArray(summary.barrierZ) ? summary.barrierZ : []),
|
|
17
|
+
].filter(z => z >= 0))].sort((a, b) => a - b);
|
|
18
|
+
const bands = new Map();
|
|
19
|
+
const bandAt = (z) => {
|
|
20
|
+
const key = barriers.filter(barrier => barrier < z).length;
|
|
21
|
+
let band = bands.get(key);
|
|
22
|
+
if (!band) {
|
|
23
|
+
band = { key, zIndex: z, baseIndices: [], entries: [] };
|
|
24
|
+
bands.set(key, band);
|
|
25
|
+
}
|
|
26
|
+
band.zIndex = Math.max(band.zIndex, z);
|
|
27
|
+
return band;
|
|
28
|
+
};
|
|
29
|
+
const cutZ = (id) => {
|
|
30
|
+
const cut = summary.cutsById?.[id] ?? summary.cuts?.[Number(id.slice('cut-'.length))];
|
|
31
|
+
return zOfItem(cut?.id, cut?.trackId, cut?.renderTrack);
|
|
32
|
+
};
|
|
33
|
+
if (plan.base.length) {
|
|
34
|
+
const baseByBand = new Map();
|
|
35
|
+
plan.base.forEach((cut, index) => {
|
|
36
|
+
const band = bandAt(cutZ(cut.id));
|
|
37
|
+
const group = baseByBand.get(band.key) ?? { band, indices: [] };
|
|
38
|
+
group.indices.push(index);
|
|
39
|
+
baseByBand.set(band.key, group);
|
|
40
|
+
});
|
|
41
|
+
for (const { band, indices } of baseByBand.values()) {
|
|
42
|
+
// Transition pairs stay together. A lone upper cut needs a transparent layer.
|
|
43
|
+
if (band.key === 0 || indices.length > 1) {
|
|
44
|
+
band.baseIndices.push(...indices);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const index = indices[0];
|
|
48
|
+
const cut = plan.base[index];
|
|
49
|
+
const visual = cut.visual;
|
|
50
|
+
band.entries.push({ baseIndex: index, spec: {
|
|
51
|
+
...cut, kind: cut.kind ?? 'video', cutVisual: visual,
|
|
52
|
+
visual: { crop: visual.layerStyle?.crop ?? { x: 0, y: 0, width: 1, height: 1 },
|
|
53
|
+
perspective: null, transform: visual.transform },
|
|
54
|
+
mask: null, blend: 'normal', opacity: visual.opacity,
|
|
55
|
+
...(visual.adjustLut ? { adjustLut: visual.adjustLut } : {}),
|
|
56
|
+
...(visual.adjustFx ? { adjustFx: visual.adjustFx } : {})
|
|
57
|
+
} });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
plan.layers.forEach((layer, index) => {
|
|
62
|
+
const declared = summary.layers?.find((item) => String(item.id) === layer.id);
|
|
63
|
+
const z = layer.cutVisual ? cutZ(layer.id)
|
|
64
|
+
: zOfItem(declared?.id ?? layer.id, declared?.trackId, declared?.renderTrack);
|
|
65
|
+
bandAt(z).entries.push({ layerIndex: index, spec: layer });
|
|
66
|
+
});
|
|
67
|
+
if (!bands.has(0))
|
|
68
|
+
bands.set(0, { key: 0, zIndex: -1, baseIndices: [], entries: [] });
|
|
69
|
+
return [...bands.values()].sort((a, b) => a.key - b.key);
|
|
70
|
+
}
|
|
@@ -1002,7 +1002,7 @@ function marqueeHits(candidates, rect) {
|
|
|
1002
1002
|
return;
|
|
1003
1003
|
}
|
|
1004
1004
|
if (!selectedOverlay && selectedId && selectionTree().length) {
|
|
1005
|
-
if (!treeNode(selectedId)) { clearSelection(); publishScopedSelection(
|
|
1005
|
+
if (!treeNode(selectedId)) { clearSelection(); publishScopedSelection(); return; }
|
|
1006
1006
|
const replacement = containerById(selectedId);
|
|
1007
1007
|
if (replacement) {
|
|
1008
1008
|
selectedOverlay = replacement;
|
|
@@ -1063,6 +1063,7 @@ function marqueeHits(candidates, rect) {
|
|
|
1063
1063
|
if (activeRotate?.container === container) cancelRotate();
|
|
1064
1064
|
if (activeEdit?.container === container) void commitEdit();
|
|
1065
1065
|
clearSelection();
|
|
1066
|
+
publishScopedSelection();
|
|
1066
1067
|
}
|
|
1067
1068
|
|
|
1068
1069
|
function selectOverlay(container) {
|
|
@@ -1202,7 +1203,10 @@ function marqueeHits(candidates, rect) {
|
|
|
1202
1203
|
startSelectionTracking();
|
|
1203
1204
|
} else if (next.selectId === null) clearSelection();
|
|
1204
1205
|
else if (!selectOverlay(containerById(next.selectId))) {
|
|
1205
|
-
if (!node?.lazy || lazyBagForScope(tree, scopeId) !== node.parentId)
|
|
1206
|
+
if (!node?.lazy || lazyBagForScope(tree, scopeId) !== node.parentId) {
|
|
1207
|
+
scopeId = previousScopeId;
|
|
1208
|
+
return false;
|
|
1209
|
+
}
|
|
1206
1210
|
clearSelection();
|
|
1207
1211
|
selectedId = node.id;
|
|
1208
1212
|
selectedIds = [node.id];
|
|
@@ -2737,7 +2741,7 @@ function marqueeHits(candidates, rect) {
|
|
|
2737
2741
|
+ '[data-akari-interaction], [data-akari-ui="preview-scope-breadcrumb"], '
|
|
2738
2742
|
+ '.caption-row-plate, #caption-select-box, #layer-select-box, #cut-select-box, '
|
|
2739
2743
|
+ '.transport-controls, [role="menu"]');
|
|
2740
|
-
if (outside && !protectedTarget) { clearSelection(); return; }
|
|
2744
|
+
if (outside && !protectedTarget) { clearSelection(); publishScopedSelection(); return; }
|
|
2741
2745
|
}
|
|
2742
2746
|
flushNudge();
|
|
2743
2747
|
hideHover();
|
|
@@ -87,7 +87,7 @@ const FALLBACK_WORKFLOW = {
|
|
|
87
87
|
{ path: 'exports', label: '書き出し', kind: 'exports' }
|
|
88
88
|
],
|
|
89
89
|
tree: {
|
|
90
|
-
hidden: ['.claude', '.agents', '.codex', '.cursor', '.akari', 'CLAUDE.md', 'AGENTS.md', '.gitignore', '.gitkeep'],
|
|
90
|
+
hidden: ['.claude', '.agents', '.codex', '.cursor', '.opencode', '.devin', '.akari', 'CLAUDE.md', 'AGENTS.md', '.gitignore', '.gitkeep'],
|
|
91
91
|
sidecarSuffixes: ['.meta.json', '.decisions.json', '.analysis.json'],
|
|
92
92
|
developerModePreference: 'akari.developerMode'
|
|
93
93
|
},
|
|
@@ -216,7 +216,7 @@ export async function writeFallbackTemplate(destinationDir) {
|
|
|
216
216
|
return { writtenFiles, skippedExisting };
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
const SKILL_ADAPTER_DIRECTORIES = ['.agents', '.codex', '.cursor', '.opencode'];
|
|
219
|
+
const SKILL_ADAPTER_DIRECTORIES = ['.agents', '.codex', '.cursor', '.opencode', '.devin'];
|
|
220
220
|
|
|
221
221
|
function isPermissionDenied(error) {
|
|
222
222
|
return error && typeof error === 'object' && (error.code === 'EPERM' || error.code === 'EACCES');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { writeFallbackTemplate } from '../src/index.mjs';
|
|
7
|
+
|
|
8
|
+
test('scaffold の fallback workflow は全スキルリンク置き場を隠す', async t => {
|
|
9
|
+
const root = await mkdtemp(join(tmpdir(), 'akari-hidden-adapters-'));
|
|
10
|
+
t.after(() => rm(root, { recursive: true, force: true }));
|
|
11
|
+
await writeFallbackTemplate(root);
|
|
12
|
+
const workflow = JSON.parse(await readFile(join(root, '.akari', 'workflow.json'), 'utf8'));
|
|
13
|
+
for (const adapter of ['.agents', '.codex', '.cursor', '.opencode', '.devin']) {
|
|
14
|
+
assert.ok(workflow.tree.hidden.includes(adapter), adapter);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
@@ -17,7 +17,7 @@ test('installSkillAdapters: kits の skill を全アダプタへ合成し、重
|
|
|
17
17
|
await mkdir(path.join(kitSkills, 'official'), { recursive: true });
|
|
18
18
|
|
|
19
19
|
const report = await installSkillAdapters(root, { env: { AKARI_HOME: home } });
|
|
20
|
-
for (const adapter of ['.agents', '.codex', '.cursor', '.opencode']) {
|
|
20
|
+
for (const adapter of ['.agents', '.codex', '.cursor', '.opencode', '.devin']) {
|
|
21
21
|
const kitLink = path.join(root, adapter, 'skills', 'kit-only');
|
|
22
22
|
assert.equal(
|
|
23
23
|
path.resolve(path.dirname(kitLink), await readlink(kitLink)),
|
|
@@ -29,7 +29,7 @@ test('installSkillAdapters: kits の skill を全アダプタへ合成し、重
|
|
|
29
29
|
path.join(root, '.claude', 'skills', 'official')
|
|
30
30
|
);
|
|
31
31
|
}
|
|
32
|
-
assert.equal(report.warnings.length,
|
|
32
|
+
assert.equal(report.warnings.length, 5);
|
|
33
33
|
assert.ok(report.warnings.every((line) => line.includes('純正スキルを優先')));
|
|
34
34
|
} finally {
|
|
35
35
|
await rm(root, { recursive: true, force: true });
|
|
@@ -182,10 +182,11 @@ test("installSkillAdapters: on win32 with symlink EPERM, all adapters degrade to
|
|
|
182
182
|
".agents/skills/analyze-footage",
|
|
183
183
|
".codex/skills/analyze-footage",
|
|
184
184
|
".cursor/skills/analyze-footage",
|
|
185
|
+
".devin/skills/analyze-footage",
|
|
185
186
|
".opencode/skills/analyze-footage"
|
|
186
187
|
]);
|
|
187
188
|
assert.deepEqual(report.skippedExisting, []);
|
|
188
|
-
assert.equal(report.degraded.length,
|
|
189
|
+
assert.equal(report.degraded.length, 5);
|
|
189
190
|
assert.ok(report.degraded.every((entry) => entry.method === "junction"));
|
|
190
191
|
});
|
|
191
192
|
});
|
|
@@ -201,6 +202,7 @@ test("installSkillAdapters: real fs on the current platform still creates plain
|
|
|
201
202
|
".agents/skills/analyze-footage",
|
|
202
203
|
".codex/skills/analyze-footage",
|
|
203
204
|
".cursor/skills/analyze-footage",
|
|
205
|
+
".devin/skills/analyze-footage",
|
|
204
206
|
".opencode/skills/analyze-footage"
|
|
205
207
|
]);
|
|
206
208
|
assert.deepEqual(report.degraded, []);
|
|
@@ -88,6 +88,16 @@ npm run lint # eslint "extensions/*/src/**
|
|
|
88
88
|
|
|
89
89
|
### 既知の地雷(L1)
|
|
90
90
|
|
|
91
|
+
0. **`ELECTRON_RUN_AS_NODE=1` が環境に残っていると Electron が即死する**(2026-09-27 実測・
|
|
92
|
+
`library-tile-cards` レーン)。この変数が立っていると Electron は GUI ではなく Node として
|
|
93
|
+
起動するため、`app` が未定義のまま
|
|
94
|
+
```
|
|
95
|
+
Cannot read properties of undefined (reading 'requestSingleInstanceLock')
|
|
96
|
+
```
|
|
97
|
+
で落ちる。L0 §地雷 3 の「パッケージ版でのパス解決」検証(`ELECTRON_RUN_AS_NODE=1` +
|
|
98
|
+
`cwd=/`)を先に回した同じシェルで L1 に入ると踏む。起動時に
|
|
99
|
+
`env -u ELECTRON_RUN_AS_NODE <electron のパス> …` で必ず外すこと。
|
|
100
|
+
|
|
91
101
|
1. **拡張ディレクトリ内のネスト `node_modules`(この層で最も踏まれている地雷)**: `apps/shell/extensions/<ext>/node_modules/` に `@theia/core` の 2 つ目の実体が入ると、esbuild は realpath 単位でモジュールを束ねるため 1 つの `bundle.js` に `frontend-application-config-provider` が 2 本入る。`src-gen/frontend/index.js` は片方に `set()` し、拡張側はもう片方から `get()` するので、フロントエンドが
|
|
92
102
|
```
|
|
93
103
|
Failed to start the frontend application.
|