@volter/editor-blender 0.1.150 → 0.1.152
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/LICENSE +18 -9
- package/README.md +5 -5
- package/contributions/blender-animation-editors.tsx +17 -69
- package/contributions/blender-display-settings.ts +36 -0
- package/contributions/blender-export-gltf.ts +228 -0
- package/contributions/blender-export.verbs.ts +39 -0
- package/contributions/blender-gltf-export.verbs.ts +312 -0
- package/contributions/blender-header-menus.tsx +37 -133
- package/contributions/blender-icon-trace.mjs +8 -98
- package/contributions/blender-icons.source.mjs +96 -860
- package/contributions/blender-mixer-pose.ts +369 -0
- package/contributions/blender-model-opening.tsx +26 -56
- package/contributions/blender-node-editor.document.tsx +30 -181
- package/contributions/blender-node-geometry.ts +44 -230
- package/contributions/blender-node-panels.source.mjs +7 -121
- package/contributions/blender-outliner-authoring.ts +61 -415
- package/contributions/blender-outliner-model.ts +15 -112
- package/contributions/blender-play-area.tsx +99 -0
- package/contributions/blender-playback-movie.ts +409 -0
- package/contributions/blender-pose.ts +18 -368
- package/contributions/blender-properties-context.ts +1 -1
- package/contributions/blender-properties-model.ts +59 -91
- package/contributions/blender-properties-tab.tsx +3 -50
- package/contributions/blender-properties-view.tsx +39 -181
- package/contributions/blender-remedy.ts +14 -0
- package/contributions/blender-runtime-skin.ts +103 -99
- package/contributions/blender-runtime.document.tsx +170 -618
- package/contributions/blender-scene-mixer.ts +51 -0
- package/contributions/blender-screenshot.service.ts +28 -0
- package/contributions/blender-timeline-geometry.ts +16 -136
- package/contributions/blender-timeline.document.tsx +145 -333
- package/contributions/blender-uv-editor.document.tsx +48 -112
- package/contributions/blender-uv-geometry.ts +14 -179
- package/contributions/blender-version.status.tsx +45 -31
- package/contributions/blender.environment.ts +2 -2
- package/contributions/blender.icons.json +0 -9
- package/contributions/blender.keymap.ts +9 -51
- package/contributions/blender.node-panels.json +4 -13
- package/contributions/blender.page.ts +64 -0
- package/contributions/blender.status.tsx +8 -150
- package/contributions/blender.style.ts +25 -152
- package/contributions/blender.verbs.ts +52 -0
- package/contributions/model.layout.ts +2 -34
- package/contributions/models.finder.ts +3 -30
- package/contributions/properties-bone-constraints.inspector.tsx +1 -16
- package/contributions/properties-bone.inspector.tsx +2 -31
- package/contributions/properties-collection.inspector.tsx +1 -34
- package/contributions/properties-constraints.inspector.tsx +1 -21
- package/contributions/properties-data.inspector.tsx +2 -39
- package/contributions/properties-material.inspector.tsx +2 -33
- package/contributions/properties-modifiers.inspector.tsx +2 -30
- package/contributions/properties-object.inspector.tsx +2 -44
- package/contributions/properties-output.inspector.tsx +1 -27
- package/contributions/properties-particles.inspector.tsx +2 -46
- package/contributions/properties-physics.inspector.tsx +2 -41
- package/contributions/properties-render.inspector.tsx +1 -45
- package/contributions/properties-scene.inspector.tsx +1 -35
- package/contributions/properties-texture.inspector.tsx +1 -49
- package/contributions/properties-view-layer.inspector.tsx +1 -29
- package/contributions/properties-world.inspector.tsx +2 -39
- package/contributions/shading.layout.ts +3 -78
- package/contributions/uv-editing.layout.ts +3 -70
- package/dist-node/serving.mjs +13 -32
- package/host/blender-runtime-host.ts +795 -412
- package/host/scene-sound.ts +143 -0
- package/node/blender-mcp.ts +52 -95
- package/package.json +1221 -46
- package/producticons/blender-icons.otf +0 -0
- package/producticons/blender-product-icon-theme.json +129 -0
- package/serving/blender-routes.ts +9 -48
- package/serving/blender-wasm-artifact.ts +29 -166
- package/serving/index.ts +1 -1
- package/src/api.ts +9 -0
- package/src/layouts.tsx +1 -17
- package/src/looks.ts +1 -12
- package/src/model-document-saving.ts +25 -0
- package/src/node-view-state.ts +3 -25
- package/src/play-mode.ts +13 -42
- package/src/play-tool.ts +44 -0
- package/src/presentation.ts +6 -18
- package/src/regions.ts +3 -43
- package/src/start-document.ts +15 -0
- package/src/timeline-view-state.ts +4 -39
- package/src/uv-view-state.ts +4 -20
- package/themes/blender.json +299 -0
- package/view/blender-view-routes.ts +1 -1
- package/contributions/blender-game-panel.tsx +0 -809
- package/contributions/blender-palette.source.mjs +0 -350
- package/contributions/blender-play-skin.ts +0 -508
- package/contributions/blender-screenshot.menu.ts +0 -32
- package/contributions/blender.command.ts +0 -109
- package/contributions/blender.palette.json +0 -102
- package/src/play-live.ts +0 -27
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ONE MIXER PER PRESENTED SCENE (`docs/SCENE-ANIMATION.md`, steps 3 and 4).
|
|
3
|
+
*
|
|
4
|
+
* Every armature's layers (`blender-mixer-pose.ts`, each bound to its own rig so bones of the same
|
|
5
|
+
* name in two characters never meet) and the scene's movie (`blender-playback-movie.ts`, its objects and
|
|
6
|
+
* cameras) are actions on ONE `THREE.AnimationMixer` rooted at the view's root, on one clock. A
|
|
7
|
+
* frame is placed in three steps for the Timeline preview:
|
|
8
|
+
*
|
|
9
|
+
* 1. PLACE: each player sets its actions' times and weights for the frame (`place`).
|
|
10
|
+
* 2. EVALUATE: the mixer applies every enabled action at once (`evaluate`), once per frame however
|
|
11
|
+
* many characters there are.
|
|
12
|
+
* 3. SETTLE: what the mixer does not do on its own, after it (`settle`): Damped Track and Track To,
|
|
13
|
+
* and the fidelity check against Blender's own pose.
|
|
14
|
+
*
|
|
15
|
+
* An action left enabled keeps writing its last placement every evaluation, so a player that is not
|
|
16
|
+
* placed this frame (the movie while the Timeline stands at Blender's own frame) disables its actions
|
|
17
|
+
* rather than leaving them behind.
|
|
18
|
+
*/
|
|
19
|
+
import * as THREE from 'three';
|
|
20
|
+
|
|
21
|
+
export class SceneMixer {
|
|
22
|
+
readonly mixer: THREE.AnimationMixer;
|
|
23
|
+
|
|
24
|
+
constructor(readonly root: THREE.Object3D) {
|
|
25
|
+
this.mixer = new THREE.AnimationMixer(root);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Apply every placed action, all at once. */
|
|
29
|
+
evaluate(): void {
|
|
30
|
+
this.mixer.update(0);
|
|
31
|
+
this.root.updateMatrixWorld(true);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
dispose(): void {
|
|
35
|
+
this.mixer.stopAllAction();
|
|
36
|
+
this.mixer.uncacheRoot(this.root);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const mixers = new WeakMap<object, SceneMixer>();
|
|
41
|
+
|
|
42
|
+
/** The one mixer of a presented view: made on first use,
|
|
43
|
+
* and made again when the view's root is a new object (a new present). */
|
|
44
|
+
export function sceneMixer(view: { readonly root: THREE.Object3D }): SceneMixer {
|
|
45
|
+
const held = mixers.get(view);
|
|
46
|
+
if (held && held.root === view.root) return held;
|
|
47
|
+
held?.dispose();
|
|
48
|
+
const made = new SceneMixer(view.root);
|
|
49
|
+
mixers.set(view, made);
|
|
50
|
+
return made;
|
|
51
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Save Screenshot (Editor)… — Blender's own Window menu row (`TOPBAR_MT_window`,
|
|
3
|
+
* `screen.screenshot_area`), the person's door to the photograph an agent takes with
|
|
4
|
+
* `get_viewport_screenshot`. It is this package's command `volter.blender.saveScreenshotEditor`
|
|
5
|
+
* (`contributes.commands`, in the palette); this runs it.
|
|
6
|
+
*
|
|
7
|
+
* Blender asks for a path in its file browser; the files door has no file picker, so the picture
|
|
8
|
+
* goes to the project's `screenshots/` folder and a notification says which file it is. Like every
|
|
9
|
+
* photograph of the document it flashes the document and shows itself in the corner.
|
|
10
|
+
*/
|
|
11
|
+
import { bytesFromBase64 } from '@volter/blender-engine/browser/three/blender-base64';
|
|
12
|
+
import { editorHost } from '@volter/sdk/host';
|
|
13
|
+
import { announcePhotograph } from '@volter/sdk/photograph-notice';
|
|
14
|
+
|
|
15
|
+
export const point = 'workspace.service';
|
|
16
|
+
|
|
17
|
+
async function saveScreenshot(): Promise<void> {
|
|
18
|
+
const host = editorHost();
|
|
19
|
+
const capture = await host.documents.captureActive();
|
|
20
|
+
announcePhotograph(capture.base64);
|
|
21
|
+
const path = `screenshots/screenshot-${new Date().toISOString().replace(/[:.]/g, '-')}.png`;
|
|
22
|
+
await host.files.write(path, bytesFromBase64(capture.base64));
|
|
23
|
+
host.notify({ id: 'blender-screenshot', tone: 'info', title: 'Saved the screenshot', detail: path });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function start(): () => void {
|
|
27
|
+
return editorHost().keyboard.bindActions([{ id: 'blender.saveScreenshotEditor', run: () => void saveScreenshot() }]);
|
|
28
|
+
}
|
|
@@ -1,54 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* THE TIMELINE'S CONSTANTS — every one read from Blender's source at the
|
|
3
|
-
* engine's pin (5.2.0, `fbe6228777e7`) AND checked against a PIXEL in Blender's
|
|
4
|
-
* own frame (5.2.1 on this box, `scripts/blender-reference-frames.py`).
|
|
5
|
-
*
|
|
6
|
-
* This is the first unit of the inspection arc with a sighted read behind it.
|
|
7
|
-
* I1-I5 each recorded that the reference frames were not at hand, so each was graded
|
|
8
|
-
* against Blender's SOURCE alone; Blender is installed here, the script above
|
|
9
|
-
* drives its real GUI, and the numbers below carry three things each: the
|
|
10
|
-
* source that defines them, the pixel that confirms them, and what we draw.
|
|
11
|
-
*
|
|
12
|
-
* ## The screenshot reads ONE 8-BIT LEVEL BELOW the theme byte
|
|
13
|
-
*
|
|
14
|
-
* Measured on every flat fill in the frame: the theme's `#303030` reads
|
|
15
|
-
* `#2f2f2f`, `#1d1d1d` reads `#1c1c1c`, `#161616` reads `#151515`,
|
|
16
|
-
* `#4772b3` reads `#4671b2`, `#ffbe33` reads `#ffbd32`. It is uniform and it
|
|
17
|
-
* is the screenshot path's, not the theme's — so the values below are the
|
|
18
|
-
* THEME BYTES (what Blender means) and a reader comparing them against a
|
|
19
|
-
* reference PNG should expect that −1.
|
|
20
|
-
*
|
|
21
|
-
* ## The theme's own table disagrees with the source's default in one place,
|
|
22
|
-
* and the running theme wins
|
|
23
|
-
*
|
|
24
|
-
* `userdef_default_theme.c:490` declares `.space_action.back` as
|
|
25
|
-
* `RGBA(0x30303000)` — alpha ZERO. Read off the RUNNING factory theme
|
|
26
|
-
* (`bpy.context.preferences.themes[0].dopesheet_editor.space.back`) it is
|
|
27
|
-
* `#303030ff`. The difference is load-bearing: `ANIM_draw_framerange` paints
|
|
28
|
-
* the out-of-range area with `TH_BACK` shaded −25 at alpha −100, which at
|
|
29
|
-
* alpha 0 would draw NOTHING and at alpha 255 draws a visible darkening. The
|
|
30
|
-
* frame settles it — outside the range is `#202020` against `#2f2f2f` inside,
|
|
31
|
-
* which is exactly `48 + (23 − 48) × 155/255 = 32.8`. The screenshot wins.
|
|
32
|
-
*/
|
|
1
|
+
/** Timeline geometry and theme constants follow Blender at the engine pin. Use the running factory theme’s opaque space_action.back, which controls out-of-range shading. */
|
|
33
2
|
|
|
34
|
-
/**
|
|
35
|
-
* pixelsize` = 20 at scale 1. Confirmed in the frame: a transport button
|
|
36
|
-
* block of six is 120 px wide (x 697…816), so each is 20. */
|
|
3
|
+
/** U.widget_unit is 20 at scale 1 (wm_window.cc:779). */
|
|
37
4
|
export const WIDGET_UNIT = 20;
|
|
38
5
|
|
|
39
|
-
/**
|
|
40
|
-
* strip the ruler and the playhead's number pill live in. CONFIRMED: the
|
|
41
|
-
* Timeline area's `.regions.scrubbing.back` band is exactly 23 px tall
|
|
42
|
-
* (image rows 26…48 under a 26-px header). */
|
|
6
|
+
/** UI_TIME_SCRUB_MARGIN_Y is 23 × UI_SCALE_FAC (UI_view2d.hh:491). */
|
|
43
7
|
export const SCRUB_HEIGHT = 23;
|
|
44
8
|
|
|
45
|
-
/**
|
|
46
|
-
* at 26 px, with its 20-px widgets centred (3 px above, 3 below). */
|
|
9
|
+
/** HEADERY is 20 + HEADER_PADDING_Y = 26 (DNA_screen_types.h:602). */
|
|
47
10
|
export const HEADER_HEIGHT = 26;
|
|
48
11
|
|
|
49
|
-
/**
|
|
50
|
-
* `0.8 * keyframe_scale_fac * widget_unit`; `keyframe_scale_fac` is 1.0
|
|
51
|
-
* (`userdef_default_theme.c:508`, and read back 1.0 off the running theme). */
|
|
12
|
+
/** Channel height is 0.8 × keyframe_scale_fac × widget_unit; the theme scale is 1 (anim_channels_defines.cc:5147-5150). */
|
|
52
13
|
export const CHANNEL_HEIGHT = 16;
|
|
53
14
|
/** `ANIM_UI_get_channel_skip` — `:5152-5155`, `0.1 * widget_unit`. */
|
|
54
15
|
export const CHANNEL_SKIP = 2;
|
|
@@ -56,24 +17,6 @@ export const CHANNEL_SKIP = 2;
|
|
|
56
17
|
* `-UI_TIME_SCRUB_MARGIN_Y - skip` below the region's top edge. */
|
|
57
18
|
export const FIRST_CHANNEL_TOP = SCRUB_HEIGHT + CHANNEL_SKIP;
|
|
58
19
|
|
|
59
|
-
/**
|
|
60
|
-
* THE TIMELINE DRAWS EXACTLY ONE CHANNEL ROW, and that is a source fact rather
|
|
61
|
-
* than a layout choice: `action_create` sets `ADS_FLAG_SUMMARY_COLLAPSED` when
|
|
62
|
-
* the area's subtype is `SACTCONT_TIMELINE` (`space_action.cc:80-83`), and
|
|
63
|
-
* `animdata_filter_dopesheet` returns 0 right after adding the summary channel
|
|
64
|
-
* when that flag is set (`anim_filter.cc:3845-3849`). The channel-NAMES region
|
|
65
|
-
* is not drawn at all in Timeline mode
|
|
66
|
-
* (`action_region_poll_hide_in_timeline`, `space_action.cc:785-790`).
|
|
67
|
-
*
|
|
68
|
-
* THE ROW HAS NO BACKDROP EITHER. `draw_backdrops` branches on
|
|
69
|
-
* `ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET, ANIMCONT_SHAPEKEY)`
|
|
70
|
-
* and then on GPENCIL and MASK (`action_draw.cc:218-300`); `ANIMCONT_TIMELINE`
|
|
71
|
-
* matches none of them, so the summary row is drawn straight onto the region's
|
|
72
|
-
* own clear. CONFIRMED: the row is `#2f2f2f` inside the frame range, the same
|
|
73
|
-
* value as the rest of the region.
|
|
74
|
-
*/
|
|
75
|
-
export const TIMELINE_ROWS = 1;
|
|
76
|
-
|
|
77
20
|
/** `MIN_MAJOR_LINE_DISTANCE` — `view2d_draw.cc:41`, `U.v2d_min_gridsize *
|
|
78
21
|
* UI_SCALE_FAC`, default 35 (`versioning_userdef.cc:1028-1029`). The real
|
|
79
22
|
* minimum is `max(35, label width + 6)` (`get_min_line_distance_x`, `:485`,
|
|
@@ -152,27 +95,20 @@ export function minorStep(major: number): number | null {
|
|
|
152
95
|
* running theme is what draws, and the disagreement is named.
|
|
153
96
|
*/
|
|
154
97
|
export const TIMELINE_THEME = {
|
|
155
|
-
/**
|
|
156
|
-
* `0x30303000`; the RUNNING theme is `#303030ff` (see the module header).
|
|
157
|
-
* The region's clear, `frame_buffer_clear(TH_BACK)`. */
|
|
98
|
+
/** space_action.back is #303030ff in the running factory theme; frame_buffer_clear uses its RGB. */
|
|
158
99
|
back: '#303030',
|
|
159
|
-
/**
|
|
160
|
-
* `#2f2f2f` in the frame, which at alpha 0xb3 over `#2d2d2d`-ish window
|
|
161
|
-
* chrome is what this value resolves to; drawn flat here. */
|
|
100
|
+
/** space_action.header from userdef_default_theme.c, drawn flat. */
|
|
162
101
|
header: '#303030',
|
|
163
102
|
/** `.space_action.grid` — `:498`. The MAJOR frame lines. */
|
|
164
103
|
grid: '#161616',
|
|
165
|
-
/**
|
|
166
|
-
* lines, exactly 16 levels lighter. CONFIRMED `#252525` in the frame
|
|
167
|
-
* against the major's `#151515`. */
|
|
104
|
+
/** Minor grid lines use TH_GRID shaded 16 levels lighter (view2d_draw.cc). */
|
|
168
105
|
gridMinor: '#262626',
|
|
169
106
|
/** `.regions.scrubbing.back` — `userdef_default_theme.c:300`. */
|
|
170
107
|
scrubBack: '#1d1d1d',
|
|
171
108
|
/** `.regions.scrubbing.text` — `:301`, the ruler's numbers
|
|
172
109
|
* (`TH_TIME_SCRUB_TEXT`, passed by `ED_time_scrub_draw`). */
|
|
173
110
|
scrubText: '#808080',
|
|
174
|
-
/**
|
|
175
|
-
* its tip. CONFIRMED `#4671b2` in the frame. */
|
|
111
|
+
/** TH_CFRAME supplies the playhead stalk, pill and tip. */
|
|
176
112
|
playhead: '#4772b3',
|
|
177
113
|
/** `TH_HEADER_TEXT_HI` — the pill's number (`draw_playhead_box`,
|
|
178
114
|
* `time_scrub_ui.cc:182`); `.space_action.header_text_hi` `:496`. */
|
|
@@ -187,8 +123,7 @@ export const TIMELINE_THEME = {
|
|
|
187
123
|
longKeyAlpha: 0x1f / 255,
|
|
188
124
|
longKeySelected: '#ff8c00',
|
|
189
125
|
longKeySelectedAlpha: 0x99 / 255,
|
|
190
|
-
/**
|
|
191
|
-
* (confirmed off the running theme: `#000000ff` each). */
|
|
126
|
+
/** space_action.keyborder and keyborder_select are both black. */
|
|
192
127
|
keyBorder: '#000000',
|
|
193
128
|
} as const;
|
|
194
129
|
|
|
@@ -215,58 +150,13 @@ export const KEY_SIZE_FACTOR: Record<string, number> = {
|
|
|
215
150
|
JITTER: 0.8,
|
|
216
151
|
GENERATED: 0.75,
|
|
217
152
|
};
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* THE DIAMOND, and this is the one number a lane must not re-derive by eye.
|
|
221
|
-
*
|
|
222
|
-
* `icon_size = widget_unit * 0.5 * yscale_fac` = 10 (`keyframes_draw.cc:218`),
|
|
223
|
-
* and the SHAPE is the shader's arithmetic
|
|
224
|
-
* (`gpu_shader_keyframe_shape_vert.glsl:46-66`, `outline_scale` 1 at its one
|
|
225
|
-
* caller `keyframes_draw.cc:660`):
|
|
226
|
-
*
|
|
227
|
-
* half_width = 0.06 + (size − 10) × 0.04 → 0.06
|
|
228
|
-
* line_width = half_width + line_falloff(1.0) → 1.06
|
|
229
|
-
* thresholds = (max(0, line_width − 1), line_width) → (0.06, 1.06)
|
|
230
|
-
* ext_radius = round(0.5 × size) + thresholds.x → 5.06
|
|
231
|
-
* pointSize = ceil(ext_radius + thresholds.y) × 2 + 1 → 15
|
|
232
|
-
*
|
|
233
|
-
* and the fragment's diamond test is an L1 ball: `radius = (|x| + |y|) ×
|
|
234
|
-
* √0.5` against `radii[0] = ext_radius × √0.5`, with
|
|
235
|
-
* `alpha = 1 − smoothstep(thresholds.x, thresholds.y, |outline_dist|)`. So the
|
|
236
|
-
* OUTLINE is a band around L1 = 5.06 whose PERPENDICULAR half-extent is
|
|
237
|
-
* `line_width` = 1.06 px, and the fill shows inside L1 ≈ 3.56.
|
|
238
|
-
*
|
|
239
|
-
* CONFIRMED PIXEL FOR PIXEL in Blender's frame: the sprite is 15 rows tall
|
|
240
|
-
* (image rows 51…65 for a key centred at 58), the filled core is 7 rows
|
|
241
|
-
* (55…61 — L1 ≤ 3.5), and the black band reaches L1 ≈ 6.5.
|
|
242
|
-
*
|
|
243
|
-
* WE DRAW IT as an SVG polygon of half-diagonal `DIAMOND_RADIUS` with a
|
|
244
|
-
* `DIAMOND_STROKE`-wide stroke straddling it, which reproduces both extents
|
|
245
|
-
* exactly — 3.56 for the fill's edge and 6.56 for the outline's. The stated
|
|
246
|
-
* difference: Blender's band is a 0.06 → 1.06 smoothstep in each direction and
|
|
247
|
-
* SVG has no such ramp, so the browser antialiases its own hard edges instead.
|
|
248
|
-
* The INK-weighted equivalent would be 1.12 px (the integral of
|
|
249
|
-
* `1 − smoothstep(0,1,u)` is exactly ½, so each side deposits 0.06 + 0.5), and
|
|
250
|
-
* it would put the fill 0.7 px further out than Blender's — the extents are
|
|
251
|
-
* what a parity read measures, so the extent is what is matched.
|
|
252
|
-
*/
|
|
253
|
-
export const KEY_ICON_SIZE = 10;
|
|
254
153
|
export const DIAMOND_RADIUS = 5.06;
|
|
255
154
|
export const DIAMOND_STROKE = 2.12;
|
|
256
155
|
/** `gl_PointSize` — the sprite Blender rasterises the shape into. Reported in
|
|
257
156
|
* the view's `state` so a parity reading can check it. */
|
|
258
157
|
export const DIAMOND_SPRITE = 15;
|
|
259
158
|
|
|
260
|
-
/**
|
|
261
|
-
* THE PLAYHEAD — `get_playhead_dimensions` (`time_scrub_ui.cc:96-116`) and
|
|
262
|
-
* `draw_playhead_box` / `draw_playhead_tip` (`:161-206`).
|
|
263
|
-
*
|
|
264
|
-
* CONFIRMED: a 24-px pill (22 px of flat `#4671b2` plus a pixel of
|
|
265
|
-
* antialiasing each side) starting 2 px below the scrub strip's top, and a
|
|
266
|
-
* 3-px stalk (`draw_playhead_stalk`'s `rect` at `UI_SCALE_FAC ≥ 0.91`:
|
|
267
|
-
* `floor(x − 1) − 1` to `floor(x + 2) + 1`, of which the shadow is the outer
|
|
268
|
-
* pixel on each side).
|
|
269
|
-
*/
|
|
159
|
+
/** Playhead dimensions follow get_playhead_dimensions and draw_playhead_box/tip in time_scrub_ui.cc. */
|
|
270
160
|
export const PLAYHEAD = {
|
|
271
161
|
/** `box_min_width = 24 * UI_SCALE_FAC` (`:109`); a wider number grows it to
|
|
272
162
|
* `text_width + 2 * text_padding`. */
|
|
@@ -280,35 +170,25 @@ export const PLAYHEAD = {
|
|
|
280
170
|
/** `tri_half_width` / `tri_height` = `6 * UI_SCALE_FAC` (`:113-114`). */
|
|
281
171
|
tipHalfWidth: 6,
|
|
282
172
|
tipHeight: 6,
|
|
283
|
-
/** The stalk
|
|
173
|
+
/** The stalk’s core is 3 CSS px. */
|
|
284
174
|
stalkWidth: 3,
|
|
285
175
|
} as const;
|
|
286
176
|
|
|
287
|
-
/**
|
|
288
|
-
* `ANIM_draw_framerange` — `anim_draw.cc:172-190`: the area OUTSIDE
|
|
289
|
-
* `scene.frame_start … frame_end` takes `TH_BACK` shaded −25 with alpha −100.
|
|
290
|
-
* With the running theme's `#303030ff` that resolves to
|
|
291
|
-
* `48 + (23 − 48) × 155/255 = 32.8`, and the frame reads `#202020` (32).
|
|
292
|
-
* Drawn here as one flat colour rather than a blend, because the blend has one
|
|
293
|
-
* possible answer over the region's own clear.
|
|
294
|
-
*/
|
|
177
|
+
/** Out-of-range fill is TH_BACK shaded −25 at alpha −100 (ANIM_draw_framerange), resolved once over the opaque region clear. */
|
|
295
178
|
export const OUT_OF_RANGE = '#212121';
|
|
296
179
|
|
|
297
180
|
/** `.space_action.text` — `userdef_default_theme.c:492`, the status line's. */
|
|
298
181
|
export const TIMELINE_CHROME = {
|
|
299
182
|
text: '#a6a6a6',
|
|
300
183
|
textHi: '#ffffff',
|
|
301
|
-
/**
|
|
184
|
+
/** ui.editor_border from the running factory theme. */
|
|
302
185
|
rule: '#161616',
|
|
303
|
-
/**
|
|
304
|
-
* theme: `#545454` / `#e6e6e6` / `#3d3d3d`, roundness 0.4. A 20-px widget
|
|
305
|
-
* therefore has a 4-px corner radius. */
|
|
186
|
+
/** wcol_tool inner/text/outline and roundness 0.4; a 20-px widget has a 4-px corner radius. */
|
|
306
187
|
widget: '#545454',
|
|
307
188
|
widgetText: '#e6e6e6',
|
|
308
189
|
widgetOutline: '#3d3d3d',
|
|
309
190
|
widgetRadius: 4,
|
|
310
|
-
/**
|
|
311
|
-
* transport buttons span exactly 120 px. */
|
|
191
|
+
/** UI_UNIT_X and UI_UNIT_Y equal one widget_unit. */
|
|
312
192
|
unit: WIDGET_UNIT,
|
|
313
193
|
refusal: '#ffaf23',
|
|
314
194
|
/** The header's own padding and the gap between its widget groups —
|