@nice2dev/ui-graphics 1.2.0 → 1.2.1
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 +103 -0
- package/dist/cjs/animation/Audience.js +3 -1
- package/dist/cjs/animation/rig/RigPlayer.js +1 -1
- package/dist/cjs/animation/rig/presets.js +67 -55
- package/dist/cjs/animation/shapes/headwear.js +4 -1
- package/dist/cjs/core/NiceBrushEngine.js +5 -3
- package/dist/cjs/core/editorAsk.js +48 -0
- package/dist/cjs/core/i18n.js +7 -3
- package/dist/cjs/core/plugins.js +1 -1
- package/dist/cjs/core/rtl.js +1 -1
- package/dist/cjs/core/theme.js +1 -1
- package/dist/cjs/font/NiceFontEditor.js +13 -11
- package/dist/cjs/game/GameAsset2dEditor.js +28 -16
- package/dist/cjs/nice2dev-ui-graphics.css +1 -1
- package/dist/cjs/photo/FilterThumb.js +3 -3
- package/dist/cjs/photo/NiceAITools.js +5 -3
- package/dist/cjs/photo/PhotoEditor.js +3 -1
- package/dist/cjs/photo/PhotoEditorCanvas.js +8 -6
- package/dist/cjs/photo/PhotoEditorLeftPanel.js +19 -19
- package/dist/cjs/photo/PhotoEditorToolbar.js +4 -4
- package/dist/cjs/photo/photoFilters.js +1 -0
- package/dist/cjs/pixel/PixelEditor.js +6 -3
- package/dist/cjs/pixel/PixelEditorCanvas.js +10 -6
- package/dist/cjs/pixel/PixelEditorMenuBar.js +3 -3
- package/dist/cjs/pixel/PixelEditorRightPanel.js +28 -24
- package/dist/cjs/pixel/PixelEditorStatusBar.js +8 -4
- package/dist/cjs/pixel/PixelEditorTimeline.js +11 -2
- package/dist/cjs/pixel/PixelEditorToolbar.js +10 -6
- package/dist/cjs/pixel/SpriteGeneratorPanel.js +11 -7
- package/dist/cjs/pixel/animalGeneratorPresets.js +31 -31
- package/dist/cjs/pixel/buildingGeneratorPresets.js +24 -24
- package/dist/cjs/pixel/characterGeneratorPresets.js +25 -25
- package/dist/cjs/pixel/usePixelEditor.js +13 -10
- package/dist/cjs/texture/Nice3DTexturePainter.js +10 -8
- package/dist/cjs/ui/NiceUIDesigner.js +17 -13
- package/dist/cjs/vector/NiceSvgToCode.js +6 -4
- package/dist/cjs/vector/VectorEditor.js +3 -2
- package/dist/cjs/vector/VectorEditorShapeRenderer.js +3 -1
- package/dist/cjs/vector/useVectorEditor.js +38 -29
- package/dist/cjs/vector/vectorEditorImport.js +1 -0
- package/dist/cjs/vector/vectorIconImport.js +1 -0
- package/dist/esm/animation/Audience.js +3 -1
- package/dist/esm/animation/rig/RigPlayer.js +1 -1
- package/dist/esm/animation/rig/presets.js +68 -56
- package/dist/esm/animation/shapes/headwear.js +4 -1
- package/dist/esm/animation/spineExport.js +1 -1
- package/dist/esm/core/NiceBrushEngine.js +5 -3
- package/dist/esm/core/editorAsk.js +45 -0
- package/dist/esm/core/i18n.js +8 -4
- package/dist/esm/core/plugins.js +1 -1
- package/dist/esm/core/rtl.js +1 -1
- package/dist/esm/core/theme.js +1 -1
- package/dist/esm/font/NiceFontEditor.js +13 -11
- package/dist/esm/game/GameAsset2dEditor.js +28 -16
- package/dist/esm/nice2dev-ui-graphics.css +1 -1
- package/dist/esm/photo/FilterThumb.js +3 -3
- package/dist/esm/photo/NiceAITools.js +5 -3
- package/dist/esm/photo/PhotoEditor.js +3 -1
- package/dist/esm/photo/PhotoEditorCanvas.js +8 -6
- package/dist/esm/photo/PhotoEditorLeftPanel.js +19 -19
- package/dist/esm/photo/PhotoEditorToolbar.js +4 -4
- package/dist/esm/photo/photoFilters.js +1 -0
- package/dist/esm/pixel/PixelEditor.js +6 -3
- package/dist/esm/pixel/PixelEditorCanvas.js +10 -6
- package/dist/esm/pixel/PixelEditorMenuBar.js +3 -3
- package/dist/esm/pixel/PixelEditorRightPanel.js +28 -24
- package/dist/esm/pixel/PixelEditorStatusBar.js +8 -4
- package/dist/esm/pixel/PixelEditorTimeline.js +11 -2
- package/dist/esm/pixel/PixelEditorToolbar.js +10 -6
- package/dist/esm/pixel/SpriteGeneratorPanel.js +11 -7
- package/dist/esm/pixel/animalGeneratorPresets.js +32 -32
- package/dist/esm/pixel/buildingGeneratorPresets.js +25 -25
- package/dist/esm/pixel/characterGeneratorPresets.js +26 -26
- package/dist/esm/pixel/usePixelEditor.js +13 -10
- package/dist/esm/texture/Nice3DTexturePainter.js +10 -8
- package/dist/esm/ui/NiceUIDesigner.js +17 -13
- package/dist/esm/vector/NiceSvgToCode.js +6 -4
- package/dist/esm/vector/VectorEditor.js +3 -2
- package/dist/esm/vector/VectorEditorShapeRenderer.js +3 -1
- package/dist/esm/vector/useVectorEditor.js +38 -29
- package/dist/esm/vector/vectorEditorImport.js +1 -0
- package/dist/esm/vector/vectorIconImport.js +1 -0
- package/dist/types/core/LocalUI.d.ts +1 -2
- package/dist/types/core/editorAsk.d.ts +26 -0
- package/dist/types/core/i18n.d.ts +1 -17
- package/dist/types/pixel/usePixelEditor.d.ts +3 -0
- package/dist/types/vector/useVectorEditor.d.ts +2 -0
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,108 @@
|
|
|
1
1
|
# @nice2dev/ui-graphics — Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8c82c30: 1.2.1 — the decisions that waited since 1.2.0, and the debts the CI gates were holding. One API
|
|
8
|
+
change, agreed: `NiceIconSuggestorAI` suggests Nice icons only. Two stylesheets to import if you use
|
|
9
|
+
the packages: `@nice2dev/social/style.css`, `@nice2dev/ui-science/style.css`. Upgrade notes:
|
|
10
|
+
`documentation/omniverk/OMNIVERK_UPGRADE_1.2.1.md`.
|
|
11
|
+
|
|
12
|
+
**Tree-shaking.** An import from a package no longer brings the whole package. Module-level
|
|
13
|
+
statements that may have a side effect — a `displayName` assigned after each component (now
|
|
14
|
+
`Object.assign` inside `/* @__PURE__ */`, or under a development check for plain functions),
|
|
15
|
+
unannotated `forwardRef` / `memo` / `createContext` / icon-factory calls, spreads, registries filled
|
|
16
|
+
at import, `<style>` tags injected at import, a direct `eval` in ui-math — are gone. One import from
|
|
17
|
+
each of 53 packages: 5066.5 kB → 1171.6 kB (`@nice2dev/ui` 1928.2 kB → 12.9 kB, icon packs 48–275 kB
|
|
18
|
+
→ 1.6–2.6 kB). `defaultViewRegistry` has the Nice\* input controls from the start, each loading its
|
|
19
|
+
module on first render; sixteen controls add their styles to `<head>` on first render.
|
|
20
|
+
|
|
21
|
+
**Dead code.** 405 source files no entry point imported (never on npm) are deleted;
|
|
22
|
+
`NiceClipboardStack` (`@nice2dev/ui`) and `NiceSampleBrowser` (`@nice2dev/ui-audio`), finished and
|
|
23
|
+
documented, are exported.
|
|
24
|
+
|
|
25
|
+
**ui-mobile draws Nice icons.** New peer `@nice2dev/icons ^1.2.0`; `TabIcons`, `DrawerIcons`,
|
|
26
|
+
`HeaderIcons`, the `NiceSwipeActions` presets and the `NiceInfiniteScroll` states are `Ntd*` icons
|
|
27
|
+
instead of pasted `<svg>`, simple glyphs are CSS marks. `@nice2dev/icons` adds `NtdReply` and
|
|
28
|
+
`NtdInbox`; an icon no longer throws without `window.matchMedia`.
|
|
29
|
+
|
|
30
|
+
**`NiceIconSuggestorAI` (breaking, agreed).** `AIIconLibrary` is `'nice'`; `svgPath` and `unicode`
|
|
31
|
+
left `AISuggestedIcon`; the preview is a `<NiceIcon>`, "Copy SVG" is "Copy JSX".
|
|
32
|
+
|
|
33
|
+
**Peer ranges.** Every internal `@nice2dev/*` peer is `^1.2.0` (was as wide as `^1.0.20`), except
|
|
34
|
+
`@nice2dev/icons ^1.2.1` where a package draws an icon new in 1.2.1: ui-mobile (`NtdReply`,
|
|
35
|
+
`NtdInbox`), ui-audio (`NtdMicrophoneOff`) and ui-security (`NtdBackspace`). ui-security, licensing
|
|
36
|
+
(`^1.2.0`) and templates (`^1.2.0`) gain the `@nice2dev/icons` peer: their glyph icons (`⌫`, `←`,
|
|
37
|
+
`→`, `↑`, `↓`) are Nice icons now. `@nice2dev/icons` adds `NtdBackspace`.
|
|
38
|
+
|
|
39
|
+
**Class names inside components.** 834 class names in library stylesheets had no `nice-` prefix
|
|
40
|
+
(`.tab-button`, `.status-badge`, `.selected`, …) and styled every element of that name on the page.
|
|
41
|
+
They follow `nice-{block}__{element}--{modifier}` now — no change on screen. The class theme hooks
|
|
42
|
+
next to `[data-theme]` on seven controls (`dark` on the AI chart / data grid / code and HTML
|
|
43
|
+
editors / form builder, `light-theme` on `NiceMAM` and `NiceVideoAnnotator`) are `nice-dark` /
|
|
44
|
+
`nice-light`; `NiceLowPowerMode` marks `<html>` with `nice-lpm-no-animations` /
|
|
45
|
+
`nice-lpm-reduced-quality`; the `NiceVideoEditor` of `@nice2dev/ui-video` styles its elements under
|
|
46
|
+
`nice-video-studio`, so they no longer collide with the `NiceVideoEditor` of `@nice2dev/ui`.
|
|
47
|
+
Rules for classes nothing rendered are gone, among them the stylesheet of `NiceGanttChart`, which
|
|
48
|
+
left ui-erp in 1.0.10.
|
|
49
|
+
|
|
50
|
+
**Translations.** About 3,400 UI texts in 32 packages were hard-coded; they go through `t()` now,
|
|
51
|
+
with the English text unchanged and pl / de / fr / es / uk in `@nice2dev/i18n` (ui-diagrams,
|
|
52
|
+
ui-presentations and ui-math in their own dictionaries). In `@nice2dev/events` and
|
|
53
|
+
`@nice2dev/admin` that includes column headers, option labels and the statuses and roles they used
|
|
54
|
+
to print raw (`checked-in`, `no-show`) — English labels for those come from the dictionary. ui-3d and ui-graphics read the app's
|
|
55
|
+
global translator, as ui-audio does since 1.2.0 — their own provider overrides it. ui-audio's
|
|
56
|
+
controls used 631 keys the dictionary did not have (English in every language), and the radio
|
|
57
|
+
player showed raw keys (`search_stations`). Texts that were hard-coded **in Polish** — in
|
|
58
|
+
`NiceSidebarNav`, `NiceNetworkDiscoveryPanel`, the backup and smart-home controls,
|
|
59
|
+
`NiceSavedQueryPanel`, `NiceNotificationPreferences`, the ui-buildings panels, the ui-printing
|
|
60
|
+
template editor and the photo editor — default to English now; Polish is in the dictionary. English
|
|
61
|
+
plurals glued on (`result{s}`) are two keys. `presentationsTranslator(lang)` (ui-presentations)
|
|
62
|
+
translates outside its provider.
|
|
63
|
+
|
|
64
|
+
**List props are optional.** 170 list props (`items`, `columns`, `data`, …) in 15 packages that
|
|
65
|
+
were required without a default are optional, with a stable empty default; every one of those
|
|
66
|
+
controls mounts without data (tested). `NiceStockChart` without data no longer computes NaN
|
|
67
|
+
coordinates.
|
|
68
|
+
|
|
69
|
+
**Unstyled components get stylesheets.** 98 components rendered class names no stylesheet had —
|
|
70
|
+
admin panels, auth forms, events rounds and pickers, the ERP invoice / purchase order / VAT table /
|
|
71
|
+
HR dashboard, the radio player, `NiceInbox`, PWA prompts, `NiceViewRenderer`, shells, smart-home,
|
|
72
|
+
… The fixed layout that sat in inline style objects is in classes now, so an app can override it;
|
|
73
|
+
values that depend on data stay inline. **`@nice2dev/social` and `@nice2dev/ui-science` ship a
|
|
74
|
+
stylesheet — import `@nice2dev/social/style.css` / `@nice2dev/ui-science/style.css`.** ui-science's
|
|
75
|
+
`sideEffects` is `["*.css"]`. Colours, spacing and radii in every stylesheet come from theme tokens
|
|
76
|
+
(visible in dark themes: `NiceRecipe` difficulty badges, transcoder states, warehouse heat map).
|
|
77
|
+
|
|
78
|
+
**Icons only from Nice.** About 130 glyphs (`×`, `→`, `↑`) and 100 pasted `<svg>` used as icons
|
|
79
|
+
are Nice icons; 11 `<svg>` stay as the control's own graphic (logos, spinners, checkbox mark). `NiceIcon`
|
|
80
|
+
gains `pin`, `mic-off`, `log-out`, `chevrons-left`, `chevrons-right`, `zoom-in`, `zoom-out`,
|
|
81
|
+
`navigation`, `refresh-ccw`, `minimize` and `smartphone`; `@nice2dev/icons` gains
|
|
82
|
+
`NtdMicrophoneOff`. The `icon` fields of the module, menu, tab, mode, quick-action, scene and admin
|
|
83
|
+
widget DTOs, notification channels and view toolbar items are rendered through `NiceIcon` — they
|
|
84
|
+
used to be printed as text. `NiceRatings` `thumbs` draws shapes instead of emoji.
|
|
85
|
+
|
|
86
|
+
**Dialogs instead of `window.prompt` / `confirm` / `alert`.** New `useNiceDialogs()` — `prompt`,
|
|
87
|
+
`confirm` and `alert` as promises on `NicePromptDialog` / `NiceConfirmDialog` — and
|
|
88
|
+
`NiceConfirmDialog.showReject`. Seventeen controls asked through the browser's blocking dialogs and
|
|
89
|
+
use the library's now (`NiceFileManager`, `NiceHtmlEditor`, `NiceViewRenderer` action `confirm`, the 3D,
|
|
90
|
+
vector and pixel editors, …). Keys pressed in `NicePromptDialog` / `NiceConfirmDialog` no longer
|
|
91
|
+
reach the page's shortcuts (Escape still closes them), and `NicePromptDialog`'s backdrop covers the
|
|
92
|
+
whole viewport.
|
|
93
|
+
|
|
94
|
+
**Smaller fixes.** `NiceFileManager.items` is optional. `NiceMLStudio` status badges: `draft`,
|
|
95
|
+
`registered`, `archived`, `cancelled` get a neutral colour, deploying / stopped deployments theirs.
|
|
96
|
+
Component docs (`doc.json`) list only props the components have. `NiceMobileTabs`: tabs beyond
|
|
97
|
+
`maxTabs` open from "More" (it only logged). `NiceInventoryManager`: new `onCreatePurchaseOrder`;
|
|
98
|
+
without it the "Create PO" button is gone (it only logged). Keyboard: `NiceNavShell` tabs follow the
|
|
99
|
+
WAI-ARIA tabs pattern, clickable `NiceDeviceTile` / `NiceCardGroup` cards and animated icons with
|
|
100
|
+
`trigger="click"` are focusable. ui-math: `CalculatorService.evaluate` parses instead of running
|
|
101
|
+
`new Function` (`asin`, `sinh`, `ceil`, exponents and `angleUnit` were wrong); `ExpressionParser`
|
|
102
|
+
takes `functions`, `constants`, `ignoreCase` and `strict`, multiplies implicitly (`2x`) and reads `^`
|
|
103
|
+
right to left. `InvoiceLine.vatRate` and `VatRate.rate` are documented as fractions (0.23).
|
|
104
|
+
`AmqpServerBinding` is `Record<string, never>`.
|
|
105
|
+
|
|
3
106
|
## 1.2.0
|
|
4
107
|
|
|
5
108
|
### Minor Changes
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var ui = require('@nice2dev/ui');
|
|
5
5
|
var React = require('react');
|
|
6
|
+
var i18n = require('../core/i18n.js');
|
|
6
7
|
var AnimatedPerson = require('./AnimatedPerson.js');
|
|
7
8
|
var characterTypes = require('./characterTypes.js');
|
|
8
9
|
var choreoDSL = require('./choreoDSL.js');
|
|
@@ -35,6 +36,7 @@ function makeAudience(n) {
|
|
|
35
36
|
}
|
|
36
37
|
/** Grid of animated audience members with wave/cheer actions. */
|
|
37
38
|
const NiceAudience = ({ count = 36, columns = 12 }) => {
|
|
39
|
+
const { t } = i18n.useNiceTranslation();
|
|
38
40
|
const people = React.useMemo(() => makeAudience(count), [count]);
|
|
39
41
|
const refs = React.useRef([]);
|
|
40
42
|
refs.current = [];
|
|
@@ -59,7 +61,7 @@ const NiceAudience = ({ count = 36, columns = 12 }) => {
|
|
|
59
61
|
display: 'grid',
|
|
60
62
|
gap: 8,
|
|
61
63
|
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
|
|
62
|
-
}, children: people.map((c, i) => (jsxRuntime.jsx(AnimatedPerson, { character: c, score: 10, startPose: "idle", onReady: (fx) => (refs.current[i] = fx) }, i))) }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 8 }, children: [jsxRuntime.jsx(ui.NiceButton, { variant: "ghost", size: "sm", onClick: wave, children:
|
|
64
|
+
}, children: people.map((c, i) => (jsxRuntime.jsx(AnimatedPerson, { character: c, score: 10, startPose: "idle", onReady: (fx) => (refs.current[i] = fx) }, i))) }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 8 }, children: [jsxRuntime.jsx(ui.NiceButton, { variant: "ghost", size: "sm", onClick: wave, children: t('audience.wave', 'Wave') }), jsxRuntime.jsx(ui.NiceButton, { variant: "ghost", size: "sm", onClick: cheer, children: t('audience.cheer', 'Cheer') })] })] }));
|
|
63
65
|
};
|
|
64
66
|
|
|
65
67
|
module.exports = NiceAudience;
|
|
@@ -9,7 +9,7 @@ var RiggedBody = require('./RiggedBody.js');
|
|
|
9
9
|
var rigMath = require('./rigMath.js');
|
|
10
10
|
var rigTypes = require('./rigTypes.js');
|
|
11
11
|
|
|
12
|
-
const NiceRigPlayer = React.forwardRef(({ character, rig, pose, size, debug }, ref) => {
|
|
12
|
+
const NiceRigPlayer = /* @__PURE__ */ React.forwardRef(({ character, rig, pose, size, debug }, ref) => {
|
|
13
13
|
var _a;
|
|
14
14
|
const [current, setCurrent] = React.useState({ ...rigTypes.DEFAULT_POSE, ...(pose !== null && pose !== void 0 ? pose : {}) });
|
|
15
15
|
const r = rig !== null && rig !== void 0 ? rig : rigTypes.DEFAULT_RIG;
|
|
@@ -3,66 +3,78 @@
|
|
|
3
3
|
var rigTypes = require('./rigTypes.js');
|
|
4
4
|
|
|
5
5
|
/** Slim proportions preset. */
|
|
6
|
-
const RIG_SLIM = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
6
|
+
const RIG_SLIM = /* @__PURE__ */ (function () {
|
|
7
|
+
return {
|
|
8
|
+
...rigTypes.DEFAULT_RIG,
|
|
9
|
+
lUpperArm: { ...rigTypes.DEFAULT_RIG.lUpperArm, length: 0.11 },
|
|
10
|
+
lForearm: { ...rigTypes.DEFAULT_RIG.lForearm, length: 0.11 },
|
|
11
|
+
rUpperArm: { ...rigTypes.DEFAULT_RIG.rUpperArm, length: 0.11 },
|
|
12
|
+
rForearm: { ...rigTypes.DEFAULT_RIG.rForearm, length: 0.11 },
|
|
13
|
+
lThigh: { ...rigTypes.DEFAULT_RIG.lThigh, length: 0.14 },
|
|
14
|
+
lShin: { ...rigTypes.DEFAULT_RIG.lShin, length: 0.14 },
|
|
15
|
+
rThigh: { ...rigTypes.DEFAULT_RIG.rThigh, length: 0.14 },
|
|
16
|
+
rShin: { ...rigTypes.DEFAULT_RIG.rShin, length: 0.14 },
|
|
17
|
+
};
|
|
18
|
+
})();
|
|
17
19
|
/** Broad shoulders preset. */
|
|
18
|
-
const RIG_HERO = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
20
|
+
const RIG_HERO = /* @__PURE__ */ (function () {
|
|
21
|
+
return {
|
|
22
|
+
...rigTypes.DEFAULT_RIG,
|
|
23
|
+
lShoulder: { ...rigTypes.DEFAULT_RIG.lShoulder, offset: [-0.13, 0] },
|
|
24
|
+
rShoulder: { ...rigTypes.DEFAULT_RIG.rShoulder, offset: [0.13, 0] },
|
|
25
|
+
lUpperArm: { ...rigTypes.DEFAULT_RIG.lUpperArm, length: 0.13 },
|
|
26
|
+
rUpperArm: { ...rigTypes.DEFAULT_RIG.rUpperArm, length: 0.13 },
|
|
27
|
+
};
|
|
28
|
+
})();
|
|
25
29
|
/** Robot-like short limbs preset. */
|
|
26
|
-
const RIG_ROBOT = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
30
|
+
const RIG_ROBOT = /* @__PURE__ */ (function () {
|
|
31
|
+
return {
|
|
32
|
+
...rigTypes.DEFAULT_RIG,
|
|
33
|
+
lUpperArm: { ...rigTypes.DEFAULT_RIG.lUpperArm, length: 0.1 },
|
|
34
|
+
lForearm: { ...rigTypes.DEFAULT_RIG.lForearm, length: 0.1 },
|
|
35
|
+
rUpperArm: { ...rigTypes.DEFAULT_RIG.rUpperArm, length: 0.1 },
|
|
36
|
+
rForearm: { ...rigTypes.DEFAULT_RIG.rForearm, length: 0.1 },
|
|
37
|
+
lThigh: { ...rigTypes.DEFAULT_RIG.lThigh, length: 0.13 },
|
|
38
|
+
lShin: { ...rigTypes.DEFAULT_RIG.lShin, length: 0.13 },
|
|
39
|
+
rThigh: { ...rigTypes.DEFAULT_RIG.rThigh, length: 0.13 },
|
|
40
|
+
rShin: { ...rigTypes.DEFAULT_RIG.rShin, length: 0.13 },
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
37
43
|
/** Happy pose. */
|
|
38
|
-
const POSE_HAPPY = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
const POSE_HAPPY = /* @__PURE__ */ (function () {
|
|
45
|
+
return {
|
|
46
|
+
...rigTypes.DEFAULT_POSE,
|
|
47
|
+
spineUpper: 80,
|
|
48
|
+
neck: 70,
|
|
49
|
+
head: 60,
|
|
50
|
+
lUpperArm: 60,
|
|
51
|
+
lForearm: 20,
|
|
52
|
+
rUpperArm: 120,
|
|
53
|
+
rForearm: 160,
|
|
54
|
+
};
|
|
55
|
+
})();
|
|
48
56
|
/** Angry pose. */
|
|
49
|
-
const POSE_ANGRY = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
const POSE_ANGRY = /* @__PURE__ */ (function () {
|
|
58
|
+
return {
|
|
59
|
+
...rigTypes.DEFAULT_POSE,
|
|
60
|
+
head: 90,
|
|
61
|
+
lUpperArm: 150,
|
|
62
|
+
rUpperArm: 30,
|
|
63
|
+
lForearm: 160,
|
|
64
|
+
rForearm: 20,
|
|
65
|
+
};
|
|
66
|
+
})();
|
|
57
67
|
/** Dance pose A. */
|
|
58
|
-
const POSE_DANCE_A = {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
const POSE_DANCE_A = /* @__PURE__ */ (function () {
|
|
69
|
+
return {
|
|
70
|
+
...rigTypes.DEFAULT_POSE,
|
|
71
|
+
pelvis: 100,
|
|
72
|
+
lThigh: 120,
|
|
73
|
+
rThigh: 70,
|
|
74
|
+
lUpperArm: 30,
|
|
75
|
+
rUpperArm: 150,
|
|
76
|
+
};
|
|
77
|
+
})();
|
|
66
78
|
|
|
67
79
|
exports.POSE_ANGRY = POSE_ANGRY;
|
|
68
80
|
exports.POSE_DANCE_A = POSE_DANCE_A;
|
|
@@ -10,13 +10,16 @@ const HeadwearCap = ({ colors, scale = 1, transform }) => {
|
|
|
10
10
|
};
|
|
11
11
|
const HeadwearCrown = ({ colors, scale = 1, transform }) => {
|
|
12
12
|
const base = characterTypes.pick(colors, 0, 'var(--nice-warning, #FFD700)');
|
|
13
|
-
return (jsxRuntime.jsx("g", { transform: `${transform !== null && transform !== void 0 ? transform : ''} scale(${scale})`, children: jsxRuntime.jsx("path", { d: "M-22 -30 l6 -16 l8 10 l8 -14 l8 14 l8 -10 l6 16 z", fill: base,
|
|
13
|
+
return (jsxRuntime.jsx("g", { transform: `${transform !== null && transform !== void 0 ? transform : ''} scale(${scale})`, children: jsxRuntime.jsx("path", { d: "M-22 -30 l6 -16 l8 10 l8 -14 l8 14 l8 -10 l6 16 z", fill: base,
|
|
14
|
+
// audit-allow: literal-color — a character-generator shape: its own drawing colour
|
|
15
|
+
stroke: "#B8860B", strokeWidth: 1.2 }) }));
|
|
14
16
|
};
|
|
15
17
|
const HeadwearHeadphones = ({ colors, scale = 1, transform }) => {
|
|
16
18
|
const base = characterTypes.pick(colors, 0, 'var(--nice-text, #333)');
|
|
17
19
|
return (jsxRuntime.jsxs("g", { transform: `${transform !== null && transform !== void 0 ? transform : ''} scale(${scale})`, stroke: base, strokeWidth: 2.5, fill: "none", children: [jsxRuntime.jsx("path", { d: "M-30 0 q0 -40 30 -42 q30 2 30 42" }), jsxRuntime.jsx("rect", { x: -36, y: -6, width: 10, height: 16, rx: 3, fill: base }), jsxRuntime.jsx("rect", { x: 26, y: -6, width: 10, height: 16, rx: 3, fill: base })] }));
|
|
18
20
|
};
|
|
19
21
|
const HeadwearBeanie = ({ colors, scale = 1, transform }) => {
|
|
22
|
+
// audit-allow: literal-color — the hat's default colour when the character sets none
|
|
20
23
|
const base = characterTypes.pick(colors, 0, '#E53E3E');
|
|
21
24
|
return (jsxRuntime.jsxs("g", { transform: `${transform !== null && transform !== void 0 ? transform : ''} scale(${scale})`, children: [jsxRuntime.jsx("path", { d: "M-28 -14 q0 -22 28 -26 q28 4 28 26 z", fill: base }), jsxRuntime.jsx("circle", { cx: 0, cy: -42, r: 6, fill: base })] }));
|
|
22
25
|
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var uiCore = require('@nice2dev/ui-core');
|
|
5
5
|
var React = require('react');
|
|
6
|
+
var i18n = require('./i18n.js');
|
|
6
7
|
|
|
7
8
|
/* ── Brush Presets ── */
|
|
8
9
|
const DEFAULT_DYNAMICS = {
|
|
@@ -543,6 +544,7 @@ color = '#000000') {
|
|
|
543
544
|
}
|
|
544
545
|
/* ── Component ── */
|
|
545
546
|
const NiceBrushEngine = ({ canvas, color = '#000000', onStrokeEnd, className = '', }) => {
|
|
547
|
+
const { t } = i18n.useNiceTranslation();
|
|
546
548
|
const engine = useBrushEngine(canvas, color);
|
|
547
549
|
// Override endStroke to fire callback
|
|
548
550
|
React.useEffect(() => {
|
|
@@ -559,7 +561,7 @@ const NiceBrushEngine = ({ canvas, color = '#000000', onStrokeEnd, className = '
|
|
|
559
561
|
return () => canvas.removeEventListener('pointerup', onUp);
|
|
560
562
|
}, [canvas, engine.endStroke, onStrokeEnd]);
|
|
561
563
|
const s = engine.settings;
|
|
562
|
-
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 }), " Brush Engine v2
|
|
564
|
+
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) => {
|
|
563
565
|
var _a, _b;
|
|
564
566
|
return (jsxRuntime.jsx("button", { type: "button", onClick: () => engine.applyPreset(name), style: {
|
|
565
567
|
padding: '2px 6px',
|
|
@@ -572,13 +574,13 @@ const NiceBrushEngine = ({ canvas, color = '#000000', onStrokeEnd, className = '
|
|
|
572
574
|
: 'transparent',
|
|
573
575
|
color: s.name === ((_b = BRUSH_PRESETS[name]) === null || _b === void 0 ? void 0 : _b.name) ? 'var(--nice-color-white, #fff)' : 'inherit',
|
|
574
576
|
}, children: BRUSH_PRESETS[name].name }, name));
|
|
575
|
-
}) }), jsxRuntime.jsxs("label", { children: ["Size: ", s.size, "px", 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: ["Opacity: ", 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: ["Flow: ", 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: ["Hardness: ", 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: ["Smoothing: ", 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: ["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:
|
|
577
|
+
}) }), jsxRuntime.jsxs("label", { children: ["Size: ", s.size, "px", 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: ["Opacity: ", 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: ["Flow: ", 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: ["Hardness: ", 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: ["Smoothing: ", 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: ["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: ["Roundness: ", 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: ["Source:", jsxRuntime.jsxs("select", { value: s.sizeDynamics.source, onChange: (e) => engine.setSettings({
|
|
576
578
|
...s,
|
|
577
579
|
sizeDynamics: {
|
|
578
580
|
...s.sizeDynamics,
|
|
579
581
|
source: e.target.value,
|
|
580
582
|
},
|
|
581
|
-
}), style: { width: '100%' }, children: [jsxRuntime.jsx("option", { value: "none", children: "Off" }), jsxRuntime.jsx("option", { value: "pressure", children:
|
|
583
|
+
}), style: { width: '100%' }, children: [jsxRuntime.jsx("option", { value: "none", children: "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: ["Scatter: ", 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: ["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')] })] }));
|
|
582
584
|
};
|
|
583
585
|
|
|
584
586
|
exports.BRUSH_PRESETS = BRUSH_PRESETS;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var ui = require('@nice2dev/ui');
|
|
5
|
+
var React = require('react');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The library's dialogs in place of `window.prompt` / `window.confirm` for the editor hooks,
|
|
9
|
+
* which hold state but render nothing: `ask` resolves with the answer (`null` when cancelled)
|
|
10
|
+
* and `pendingAsk` is the open question, for the editor component to pass to
|
|
11
|
+
* {@link EditorAskDialog}. One question at a time: a new one settles the open one as cancelled.
|
|
12
|
+
*/
|
|
13
|
+
function useEditorAsk() {
|
|
14
|
+
const [pendingAsk, setPendingAsk] = React.useState(null);
|
|
15
|
+
const pendingRef = React.useRef(null);
|
|
16
|
+
const ask = React.useCallback((kind, text, defaultValue) => new Promise((resolve) => {
|
|
17
|
+
var _a;
|
|
18
|
+
(_a = pendingRef.current) === null || _a === void 0 ? void 0 : _a.answer(null);
|
|
19
|
+
const next = {
|
|
20
|
+
kind,
|
|
21
|
+
text,
|
|
22
|
+
defaultValue,
|
|
23
|
+
answer: (value) => {
|
|
24
|
+
if (pendingRef.current === next) {
|
|
25
|
+
pendingRef.current = null;
|
|
26
|
+
setPendingAsk(null);
|
|
27
|
+
}
|
|
28
|
+
resolve(value);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
pendingRef.current = next;
|
|
32
|
+
setPendingAsk(next);
|
|
33
|
+
}), []);
|
|
34
|
+
return { pendingAsk, ask };
|
|
35
|
+
}
|
|
36
|
+
/** Shows the question {@link useEditorAsk} is waiting on, if any. */
|
|
37
|
+
const EditorAskDialog = ({ ask }) => {
|
|
38
|
+
if (!ask) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
if (ask.kind === 'confirm') {
|
|
42
|
+
return (jsxRuntime.jsx(ui.NiceConfirmDialog, { visible: true, message: ask.text, onAccept: () => ask.answer(''), onReject: () => ask.answer(null) }));
|
|
43
|
+
}
|
|
44
|
+
return (jsxRuntime.jsx(ui.NicePromptDialog, { open: true, title: ask.text, defaultValue: ask.defaultValue, onSubmit: (value) => ask.answer(value), onCancel: () => ask.answer(null) }));
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
exports.EditorAskDialog = EditorAskDialog;
|
|
48
|
+
exports.useEditorAsk = useEditorAsk;
|
package/dist/cjs/core/i18n.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var uiCore = require('@nice2dev/ui-core');
|
|
4
5
|
var React = require('react');
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
const NiceI18nContext = React.createContext(
|
|
7
|
+
/** `null` outside this package's provider: the global translator applies. */
|
|
8
|
+
const NiceI18nContext = /* @__PURE__ */ React.createContext(null);
|
|
9
|
+
/** Overrides the app's global translator for the graphics controls below it. */
|
|
8
10
|
const NiceI18nProvider = ({ t, children }) => (jsxRuntime.jsx(NiceI18nContext.Provider, { value: t, children: children }));
|
|
9
11
|
function useNiceTranslation() {
|
|
10
|
-
const
|
|
12
|
+
const own = React.useContext(NiceI18nContext);
|
|
13
|
+
const { t: global } = uiCore.useNiceTranslation();
|
|
14
|
+
const t = React.useMemo(() => own !== null && own !== void 0 ? own : ((key, defaultValue) => global(key, defaultValue)), [own, global]);
|
|
11
15
|
return { t };
|
|
12
16
|
}
|
|
13
17
|
|
package/dist/cjs/core/plugins.js
CHANGED
|
@@ -112,7 +112,7 @@ function usePlugins(editor) {
|
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
114
|
// ── Plugin Context (React) ──────────────────────────────────────
|
|
115
|
-
const PluginContext = React.createContext(PluginRegistry);
|
|
115
|
+
const PluginContext = /* @__PURE__ */ React.createContext(PluginRegistry);
|
|
116
116
|
const NicePluginProvider = ({ plugins, children }) => {
|
|
117
117
|
React.useEffect(() => {
|
|
118
118
|
if (plugins) {
|
package/dist/cjs/core/rtl.js
CHANGED
|
@@ -14,7 +14,7 @@ const defaultRTLConfig = {
|
|
|
14
14
|
mirrorToolbar: false,
|
|
15
15
|
mirrorPanels: false,
|
|
16
16
|
};
|
|
17
|
-
const RTLContext = React.createContext(defaultRTLConfig);
|
|
17
|
+
const RTLContext = /* @__PURE__ */ React.createContext(defaultRTLConfig);
|
|
18
18
|
/** Provider for RTL configuration. Wraps editors to enable RTL support. */
|
|
19
19
|
const NiceRTLProvider = ({ direction = 'ltr', mirrorToolbar = true, mirrorPanels = true, children }) => {
|
|
20
20
|
const config = React.useMemo(() => ({
|
package/dist/cjs/core/theme.js
CHANGED
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var ui = require('@nice2dev/ui');
|
|
5
5
|
var uiCore = require('@nice2dev/ui-core');
|
|
6
6
|
var React = require('react');
|
|
7
|
+
var i18n = require('../core/i18n.js');
|
|
7
8
|
|
|
8
9
|
/** Built-in guided tour (EN inline + i18n keys → PL and other locales). */
|
|
9
10
|
const FONT_EDITOR_TOUR = [
|
|
@@ -134,6 +135,7 @@ function useFontEditor(initialName) {
|
|
|
134
135
|
/** Font glyph editor with metrics, visual path editing, and preview. */
|
|
135
136
|
const NiceFontEditor = ({ fontName = 'MyFont', onSave, className = '', tutorial, }) => {
|
|
136
137
|
var _a;
|
|
138
|
+
const { t } = i18n.useNiceTranslation();
|
|
137
139
|
const editor = useFontEditor(fontName);
|
|
138
140
|
const [drawMode, setDrawMode] = React.useState('select');
|
|
139
141
|
const [pathPoints, setPathPoints] = React.useState([]);
|
|
@@ -221,7 +223,7 @@ const NiceFontEditor = ({ fontName = 'MyFont', onSave, className = '', tutorial,
|
|
|
221
223
|
color: 'var(--nice-text, #c1c2c5)',
|
|
222
224
|
padding: 12,
|
|
223
225
|
borderRadius: 8,
|
|
224
|
-
}, children: [jsxRuntime.jsx("span", { style: { position: 'absolute', top: 6, right: 6, zIndex: 20 }, children: jsxRuntime.jsx(ui.NiceTutorialButton, { steps: ui.resolveTutorial(tutorial, FONT_EDITOR_TOUR) }) }), jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, fontSize: 12 }, children: [jsxRuntime.jsx("strong", { style: { fontSize: 14 }, children:
|
|
226
|
+
}, children: [jsxRuntime.jsx("span", { style: { position: 'absolute', top: 6, right: 6, zIndex: 20 }, children: jsxRuntime.jsx(ui.NiceTutorialButton, { steps: ui.resolveTutorial(tutorial, FONT_EDITOR_TOUR) }) }), jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, fontSize: 12 }, children: [jsxRuntime.jsx("strong", { style: { fontSize: 14 }, children: t('fontEditor.fontEditor', 'Font Editor') }), jsxRuntime.jsx("input", { value: editor.fontName, onChange: (e) => editor.setFontName(e.target.value), placeholder: t('fontEditor.fontName', 'Font name'), style: { flex: 1, maxWidth: 200, ...fieldStyle, fontSize: 12 } }), jsxRuntime.jsx("button", { onClick: handleSave, style: {
|
|
225
227
|
padding: '4px 12px',
|
|
226
228
|
background: 'var(--nice-success, #2f9e44)',
|
|
227
229
|
color: 'var(--nice-text-on-primary, #fff)',
|
|
@@ -229,7 +231,7 @@ const NiceFontEditor = ({ fontName = 'MyFont', onSave, className = '', tutorial,
|
|
|
229
231
|
borderRadius: 3,
|
|
230
232
|
cursor: 'pointer',
|
|
231
233
|
fontSize: 11,
|
|
232
|
-
}, children:
|
|
234
|
+
}, children: t('fontEditor.save', 'Save') }), jsxRuntime.jsx("button", { onClick: handleExportSvg, style: {
|
|
233
235
|
padding: '4px 12px',
|
|
234
236
|
background: '#228be6',
|
|
235
237
|
color: 'var(--nice-color-white, #fff)',
|
|
@@ -237,7 +239,7 @@ const NiceFontEditor = ({ fontName = 'MyFont', onSave, className = '', tutorial,
|
|
|
237
239
|
borderRadius: 3,
|
|
238
240
|
cursor: 'pointer',
|
|
239
241
|
fontSize: 11,
|
|
240
|
-
}, children:
|
|
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.jsxs("div", { style: {
|
|
241
243
|
fontSize: 11,
|
|
242
244
|
marginBottom: 4,
|
|
243
245
|
color: 'var(--nice-text-secondary, #868e96)',
|
|
@@ -272,7 +274,7 @@ const NiceFontEditor = ({ fontName = 'MyFont', onSave, className = '', tutorial,
|
|
|
272
274
|
border: g.path
|
|
273
275
|
? '1px solid var(--nice-border, #373a40)'
|
|
274
276
|
: '1px solid transparent',
|
|
275
|
-
}, 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:
|
|
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: "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: {
|
|
276
278
|
background: 'var(--nice-bg, #141517)',
|
|
277
279
|
borderRadius: 4,
|
|
278
280
|
cursor: drawMode === 'pen' ? 'crosshair' : 'default',
|
|
@@ -280,32 +282,32 @@ const NiceFontEditor = ({ fontName = 'MyFont', onSave, className = '', tutorial,
|
|
|
280
282
|
Array.from({ length: 11 }, (_, i) => {
|
|
281
283
|
const pos = (i / 10) * em;
|
|
282
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));
|
|
283
|
-
}), 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: "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: "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: "cap" })] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { 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: "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: "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: "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, {
|
|
284
286
|
advanceWidth: Number(e.target.value),
|
|
285
|
-
}), style: fieldStyle }), jsxRuntime.jsx("label", { children:
|
|
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: {
|
|
286
288
|
color: 'var(--nice-text-secondary, #868e96)',
|
|
287
289
|
padding: 20,
|
|
288
290
|
textAlign: 'center',
|
|
289
|
-
}, children:
|
|
291
|
+
}, children: t('fontEditor.selectAGlyphToEdit', 'Select a glyph to edit') })) })] })), tab === 'metrics' && (jsxRuntime.jsxs("div", { style: { maxWidth: 400 }, children: [jsxRuntime.jsx("strong", { style: { fontSize: 12, display: 'block', marginBottom: 8 }, children: t('fontEditor.fontMetrics', 'Font Metrics') }), [
|
|
290
292
|
['unitsPerEm', 'Units per Em'],
|
|
291
293
|
['ascent', 'Ascent'],
|
|
292
294
|
['descent', 'Descent'],
|
|
293
295
|
['lineGap', 'Line Gap'],
|
|
294
296
|
['xHeight', 'x-Height'],
|
|
295
297
|
['capHeight', 'Cap Height'],
|
|
296
|
-
].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.jsxs("text", { x: em * 0.02, y: em - editor.metrics.ascent + em * 0.05, fill: "var(--nice-info, #339af0)", fontSize: em * 0.04, children: ["Ascent (", 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:
|
|
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.jsxs("text", { x: em * 0.02, y: em - editor.metrics.ascent + em * 0.05, fill: "var(--nice-info, #339af0)", fontSize: em * 0.04, children: ["Ascent (", 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.jsxs("text", { x: em * 0.02, y: em - editor.metrics.xHeight + em * 0.05, fill: "#f9e2af", fontSize: em * 0.04, children: ["x-Height (", editor.metrics.xHeight, ")"] }), jsxRuntime.jsxs("text", { x: em * 0.02, y: em - editor.metrics.capHeight + em * 0.05, fill: "#f38ba8", fontSize: em * 0.04, children: ["Cap (", 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: () => {
|
|
297
299
|
if (newKernLeft && newKernRight) {
|
|
298
300
|
setKerningPairs((prev) => [
|
|
299
301
|
...prev,
|
|
300
302
|
{ left: newKernLeft, right: newKernRight, value: -50 },
|
|
301
303
|
]);
|
|
302
304
|
}
|
|
303
|
-
}, style: btnStyle(false), children:
|
|
305
|
+
}, style: btnStyle(false), children: t('fontEditor.addPair', 'Add Pair') })] }), jsxRuntime.jsx("div", { style: { maxHeight: 200, overflowY: 'auto' }, children: kerningPairs.length === 0 ? (jsxRuntime.jsx("div", { style: {
|
|
304
306
|
color: 'var(--nice-text-secondary, #868e96)',
|
|
305
307
|
fontSize: 11,
|
|
306
308
|
padding: 8,
|
|
307
309
|
textAlign: 'center',
|
|
308
|
-
}, children:
|
|
310
|
+
}, children: t('fontEditor.noKerningPairsDefined', 'No kerning pairs defined') })) : (kerningPairs.map((kp, i) => (jsxRuntime.jsxs("div", { style: {
|
|
309
311
|
display: 'flex',
|
|
310
312
|
gap: 8,
|
|
311
313
|
alignItems: 'center',
|
|
@@ -316,7 +318,7 @@ const NiceFontEditor = ({ fontName = 'MyFont', onSave, className = '', tutorial,
|
|
|
316
318
|
border: 'none',
|
|
317
319
|
color: 'var(--nice-danger, #ff6b6b)',
|
|
318
320
|
cursor: 'pointer',
|
|
319
|
-
}, "aria-label":
|
|
321
|
+
}, "aria-label": t('fontEditor.removeKerningPair', 'Remove kerning pair'), title: t('fontEditor.removeKerningPair', 'Remove kerning pair'), children: jsxRuntime.jsx(uiCore.NiceIcon, { name: "close", size: 14 }) })] }, i)))) })] })), jsxRuntime.jsxs("div", { style: { marginTop: 8, borderTop: '1px solid var(--nice-border, #373a40)', paddingTop: 8 }, children: [jsxRuntime.jsx("div", { style: { fontSize: 11, color: 'var(--nice-text-secondary, #868e96)', marginBottom: 4 }, children: t('fontEditor.preview', 'Preview') }), jsxRuntime.jsx("input", { value: editor.previewText, onChange: (e) => editor.setPreviewText(e.target.value), style: { width: '100%', padding: '6px 8px', ...fieldStyle, fontSize: 12 } }), jsxRuntime.jsx("div", { style: {
|
|
320
322
|
marginTop: 8,
|
|
321
323
|
padding: 16,
|
|
322
324
|
background: 'var(--nice-bg, #fff)',
|