@things-factory/figure-ui 10.1.175 → 10.1.176
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/client/modeller/editor-styles.ts +1 -1
- package/client/modeller/figure-links.ts +5 -3
- package/client/modeller/figure-parts.ts +31 -14
- package/client/modeller/figure-properties.ts +2 -2
- package/client/pages/figure-modeller-page.ts +34 -11
- package/dist-client/modeller/editor-styles.js +1 -1
- package/dist-client/modeller/editor-styles.js.map +1 -1
- package/dist-client/modeller/figure-links.js +5 -3
- package/dist-client/modeller/figure-links.js.map +1 -1
- package/dist-client/modeller/figure-parts.js +31 -14
- package/dist-client/modeller/figure-parts.js.map +1 -1
- package/dist-client/modeller/figure-properties.js +2 -2
- package/dist-client/modeller/figure-properties.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.js +34 -11
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/test/browser/acceptance-page.js +2 -1
- package/test/browser/build.mjs +9 -1
- package/test/browser/links.check.mjs +6 -0
- package/test/browser/props.check.mjs +40 -2
- package/test/browser/screen.check.mjs +22 -0
- package/test/browser/serve-acceptance.mjs +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/figure-ui",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.176",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"@things-factory:registry": "https://registry.npmjs.org"
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"@operato/slot-layout-editor": "^10.29.25",
|
|
42
42
|
"@operato/styles": "^10.0.0",
|
|
43
43
|
"@operato/utils": "^10.0.0",
|
|
44
|
-
"@things-factory/ai-assistant": "^10.1.
|
|
45
|
-
"@things-factory/figure-service": "^10.1.
|
|
46
|
-
"@things-factory/figure-viewer": "^10.1.
|
|
47
|
-
"@things-factory/setting-base": "^10.1.
|
|
44
|
+
"@things-factory/ai-assistant": "^10.1.176",
|
|
45
|
+
"@things-factory/figure-service": "^10.1.176",
|
|
46
|
+
"@things-factory/figure-viewer": "^10.1.176",
|
|
47
|
+
"@things-factory/setting-base": "^10.1.176",
|
|
48
48
|
"three": "^0.185.1"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "24928f127d73de0e9946423bd4c3295f54cce03d"
|
|
51
51
|
}
|
|
@@ -214,7 +214,8 @@ async function start() {
|
|
|
214
214
|
}
|
|
215
215
|
const properties = document.createElement('figure-properties')
|
|
216
216
|
properties.slot = 'figure'
|
|
217
|
-
|
|
217
|
+
/* As the modeller page gives it: the revision on screen (it gave the source, which the panel no longer reads -- N6). */
|
|
218
|
+
const keep = () => (properties.revision = editor.session.revisionNow())
|
|
218
219
|
keep()
|
|
219
220
|
editor.addEventListener('changed', keep)
|
|
220
221
|
/* The figure panel's commands, as the modeller page runs them: one step of the session's history. */
|
package/test/browser/build.mjs
CHANGED
|
@@ -40,6 +40,14 @@ for (const signal of ['SIGINT', 'SIGTERM'])
|
|
|
40
40
|
* `standIns` maps a module the page would import to a file beside this one: for a module that cannot run here (the
|
|
41
41
|
* assistant dock needs the whole app's tool bar), a stand-in that records what the product asked of it.
|
|
42
42
|
*/
|
|
43
|
+
/*
|
|
44
|
+
Packages that must stand once on a page: what defines custom elements (lit, the @operato and @material elements) and
|
|
45
|
+
what holds one state (i18next). A sibling checkout linked into node_modules for local work -- the operato
|
|
46
|
+
webcomponents, 2026-10-17 -- brought its own @operato/i18n, the page defined ox-i18n twice and stopped before any
|
|
47
|
+
check could start. Each is taken from this checkout's node_modules, whoever imports it.
|
|
48
|
+
*/
|
|
49
|
+
const ONE_COPY = id => /^(lit|lit-html|lit-element|@lit\/|@lit-labs\/|i18next|@operato\/|@material\/)/.test(id)
|
|
50
|
+
|
|
43
51
|
export async function buildPage(page = 'modeller-page.js', { standIns = {} } = {}) {
|
|
44
52
|
/*
|
|
45
53
|
The real path: on macOS the temp dir is a link (/var -> /private/var). A module named both through
|
|
@@ -87,7 +95,7 @@ export async function buildPage(page = 'modeller-page.js', { standIns = {} } = {
|
|
|
87
95
|
}
|
|
88
96
|
},
|
|
89
97
|
replace({ preventAssignment: true, values: { 'process.env.NODE_ENV': JSON.stringify('production') } }),
|
|
90
|
-
nodeResolve({ browser: true, modulePaths: [join(PACKAGE, 'node_modules'), join(ROOT, 'node_modules')] }),
|
|
98
|
+
nodeResolve({ browser: true, modulePaths: [join(PACKAGE, 'node_modules'), join(ROOT, 'node_modules')], rootDir: PACKAGE, dedupe: ONE_COPY }),
|
|
91
99
|
commonjs()
|
|
92
100
|
]
|
|
93
101
|
})
|
|
@@ -131,6 +131,12 @@ async function press(selector, text, what = text ?? selector) {
|
|
|
131
131
|
text
|
|
132
132
|
)
|
|
133
133
|
assert.ok(await element.evaluate(e => !!e), `${what} is on the page`)
|
|
134
|
+
/*
|
|
135
|
+
In the middle of the panel first, as a hand scrolls to what it presses: brought only just into view, at the panel's
|
|
136
|
+
top, it lay under the panel's sticky head and the press fell there (2026-10-17, the leaf's fasten-start once the
|
|
137
|
+
rows were tightened).
|
|
138
|
+
*/
|
|
139
|
+
await element.evaluate(e => e.scrollIntoView({ block: 'center' }))
|
|
134
140
|
await element.click()
|
|
135
141
|
await settled()
|
|
136
142
|
}
|
|
@@ -232,16 +232,19 @@ describe('the parts panel stands at the side\'s heights (2026-10-16)', () => {
|
|
|
232
232
|
before(() => mount('rollers'))
|
|
233
233
|
|
|
234
234
|
test('the detail level is one height with the side\'s fields, and the palette\'s tiles one height with each other', async () => {
|
|
235
|
+
await pick('bed')
|
|
235
236
|
const heights = await read(() => {
|
|
236
237
|
const parts = document.querySelector('figure-editor').shadowRoot.querySelector('figure-parts').shadowRoot
|
|
237
238
|
const h = e => Math.round(e.getBoundingClientRect().height)
|
|
238
239
|
const tiles = [...parts.querySelectorAll('div[palette] button[shape]:not([import])')].map(h)
|
|
239
240
|
return { level: h(parts.querySelector('div[level] select')), tiles: [...new Set(tiles)], count: tiles.length, import: h(parts.querySelector('button[shape][import]')) }
|
|
240
241
|
})
|
|
241
|
-
|
|
242
|
+
const side = await read(() => Math.round(document.querySelector('figure-editor').shadowRoot.querySelector('input[name-field]')?.getBoundingClientRect().height ?? 0))
|
|
243
|
+
assert.ok(side > 0, 'a side field to measure against')
|
|
244
|
+
assert.equal(heights.level, side, 'the detail level select, one height with the side\'s fields')
|
|
242
245
|
assert.ok(heights.count >= 9, `the starting shapes: ${heights.count}`)
|
|
243
246
|
assert.equal(heights.tiles.length, 1, `one tile height: ${heights.tiles}`)
|
|
244
|
-
assert.equal(heights.import,
|
|
247
|
+
assert.equal(heights.import, side, 'the file import on one line, its own row, at the fields\' height')
|
|
245
248
|
})
|
|
246
249
|
|
|
247
250
|
/* A static guard sees the sizes written down; an icon with none stands at md-icon's own 24 (the recipe catalog's did). */
|
|
@@ -260,3 +263,38 @@ describe('the parts panel stands at the side\'s heights (2026-10-16)', () => {
|
|
|
260
263
|
assert.deepEqual(sizes.filter(s => !['16', '18', '20'].includes(s.split(' ')[0])), [])
|
|
261
264
|
})
|
|
262
265
|
})
|
|
266
|
+
|
|
267
|
+
describe('a label and its field on one line, the field a line not a box (user 2026-10-17)', () => {
|
|
268
|
+
before(() => mount('rollers'))
|
|
269
|
+
|
|
270
|
+
/* Every visible label beside the control after it: the field's row, not the row above. */
|
|
271
|
+
const rows = () =>
|
|
272
|
+
read(() => {
|
|
273
|
+
const all = []
|
|
274
|
+
const walk = root => { for (const e of root.querySelectorAll('*')) { all.push(e); if (e.shadowRoot) walk(e.shadowRoot) } }
|
|
275
|
+
walk(document.querySelector('figure-editor').shadowRoot.querySelector('div[panel]:not([off])'))
|
|
276
|
+
const out = []
|
|
277
|
+
for (const field of all.filter(e => e.matches?.('div[field], div[inline-field], div[form]') && e.checkVisibility())) {
|
|
278
|
+
const label = field.querySelector(':scope > label, :scope > span')
|
|
279
|
+
const control = field.querySelector(':scope > input, :scope > select, :scope > div[swatches], :scope > div[row]')
|
|
280
|
+
if (!label || !control || !label.checkVisibility() || !control.checkVisibility()) continue
|
|
281
|
+
const a = label.getBoundingClientRect(), b = control.getBoundingClientRect()
|
|
282
|
+
out.push({ label: label.textContent.trim().slice(0, 20), sameLine: a.top < b.bottom && b.top < a.bottom, leftOf: a.right <= b.left + 1 })
|
|
283
|
+
}
|
|
284
|
+
const boxed = all.filter(e => e.matches?.('input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]), select') && e.checkVisibility() && !e.closest('div[cell]'))
|
|
285
|
+
.map(e => { const s = getComputedStyle(e); return { top: s.borderTopWidth, left: s.borderLeftWidth, bottom: s.borderBottomWidth } })
|
|
286
|
+
return { out, boxed }
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
/* The figure's own group is the modeller page's (figure-properties), not this page's: its rows are checked in screen.check. */
|
|
290
|
+
for (const part of ['bed', 'roller']) {
|
|
291
|
+
test(`${part}: every label beside its field, every field a line`, async () => {
|
|
292
|
+
await pick(part)
|
|
293
|
+
const { out, boxed } = await rows()
|
|
294
|
+
assert.ok(out.length >= 2, `rows read: ${out.length}`)
|
|
295
|
+
assert.deepEqual(out.filter(r => !r.sameLine || !r.leftOf), [], 'labels on their field\'s line, to its left')
|
|
296
|
+
assert.ok(boxed.length > 0, 'fields read')
|
|
297
|
+
assert.deepEqual(boxed.filter(b => b.top !== '0px' || b.left !== '0px' || b.bottom !== '1px'), [], 'only the line under it')
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
})
|
|
@@ -1008,6 +1008,28 @@ describe('the modeller page: the editor with the page\'s figure panel, verdict a
|
|
|
1008
1008
|
}
|
|
1009
1009
|
})
|
|
1010
1010
|
|
|
1011
|
+
/* The figure's own group in the side's one-line rows (user 2026-10-17): the identifier, the name, where it stands. */
|
|
1012
|
+
test('the figure panel\'s labels stand beside their fields, the identifier\'s too, each field a line', async () => {
|
|
1013
|
+
await pressTab(TAB('properties'))
|
|
1014
|
+
const rows = await read(() => {
|
|
1015
|
+
const properties = screenCheck.deep('figure-properties')[0]
|
|
1016
|
+
const out = []
|
|
1017
|
+
const type = properties.querySelector('label[type-field]')
|
|
1018
|
+
const pairs = [[type?.querySelector('span'), type?.querySelector('input, code')]]
|
|
1019
|
+
for (const field of properties.shadowRoot.querySelectorAll('div[field]')) pairs.push([field.querySelector(':scope > label'), field.querySelector(':scope > input, :scope > select, :scope > div[row]')])
|
|
1020
|
+
for (const [label, control] of pairs) {
|
|
1021
|
+
if (!label || !control || !label.checkVisibility() || !control.checkVisibility()) continue
|
|
1022
|
+
const a = label.getBoundingClientRect(), b = control.getBoundingClientRect()
|
|
1023
|
+
const line = control.matches('input, select') ? getComputedStyle(control) : null
|
|
1024
|
+
out.push({ label: label.textContent.trim(), sameLine: a.top < b.bottom && b.top < a.bottom, leftOf: a.right <= b.left + 1, box: line ? line.borderTopWidth !== '0px' || line.borderLeftWidth !== '0px' : false })
|
|
1025
|
+
}
|
|
1026
|
+
return out
|
|
1027
|
+
})
|
|
1028
|
+
assert.ok(rows.length >= 3, `rows read: ${rows.map(r => r.label)}`)
|
|
1029
|
+
assert.deepEqual(rows.filter(r => !r.sameLine || !r.leftOf), [], 'labels on their field\'s line, to its left')
|
|
1030
|
+
assert.deepEqual(rows.filter(r => r.box), [], 'fields drawn as a line, not a box')
|
|
1031
|
+
})
|
|
1032
|
+
|
|
1011
1033
|
test('the figure\'s type is the first field of the figure panel, under its heading', async () => {
|
|
1012
1034
|
await pressTab(TAB('properties'))
|
|
1013
1035
|
const first = await read(() => {
|
|
@@ -24,7 +24,8 @@ import { buildPage } from './build.mjs'
|
|
|
24
24
|
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
25
25
|
const bundle = await buildPage('acceptance-page.js')
|
|
26
26
|
const dir = dirname(bundle)
|
|
27
|
-
|
|
27
|
+
/* The figure panel's words are figure-viewer's: both packages' Korean, as the app loads every module's. */
|
|
28
|
+
const ko = Buffer.from(JSON.stringify({ ...JSON.parse(readFileSync(resolve(HERE, '../../../figure-viewer/translations/ko.json'), 'utf-8')), ...JSON.parse(readFileSync(resolve(HERE, '../../translations/ko.json'), 'utf-8')) }))
|
|
28
29
|
const APP = resolve(process.env.OPERATO_APP ?? resolve(HERE, '../../../../../../operato-application/packages/operato-figure'))
|
|
29
30
|
/* The builder's ThemeOverridePlugin, run from the app root so its module order is the app's. */
|
|
30
31
|
const themeFiles = JSON.parse(
|