dazscript-framework 1.0.20 → 1.0.22
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
CHANGED
|
@@ -175,7 +175,7 @@ npx dazscript init --menu-path /MyScripts --scripts-path ./src --out-dir ./out -
|
|
|
175
175
|
| `--out-dir` | Where `build` writes `.dsa` files and copies icons |
|
|
176
176
|
| `--app-data-path` | AppData namespace for launcher fallback (`Author/Product` format) |
|
|
177
177
|
|
|
178
|
-
Builds also accept `--log-level <trace|debug|info|warn|error|off>`. This sets the minimum runtime log level for the compiled scripts. When omitted, builds default to `
|
|
178
|
+
Builds also accept `--log-level <trace|debug|info|warn|error|off>`. This sets the minimum runtime log level for the compiled scripts. When omitted, builds default to `debug`; use `warn` for release packages.
|
|
179
179
|
|
|
180
180
|
Use `--scripts-path ./src/scripts` when runnable files live under a subfolder; use `--scripts-path ./src` when they are at the source root.
|
|
181
181
|
|
package/package.json
CHANGED
package/src/common/log.test.ts
CHANGED
|
@@ -43,10 +43,11 @@ describe('log levels', () => {
|
|
|
43
43
|
expect(shouldLog('debug')).toBe(true)
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
it('normalizes an empty build level to
|
|
46
|
+
it('normalizes an empty build level to debug', () => {
|
|
47
47
|
setLogLevel('')
|
|
48
48
|
|
|
49
|
-
expect(getLogLevel()).toBe('
|
|
50
|
-
expect(shouldLog('
|
|
49
|
+
expect(getLogLevel()).toBe('debug')
|
|
50
|
+
expect(shouldLog('debug')).toBe(true)
|
|
51
|
+
expect(shouldLog('trace')).toBe(false)
|
|
51
52
|
})
|
|
52
53
|
})
|
package/src/common/log.ts
CHANGED
|
@@ -15,7 +15,7 @@ const logLevelWeights: { [key in LogLevel]: number } = {
|
|
|
15
15
|
let currentLogLevel: LogLevel = normalizeLogLevel(
|
|
16
16
|
typeof __DAZSCRIPT_LOG_LEVEL__ === 'string'
|
|
17
17
|
? __DAZSCRIPT_LOG_LEVEL__
|
|
18
|
-
: '
|
|
18
|
+
: 'debug'
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
export const setLogLevel = (level: string): LogLevel => {
|
|
@@ -26,7 +26,7 @@ export const setLogLevel = (level: string): LogLevel => {
|
|
|
26
26
|
export const getLogLevel = (): LogLevel => currentLogLevel
|
|
27
27
|
|
|
28
28
|
export function normalizeLogLevel(level: string): LogLevel {
|
|
29
|
-
if (!level) return '
|
|
29
|
+
if (!level) return 'debug'
|
|
30
30
|
const normalized = level.toLowerCase() as LogLevel
|
|
31
31
|
return logLevels.indexOf(normalized) >= 0 ? normalized : 'debug'
|
|
32
32
|
}
|
|
@@ -23,7 +23,11 @@ export abstract class BasicDialog {
|
|
|
23
23
|
|
|
24
24
|
run(): boolean {
|
|
25
25
|
this.init()
|
|
26
|
-
|
|
26
|
+
this.builder.restoreObjectName()
|
|
27
|
+
this.builder.traceGeometry('beforeExec')
|
|
28
|
+
let result = this.dialog.exec()
|
|
29
|
+
this.builder.traceGeometry('afterExec', { result })
|
|
30
|
+
return result
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
ok(): boolean {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { DialogProperties } from '../shared';
|
|
2
2
|
import { WidgetBuilderContext, WidgetsBuilder } from './widgets-builder';
|
|
3
|
+
import { debug } from '@dsf/common/log';
|
|
3
4
|
|
|
4
5
|
export class DialogBuilder extends WidgetsBuilder {
|
|
5
6
|
public properties: DialogProperties
|
|
7
|
+
private objectName: string
|
|
6
8
|
|
|
7
9
|
constructor(private title: string, private id?: string) {
|
|
8
10
|
super(new WidgetBuilderContext(new DzBasicDialog()))
|
|
@@ -15,16 +17,25 @@ export class DialogBuilder extends WidgetsBuilder {
|
|
|
15
17
|
|
|
16
18
|
build(setup: () => void): DzBasicDialog {
|
|
17
19
|
this.init()
|
|
20
|
+
this.traceGeometry('init')
|
|
18
21
|
setup()
|
|
22
|
+
this.restoreObjectName()
|
|
23
|
+
this.traceGeometry('afterSetup')
|
|
19
24
|
this.resize()
|
|
25
|
+
this.restoreObjectName()
|
|
26
|
+
this.traceGeometry('afterResize')
|
|
20
27
|
return this.context.dialog
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
private init() {
|
|
24
31
|
this.context.dialog.caption = this.title
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
this.objectName = (this.id ?? this.context.dialog.caption).replace(/ /g, "") + "Dlg"
|
|
33
|
+
this.restoreObjectName()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
restoreObjectName() {
|
|
37
|
+
if (!this.objectName) return
|
|
38
|
+
this.context.dialog.getWidget().objectName = this.objectName
|
|
28
39
|
}
|
|
29
40
|
|
|
30
41
|
private resize() {
|
|
@@ -51,5 +62,64 @@ export class DialogBuilder extends WidgetsBuilder {
|
|
|
51
62
|
this.context.dialog.setFixedHeight(height);
|
|
52
63
|
}
|
|
53
64
|
}
|
|
54
|
-
}
|
|
55
65
|
|
|
66
|
+
traceGeometry(phase: string, extra: { [key: string]: string | number | boolean | undefined } = {}) {
|
|
67
|
+
let dialog = this.context.dialog
|
|
68
|
+
let dialogWidget = dialog.getWidget()
|
|
69
|
+
let objectName = this.read(dialogWidget, 'objectName')
|
|
70
|
+
let caption = this.read(dialog, 'caption')
|
|
71
|
+
let sizeHint = this.read(dialogWidget, 'minimumSizeHint')
|
|
72
|
+
let key = objectName ?? ''
|
|
73
|
+
let fields: { [key: string]: string | number | boolean | undefined } = {
|
|
74
|
+
event: 'dialog.geometry',
|
|
75
|
+
phase,
|
|
76
|
+
title: this.title,
|
|
77
|
+
id: this.id ?? '',
|
|
78
|
+
caption,
|
|
79
|
+
objectName,
|
|
80
|
+
windowGeometryKey: key,
|
|
81
|
+
dialogX: this.read(dialog, 'x'),
|
|
82
|
+
dialogY: this.read(dialog, 'y'),
|
|
83
|
+
dialogWidth: this.read(dialog, 'width'),
|
|
84
|
+
dialogHeight: this.read(dialog, 'height'),
|
|
85
|
+
widgetX: this.read(dialogWidget, 'x'),
|
|
86
|
+
widgetY: this.read(dialogWidget, 'y'),
|
|
87
|
+
widgetWidth: this.read(dialogWidget, 'width'),
|
|
88
|
+
widgetHeight: this.read(dialogWidget, 'height'),
|
|
89
|
+
sizeHintWidth: this.read(sizeHint, 'width'),
|
|
90
|
+
sizeHintHeight: this.read(sizeHint, 'height'),
|
|
91
|
+
propertyWidth: this.properties.width,
|
|
92
|
+
propertyHeight: this.properties.height,
|
|
93
|
+
propertyMaxWidth: this.properties.maxWidth,
|
|
94
|
+
propertyMaxHeight: this.properties.maxHeight,
|
|
95
|
+
resizable: this.properties.resizable === true,
|
|
96
|
+
registrySubKey: key ? `WindowGeometries\\${key}` : ''
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
Object.keys(extra).forEach((name) => fields[name] = extra[name])
|
|
100
|
+
debug(`[DialogGeometry] ${this.formatGeometryTrace(fields)}`)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private read(source: any, field: string): any {
|
|
104
|
+
if (!source) return undefined
|
|
105
|
+
try {
|
|
106
|
+
return source[field]
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
return undefined
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private formatGeometryTrace(fields: { [key: string]: string | number | boolean | undefined }): string {
|
|
114
|
+
return Object.keys(fields)
|
|
115
|
+
.filter((name) => fields[name] !== undefined)
|
|
116
|
+
.map((name) => `${name}=${this.formatGeometryValue(name, fields[name])}`)
|
|
117
|
+
.join(' ')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private formatGeometryValue(name: string, value: string | number | boolean | undefined): string {
|
|
121
|
+
if (name === 'event' || name === 'phase') return String(value)
|
|
122
|
+
if (typeof value === 'string') return JSON.stringify(value)
|
|
123
|
+
return String(value)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { BasicDialog } from './basic-dialog'
|
|
3
|
+
import { setLogLevel } from '../common/log'
|
|
4
|
+
|
|
5
|
+
class TestDialog extends BasicDialog {
|
|
6
|
+
protected build(): void {
|
|
7
|
+
this.builder.options({ resizable: true })
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class HeaderDialog extends BasicDialog {
|
|
12
|
+
protected build(): void {
|
|
13
|
+
this.builder.header({ text: 'Header' }).build()
|
|
14
|
+
this.builder.options({ resizable: true })
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('dialog geometry trace', () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
; (globalThis as any).App = {
|
|
21
|
+
debug: vi.fn(),
|
|
22
|
+
log: vi.fn(),
|
|
23
|
+
warning: vi.fn(),
|
|
24
|
+
flushLogBuffer: vi.fn(),
|
|
25
|
+
statusLine: vi.fn()
|
|
26
|
+
}
|
|
27
|
+
; (globalThis as any).DzBasicDialog = class {
|
|
28
|
+
caption = ''
|
|
29
|
+
width = 300
|
|
30
|
+
height = 200
|
|
31
|
+
minHeight = 0
|
|
32
|
+
sizeGripEnabled = false
|
|
33
|
+
private widget = {
|
|
34
|
+
objectName: '',
|
|
35
|
+
x: 10,
|
|
36
|
+
y: 20,
|
|
37
|
+
width: 300,
|
|
38
|
+
height: 200,
|
|
39
|
+
minimumSizeHint: { width: 300, height: 200 }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getWidget() {
|
|
43
|
+
return this.widget
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
inherits(typeName: string) {
|
|
47
|
+
return typeName === 'DzWidget'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
addLayout() { }
|
|
51
|
+
|
|
52
|
+
exec() {
|
|
53
|
+
this.width = 640
|
|
54
|
+
this.height = 480
|
|
55
|
+
this.widget.width = 640
|
|
56
|
+
this.widget.height = 480
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const Layout = class {
|
|
61
|
+
direction = 0
|
|
62
|
+
constructor(public parent: unknown) { }
|
|
63
|
+
inherits(typeName: string) {
|
|
64
|
+
return typeName === 'DzLayout'
|
|
65
|
+
}
|
|
66
|
+
addWidget() { }
|
|
67
|
+
}
|
|
68
|
+
class Widget {
|
|
69
|
+
private _name = ''
|
|
70
|
+
height = 30
|
|
71
|
+
width = 100
|
|
72
|
+
minHeight = 0
|
|
73
|
+
maxHeight = 0
|
|
74
|
+
toolTip = ''
|
|
75
|
+
whatsThis = ''
|
|
76
|
+
constructor(private parent: any) { }
|
|
77
|
+
set name(value: string) {
|
|
78
|
+
this._name = value
|
|
79
|
+
if (this.parent?.getWidget) this.parent.getWidget().objectName = value
|
|
80
|
+
}
|
|
81
|
+
get name() {
|
|
82
|
+
return this._name
|
|
83
|
+
}
|
|
84
|
+
getWidget() {
|
|
85
|
+
return this
|
|
86
|
+
}
|
|
87
|
+
inherits(typeName: string) {
|
|
88
|
+
return typeName === 'DzWidget'
|
|
89
|
+
}
|
|
90
|
+
setFixedHeight(value: number) {
|
|
91
|
+
this.height = value
|
|
92
|
+
}
|
|
93
|
+
setFixedWidth(value: number) {
|
|
94
|
+
this.width = value
|
|
95
|
+
}
|
|
96
|
+
show() { }
|
|
97
|
+
hide() { }
|
|
98
|
+
}
|
|
99
|
+
; (globalThis as any).DzVBoxLayout = Layout
|
|
100
|
+
; (globalThis as any).DzHBoxLayout = Layout
|
|
101
|
+
; (globalThis as any).DzWidget = Widget
|
|
102
|
+
; (globalThis as any).DzLabel = Widget
|
|
103
|
+
; (globalThis as any).DzTextBrowser = class extends Widget {
|
|
104
|
+
html = ''
|
|
105
|
+
readOnly = false
|
|
106
|
+
lineWrapMode = 0
|
|
107
|
+
wordWrapMode = 0
|
|
108
|
+
}
|
|
109
|
+
; (globalThis as any).DzTextEdit = { WidgetWidth: 1, WordWrap: 2 }
|
|
110
|
+
; (globalThis as any).DzSplitter = { Vertical: 1, Horizontal: 2 }
|
|
111
|
+
setLogLevel('trace')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('logs dialog objectName and geometry before and after exec', () => {
|
|
115
|
+
new TestDialog('Power Loader', 'Vholf3D Power Loader').run()
|
|
116
|
+
|
|
117
|
+
const messages = (globalThis as any).App.debug.mock.calls.map((call: unknown[]) => String(call[0]))
|
|
118
|
+
|
|
119
|
+
expect(messages.some((message: string) =>
|
|
120
|
+
message.includes('event=dialog.geometry phase=init') &&
|
|
121
|
+
message.includes('objectName="Vholf3DPowerLoaderDlg"')
|
|
122
|
+
)).toBe(true)
|
|
123
|
+
expect(messages.some((message: string) =>
|
|
124
|
+
message.includes('event=dialog.geometry phase=beforeExec') &&
|
|
125
|
+
message.includes('dialogWidth=300')
|
|
126
|
+
)).toBe(true)
|
|
127
|
+
expect(messages.some((message: string) =>
|
|
128
|
+
message.includes('event=dialog.geometry phase=afterExec') &&
|
|
129
|
+
message.includes('dialogWidth=640') &&
|
|
130
|
+
message.includes('result=true')
|
|
131
|
+
)).toBe(true)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('keeps the DAZ geometry objectName stable after building a header widget', () => {
|
|
135
|
+
new HeaderDialog('Power Menu - Active Tool', 'Vholf3D Power Menu - Active Tool').run()
|
|
136
|
+
|
|
137
|
+
const messages = (globalThis as any).App.debug.mock.calls.map((call: unknown[]) => String(call[0]))
|
|
138
|
+
|
|
139
|
+
expect(messages.some((message: string) =>
|
|
140
|
+
message.includes('event=dialog.geometry phase=afterSetup') &&
|
|
141
|
+
message.includes('objectName="Vholf3DPowerMenu-ActiveToolDlg"')
|
|
142
|
+
)).toBe(true)
|
|
143
|
+
expect(messages.some((message: string) =>
|
|
144
|
+
message.includes('event=dialog.geometry phase=beforeExec') &&
|
|
145
|
+
message.includes('objectName="Vholf3DPowerMenu-ActiveToolDlg"')
|
|
146
|
+
)).toBe(true)
|
|
147
|
+
})
|
|
148
|
+
})
|