akari-video 0.1.29 → 0.1.30
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/docs/contract-2026-08-30-edit-json-v2-object-tree-v0.md +6 -0
- package/vendor/packages/akari-launcher/package.json +1 -1
- package/vendor/packages/edit-store/lib/caption-display.d.ts +2 -2
- package/vendor/packages/edit-store/lib/caption-display.js +13 -4
- package/vendor/packages/edit-store/lib/tree-ops.d.ts +12 -0
- package/vendor/packages/edit-store/lib/tree-ops.js +184 -0
- package/vendor/packages/edit-store/lib/webview-kernel.js +10 -2
- package/vendor/packages/overlay-runtime/package.json +1 -1
- package/vendor/packages/schemas/captions.schema.json +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
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": {
|
|
@@ -127,6 +127,12 @@ updated: 2026-08-30
|
|
|
127
127
|
- 分離(§3.1)は不変条件 1 の帰結として**必ず新しい段を生やす**(同じ段に重ねられないため)。戻せば(⌘Z)段は消える
|
|
128
128
|
- 音声段も同じ 4 条件に従う(既存の重なり禁止と同じ)
|
|
129
129
|
|
|
130
|
+
### 2-5 追記(2026-08-31・オーナー裁定): 字幕にも特別な z 規則を置かない
|
|
131
|
+
|
|
132
|
+
- **字幕(袋グループ・分離した行・テロップ変換後)も z は段どおり**。「字幕は常に一番上」という特別規則は**廃止**する(描画側の `generatedFrom` による無条件最上段寄せも撤去)
|
|
133
|
+
- 取り込み時に字幕袋が最前面の段に乗るのは**既定の置き場**であって規則ではない。ユーザー / AI は字幕の段を自由に前後へ動かせる
|
|
134
|
+
- 狙い: 種別ごとの特別規則を増やさない(§2 の 4 不変条件だけで全種別が説明できる状態を保つ)。プレビュー / osr / gpu / render-cut の 4 出口で同時に切り替え、パリティで担保する
|
|
135
|
+
|
|
130
136
|
## 3. 操作の意味論(データ変換として定義。UI のコマンド・キー割り当ては別契約)
|
|
131
137
|
|
|
132
138
|
操作は **出す / まとめる / ばらす** の 3 つ + 切り出し・テロップに変換。**「戻す」は無い**(⌘Z のみ。再グループ化は「まとめる」で足りる)。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
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": [
|
|
@@ -101,8 +101,8 @@ export declare function mergeCaptionDisplayStyles(base: unknown, override: unkno
|
|
|
101
101
|
* (プレビュー = shell captionTextStyleVars / 書き出し = render-cut captions.mjs の両消費者が
|
|
102
102
|
* これを使う — 2026-08-26 akari-reel 実機: プレビュー側だけ text_anchor/position を落として
|
|
103
103
|
* 明示位置付き字幕が既定の下段 7% に出る「出力とプレビューの位置不一致」の再発防止)。
|
|
104
|
-
* position 未指定なら anchor は zone 相当の縁寄せとして効く。position 指定時は
|
|
105
|
-
*
|
|
104
|
+
* position 未指定なら anchor は zone 相当の縁寄せとして効く。position.y 指定時は
|
|
105
|
+
* b は下端、t は上端、m は中心をその座標へ合わせる。
|
|
106
106
|
* 不正な anchor / vertical_align は未宣言として無視する(書き込み時検証済みが前提の防御)。
|
|
107
107
|
*/
|
|
108
108
|
export declare function captionAnchorPositionVars(anchorValue: unknown, positionValue: unknown, verticalAlignValue: unknown): Record<string, string>;
|
|
@@ -720,8 +720,8 @@ function mergeCaptionDisplayStyles(base, override) {
|
|
|
720
720
|
* (プレビュー = shell captionTextStyleVars / 書き出し = render-cut captions.mjs の両消費者が
|
|
721
721
|
* これを使う — 2026-08-26 akari-reel 実機: プレビュー側だけ text_anchor/position を落として
|
|
722
722
|
* 明示位置付き字幕が既定の下段 7% に出る「出力とプレビューの位置不一致」の再発防止)。
|
|
723
|
-
* position 未指定なら anchor は zone 相当の縁寄せとして効く。position 指定時は
|
|
724
|
-
*
|
|
723
|
+
* position 未指定なら anchor は zone 相当の縁寄せとして効く。position.y 指定時は
|
|
724
|
+
* b は下端、t は上端、m は中心をその座標へ合わせる。
|
|
725
725
|
* 不正な anchor / vertical_align は未宣言として無視する(書き込み時検証済みが前提の防御)。
|
|
726
726
|
*/
|
|
727
727
|
function captionAnchorPositionVars(anchorValue, positionValue, verticalAlignValue) {
|
|
@@ -739,8 +739,17 @@ function captionAnchorPositionVars(anchorValue, positionValue, verticalAlignValu
|
|
|
739
739
|
const horizontal = anchor ? anchor[1] : 'c';
|
|
740
740
|
if (typeof position?.y === 'number' && Number.isFinite(position.y)) {
|
|
741
741
|
const clamped = Math.min(1, Math.max(0, position.y));
|
|
742
|
-
|
|
743
|
-
|
|
742
|
+
if ((anchor || verticalAlign) && vertical === 'b') {
|
|
743
|
+
vars['--caption-top'] = 'auto';
|
|
744
|
+
vars['--caption-bottom'] = `${Math.round((1 - clamped) * 10000) / 100}%`;
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
vars['--caption-top'] = `${Math.round(clamped * 10000) / 100}%`;
|
|
748
|
+
vars['--caption-bottom'] = 'auto';
|
|
749
|
+
if ((anchor || verticalAlign) && vertical === 'm') {
|
|
750
|
+
vars['--caption-translate'] = '0 -50%';
|
|
751
|
+
}
|
|
752
|
+
}
|
|
744
753
|
}
|
|
745
754
|
else if (anchor || verticalAlign) {
|
|
746
755
|
vars['--caption-top'] = vertical === 't' ? '7%' : vertical === 'm' ? '0' : 'auto';
|
|
@@ -25,6 +25,8 @@ export interface ProjectedItemTiming {
|
|
|
25
25
|
at: number;
|
|
26
26
|
duration: number;
|
|
27
27
|
}
|
|
28
|
+
export type KeyframeProperty = 'transform.x' | 'transform.y' | 'transform.scale' | 'transform.rotate' | 'opacity' | 'crop' | 'perspective';
|
|
29
|
+
export type SegmentEasing = string;
|
|
28
30
|
export interface ConvertCaptionToTelopOptions {
|
|
29
31
|
preset?: string;
|
|
30
32
|
text: string;
|
|
@@ -60,6 +62,16 @@ export interface ItemLocation {
|
|
|
60
62
|
}
|
|
61
63
|
export declare function attachEditHelpers(edit: EditableEditV2): void;
|
|
62
64
|
export declare function updateItem(edit: EditableEditV2, id: string, patch: JsonRecord): ProjectItemV2;
|
|
65
|
+
/** inline 点列へ値を打つ。最初の点では反対側の端にも同じ値を置き minItems:2 を守る。 */
|
|
66
|
+
export declare function setKeyframe(edit: EditableEditV2, id: string, property: KeyframeProperty, t: number, value: unknown): ProjectItemV2;
|
|
67
|
+
/** 指定プロパティの点だけを消し、空点を除去する。2 点未満なら点列自体を外す。 */
|
|
68
|
+
export declare function removeKeyframe(edit: EditableEditV2, id: string, property: KeyframeProperty, t: number): ProjectItemV2;
|
|
69
|
+
/** 1 プロパティの点を別時刻へ移す。同時刻の既存点があれば値をマージする。 */
|
|
70
|
+
export declare function moveKeyframe(edit: EditableEditV2, id: string, property: KeyframeProperty, fromT: number, toT: number): ProjectItemV2;
|
|
71
|
+
/** easing は「その点へ入る区間」に置く。プロパティ別指定は object 形へ正規化する。 */
|
|
72
|
+
export declare function setSegmentEasing(edit: EditableEditV2, id: string, property: KeyframeProperty, toT: number, easing: SegmentEasing): ProjectItemV2;
|
|
73
|
+
/** motion 袋を読んだ UI が参照形を inline へ戻すための純関数。 */
|
|
74
|
+
export declare function hydrateKeyframes(edit: EditableEditV2, id: string, points: readonly KeyframeV2[]): ProjectItemV2;
|
|
63
75
|
export declare function moveItem(edit: EditableEditV2, id: string, target: MoveTarget): ProjectItemV2;
|
|
64
76
|
export declare function insertItem(edit: EditableEditV2, target: string, item: MutableItem, index?: number): ProjectItemV2;
|
|
65
77
|
export declare function removeItem(edit: EditableEditV2, id: string): ProjectItemV2;
|
|
@@ -3,6 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DEFAULT_CAPTION_TELOP_PRESET = void 0;
|
|
4
4
|
exports.attachEditHelpers = attachEditHelpers;
|
|
5
5
|
exports.updateItem = updateItem;
|
|
6
|
+
exports.setKeyframe = setKeyframe;
|
|
7
|
+
exports.removeKeyframe = removeKeyframe;
|
|
8
|
+
exports.moveKeyframe = moveKeyframe;
|
|
9
|
+
exports.setSegmentEasing = setSegmentEasing;
|
|
10
|
+
exports.hydrateKeyframes = hydrateKeyframes;
|
|
6
11
|
exports.moveItem = moveItem;
|
|
7
12
|
exports.insertItem = insertItem;
|
|
8
13
|
exports.removeItem = removeItem;
|
|
@@ -29,6 +34,12 @@ exports.composeTransforms = composeTransforms;
|
|
|
29
34
|
exports.relativeTransform = relativeTransform;
|
|
30
35
|
exports.ensureChildren = ensureChildren;
|
|
31
36
|
exports.clone = clone;
|
|
37
|
+
const SEGMENT_EASINGS = new Set([
|
|
38
|
+
'linear', 'ease-in-out', 'in-quad', 'out-quad', 'in-out-quad',
|
|
39
|
+
'in-cubic', 'out-cubic', 'in-out-cubic', 'in-quart', 'out-quart', 'in-out-quart',
|
|
40
|
+
'in-expo', 'out-expo', 'in-out-expo', 'in-back', 'out-back', 'in-out-back',
|
|
41
|
+
'out-bounce', 'out-elastic', 'hold'
|
|
42
|
+
]);
|
|
32
43
|
// 助詞ミニマは通常字幕に近く、ワードスナップほど演出を強くしないため変換の既定にする。
|
|
33
44
|
exports.DEFAULT_CAPTION_TELOP_PRESET = 'ref3_particle_min';
|
|
34
45
|
function attachEditHelpers(edit) {
|
|
@@ -63,6 +74,100 @@ function updateItem(edit, id, patch) {
|
|
|
63
74
|
}
|
|
64
75
|
return location.item;
|
|
65
76
|
}
|
|
77
|
+
/** inline 点列へ値を打つ。最初の点では反対側の端にも同じ値を置き minItems:2 を守る。 */
|
|
78
|
+
function setKeyframe(edit, id, property, t, value) {
|
|
79
|
+
const item = requireLocation(edit, id).item;
|
|
80
|
+
const time = requireKeyframeTime(t, item.duration);
|
|
81
|
+
const points = editableKeyframes(item);
|
|
82
|
+
if (points.length === 0) {
|
|
83
|
+
const opposite = time === 0 ? item.duration : 0;
|
|
84
|
+
points.push(pointWithValue(time, property, value), pointWithValue(opposite, property, value));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const point = points.find(candidate => candidate.t === time);
|
|
88
|
+
if (point)
|
|
89
|
+
assignKeyframeValue(point, property, value);
|
|
90
|
+
else
|
|
91
|
+
points.push(pointWithValue(time, property, value));
|
|
92
|
+
}
|
|
93
|
+
item.keyframes = normalizeKeyframes(points);
|
|
94
|
+
return item;
|
|
95
|
+
}
|
|
96
|
+
/** 指定プロパティの点だけを消し、空点を除去する。2 点未満なら点列自体を外す。 */
|
|
97
|
+
function removeKeyframe(edit, id, property, t) {
|
|
98
|
+
const item = requireLocation(edit, id).item;
|
|
99
|
+
const points = editableKeyframes(item);
|
|
100
|
+
const point = points.find(candidate => candidate.t === t);
|
|
101
|
+
if (!point)
|
|
102
|
+
return item;
|
|
103
|
+
deleteKeyframeValue(point, property);
|
|
104
|
+
const remaining = points.filter(hasKeyframeValue);
|
|
105
|
+
if (remaining.length < 2)
|
|
106
|
+
delete item.keyframes;
|
|
107
|
+
else
|
|
108
|
+
item.keyframes = normalizeKeyframes(remaining);
|
|
109
|
+
return item;
|
|
110
|
+
}
|
|
111
|
+
/** 1 プロパティの点を別時刻へ移す。同時刻の既存点があれば値をマージする。 */
|
|
112
|
+
function moveKeyframe(edit, id, property, fromT, toT) {
|
|
113
|
+
const item = requireLocation(edit, id).item;
|
|
114
|
+
const targetTime = requireKeyframeTime(toT, item.duration);
|
|
115
|
+
const points = editableKeyframes(item);
|
|
116
|
+
const source = points.find(point => point.t === fromT);
|
|
117
|
+
const value = source ? keyframeValue(source, property) : undefined;
|
|
118
|
+
if (!source || value === undefined)
|
|
119
|
+
throw new Error(`キーフレームが見つかりません: ${id} ${property} t=${fromT}`);
|
|
120
|
+
const easing = source.easing;
|
|
121
|
+
deleteKeyframeValue(source, property);
|
|
122
|
+
let target = points.find(point => point.t === targetTime);
|
|
123
|
+
if (!target) {
|
|
124
|
+
target = { t: targetTime };
|
|
125
|
+
points.push(target);
|
|
126
|
+
}
|
|
127
|
+
assignKeyframeValue(target, property, value);
|
|
128
|
+
if (easing !== undefined && target.easing === undefined)
|
|
129
|
+
target.easing = clone(easing);
|
|
130
|
+
const remaining = points.filter(hasKeyframeValue);
|
|
131
|
+
if (remaining.length < 2)
|
|
132
|
+
throw new Error('キーフレームは 2 点以上必要です。');
|
|
133
|
+
item.keyframes = normalizeKeyframes(remaining);
|
|
134
|
+
return item;
|
|
135
|
+
}
|
|
136
|
+
/** easing は「その点へ入る区間」に置く。プロパティ別指定は object 形へ正規化する。 */
|
|
137
|
+
function setSegmentEasing(edit, id, property, toT, easing) {
|
|
138
|
+
requireSegmentEasing(easing);
|
|
139
|
+
const item = requireLocation(edit, id).item;
|
|
140
|
+
const points = editableKeyframes(item);
|
|
141
|
+
const index = points.findIndex(point => point.t === toT);
|
|
142
|
+
if (index <= 0 || keyframeValue(points[index], property) === undefined) {
|
|
143
|
+
throw new Error('イージングを設定する区間が見つかりません。');
|
|
144
|
+
}
|
|
145
|
+
const point = points[index];
|
|
146
|
+
const declared = keyframeProperties(point);
|
|
147
|
+
if (declared.length <= 1) {
|
|
148
|
+
point.easing = easing;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const previous = typeof point.easing === 'string' ? point.easing : 'linear';
|
|
152
|
+
const perProperty = isRecord(point.easing) ? clone(point.easing) : {};
|
|
153
|
+
for (const declaredProperty of declared) {
|
|
154
|
+
if (!(declaredProperty in perProperty))
|
|
155
|
+
perProperty[declaredProperty] = previous;
|
|
156
|
+
}
|
|
157
|
+
perProperty[property] = easing;
|
|
158
|
+
point.easing = perProperty;
|
|
159
|
+
}
|
|
160
|
+
item.keyframes = normalizeKeyframes(points);
|
|
161
|
+
return item;
|
|
162
|
+
}
|
|
163
|
+
/** motion 袋を読んだ UI が参照形を inline へ戻すための純関数。 */
|
|
164
|
+
function hydrateKeyframes(edit, id, points) {
|
|
165
|
+
if (points.length > 0 && points.length < 2)
|
|
166
|
+
throw new Error('キーフレームは 2 点以上必要です。');
|
|
167
|
+
const item = requireLocation(edit, id).item;
|
|
168
|
+
item.keyframes = normalizeKeyframes(points.map(point => clone(point)));
|
|
169
|
+
return item;
|
|
170
|
+
}
|
|
66
171
|
function moveItem(edit, id, target) {
|
|
67
172
|
if ((target.track === undefined) === (target.parent === undefined)) {
|
|
68
173
|
throw new Error('move の置き先は track または parent のどちらか一方で指定してください。');
|
|
@@ -457,6 +562,85 @@ function ensureChildren(item, create = true) {
|
|
|
457
562
|
function clone(value) {
|
|
458
563
|
return structuredClone(value);
|
|
459
564
|
}
|
|
565
|
+
function editableKeyframes(item) {
|
|
566
|
+
if (item.keyframes === undefined)
|
|
567
|
+
return [];
|
|
568
|
+
if (!Array.isArray(item.keyframes)) {
|
|
569
|
+
throw new Error('motion 袋を inline に戻してからキーフレームを編集してください。');
|
|
570
|
+
}
|
|
571
|
+
return item.keyframes.map(point => clone(point));
|
|
572
|
+
}
|
|
573
|
+
function requireSegmentEasing(value) {
|
|
574
|
+
const cubic = /^cubic-bezier\(\s*-?\d*\.?\d+\s*,\s*-?\d*\.?\d+\s*,\s*-?\d*\.?\d+\s*,\s*-?\d*\.?\d+\s*\)$/u;
|
|
575
|
+
if (!SEGMENT_EASINGS.has(value) && !cubic.test(value))
|
|
576
|
+
throw new Error(`未対応の easing です: ${value}`);
|
|
577
|
+
}
|
|
578
|
+
function requireKeyframeTime(t, duration) {
|
|
579
|
+
if (!Number.isInteger(t) || t < 0 || t > duration) {
|
|
580
|
+
throw new Error(`キーフレーム時刻は 0〜${duration} の整数フレームで指定してください。`);
|
|
581
|
+
}
|
|
582
|
+
return t;
|
|
583
|
+
}
|
|
584
|
+
function normalizeKeyframes(points) {
|
|
585
|
+
const result = points.map(point => clone(point)).sort((left, right) => left.t - right.t);
|
|
586
|
+
for (let index = 1; index < result.length; index++) {
|
|
587
|
+
if (result[index - 1].t === result[index].t)
|
|
588
|
+
throw new Error('同じ時刻にキーフレームを重ねられません。');
|
|
589
|
+
}
|
|
590
|
+
return result;
|
|
591
|
+
}
|
|
592
|
+
function pointWithValue(t, property, value) {
|
|
593
|
+
const point = { t };
|
|
594
|
+
assignKeyframeValue(point, property, value);
|
|
595
|
+
return point;
|
|
596
|
+
}
|
|
597
|
+
function assignKeyframeValue(point, property, value) {
|
|
598
|
+
if (property.startsWith('transform.')) {
|
|
599
|
+
const key = property.slice('transform.'.length);
|
|
600
|
+
point.transform = { ...(point.transform ?? {}), [key]: clone(value) };
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
point[property] = clone(value);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function deleteKeyframeValue(point, property) {
|
|
607
|
+
if (property.startsWith('transform.')) {
|
|
608
|
+
const key = property.slice('transform.'.length);
|
|
609
|
+
if (point.transform) {
|
|
610
|
+
delete point.transform[key];
|
|
611
|
+
if (Object.keys(point.transform).length === 0)
|
|
612
|
+
delete point.transform;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
delete point[property];
|
|
617
|
+
}
|
|
618
|
+
if (isRecord(point.easing)) {
|
|
619
|
+
delete point.easing[property];
|
|
620
|
+
if (Object.keys(point.easing).length === 0)
|
|
621
|
+
delete point.easing;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
function keyframeValue(point, property) {
|
|
625
|
+
if (!property.startsWith('transform.'))
|
|
626
|
+
return point[property];
|
|
627
|
+
return point.transform?.[property.slice('transform.'.length)];
|
|
628
|
+
}
|
|
629
|
+
function keyframeProperties(point) {
|
|
630
|
+
const result = [];
|
|
631
|
+
for (const property of ['transform.x', 'transform.y', 'transform.scale', 'transform.rotate']) {
|
|
632
|
+
if (keyframeValue(point, property) !== undefined)
|
|
633
|
+
result.push(property);
|
|
634
|
+
}
|
|
635
|
+
for (const property of ['opacity', 'crop', 'perspective']) {
|
|
636
|
+
if (keyframeValue(point, property) !== undefined)
|
|
637
|
+
result.push(property);
|
|
638
|
+
}
|
|
639
|
+
return result;
|
|
640
|
+
}
|
|
641
|
+
function hasKeyframeValue(point) {
|
|
642
|
+
return keyframeProperties(point).length > 0 || point.animator !== undefined;
|
|
643
|
+
}
|
|
460
644
|
function requireLocation(edit, id) {
|
|
461
645
|
const location = locate(edit, id);
|
|
462
646
|
if (!location)
|
|
@@ -983,8 +983,16 @@ var AkariEditKernel = (() => {
|
|
|
983
983
|
const horizontal = anchor ? anchor[1] : "c";
|
|
984
984
|
if (typeof position?.y === "number" && Number.isFinite(position.y)) {
|
|
985
985
|
const clamped = Math.min(1, Math.max(0, position.y));
|
|
986
|
-
|
|
987
|
-
|
|
986
|
+
if ((anchor || verticalAlign) && vertical === "b") {
|
|
987
|
+
vars["--caption-top"] = "auto";
|
|
988
|
+
vars["--caption-bottom"] = `${Math.round((1 - clamped) * 1e4) / 100}%`;
|
|
989
|
+
} else {
|
|
990
|
+
vars["--caption-top"] = `${Math.round(clamped * 1e4) / 100}%`;
|
|
991
|
+
vars["--caption-bottom"] = "auto";
|
|
992
|
+
if ((anchor || verticalAlign) && vertical === "m") {
|
|
993
|
+
vars["--caption-translate"] = "0 -50%";
|
|
994
|
+
}
|
|
995
|
+
}
|
|
988
996
|
} else if (anchor || verticalAlign) {
|
|
989
997
|
vars["--caption-top"] = vertical === "t" ? "7%" : vertical === "m" ? "0" : "auto";
|
|
990
998
|
vars["--caption-bottom"] = vertical === "b" ? "7%" : vertical === "m" ? "0" : "auto";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Shell-agnostic overlay DOM runtime: mount/tick of edit.json overlays (#overlay-stage), pointer-driven select/drag/resize/edit interaction layer, and zoom minimap. Host-agnostic — see README.md for the window.akari.* adapter contract a hosting shell must provide.",
|
|
6
6
|
"main": "src/overlay-runtime.js",
|
|
7
|
-
"exports": { ".": "./src/overlay-runtime.js", "./parts": { "types": "./src/parts.d.ts", "import": "./src/parts.mjs", "require": "./src/parts.mjs" } },
|
|
7
|
+
"exports": { ".": "./src/overlay-runtime.js", "./parts": { "types": "./src/parts.d.ts", "import": "./src/parts.mjs", "require": "./src/parts.mjs" }, "./keyframes": { "types": "./src/keyframes.d.ts", "import": "./src/keyframes.mjs" } },
|
|
8
8
|
"files": [
|
|
9
9
|
"src/",
|
|
10
10
|
"docs/",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"textAnchor": {
|
|
77
77
|
"type": "string",
|
|
78
78
|
"enum": ["tl", "tc", "tr", "ml", "mc", "mr", "bl", "bc", "br"],
|
|
79
|
-
"description": "9 点アンカー。1 文字目が縦(t/m/b)、2 文字目が横(l/c/r)。"
|
|
79
|
+
"description": "9 点アンカー。1 文字目が縦(t/m/b)、2 文字目が横(l/c/r)。position.y 指定時の縦成分は b = 下端基準、t = 上端基準、m = 中心基準。"
|
|
80
80
|
},
|
|
81
81
|
"textPosition": {
|
|
82
82
|
"type": "object",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"x": { "type": "number" },
|
|
86
86
|
"y": { "type": "number" }
|
|
87
87
|
},
|
|
88
|
-
"description": "フレーム比 0..1 の配置座標。指定した軸だけが効き、未指定の軸は zone / text_anchor に従う。"
|
|
88
|
+
"description": "フレーム比 0..1 の配置座標。指定した軸だけが効き、未指定の軸は zone / text_anchor に従う。position.y 指定時の text_anchor 縦成分は b = 下端基準、t = 上端基準、m = 中心基準。"
|
|
89
89
|
},
|
|
90
90
|
"textShadowStyle": {
|
|
91
91
|
"type": "object",
|