@emasoft/svg-matrix 1.0.6 → 1.0.8
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/README.md +231 -2
- package/bin/svg-matrix.js +1000 -0
- package/package.json +12 -2
- package/scripts/bootstrap_repo.sh +99 -0
- package/scripts/postinstall.js +252 -0
- package/src/clip-path-resolver.js +2 -1
- package/src/index.js +15 -1
- package/src/logger.js +302 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emasoft/svg-matrix",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Arbitrary-precision matrix, vector and affine transformation library for JavaScript using decimal.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"test:browser": "node test/browser-verify.mjs",
|
|
10
10
|
"test:playwright": "node test/playwright-diagnose.js",
|
|
11
11
|
"ci-test": "npm ci && npm test",
|
|
12
|
-
"prepublishOnly": "npm test"
|
|
12
|
+
"prepublishOnly": "npm test",
|
|
13
|
+
"postinstall": "node scripts/postinstall.js || true"
|
|
13
14
|
},
|
|
14
15
|
"repository": {
|
|
15
16
|
"type": "git",
|
|
@@ -42,11 +43,20 @@
|
|
|
42
43
|
},
|
|
43
44
|
"author": "Emasoft",
|
|
44
45
|
"license": "MIT",
|
|
46
|
+
"homepage": "https://github.com/Emasoft/SVG-MATRIX#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/Emasoft/SVG-MATRIX/issues"
|
|
49
|
+
},
|
|
50
|
+
"bin": {
|
|
51
|
+
"svg-matrix": "./bin/svg-matrix.js"
|
|
52
|
+
},
|
|
45
53
|
"engines": {
|
|
46
54
|
"node": ">=24.0.0"
|
|
47
55
|
},
|
|
48
56
|
"files": [
|
|
49
57
|
"src/",
|
|
58
|
+
"bin/",
|
|
59
|
+
"scripts/",
|
|
50
60
|
"LICENSE",
|
|
51
61
|
"README.md"
|
|
52
62
|
],
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# bootstrap_repo.sh
|
|
5
|
+
# Creates a new repository (owner/repo), populates it with SVG-MATRIX files,
|
|
6
|
+
# commits and pushes initial branch, and optionally sets OIDC secrets.
|
|
7
|
+
#
|
|
8
|
+
# Requirements:
|
|
9
|
+
# - gh CLI authenticated (gh auth status)
|
|
10
|
+
# - git installed
|
|
11
|
+
# - node/npm not required to run script (only to run tests later)
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# OWNER=Emasoft REPO=SVG-MATRIX ./bootstrap_repo.sh
|
|
15
|
+
# Optional env vars:
|
|
16
|
+
# VISIBILITY (public|private) default: public
|
|
17
|
+
# BRANCH default: main
|
|
18
|
+
# NPM_OIDC_TOKEN_URL and NPM_OIDC_AUDIENCE - optional secrets to set in the repo (if provided they'll be added)
|
|
19
|
+
|
|
20
|
+
OWNER="${OWNER:-Emasoft}"
|
|
21
|
+
REPO="${REPO:-SVG-MATRIX}"
|
|
22
|
+
VISIBILITY="${VISIBILITY:-public}"
|
|
23
|
+
BRANCH="${BRANCH:-main}"
|
|
24
|
+
DIR="${REPO}"
|
|
25
|
+
|
|
26
|
+
echo "Bootstrap repo: ${OWNER}/${REPO} (visibility=${VISIBILITY}, branch=${BRANCH})"
|
|
27
|
+
|
|
28
|
+
# check prerequisites
|
|
29
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
30
|
+
echo "gh CLI not found. Install and authenticate (gh auth login) and try again."
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
if ! command -v git >/dev/null 2>&1; then
|
|
34
|
+
echo "git not found. Please install git and try again."
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# check gh auth
|
|
39
|
+
if ! gh auth status >/dev/null 2>&1; then
|
|
40
|
+
echo "gh CLI not authenticated. Run: gh auth login"
|
|
41
|
+
gh auth status || true
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# create local dir (safe)
|
|
46
|
+
if [ -d "${DIR}" ]; then
|
|
47
|
+
echo "Directory ${DIR} already exists. Please remove or move it and re-run, or run in a different location."
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
mkdir "${DIR}"
|
|
52
|
+
cd "${DIR}"
|
|
53
|
+
|
|
54
|
+
# create files (each file is written with a here-doc)
|
|
55
|
+
mkdir -p src test .github/workflows scripts
|
|
56
|
+
|
|
57
|
+
cat > .gitignore <<'EOF'
|
|
58
|
+
node_modules
|
|
59
|
+
dist
|
|
60
|
+
.DS_Store
|
|
61
|
+
.env
|
|
62
|
+
EOF
|
|
63
|
+
|
|
64
|
+
cat > package.json <<'EOF'
|
|
65
|
+
{
|
|
66
|
+
"name": "svg-matrix",
|
|
67
|
+
"version": "1.0.0",
|
|
68
|
+
"description": "Arbitrary-precision matrix, vector and affine transformation library for JavaScript using decimal.js",
|
|
69
|
+
"type": "module",
|
|
70
|
+
"main": "src/index.js",
|
|
71
|
+
"scripts": {
|
|
72
|
+
"test": "node test/examples.js",
|
|
73
|
+
"ci-test": "npm ci && npm test",
|
|
74
|
+
"prepublishOnly": "npm test"
|
|
75
|
+
},
|
|
76
|
+
"repository": {
|
|
77
|
+
"type": "git",
|
|
78
|
+
"url": "https://github.com/Emasoft/SVG-MATRIX.git"
|
|
79
|
+
},
|
|
80
|
+
"keywords": [
|
|
81
|
+
"matrix",
|
|
82
|
+
"vector",
|
|
83
|
+
"arbitrary-precision",
|
|
84
|
+
"decimal",
|
|
85
|
+
"linear-algebra",
|
|
86
|
+
"affine",
|
|
87
|
+
"transform",
|
|
88
|
+
"svg",
|
|
89
|
+
"geometry"
|
|
90
|
+
],
|
|
91
|
+
"author": "Emasoft",
|
|
92
|
+
"license": "MIT",
|
|
93
|
+
"dependencies": {
|
|
94
|
+
"decimal.js": "^11.4.3"
|
|
95
|
+
},
|
|
96
|
+
"devDependencies": {}
|
|
97
|
+
}
|
|
98
|
+
EOF
|
|
99
|
+
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Post-install welcome message for @emasoft/svg-matrix
|
|
4
|
+
* Displays a compact summary of available features after npm install.
|
|
5
|
+
*
|
|
6
|
+
* Design principles:
|
|
7
|
+
* - Fail silently: npm install must never fail because of postinstall.
|
|
8
|
+
* Any error is caught and swallowed with exit(0).
|
|
9
|
+
* - Be respectful of terminal real estate: Keep the message compact.
|
|
10
|
+
* Users want to continue working, not read documentation.
|
|
11
|
+
* - Cross-platform: Handle Windows legacy terminals that don't support ANSI.
|
|
12
|
+
* - Standards-compliant: Respect NO_COLOR environment variable.
|
|
13
|
+
*
|
|
14
|
+
* @module scripts/postinstall
|
|
15
|
+
* @license MIT
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readFileSync } from 'fs';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
import { dirname, join } from 'path';
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// VERSION DETECTION
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* Read version from package.json dynamically.
|
|
27
|
+
*
|
|
28
|
+
* Why dynamic reading instead of hardcoding:
|
|
29
|
+
* - Avoids version mismatch bugs where postinstall shows wrong version.
|
|
30
|
+
* - Single source of truth in package.json.
|
|
31
|
+
*
|
|
32
|
+
* Why wrapped in try-catch:
|
|
33
|
+
* - postinstall scripts must NEVER fail - this would break npm install.
|
|
34
|
+
* - Return 'unknown' gracefully if file read fails for any reason.
|
|
35
|
+
*
|
|
36
|
+
* @returns {string} Version string or 'unknown' on error
|
|
37
|
+
*/
|
|
38
|
+
function getVersion() {
|
|
39
|
+
try {
|
|
40
|
+
// Why: ESM doesn't have __dirname, must derive from import.meta.url
|
|
41
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
42
|
+
// Why: Go up one level because this script is in scripts/ subfolder
|
|
43
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
44
|
+
return pkg.version || 'unknown';
|
|
45
|
+
} catch {
|
|
46
|
+
// Why: Return safe default - never throw from postinstall
|
|
47
|
+
return 'unknown';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// COLOR SUPPORT DETECTION
|
|
53
|
+
// ============================================================================
|
|
54
|
+
/**
|
|
55
|
+
* Check if colors should be disabled.
|
|
56
|
+
*
|
|
57
|
+
* Why this function:
|
|
58
|
+
* - Not all terminals support ANSI escape codes.
|
|
59
|
+
* - Showing raw escape codes is worse than no colors.
|
|
60
|
+
* - NO_COLOR is an emerging standard: https://no-color.org
|
|
61
|
+
*
|
|
62
|
+
* @returns {boolean} True if colors should be disabled
|
|
63
|
+
*/
|
|
64
|
+
function shouldDisableColors() {
|
|
65
|
+
// Why: NO_COLOR standard - presence (any value) means disable colors
|
|
66
|
+
// Check for presence, not truthiness, per spec
|
|
67
|
+
if (process.env.NO_COLOR !== undefined) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Why: Windows cmd.exe (not PowerShell/Terminal) doesn't support ANSI by default
|
|
72
|
+
// Check for known ANSI-capable Windows terminals
|
|
73
|
+
if (process.platform === 'win32') {
|
|
74
|
+
const supportsAnsi =
|
|
75
|
+
process.env.WT_SESSION || // Windows Terminal sets this
|
|
76
|
+
process.env.ConEmuANSI === 'ON' || // ConEmu explicitly signals support
|
|
77
|
+
process.env.TERM_PROGRAM || // VS Code, Hyper, etc. set this
|
|
78
|
+
process.env.ANSICON; // ANSICON utility for legacy cmd
|
|
79
|
+
// Why: Disable colors unless we detect ANSI support
|
|
80
|
+
return !supportsAnsi;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Why: Unix terminals generally support ANSI, so enable by default
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* ANSI color codes for terminal output.
|
|
89
|
+
*
|
|
90
|
+
* Why function instead of constant:
|
|
91
|
+
* - Need to check color support at runtime, not module load time.
|
|
92
|
+
* - Environment variables may change between import and execution.
|
|
93
|
+
*
|
|
94
|
+
* @param {boolean} disabled - Whether colors are disabled
|
|
95
|
+
* @returns {Object} Color code object
|
|
96
|
+
*/
|
|
97
|
+
function getColors(disabled) {
|
|
98
|
+
// Why: Return empty strings instead of undefined to avoid string concat issues
|
|
99
|
+
if (disabled) {
|
|
100
|
+
return {
|
|
101
|
+
reset: '', bright: '', dim: '',
|
|
102
|
+
cyan: '', green: '', yellow: '',
|
|
103
|
+
magenta: '', blue: '', white: '',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Why: Use standard ANSI SGR (Select Graphic Rendition) codes
|
|
107
|
+
// Format: ESC[<code>m where ESC is \x1b
|
|
108
|
+
return {
|
|
109
|
+
reset: '\x1b[0m', // Reset all attributes
|
|
110
|
+
bright: '\x1b[1m', // Bold/bright
|
|
111
|
+
dim: '\x1b[2m', // Dim/faint
|
|
112
|
+
cyan: '\x1b[36m', // Cyan foreground
|
|
113
|
+
green: '\x1b[32m', // Green foreground
|
|
114
|
+
yellow: '\x1b[33m', // Yellow foreground
|
|
115
|
+
magenta: '\x1b[35m', // Magenta foreground
|
|
116
|
+
blue: '\x1b[34m', // Blue foreground
|
|
117
|
+
white: '\x1b[37m', // White foreground
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ============================================================================
|
|
122
|
+
// UNICODE SUPPORT DETECTION
|
|
123
|
+
// ============================================================================
|
|
124
|
+
/**
|
|
125
|
+
* Check if terminal likely supports Unicode box drawing characters.
|
|
126
|
+
*
|
|
127
|
+
* Why this check:
|
|
128
|
+
* - Some older terminals or SSH sessions may not render Unicode correctly.
|
|
129
|
+
* - Windows cmd.exe before Windows 10 has limited Unicode support.
|
|
130
|
+
* - Better to show ASCII fallback than broken box characters.
|
|
131
|
+
*
|
|
132
|
+
* @returns {boolean} True if Unicode is likely supported
|
|
133
|
+
*/
|
|
134
|
+
function supportsUnicode() {
|
|
135
|
+
// Why: Check common environment indicators for Unicode support
|
|
136
|
+
|
|
137
|
+
// Windows legacy cmd.exe typically has codepage issues
|
|
138
|
+
if (process.platform === 'win32') {
|
|
139
|
+
// Modern Windows Terminal supports Unicode
|
|
140
|
+
if (process.env.WT_SESSION) return true;
|
|
141
|
+
// Check for UTF-8 codepage
|
|
142
|
+
if (process.env.CHCP === '65001') return true;
|
|
143
|
+
// ConEmu and other modern terminals
|
|
144
|
+
if (process.env.ConEmuANSI === 'ON') return true;
|
|
145
|
+
if (process.env.TERM_PROGRAM) return true;
|
|
146
|
+
// Default to ASCII on Windows for safety
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Why: Unix terminals generally support Unicode if LANG/LC_CTYPE mentions UTF-8
|
|
151
|
+
const lang = process.env.LANG || process.env.LC_CTYPE || '';
|
|
152
|
+
if (lang.toLowerCase().includes('utf')) return true;
|
|
153
|
+
|
|
154
|
+
// Why: Modern terminal emulators typically support Unicode
|
|
155
|
+
if (process.env.TERM_PROGRAM) return true;
|
|
156
|
+
|
|
157
|
+
// Default to Unicode on Unix (most modern systems support it)
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ============================================================================
|
|
162
|
+
// CI DETECTION
|
|
163
|
+
// ============================================================================
|
|
164
|
+
/**
|
|
165
|
+
* Check if running in CI environment.
|
|
166
|
+
*
|
|
167
|
+
* Why skip in CI:
|
|
168
|
+
* - CI logs should be clean and focused on build/test output.
|
|
169
|
+
* - Welcome messages just add noise to CI logs.
|
|
170
|
+
* - CI systems can set CI=true to suppress this and similar messages.
|
|
171
|
+
*
|
|
172
|
+
* @returns {boolean} True if in CI
|
|
173
|
+
*/
|
|
174
|
+
function isCI() {
|
|
175
|
+
// Why: Check multiple CI indicators since there's no universal standard
|
|
176
|
+
// Each CI system sets different environment variables
|
|
177
|
+
return !!(
|
|
178
|
+
process.env.CI || // GitHub Actions, GitLab CI, CircleCI
|
|
179
|
+
process.env.CONTINUOUS_INTEGRATION || // Travis CI
|
|
180
|
+
process.env.GITHUB_ACTIONS || // GitHub Actions (redundant with CI but explicit)
|
|
181
|
+
process.env.GITLAB_CI || // GitLab CI
|
|
182
|
+
process.env.CIRCLECI || // CircleCI
|
|
183
|
+
process.env.TRAVIS || // Travis CI
|
|
184
|
+
process.env.JENKINS_URL || // Jenkins
|
|
185
|
+
process.env.BUILD_ID // Various CI systems
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Display the welcome message.
|
|
191
|
+
* Main entry point for the postinstall script.
|
|
192
|
+
*
|
|
193
|
+
* Why this design:
|
|
194
|
+
* - Skip silently in CI: CI environments don't need welcome messages, and
|
|
195
|
+
* they clutter build logs. Most CI systems set CI=true or similar.
|
|
196
|
+
* - Skip in non-TTY: If stdout isn't a terminal (e.g., piped to file),
|
|
197
|
+
* ANSI codes would be visible as garbage characters.
|
|
198
|
+
* - Compact message: Users just installed the package and want to continue
|
|
199
|
+
* their work. A brief message with the essentials respects their time.
|
|
200
|
+
* Full documentation belongs in README, not terminal output.
|
|
201
|
+
*/
|
|
202
|
+
function showWelcome() {
|
|
203
|
+
// Why: CI builds don't need welcome messages, they just add noise to logs
|
|
204
|
+
if (isCI()) {
|
|
205
|
+
process.exit(0);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Why: Non-TTY means output is being piped/redirected - ANSI codes would be garbage
|
|
209
|
+
if (!process.stdout.isTTY) {
|
|
210
|
+
process.exit(0);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const version = getVersion();
|
|
214
|
+
const colorsDisabled = shouldDisableColors();
|
|
215
|
+
const c = getColors(colorsDisabled);
|
|
216
|
+
const unicode = supportsUnicode();
|
|
217
|
+
|
|
218
|
+
// Why: Use ASCII box characters as fallback for terminals without Unicode
|
|
219
|
+
const box = unicode ? {
|
|
220
|
+
tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│'
|
|
221
|
+
} : {
|
|
222
|
+
tl: '+', tr: '+', bl: '+', br: '+', h: '-', v: '|'
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Why: Keep message compact - users want to continue their work, not read a manual.
|
|
226
|
+
// Link to docs for those who want more. Use box drawing for visual structure.
|
|
227
|
+
const message = `
|
|
228
|
+
${c.cyan}${box.tl}${box.h.repeat(45)}${box.tr}${c.reset}
|
|
229
|
+
${c.cyan}${box.v}${c.reset} ${c.bright}@emasoft/svg-matrix${c.reset} v${version} ${c.cyan}${box.v}${c.reset}
|
|
230
|
+
${c.cyan}${box.v}${c.reset} High-precision SVG matrix transforms ${c.cyan}${box.v}${c.reset}
|
|
231
|
+
${c.cyan}${box.bl}${box.h.repeat(45)}${box.br}${c.reset}
|
|
232
|
+
|
|
233
|
+
${c.green}CLI:${c.reset} npx svg-matrix flatten input.svg -o out.svg
|
|
234
|
+
${c.green}API:${c.reset} import { Matrix, Transforms2D } from '@emasoft/svg-matrix'
|
|
235
|
+
${c.green}Docs:${c.reset} ${c.blue}https://github.com/Emasoft/SVG-MATRIX#readme${c.reset}
|
|
236
|
+
`;
|
|
237
|
+
|
|
238
|
+
console.log(message);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ============================================================================
|
|
242
|
+
// MAIN ENTRY
|
|
243
|
+
// ============================================================================
|
|
244
|
+
// Why try-catch: postinstall scripts must NEVER fail or throw.
|
|
245
|
+
// A failed postinstall would break `npm install` for users, which is
|
|
246
|
+
// absolutely unacceptable. Catch everything and exit cleanly.
|
|
247
|
+
try {
|
|
248
|
+
showWelcome();
|
|
249
|
+
} catch {
|
|
250
|
+
// Why: Silent exit - don't break npm install for a welcome message
|
|
251
|
+
process.exit(0);
|
|
252
|
+
}
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
parseTransformAttribute,
|
|
42
42
|
transformPathData
|
|
43
43
|
} from './svg-flatten.js';
|
|
44
|
+
import { Logger } from './logger.js';
|
|
44
45
|
|
|
45
46
|
// Alias for cleaner code
|
|
46
47
|
const parseTransform = parseTransformAttribute;
|
|
@@ -732,7 +733,7 @@ export function polygonToPathData(polygon, precision = 6) {
|
|
|
732
733
|
export function resolveNestedClipPath(clipPathDef, defsMap, targetElement, ctm = null, visited = new Set(), options = {}) {
|
|
733
734
|
const clipId = clipPathDef.id;
|
|
734
735
|
if (clipId && visited.has(clipId)) {
|
|
735
|
-
|
|
736
|
+
Logger.warn(`Circular clipPath reference detected: ${clipId}`);
|
|
736
737
|
return [];
|
|
737
738
|
}
|
|
738
739
|
if (clipId) visited.add(clipId);
|
package/src/index.js
CHANGED
|
@@ -43,12 +43,13 @@ import * as UseSymbolResolver from './use-symbol-resolver.js';
|
|
|
43
43
|
import * as MarkerResolver from './marker-resolver.js';
|
|
44
44
|
import * as MeshGradient from './mesh-gradient.js';
|
|
45
45
|
import * as TextToPath from './text-to-path.js';
|
|
46
|
+
import { Logger, LogLevel, setLogLevel, getLogLevel as getLoggerLevel, enableFileLogging, disableFileLogging } from './logger.js';
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
49
|
* Library version
|
|
49
50
|
* @constant {string}
|
|
50
51
|
*/
|
|
51
|
-
export const VERSION = '1.0.
|
|
52
|
+
export const VERSION = '1.0.8';
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* Default precision for path output (decimal places)
|
|
@@ -83,6 +84,12 @@ export { ClipPathResolver, MaskResolver, PatternResolver };
|
|
|
83
84
|
export { UseSymbolResolver, MarkerResolver };
|
|
84
85
|
export { MeshGradient, TextToPath };
|
|
85
86
|
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// LOGGING: Configurable logging control
|
|
89
|
+
// ============================================================================
|
|
90
|
+
|
|
91
|
+
export { Logger, LogLevel, setLogLevel, enableFileLogging, disableFileLogging };
|
|
92
|
+
|
|
86
93
|
// ============================================================================
|
|
87
94
|
// CONVENIENCE FUNCTIONS: Quick access to common operations
|
|
88
95
|
// ============================================================================
|
|
@@ -409,6 +416,13 @@ export default {
|
|
|
409
416
|
MeshGradient,
|
|
410
417
|
TextToPath,
|
|
411
418
|
|
|
419
|
+
// Logging
|
|
420
|
+
Logger,
|
|
421
|
+
LogLevel,
|
|
422
|
+
setLogLevel,
|
|
423
|
+
enableFileLogging,
|
|
424
|
+
disableFileLogging,
|
|
425
|
+
|
|
412
426
|
// Convenience functions
|
|
413
427
|
translate2D,
|
|
414
428
|
rotate2D,
|