@nice2dev/ui-graphics 1.2.1 → 1.2.2
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/CHANGELOG.md +39 -0
- package/dist/cjs/animation/AnimatedPerson.js +3 -1
- package/dist/cjs/core/NiceBrushEngine.js +5 -4
- package/dist/cjs/core/collaboration.js +11 -6
- package/dist/cjs/core/minimap.js +21 -20
- package/dist/cjs/font/NiceFontEditor.js +5 -5
- package/dist/cjs/game/GameAsset2dEditor.js +57 -38
- package/dist/cjs/photo/NiceAITools.js +3 -3
- package/dist/cjs/photo/PhotoEditorLeftPanel.js +14 -14
- package/dist/cjs/pixel/PixelEditor.js +1 -1
- package/dist/cjs/pixel/PixelEditorMenuBar.js +92 -27
- package/dist/cjs/pixel/PixelEditorRightPanel.js +7 -3
- package/dist/cjs/pixel/PixelEditorStatusBar.js +6 -2
- package/dist/cjs/pixel/PixelEditorTimeline.js +5 -1
- package/dist/cjs/pixel/PixelEditorToolbar.js +3 -1
- package/dist/cjs/pixel/SpriteGeneratorPanel.js +18 -11
- package/dist/cjs/texture/Nice3DTexturePainter.js +9 -5
- package/dist/cjs/vector/NiceSvgToCode.js +3 -3
- package/dist/cjs/vector/VectorEditor.js +1 -1
- package/dist/cjs/vector/VectorEditorMenuBar.js +7 -1
- package/dist/cjs/vector/VectorEditorRightPanel.js +1 -1
- package/dist/esm/animation/AnimatedPerson.js +3 -1
- package/dist/esm/core/NiceBrushEngine.js +5 -4
- package/dist/esm/core/collaboration.js +11 -6
- package/dist/esm/core/minimap.js +21 -20
- package/dist/esm/font/NiceFontEditor.js +5 -5
- package/dist/esm/game/GameAsset2dEditor.js +57 -38
- package/dist/esm/photo/NiceAITools.js +3 -3
- package/dist/esm/photo/PhotoEditorLeftPanel.js +14 -14
- package/dist/esm/pixel/PixelEditor.js +1 -1
- package/dist/esm/pixel/PixelEditorMenuBar.js +92 -27
- package/dist/esm/pixel/PixelEditorRightPanel.js +7 -3
- package/dist/esm/pixel/PixelEditorStatusBar.js +6 -2
- package/dist/esm/pixel/PixelEditorTimeline.js +5 -1
- package/dist/esm/pixel/PixelEditorToolbar.js +3 -1
- package/dist/esm/pixel/SpriteGeneratorPanel.js +18 -11
- package/dist/esm/texture/Nice3DTexturePainter.js +9 -5
- package/dist/esm/vector/NiceSvgToCode.js +3 -3
- package/dist/esm/vector/VectorEditor.js +1 -1
- package/dist/esm/vector/VectorEditorMenuBar.js +7 -1
- package/dist/esm/vector/VectorEditorRightPanel.js +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @nice2dev/ui-graphics — Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 06eb0d2: 1.2.2 — library texts through the translator, a translator in the packages that had none, and the
|
|
8
|
+
end of the hook-dependency warnings. Nothing removed or renamed. Upgrade notes:
|
|
9
|
+
`documentation/omniverk/OMNIVERK_UPGRADE_1.2.2.md`.
|
|
10
|
+
|
|
11
|
+
**Translations.** 2,825 new keys in the global dictionary (en, pl, de, fr, es, uk), 80 in the local
|
|
12
|
+
dictionaries of ui-math, ui-diagrams and ui-presentations: column headers, option labels, texts in
|
|
13
|
+
conditions and templates, messages given to setters, labels returned by helpers, Polish texts that
|
|
14
|
+
were the English default. Relative dates through `Intl.RelativeTimeFormat`, plural forms through
|
|
15
|
+
`…One` / `…Many` keys. ui-science, ui-platform, ui-mobile, auth, licensing, templates, social and
|
|
16
|
+
the icon tools translate through `useNiceTranslation` (new peer `@nice2dev/ui-core ^1.2.0`);
|
|
17
|
+
ui-security reads the global translator when its own provider is absent.
|
|
18
|
+
|
|
19
|
+
**Additions.** `labelKey` on ui-mobile `TabItem` and `SwipeAction` (the presets set it);
|
|
20
|
+
`registerWebAuthn` / `authenticateWebAuthn` take the WebAuthn options `NiceWebAuthnButton` used to
|
|
21
|
+
drop (defaults unchanged); `NiceKeyboardHint` shows `descriptionKey`. Eight icons in
|
|
22
|
+
`@nice2dev/icons` — `NtdPoint`, `NtdSegment`, `NtdPerpendicular`, `NtdParallel`, `NtdMidpoint`,
|
|
23
|
+
`NtdAngleBisector`, `NtdGroup`, `NtdUngroup` — and the same names as `NiceIcon` built-ins.
|
|
24
|
+
|
|
25
|
+
**Behaviour changes, agreed.** ui-math, ui-diagrams and ui-presentations follow the app's global
|
|
26
|
+
`NiceI18nProvider` language when their own provider is absent (they stayed English);
|
|
27
|
+
`NicePresentationEditor` without `lang` takes the surrounding language. Icons drawn as glyphs are
|
|
28
|
+
Nice icons: the `NiceGeometry` tools, Align / Group / Ungroup / Layout of the diagram toolbar, the
|
|
29
|
+
presentation grid toggle, zoom and "Add Track" of `NiceDMXTimeline`. icons-gaming and
|
|
30
|
+
icons-filetypes name an icon by its `title` and are decorative without one, like `createIcon`
|
|
31
|
+
(screen readers read "GiHeart" or "DOCX file icon"); new optional `title` on the gaming icon props.
|
|
32
|
+
|
|
33
|
+
**Hook dependencies (161 → 0).** Fixed along the way: `NiceLowPowerMode` re-rendered forever from
|
|
34
|
+
mount and overflowed the stack on wake; `GenericPlayer` / `NiceGenericPlayerStage` restarted the
|
|
35
|
+
track on a volume change; `NiceDMXTimeline` called `onPlay` on every frame and missed cues added
|
|
36
|
+
during playback; `NiceConflictResolution` `autoResolve` never resolved; `NiceMAM` selected by the
|
|
37
|
+
previous filter; `NiceRulesEngine` "Run All" kept only the last result; `NiceCodeEditor` saved with
|
|
38
|
+
the first render's `onSave`; a consumer's cursor handler switched off `NiceCodeEditorAI`
|
|
39
|
+
completion; `useRadioStream` lost a volume set before the first play; `NiceGameEditor` shortcuts,
|
|
40
|
+
`useVoiceControl` continuous sessions, `NiceDeviceTrust` with a host storing the score.
|
|
41
|
+
|
|
3
42
|
## 1.2.1
|
|
4
43
|
|
|
5
44
|
### Patch Changes
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var framerMotion = require('framer-motion');
|
|
5
5
|
var React = require('react');
|
|
6
|
+
var i18n = require('../core/i18n.js');
|
|
6
7
|
var animationHelper = require('./animationHelper.js');
|
|
7
8
|
var characterTypes = require('./characterTypes.js');
|
|
8
9
|
|
|
@@ -11,6 +12,7 @@ const NiceAnimatedPerson = ({ id, name, size, score = 10, startPose = 'idle', on
|
|
|
11
12
|
// colors prop is destructured for API completeness but not yet fully wired into character defaults.
|
|
12
13
|
}) => {
|
|
13
14
|
var _a, _b, _c, _d, _e;
|
|
15
|
+
const { t } = i18n.useNiceTranslation();
|
|
14
16
|
const fx = animationHelper.usePersonControls();
|
|
15
17
|
const effectiveStartPose = pose !== null && pose !== void 0 ? pose : startPose;
|
|
16
18
|
const effectiveCharacter = character !== null && character !== void 0 ? character : config;
|
|
@@ -22,7 +24,7 @@ const NiceAnimatedPerson = ({ id, name, size, score = 10, startPose = 'idle', on
|
|
|
22
24
|
}, [fx, onReady, effectiveStartPose]);
|
|
23
25
|
const W = (_c = (_b = (_a = cfg.size) !== null && _a !== void 0 ? _a : effectiveSize) !== null && _b !== void 0 ? _b : characterTypes.DEFAULT_CHARACTER.size) !== null && _c !== void 0 ? _c : 180;
|
|
24
26
|
const skin = characterTypes.pick(cfg.face.colors, 0, 'var(--anim-skin, #FFD2B3)');
|
|
25
|
-
return (jsxRuntime.jsx(framerMotion.motion.div, { id: id, style: { width: W, height: W * 1.35, overflow: 'visible' }, animate: fx.root, children: jsxRuntime.jsxs("svg", { viewBox: "0 0 180 240", width: "100%", height: "100%", role: "img", "aria-label": (_e = (_d = cfg.name) !== null && _d !== void 0 ? _d : name) !== null && _e !== void 0 ? _e : 'Juror', children: [jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.body, children: [jsxRuntime.jsx("rect", { x: "68", y: "158", width: "14", height: "46", rx: "6", fill: characterTypes.pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsxRuntime.jsx("ellipse", { cx: "75", cy: "204", rx: "10", ry: "5", fill: characterTypes.pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') }), jsxRuntime.jsx("rect", { x: "98", y: "158", width: "14", height: "46", rx: "6", fill: characterTypes.pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsxRuntime.jsx("ellipse", { cx: "105", cy: "204", rx: "10", ry: "5", fill: characterTypes.pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') })] }), jsxRuntime.jsx(framerMotion.motion.g, { animate: fx.body, children: renderOutfit(cfg.outfit) }), jsxRuntime.jsx("rect", { x: "84", y: "82", width: "12", height: "12", rx: "4", fill: skin }), jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.leftArm, style: { transformOrigin: '64px 96px' }, children: [jsxRuntime.jsx("rect", { x: "36", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsxRuntime.jsx("circle", { cx: "44", cy: "136", r: "7", fill: skin })] }), jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.rightArm, style: { transformOrigin: '116px 96px' }, children: [jsxRuntime.jsx("rect", { x: "128", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsxRuntime.jsx("circle", { cx: "136", cy: "136", r: "7", fill: skin }), jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.card, children: [jsxRuntime.jsx("rect", { x: "126", y: "50", width: "42", height: "30", rx: "4", fill: "var(--nice-color-surface, #fff)", stroke: "var(--nice-text-primary, #111)", strokeWidth: 1.2 }), jsxRuntime.jsx("text", { x: "147", y: "70", textAnchor: "middle", fontSize: "16", fontWeight: 700, fill: "var(--nice-text-primary, #111)", children: String(score) })] }), renderProp(cfg.prop)] }), jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.head, children: [renderHairBack(cfg.hair), renderFace(cfg.face), jsxRuntime.jsx(framerMotion.motion.g, { animate: fx.eyes, children: renderEyes(cfg.eyes) }), renderNose(cfg.nose), jsxRuntime.jsx(framerMotion.motion.g, { animate: fx.mouth, children: renderMouth(cfg.mouth) }), renderHeadwear(cfg.headwear)] }), jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.curse, initial: { opacity: 0 }, children: [jsxRuntime.jsx("rect", { x: "62", y: "10", width: "56", height: "22", rx: "8", fill: "var(--nice-text-primary, #111)" }), jsxRuntime.jsx("text", { x: "90", y: "26", textAnchor: "middle", fontSize: "12", fill: "var(--nice-text-inverse, #fff)", children: "#!%&*" }), jsxRuntime.jsx("path", { d: "M82 32 l4 8 l4 -8", fill: "var(--nice-text-primary, #111)" })] })] }) }));
|
|
27
|
+
return (jsxRuntime.jsx(framerMotion.motion.div, { id: id, style: { width: W, height: W * 1.35, overflow: 'visible' }, animate: fx.root, children: jsxRuntime.jsxs("svg", { viewBox: "0 0 180 240", width: "100%", height: "100%", role: "img", "aria-label": (_e = (_d = cfg.name) !== null && _d !== void 0 ? _d : name) !== null && _e !== void 0 ? _e : t('animatedPerson.juror', 'Juror'), children: [jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.body, children: [jsxRuntime.jsx("rect", { x: "68", y: "158", width: "14", height: "46", rx: "6", fill: characterTypes.pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsxRuntime.jsx("ellipse", { cx: "75", cy: "204", rx: "10", ry: "5", fill: characterTypes.pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') }), jsxRuntime.jsx("rect", { x: "98", y: "158", width: "14", height: "46", rx: "6", fill: characterTypes.pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsxRuntime.jsx("ellipse", { cx: "105", cy: "204", rx: "10", ry: "5", fill: characterTypes.pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') })] }), jsxRuntime.jsx(framerMotion.motion.g, { animate: fx.body, children: renderOutfit(cfg.outfit) }), jsxRuntime.jsx("rect", { x: "84", y: "82", width: "12", height: "12", rx: "4", fill: skin }), jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.leftArm, style: { transformOrigin: '64px 96px' }, children: [jsxRuntime.jsx("rect", { x: "36", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsxRuntime.jsx("circle", { cx: "44", cy: "136", r: "7", fill: skin })] }), jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.rightArm, style: { transformOrigin: '116px 96px' }, children: [jsxRuntime.jsx("rect", { x: "128", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsxRuntime.jsx("circle", { cx: "136", cy: "136", r: "7", fill: skin }), jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.card, children: [jsxRuntime.jsx("rect", { x: "126", y: "50", width: "42", height: "30", rx: "4", fill: "var(--nice-color-surface, #fff)", stroke: "var(--nice-text-primary, #111)", strokeWidth: 1.2 }), jsxRuntime.jsx("text", { x: "147", y: "70", textAnchor: "middle", fontSize: "16", fontWeight: 700, fill: "var(--nice-text-primary, #111)", children: String(score) })] }), renderProp(cfg.prop)] }), jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.head, children: [renderHairBack(cfg.hair), renderFace(cfg.face), jsxRuntime.jsx(framerMotion.motion.g, { animate: fx.eyes, children: renderEyes(cfg.eyes) }), renderNose(cfg.nose), jsxRuntime.jsx(framerMotion.motion.g, { animate: fx.mouth, children: renderMouth(cfg.mouth) }), renderHeadwear(cfg.headwear)] }), jsxRuntime.jsxs(framerMotion.motion.g, { animate: fx.curse, initial: { opacity: 0 }, children: [jsxRuntime.jsx("rect", { x: "62", y: "10", width: "56", height: "22", rx: "8", fill: "var(--nice-text-primary, #111)" }), jsxRuntime.jsx("text", { x: "90", y: "26", textAnchor: "middle", fontSize: "12", fill: "var(--nice-text-inverse, #fff)", children: "#!%&*" }), jsxRuntime.jsx("path", { d: "M82 32 l4 8 l4 -8", fill: "var(--nice-text-primary, #111)" })] })] }) }));
|
|
26
28
|
};
|
|
27
29
|
/* ---------------- RENDERERS (redesigned proportions) ---------------- */
|
|
28
30
|
function renderOutfit(cfg) {
|
|
@@ -546,20 +546,21 @@ color = '#000000') {
|
|
|
546
546
|
const NiceBrushEngine = ({ canvas, color = '#000000', onStrokeEnd, className = '', }) => {
|
|
547
547
|
const { t } = i18n.useNiceTranslation();
|
|
548
548
|
const engine = useBrushEngine(canvas, color);
|
|
549
|
+
const { endStroke } = engine;
|
|
549
550
|
// Override endStroke to fire callback
|
|
550
551
|
React.useEffect(() => {
|
|
551
552
|
if (!canvas) {
|
|
552
553
|
return;
|
|
553
554
|
}
|
|
554
555
|
const onUp = () => {
|
|
555
|
-
const stroke =
|
|
556
|
+
const stroke = endStroke();
|
|
556
557
|
if (stroke && onStrokeEnd) {
|
|
557
558
|
onStrokeEnd(stroke);
|
|
558
559
|
}
|
|
559
560
|
};
|
|
560
561
|
canvas.addEventListener('pointerup', onUp);
|
|
561
562
|
return () => canvas.removeEventListener('pointerup', onUp);
|
|
562
|
-
}, [canvas,
|
|
563
|
+
}, [canvas, endStroke, onStrokeEnd]);
|
|
563
564
|
const s = engine.settings;
|
|
564
565
|
return (jsxRuntime.jsxs("div", { className: `nice-brush-engine ${className}`, style: { display: 'grid', gap: 6, fontSize: 13 }, children: [jsxRuntime.jsxs("strong", { children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "brush", size: 14 }), " ", t('brushEngine.brushEngineV2', 'Brush Engine v2')] }), jsxRuntime.jsx("div", { style: { display: 'flex', gap: 2, flexWrap: 'wrap' }, children: engine.presetNames.map((name) => {
|
|
565
566
|
var _a, _b;
|
|
@@ -574,13 +575,13 @@ const NiceBrushEngine = ({ canvas, color = '#000000', onStrokeEnd, className = '
|
|
|
574
575
|
: 'transparent',
|
|
575
576
|
color: s.name === ((_b = BRUSH_PRESETS[name]) === null || _b === void 0 ? void 0 : _b.name) ? 'var(--nice-color-white, #fff)' : 'inherit',
|
|
576
577
|
}, children: BRUSH_PRESETS[name].name }, name));
|
|
577
|
-
}) }), jsxRuntime.jsxs("label", { children: [
|
|
578
|
+
}) }), jsxRuntime.jsxs("label", { children: [t('brushEngine.sizePx', 'Size: {size}px').replace('{size}', String(s.size)), jsxRuntime.jsx("input", { type: "range", min: 1, max: 200, value: s.size, onChange: (e) => engine.setSettings({ ...s, size: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: [t('brushEngine.opacity', 'Opacity: {opacity}%').replace('{opacity}', String(Math.round(s.opacity * 100))), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: s.opacity, onChange: (e) => engine.setSettings({ ...s, opacity: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: [t('brushEngine.flow', 'Flow: {flow}%').replace('{flow}', String(Math.round(s.flow * 100))), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: s.flow, onChange: (e) => engine.setSettings({ ...s, flow: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: [t('brushEngine.hardness', 'Hardness: {hardness}%').replace('{hardness}', String(Math.round(s.hardness * 100))), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: s.hardness, onChange: (e) => engine.setSettings({ ...s, hardness: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: [t('brushEngine.smoothing', 'Smoothing: {smoothing}%').replace('{smoothing}', String(Math.round(s.smoothing * 100))), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: s.smoothing, onChange: (e) => engine.setSettings({ ...s, smoothing: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: [t('brushEngine.tip', 'Tip:'), jsxRuntime.jsxs("select", { value: s.tipShape, onChange: (e) => engine.setSettings({ ...s, tipShape: e.target.value }), style: { width: '100%' }, children: [jsxRuntime.jsx("option", { value: "circle", children: t('brushEngine.circle', 'Circle') }), jsxRuntime.jsx("option", { value: "square", children: t('brushEngine.square', 'Square') }), jsxRuntime.jsx("option", { value: "diamond", children: t('brushEngine.diamond', 'Diamond') }), jsxRuntime.jsx("option", { value: "texture", children: t('brushEngine.texture', 'Texture') })] })] }), jsxRuntime.jsxs("label", { children: [t('brushEngine.roundness', 'Roundness: {roundness}%').replace('{roundness}', String(Math.round(s.roundness * 100))), jsxRuntime.jsx("input", { type: "range", min: 0.05, max: 1, step: 0.05, value: s.roundness, onChange: (e) => engine.setSettings({ ...s, roundness: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("details", { children: [jsxRuntime.jsxs("summary", { style: { cursor: 'pointer', fontSize: 12 }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "bar-chart", size: 12 }), " ", t('brushEngine.sizeDynamics', 'Size Dynamics')] }), jsxRuntime.jsxs("label", { children: [t('brushEngine.source', 'Source:'), jsxRuntime.jsxs("select", { value: s.sizeDynamics.source, onChange: (e) => engine.setSettings({
|
|
578
579
|
...s,
|
|
579
580
|
sizeDynamics: {
|
|
580
581
|
...s.sizeDynamics,
|
|
581
582
|
source: e.target.value,
|
|
582
583
|
},
|
|
583
|
-
}), style: { width: '100%' }, children: [jsxRuntime.jsx("option", { value: "none", children:
|
|
584
|
+
}), style: { width: '100%' }, children: [jsxRuntime.jsx("option", { value: "none", children: t('brushEngine.off', 'Off') }), jsxRuntime.jsx("option", { value: "pressure", children: t('brushEngine.pressure', 'Pressure') }), jsxRuntime.jsx("option", { value: "tilt", children: t('brushEngine.tilt', 'Tilt') }), jsxRuntime.jsx("option", { value: "speed", children: t('brushEngine.speed', 'Speed') }), jsxRuntime.jsx("option", { value: "random", children: t('brushEngine.random', 'Random') })] })] })] }), jsxRuntime.jsxs("label", { children: [t('brushEngine.scatter', 'Scatter: {scatter}%').replace('{scatter}', String(Math.round(s.scatter * 100))), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: s.scatter, onChange: (e) => engine.setSettings({ ...s, scatter: +e.target.value }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: [t('brushEngine.blend', 'Blend:'), jsxRuntime.jsx("select", { value: s.blendMode, onChange: (e) => engine.setSettings({ ...s, blendMode: e.target.value }), style: { width: '100%' }, children: ['normal', 'multiply', 'screen', 'overlay', 'darken', 'lighten'].map((m) => (jsxRuntime.jsx("option", { value: m, children: m }, m))) })] }), jsxRuntime.jsxs("label", { style: { display: 'flex', gap: 4, alignItems: 'center' }, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: s.wetEdges, onChange: (e) => engine.setSettings({ ...s, wetEdges: e.target.checked }) }), t('brushEngine.wetEdges', 'Wet Edges')] })] }));
|
|
584
585
|
};
|
|
585
586
|
|
|
586
587
|
exports.BRUSH_PRESETS = BRUSH_PRESETS;
|
|
@@ -171,10 +171,14 @@ function useCollaboration(transport, roomId, currentUser) {
|
|
|
171
171
|
const [users, setUsers] = React.useState([]);
|
|
172
172
|
const [connected, setConnected] = React.useState(false);
|
|
173
173
|
const operationHandlers = React.useRef([]);
|
|
174
|
+
// the user as it is when the room is joined: a new object with the same id must not rejoin
|
|
175
|
+
const currentUserRef = React.useRef(currentUser);
|
|
176
|
+
currentUserRef.current = currentUser;
|
|
174
177
|
React.useEffect(() => {
|
|
175
|
-
|
|
178
|
+
const user = currentUserRef.current;
|
|
179
|
+
transport.connect(roomId, user.id);
|
|
176
180
|
setConnected(true);
|
|
177
|
-
transport.send({ type: 'join', user
|
|
181
|
+
transport.send({ type: 'join', user });
|
|
178
182
|
const unsubscribe = transport.onMessage((msg) => {
|
|
179
183
|
switch (msg.type) {
|
|
180
184
|
case 'join':
|
|
@@ -204,17 +208,18 @@ function useCollaboration(transport, roomId, currentUser) {
|
|
|
204
208
|
}
|
|
205
209
|
});
|
|
206
210
|
return () => {
|
|
207
|
-
transport.send({ type: 'leave', userId:
|
|
211
|
+
transport.send({ type: 'leave', userId: user.id });
|
|
208
212
|
unsubscribe();
|
|
209
213
|
transport.disconnect();
|
|
210
214
|
setConnected(false);
|
|
211
215
|
};
|
|
212
|
-
|
|
213
|
-
|
|
216
|
+
// a new transport reconnects (until 1.2.2 it was ignored and messages went to the old one)
|
|
217
|
+
}, [transport, roomId, currentUser.id]);
|
|
218
|
+
const sendCursor = React.useCallback((x, y) => transport.send({ type: 'cursor', userId: currentUser.id, x, y }), [transport, currentUser.id]);
|
|
214
219
|
const sendOperation = React.useCallback((op) => transport.send({
|
|
215
220
|
type: 'operation',
|
|
216
221
|
operation: { ...op, timestamp: Date.now(), userId: currentUser.id },
|
|
217
|
-
}), [currentUser.id]);
|
|
222
|
+
}), [transport, currentUser.id]);
|
|
218
223
|
const onOperation = React.useCallback((handler) => {
|
|
219
224
|
operationHandlers.current.push(handler);
|
|
220
225
|
return () => {
|
package/dist/cjs/core/minimap.js
CHANGED
|
@@ -20,6 +20,27 @@ const Minimap = ({ canvasRef, imageDataUrl, documentWidth, documentHeight, viewp
|
|
|
20
20
|
const scale = Math.min(scaleX, scaleY);
|
|
21
21
|
// Draw minimap
|
|
22
22
|
React.useEffect(() => {
|
|
23
|
+
// inside the effect: it draws with this run's viewport, scale and size
|
|
24
|
+
const drawViewport = (ctx) => {
|
|
25
|
+
// Draw viewport rect
|
|
26
|
+
const vx = viewport.x * scale;
|
|
27
|
+
const vy = viewport.y * scale;
|
|
28
|
+
const vw = viewport.width * scale;
|
|
29
|
+
const vh = viewport.height * scale;
|
|
30
|
+
ctx.strokeStyle = uiCore.resolveCssColor('var(--nice-primary-tint-80, rgba(59, 130, 246, 0.8))', ctx.canvas);
|
|
31
|
+
ctx.lineWidth = 1.5;
|
|
32
|
+
ctx.strokeRect(vx, vy, vw, vh);
|
|
33
|
+
// Dim areas outside viewport
|
|
34
|
+
ctx.fillStyle = uiCore.resolveCssColor('var(--nice-overlay-30, rgba(0, 0, 0, 0.3))', ctx.canvas);
|
|
35
|
+
// Top
|
|
36
|
+
ctx.fillRect(0, 0, mapW, vy);
|
|
37
|
+
// Bottom
|
|
38
|
+
ctx.fillRect(0, vy + vh, mapW, mapH - vy - vh);
|
|
39
|
+
// Left
|
|
40
|
+
ctx.fillRect(0, vy, vx, vh);
|
|
41
|
+
// Right
|
|
42
|
+
ctx.fillRect(vx + vw, vy, mapW - vx - vw, vh);
|
|
43
|
+
};
|
|
23
44
|
if (!visible) {
|
|
24
45
|
return;
|
|
25
46
|
}
|
|
@@ -64,26 +85,6 @@ const Minimap = ({ canvasRef, imageDataUrl, documentWidth, documentHeight, viewp
|
|
|
64
85
|
mapW,
|
|
65
86
|
mapH,
|
|
66
87
|
]);
|
|
67
|
-
function drawViewport(ctx) {
|
|
68
|
-
// Draw viewport rect
|
|
69
|
-
const vx = viewport.x * scale;
|
|
70
|
-
const vy = viewport.y * scale;
|
|
71
|
-
const vw = viewport.width * scale;
|
|
72
|
-
const vh = viewport.height * scale;
|
|
73
|
-
ctx.strokeStyle = uiCore.resolveCssColor('var(--nice-primary-tint-80, rgba(59, 130, 246, 0.8))', ctx.canvas);
|
|
74
|
-
ctx.lineWidth = 1.5;
|
|
75
|
-
ctx.strokeRect(vx, vy, vw, vh);
|
|
76
|
-
// Dim areas outside viewport
|
|
77
|
-
ctx.fillStyle = uiCore.resolveCssColor('var(--nice-overlay-30, rgba(0, 0, 0, 0.3))', ctx.canvas);
|
|
78
|
-
// Top
|
|
79
|
-
ctx.fillRect(0, 0, mapW, vy);
|
|
80
|
-
// Bottom
|
|
81
|
-
ctx.fillRect(0, vy + vh, mapW, mapH - vy - vh);
|
|
82
|
-
// Left
|
|
83
|
-
ctx.fillRect(0, vy, vx, vh);
|
|
84
|
-
// Right
|
|
85
|
-
ctx.fillRect(vx + vw, vy, mapW - vx - vw, vh);
|
|
86
|
-
}
|
|
87
88
|
const handlePointerDown = (e) => {
|
|
88
89
|
isDragging.current = true;
|
|
89
90
|
updateViewportFromPointer(e);
|
|
@@ -239,11 +239,11 @@ const NiceFontEditor = ({ fontName = 'MyFont', onSave, className = '', tutorial,
|
|
|
239
239
|
borderRadius: 3,
|
|
240
240
|
cursor: 'pointer',
|
|
241
241
|
fontSize: 11,
|
|
242
|
-
}, children: t('fontEditor.exportSvgFont', 'Export SVG Font') })] }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 4, borderBottom: '1px solid var(--nice-border, #373a40)' }, children: [jsxRuntime.jsx("button", { onClick: () => setTab('glyph'), style: tabBtnStyle(tab === 'glyph'), children: t('fontEditor.glyphs', 'Glyphs') }), jsxRuntime.jsx("button", { onClick: () => setTab('metrics'), style: tabBtnStyle(tab === 'metrics'), children: t('fontEditor.metrics', 'Metrics') }), jsxRuntime.jsx("button", { onClick: () => setTab('kerning'), style: tabBtnStyle(tab === 'kerning'), children: t('fontEditor.kerning', 'Kerning') })] }), tab === 'glyph' && (jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 8 }, children: [jsxRuntime.jsxs("div", { style: { flex: 1 }, children: [jsxRuntime.
|
|
242
|
+
}, children: t('fontEditor.exportSvgFont', 'Export SVG Font') })] }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 4, borderBottom: '1px solid var(--nice-border, #373a40)' }, children: [jsxRuntime.jsx("button", { onClick: () => setTab('glyph'), style: tabBtnStyle(tab === 'glyph'), children: t('fontEditor.glyphs', 'Glyphs') }), jsxRuntime.jsx("button", { onClick: () => setTab('metrics'), style: tabBtnStyle(tab === 'metrics'), children: t('fontEditor.metrics', 'Metrics') }), jsxRuntime.jsx("button", { onClick: () => setTab('kerning'), style: tabBtnStyle(tab === 'kerning'), children: t('fontEditor.kerning', 'Kerning') })] }), tab === 'glyph' && (jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 8 }, children: [jsxRuntime.jsxs("div", { style: { flex: 1 }, children: [jsxRuntime.jsx("div", { style: {
|
|
243
243
|
fontSize: 11,
|
|
244
244
|
marginBottom: 4,
|
|
245
245
|
color: 'var(--nice-text-secondary, #868e96)',
|
|
246
|
-
}, children:
|
|
246
|
+
}, children: t('fontEditor.glyphs2', '{glyphs} glyphs').replace('{glyphs}', String(editor.glyphs.length)) }), jsxRuntime.jsx("div", { style: {
|
|
247
247
|
display: 'grid',
|
|
248
248
|
gridTemplateColumns: 'repeat(auto-fill, 36px)',
|
|
249
249
|
gap: 2,
|
|
@@ -274,7 +274,7 @@ const NiceFontEditor = ({ fontName = 'MyFont', onSave, className = '', tutorial,
|
|
|
274
274
|
border: g.path
|
|
275
275
|
? '1px solid var(--nice-border, #373a40)'
|
|
276
276
|
: '1px solid transparent',
|
|
277
|
-
}, children: g.char }, g.unicode))) })] }), jsxRuntime.jsx("div", { style: { width: 320, fontSize: 11 }, children: selectedG ? (jsxRuntime.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [jsxRuntime.jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [jsxRuntime.jsxs("strong", { children: ["\"", selectedG.char, "\" (U+", selectedG.unicode.toString(16).toUpperCase().padStart(4, '0'), ")"] }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 2 }, children: [jsxRuntime.jsx("button", { onClick: () => setDrawMode('select'), style: btnStyle(drawMode === 'select'), children: t('fontEditor.select', 'Select') }), jsxRuntime.jsx("button", { onClick: () => setDrawMode('pen'), style: btnStyle(drawMode === 'pen'), children:
|
|
277
|
+
}, children: g.char }, g.unicode))) })] }), jsxRuntime.jsx("div", { style: { width: 320, fontSize: 11 }, children: selectedG ? (jsxRuntime.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [jsxRuntime.jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [jsxRuntime.jsxs("strong", { children: ["\"", selectedG.char, "\" (U+", selectedG.unicode.toString(16).toUpperCase().padStart(4, '0'), ")"] }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 2 }, children: [jsxRuntime.jsx("button", { onClick: () => setDrawMode('select'), style: btnStyle(drawMode === 'select'), children: t('fontEditor.select', 'Select') }), jsxRuntime.jsx("button", { onClick: () => setDrawMode('pen'), style: btnStyle(drawMode === 'pen'), children: t('fontEditor.pen', 'Pen') }), jsxRuntime.jsx("button", { onClick: clearPath, style: btnStyle(false), children: t('fontEditor.clear', 'Clear') }), jsxRuntime.jsx("button", { onClick: () => setShowGrid((g) => !g), style: btnStyle(showGrid), children: t('fontEditor.grid', 'Grid') })] })] }), jsxRuntime.jsxs("svg", { width: 280, height: 280, viewBox: `0 0 ${em} ${em}`, style: {
|
|
278
278
|
background: 'var(--nice-bg, #141517)',
|
|
279
279
|
borderRadius: 4,
|
|
280
280
|
cursor: drawMode === 'pen' ? 'crosshair' : 'default',
|
|
@@ -282,7 +282,7 @@ const NiceFontEditor = ({ fontName = 'MyFont', onSave, className = '', tutorial,
|
|
|
282
282
|
Array.from({ length: 11 }, (_, i) => {
|
|
283
283
|
const pos = (i / 10) * em;
|
|
284
284
|
return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx("line", { x1: pos, y1: 0, x2: pos, y2: em, stroke: "var(--nice-overlay-light-5, rgba(255, 255, 255, 0.05))", strokeWidth: em * 0.002 }), jsxRuntime.jsx("line", { x1: 0, y1: pos, x2: em, y2: pos, stroke: "var(--nice-overlay-light-5, rgba(255, 255, 255, 0.05))", strokeWidth: em * 0.002 })] }, i));
|
|
285
|
-
}), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.ascent, x2: em, y2: em - editor.metrics.ascent, stroke: "rgba(51,154,240,0.3)", strokeWidth: em * 0.003, strokeDasharray: `${em * 0.01}` }), jsxRuntime.jsx("line", { x1: 0, y1: em, x2: em, y2: em, stroke: "rgba(47,158,68,0.3)", strokeWidth: em * 0.003 }), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.xHeight, x2: em, y2: em - editor.metrics.xHeight, stroke: "rgba(249,226,175,0.3)", strokeWidth: em * 0.003, strokeDasharray: `${em * 0.01}` }), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.capHeight, x2: em, y2: em - editor.metrics.capHeight, stroke: "rgba(243,139,168,0.3)", strokeWidth: em * 0.003, strokeDasharray: `${em * 0.01}` }), jsxRuntime.jsx("line", { x1: selectedG.leftBearing, y1: 0, x2: selectedG.leftBearing, y2: em, stroke: "rgba(166,227,161,0.2)", strokeWidth: em * 0.002 }), jsxRuntime.jsx("line", { x1: selectedG.advanceWidth, y1: 0, x2: selectedG.advanceWidth, y2: em, stroke: "rgba(243,139,168,0.2)", strokeWidth: em * 0.002 }), selectedG.path && (jsxRuntime.jsx("path", { d: selectedG.path, fill: "rgba(205,214,244,0.8)", stroke: "#89b4fa", strokeWidth: em * 0.005 })), pathPoints.map((p, i) => (jsxRuntime.jsx("circle", { cx: p.x, cy: p.y, r: em * 0.015, fill: i === 0 ? 'var(--nice-success, #2f9e44)' : 'var(--nice-info, #339af0)', stroke: "var(--nice-bg, #fff)", strokeWidth: em * 0.003 }, i))), jsxRuntime.jsx("text", { x: em * 0.02, y: em - editor.metrics.ascent - em * 0.01, fill: "rgba(51,154,240,0.5)", fontSize: em * 0.03, children:
|
|
285
|
+
}), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.ascent, x2: em, y2: em - editor.metrics.ascent, stroke: "rgba(51,154,240,0.3)", strokeWidth: em * 0.003, strokeDasharray: `${em * 0.01}` }), jsxRuntime.jsx("line", { x1: 0, y1: em, x2: em, y2: em, stroke: "rgba(47,158,68,0.3)", strokeWidth: em * 0.003 }), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.xHeight, x2: em, y2: em - editor.metrics.xHeight, stroke: "rgba(249,226,175,0.3)", strokeWidth: em * 0.003, strokeDasharray: `${em * 0.01}` }), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.capHeight, x2: em, y2: em - editor.metrics.capHeight, stroke: "rgba(243,139,168,0.3)", strokeWidth: em * 0.003, strokeDasharray: `${em * 0.01}` }), jsxRuntime.jsx("line", { x1: selectedG.leftBearing, y1: 0, x2: selectedG.leftBearing, y2: em, stroke: "rgba(166,227,161,0.2)", strokeWidth: em * 0.002 }), jsxRuntime.jsx("line", { x1: selectedG.advanceWidth, y1: 0, x2: selectedG.advanceWidth, y2: em, stroke: "rgba(243,139,168,0.2)", strokeWidth: em * 0.002 }), selectedG.path && (jsxRuntime.jsx("path", { d: selectedG.path, fill: "rgba(205,214,244,0.8)", stroke: "#89b4fa", strokeWidth: em * 0.005 })), pathPoints.map((p, i) => (jsxRuntime.jsx("circle", { cx: p.x, cy: p.y, r: em * 0.015, fill: i === 0 ? 'var(--nice-success, #2f9e44)' : 'var(--nice-info, #339af0)', stroke: "var(--nice-bg, #fff)", strokeWidth: em * 0.003 }, i))), jsxRuntime.jsx("text", { x: em * 0.02, y: em - editor.metrics.ascent - em * 0.01, fill: "rgba(51,154,240,0.5)", fontSize: em * 0.03, children: t('fontEditor.ascent2', 'ascent') }), jsxRuntime.jsx("text", { x: em * 0.02, y: em - editor.metrics.xHeight - em * 0.01, fill: "rgba(249,226,175,0.5)", fontSize: em * 0.03, children: t('fontEditor.xHeight2', 'x-height') }), jsxRuntime.jsx("text", { x: em * 0.02, y: em - editor.metrics.capHeight - em * 0.01, fill: "rgba(243,139,168,0.5)", fontSize: em * 0.03, children: t('fontEditor.cap2', 'cap') })] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('fontEditor.svgPath', 'SVG Path') }), jsxRuntime.jsx("textarea", { value: selectedG.path, onChange: (e) => editor.updateGlyph(selectedG.unicode, { path: e.target.value }), rows: 3, style: { ...fieldStyle, fontFamily: 'monospace', resize: 'vertical' } })] }), jsxRuntime.jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4 }, children: [jsxRuntime.jsx("label", { children: t('fontEditor.advanceWidth', 'Advance Width') }), jsxRuntime.jsx("input", { type: "number", value: selectedG.advanceWidth, onChange: (e) => editor.updateGlyph(selectedG.unicode, {
|
|
286
286
|
advanceWidth: Number(e.target.value),
|
|
287
287
|
}), style: fieldStyle }), jsxRuntime.jsx("label", { children: t('fontEditor.leftBearing', 'Left Bearing') }), jsxRuntime.jsx("input", { type: "number", value: selectedG.leftBearing, onChange: (e) => editor.updateGlyph(selectedG.unicode, { leftBearing: Number(e.target.value) }), style: fieldStyle })] })] })) : (jsxRuntime.jsx("div", { style: {
|
|
288
288
|
color: 'var(--nice-text-secondary, #868e96)',
|
|
@@ -295,7 +295,7 @@ const NiceFontEditor = ({ fontName = 'MyFont', onSave, className = '', tutorial,
|
|
|
295
295
|
['lineGap', 'Line Gap'],
|
|
296
296
|
['xHeight', 'x-Height'],
|
|
297
297
|
['capHeight', 'Cap Height'],
|
|
298
|
-
].map(([key, label]) => (jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }, children: [jsxRuntime.jsx("span", { style: { minWidth: 100, fontSize: 11 }, children: label }), jsxRuntime.jsx("input", { type: "number", value: editor.metrics[key], onChange: (e) => editor.setMetrics({ ...editor.metrics, [key]: Number(e.target.value) }), style: fieldStyle })] }, key))), jsxRuntime.jsxs("svg", { width: 300, height: 200, viewBox: `0 0 ${em} ${em}`, style: { background: 'var(--nice-bg, #141517)', borderRadius: 4, marginTop: 12 }, children: [jsxRuntime.jsx("rect", { x: 0, y: em - editor.metrics.ascent, width: em, height: editor.metrics.ascent - editor.metrics.descent, fill: "rgba(51,154,240,0.05)", stroke: "rgba(51,154,240,0.2)", strokeWidth: em * 0.003 }), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.ascent, x2: em, y2: em - editor.metrics.ascent, stroke: "var(--nice-info, #339af0)", strokeWidth: em * 0.004 }), jsxRuntime.jsx("line", { x1: 0, y1: em, x2: em, y2: em, stroke: "var(--nice-success, #2f9e44)", strokeWidth: em * 0.004 }), jsxRuntime.jsx("line", { x1: 0, y1: em + Math.abs(editor.metrics.descent), x2: em, y2: em + Math.abs(editor.metrics.descent), stroke: "#e03131", strokeWidth: em * 0.004 }), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.xHeight, x2: em, y2: em - editor.metrics.xHeight, stroke: "#f9e2af", strokeWidth: em * 0.003, strokeDasharray: `${em * 0.015}` }), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.capHeight, x2: em, y2: em - editor.metrics.capHeight, stroke: "#f38ba8", strokeWidth: em * 0.003, strokeDasharray: `${em * 0.015}` }), jsxRuntime.
|
|
298
|
+
].map(([key, label]) => (jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }, children: [jsxRuntime.jsx("span", { style: { minWidth: 100, fontSize: 11 }, children: label }), jsxRuntime.jsx("input", { type: "number", value: editor.metrics[key], onChange: (e) => editor.setMetrics({ ...editor.metrics, [key]: Number(e.target.value) }), style: fieldStyle })] }, key))), jsxRuntime.jsxs("svg", { width: 300, height: 200, viewBox: `0 0 ${em} ${em}`, style: { background: 'var(--nice-bg, #141517)', borderRadius: 4, marginTop: 12 }, children: [jsxRuntime.jsx("rect", { x: 0, y: em - editor.metrics.ascent, width: em, height: editor.metrics.ascent - editor.metrics.descent, fill: "rgba(51,154,240,0.05)", stroke: "rgba(51,154,240,0.2)", strokeWidth: em * 0.003 }), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.ascent, x2: em, y2: em - editor.metrics.ascent, stroke: "var(--nice-info, #339af0)", strokeWidth: em * 0.004 }), jsxRuntime.jsx("line", { x1: 0, y1: em, x2: em, y2: em, stroke: "var(--nice-success, #2f9e44)", strokeWidth: em * 0.004 }), jsxRuntime.jsx("line", { x1: 0, y1: em + Math.abs(editor.metrics.descent), x2: em, y2: em + Math.abs(editor.metrics.descent), stroke: "#e03131", strokeWidth: em * 0.004 }), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.xHeight, x2: em, y2: em - editor.metrics.xHeight, stroke: "#f9e2af", strokeWidth: em * 0.003, strokeDasharray: `${em * 0.015}` }), jsxRuntime.jsx("line", { x1: 0, y1: em - editor.metrics.capHeight, x2: em, y2: em - editor.metrics.capHeight, stroke: "#f38ba8", strokeWidth: em * 0.003, strokeDasharray: `${em * 0.015}` }), jsxRuntime.jsx("text", { x: em * 0.02, y: em - editor.metrics.ascent + em * 0.05, fill: "var(--nice-info, #339af0)", fontSize: em * 0.04, children: t('fontEditor.ascent', 'Ascent ({ascent})').replace('{ascent}', String(editor.metrics.ascent)) }), jsxRuntime.jsx("text", { x: em * 0.02, y: em + em * 0.05, fill: "var(--nice-success, #2f9e44)", fontSize: em * 0.04, children: t('fontEditor.baseline', 'Baseline') }), jsxRuntime.jsx("text", { x: em * 0.02, y: em - editor.metrics.xHeight + em * 0.05, fill: "#f9e2af", fontSize: em * 0.04, children: t('fontEditor.xHeight', 'x-Height ({xHeight})').replace('{xHeight}', String(editor.metrics.xHeight)) }), jsxRuntime.jsx("text", { x: em * 0.02, y: em - editor.metrics.capHeight + em * 0.05, fill: "#f38ba8", fontSize: em * 0.04, children: t('fontEditor.cap', 'Cap ({capHeight})').replace('{capHeight}', String(editor.metrics.capHeight)) })] })] })), tab === 'kerning' && (jsxRuntime.jsxs("div", { style: { maxWidth: 500 }, children: [jsxRuntime.jsx("strong", { style: { fontSize: 12, display: 'block', marginBottom: 8 }, children: t('fontEditor.kerningPairs', 'Kerning Pairs') }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 4, marginBottom: 8, alignItems: 'center', fontSize: 11 }, children: [jsxRuntime.jsx("input", { value: newKernLeft, onChange: (e) => setNewKernLeft(e.target.value), maxLength: 1, style: { ...fieldStyle, width: 30, textAlign: 'center' } }), jsxRuntime.jsx("input", { value: newKernRight, onChange: (e) => setNewKernRight(e.target.value), maxLength: 1, style: { ...fieldStyle, width: 30, textAlign: 'center' } }), jsxRuntime.jsx("button", { onClick: () => {
|
|
299
299
|
if (newKernLeft && newKernRight) {
|
|
300
300
|
setKerningPairs((prev) => [
|
|
301
301
|
...prev,
|