@things-factory/figure-ui 10.1.168 → 10.1.170
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/figure-assistant.ts +10 -1
- package/client/modeller/affected-parts.ts +11 -0
- package/client/modeller/ai-proposal.ts +40 -9
- package/client/modeller/canvas.ts +15 -1
- package/client/modeller/editor-styles.ts +98 -6
- package/client/modeller/editor.ts +212 -53
- package/client/modeller/fasten-geometry.ts +19 -0
- package/client/modeller/figure-fasten-drawing.ts +4 -3
- package/client/modeller/figure-links.ts +80 -37
- package/client/modeller/figure-material-panel.ts +20 -1
- package/client/modeller/figure-preview.ts +117 -8
- package/client/modeller/figure-properties.ts +1 -2
- package/client/modeller/figure-section-panel.ts +5 -0
- package/client/modeller/figure-verdict.ts +9 -2
- package/client/modeller/gate-findings.ts +12 -4
- package/client/modeller/group-menu.ts +57 -0
- package/client/modeller/links.ts +22 -1
- package/client/modeller/modeller-keys.ts +6 -0
- package/client/modeller/preview-size.ts +32 -0
- package/client/modeller/product-session.ts +23 -9
- package/client/modeller/refusals.ts +14 -0
- package/client/modeller/stage-model.ts +11 -0
- package/client/pages/figure-modeller-page.ts +15 -6
- package/dist-client/figure-assistant.d.ts +6 -0
- package/dist-client/figure-assistant.js +9 -1
- package/dist-client/figure-assistant.js.map +1 -1
- package/dist-client/modeller/affected-parts.d.ts +6 -0
- package/dist-client/modeller/affected-parts.js +12 -0
- package/dist-client/modeller/affected-parts.js.map +1 -0
- package/dist-client/modeller/ai-proposal.d.ts +15 -5
- package/dist-client/modeller/ai-proposal.js +39 -10
- package/dist-client/modeller/ai-proposal.js.map +1 -1
- package/dist-client/modeller/canvas.d.ts +3 -0
- package/dist-client/modeller/canvas.js +12 -1
- package/dist-client/modeller/canvas.js.map +1 -1
- package/dist-client/modeller/editor-styles.js +98 -6
- package/dist-client/modeller/editor-styles.js.map +1 -1
- package/dist-client/modeller/editor.d.ts +42 -1
- package/dist-client/modeller/editor.js +220 -51
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/fasten-geometry.d.ts +27 -0
- package/dist-client/modeller/fasten-geometry.js +14 -0
- package/dist-client/modeller/fasten-geometry.js.map +1 -1
- package/dist-client/modeller/figure-fasten-drawing.js +4 -3
- package/dist-client/modeller/figure-fasten-drawing.js.map +1 -1
- package/dist-client/modeller/figure-links.d.ts +3 -1
- package/dist-client/modeller/figure-links.js +84 -38
- package/dist-client/modeller/figure-links.js.map +1 -1
- package/dist-client/modeller/figure-material-panel.d.ts +8 -1
- package/dist-client/modeller/figure-material-panel.js +24 -1
- package/dist-client/modeller/figure-material-panel.js.map +1 -1
- package/dist-client/modeller/figure-preview.d.ts +12 -0
- package/dist-client/modeller/figure-preview.js +116 -5
- package/dist-client/modeller/figure-preview.js.map +1 -1
- package/dist-client/modeller/figure-properties.js +1 -2
- package/dist-client/modeller/figure-properties.js.map +1 -1
- package/dist-client/modeller/figure-section-panel.d.ts +1 -1
- package/dist-client/modeller/figure-section-panel.js +5 -0
- package/dist-client/modeller/figure-section-panel.js.map +1 -1
- package/dist-client/modeller/figure-verdict.d.ts +1 -0
- package/dist-client/modeller/figure-verdict.js +8 -2
- package/dist-client/modeller/figure-verdict.js.map +1 -1
- package/dist-client/modeller/gate-findings.d.ts +3 -1
- package/dist-client/modeller/gate-findings.js +8 -4
- package/dist-client/modeller/gate-findings.js.map +1 -1
- package/dist-client/modeller/group-menu.d.ts +18 -0
- package/dist-client/modeller/group-menu.js +52 -0
- package/dist-client/modeller/group-menu.js.map +1 -0
- package/dist-client/modeller/links.d.ts +5 -0
- package/dist-client/modeller/links.js +21 -1
- package/dist-client/modeller/links.js.map +1 -1
- package/dist-client/modeller/modeller-keys.js +7 -0
- package/dist-client/modeller/modeller-keys.js.map +1 -1
- package/dist-client/modeller/preview-size.d.ts +23 -0
- package/dist-client/modeller/preview-size.js +32 -0
- package/dist-client/modeller/preview-size.js.map +1 -0
- package/dist-client/modeller/product-session.d.ts +0 -1
- package/dist-client/modeller/product-session.js +22 -9
- package/dist-client/modeller/product-session.js.map +1 -1
- package/dist-client/modeller/refusals.d.ts +1 -5
- package/dist-client/modeller/refusals.js +14 -1
- package/dist-client/modeller/refusals.js.map +1 -1
- package/dist-client/modeller/stage-model.d.ts +11 -0
- package/dist-client/modeller/stage-model.js +11 -0
- package/dist-client/modeller/stage-model.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +3 -0
- package/dist-client/pages/figure-modeller-page.js +18 -5
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/test/ai-proposal.test.ts +43 -4
- package/test/browser/editing-page.js +3 -1
- package/test/browser/editing.check.mjs +2 -1
- package/test/browser/links-page.js +1 -0
- package/test/browser/links.check.mjs +20 -5
- package/test/browser/modeller.check.mjs +6 -0
- package/test/browser/more-open.js +11 -0
- package/test/browser/parts.check.mjs +11 -1
- package/test/browser/places-mesh.check.mjs +9 -3
- package/test/browser/product-page.check.mjs +9 -4
- package/test/browser/props.check.mjs +224 -0
- package/test/browser/screen-page.js +17 -0
- package/test/browser/screen.check.mjs +171 -29
- package/test/browser/section-page.js +1 -0
- package/test/browser/section.check.mjs +6 -0
- package/test/command-preview.test.ts +2 -2
- package/test/fasten-geometry.test.ts +15 -0
- package/test/fields.test.ts +4 -2
- package/test/figure-assistant-surface.test.ts +38 -0
- package/test/first-minute.test.ts +15 -2
- package/test/layout.test.ts +1 -1
- package/test/preview-size.test.ts +33 -0
- package/test/refusals.test.ts +1 -1
- package/test/resize-with-placement.test.ts +1 -1
- package/test/stage-model.test.ts +11 -1
- package/test/verdict.test.ts +7 -0
- package/translations/en.json +43 -27
- package/translations/es.json +24 -8
- package/translations/ja.json +25 -9
- package/translations/ko.json +56 -40
- package/translations/ms.json +25 -9
- package/translations/zh.json +25 -9
|
@@ -12,10 +12,19 @@ import { registerProposalHandler } from '@things-factory/ai-assistant/dist-clien
|
|
|
12
12
|
|
|
13
13
|
import { fetchProposalOutcome } from './graphql/index.js'
|
|
14
14
|
import { modellerPathOf } from './modeller/modeller-route.js'
|
|
15
|
-
import { pendNewFigure, runFigureProposal, type FigureApplyDetail } from './modeller/ai-proposal.js'
|
|
15
|
+
import { pendNewFigure, proposalOutcomeFrom, runFigureProposal, type FigureApplyDetail } from './modeller/ai-proposal.js'
|
|
16
16
|
|
|
17
17
|
export const FIGURE_CANDIDATE_TOOL = 'proposeFigureCandidate'
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* The dock's "check the result" of a figure proposal, on every screen that offers its card: what the server recorded of
|
|
21
|
+
* its save (ADR-0097 §5.6 item 5). The modeller asks it, and so does an app's home where a new figure's card is made --
|
|
22
|
+
* there it was missing, and a card cut off by a reload stayed "no answer" for good (figure audit 2026-10-16 N8).
|
|
23
|
+
*/
|
|
24
|
+
export async function figureProposalOutcome(proposalKey: string) {
|
|
25
|
+
return proposalOutcomeFrom(await fetchProposalOutcome(proposalKey), (key, params) => i18next.t(key, params))
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
registerProposalHandler(FIGURE_CANDIDATE_TOOL, async (proposal, context) => {
|
|
20
29
|
const outcome = await runFigureProposal(
|
|
21
30
|
proposal,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The parts a release finding points at, by the author's names. The server's `at` lists the first word of each path it
|
|
3
|
+
* could not prove -- a part's name, or a section of the format such as 「occupancy」 -- and the screen showed it as it came
|
|
4
|
+
* (「occupancy · roller-row-1-roller」, user 2026-10-16). Only the figure's own part names are kept.
|
|
5
|
+
*/
|
|
6
|
+
export function affectedPartsOf(at: string, parts: readonly string[]): string[] {
|
|
7
|
+
return at
|
|
8
|
+
.split('·')
|
|
9
|
+
.map(word => word.trim())
|
|
10
|
+
.filter(word => parts.includes(word))
|
|
11
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { FigureProposalStep } from '@hatiolab/figure-model'
|
|
9
9
|
import type { FigureProductSession } from './product-session.js'
|
|
10
|
-
import { clipLossOf, refusalRecordOf, type FigureRefusalRecord } from './refusals.js'
|
|
10
|
+
import { clipLossOf, editRefusalOf, refusalRecordOf, type FigureRefusalRecord } from './refusals.js'
|
|
11
11
|
|
|
12
12
|
/** What the card carries, as `proposeFigureCandidate` returned it. */
|
|
13
13
|
export interface FigureProposalCard {
|
|
@@ -28,6 +28,8 @@ export interface FigureApplyDetail extends FigureRefusalRecord {
|
|
|
28
28
|
key?: string
|
|
29
29
|
steps: FigureProposalStep[]
|
|
30
30
|
create: boolean
|
|
31
|
+
/** The figure a revision was made for: the one the model read. */
|
|
32
|
+
figureId?: string
|
|
31
33
|
context?: string
|
|
32
34
|
revision?: number
|
|
33
35
|
applied?: boolean
|
|
@@ -82,7 +84,8 @@ function refused(refusal: FigureRefusalRecord & { error?: string }, say: Say): F
|
|
|
82
84
|
? (error ?? '')
|
|
83
85
|
: clips
|
|
84
86
|
? say('figure.text.proposal-would-drop-clips', { clips: clips.map(c => c.clip).join(', ') })
|
|
85
|
-
:
|
|
87
|
+
: /* A refusal with a code is said from it, as the modeller says an edit it refuses; never its message (audit N6). */
|
|
88
|
+
say('figure.text.proposal-refused', { reason: code ? editRefusalOf(refusal, (key, params) => say(key, params)) : (error ?? '') })
|
|
86
89
|
return { verdict: 'refused', errorCode: code ?? 'refused', note }
|
|
87
90
|
}
|
|
88
91
|
|
|
@@ -99,6 +102,12 @@ export function takeFigureOffer(
|
|
|
99
102
|
): boolean {
|
|
100
103
|
const view = session.read()
|
|
101
104
|
if (detail.create && onSavedFigure) return false
|
|
105
|
+
/*
|
|
106
|
+
A revision is for the figure the model read; with another open it is left untaken, and the handler says it needs that
|
|
107
|
+
figure open. The version is no proof of the figure: two figures opened in two page loads stood at the same one
|
|
108
|
+
(figure audit 2026-10-16 N3).
|
|
109
|
+
*/
|
|
110
|
+
if (!detail.create && typeof detail.figureId === 'string' && detail.figureId !== (view.id ?? view.creationId)) return false
|
|
102
111
|
if (detail.create && view.dirty) {
|
|
103
112
|
/* A new figure already being made is the person's; a proposal does not go on top of it or replace it. */
|
|
104
113
|
detail.applied = false
|
|
@@ -128,7 +137,7 @@ export function takeFigureOffer(
|
|
|
128
137
|
|
|
129
138
|
export async function runFigureProposal(card: FigureProposalCard, target: FigureProposalTarget, say: Say): Promise<FigureProposalOutcome> {
|
|
130
139
|
const steps = card?.steps
|
|
131
|
-
if (!Array.isArray(steps) || !steps.length) return { verdict: 'unavailable', errorCode: 'no-steps' }
|
|
140
|
+
if (!Array.isArray(steps) || !steps.length) return { verdict: 'unavailable', errorCode: 'no-steps', note: say('figure.text.proposal-empty') }
|
|
132
141
|
const create = card.create === true
|
|
133
142
|
/*
|
|
134
143
|
A new figure's proposal already saved into a figure is not made again: that figure is opened (ADR-0061 decision 3,
|
|
@@ -147,7 +156,7 @@ export async function runFigureProposal(card: FigureProposalCard, target: Figure
|
|
|
147
156
|
note: say('figure.text.proposal-applied', { n: String(steps.length) })
|
|
148
157
|
})
|
|
149
158
|
|
|
150
|
-
const detail = target.offer({ key: card.key, steps, create, context: card.context, revision: card.revision })
|
|
159
|
+
const detail = target.offer({ key: card.key, steps, create, figureId: create ? undefined : card.figureId, context: card.context, revision: card.revision })
|
|
151
160
|
if (detail.applied) return accepted()
|
|
152
161
|
if (detail.applied === false) return refused(detail, say)
|
|
153
162
|
|
|
@@ -166,11 +175,14 @@ export async function runFigureProposal(card: FigureProposalCard, target: Figure
|
|
|
166
175
|
modeller on a new figure; the page takes it once it has started that figure. Left untaken, it lapses -- a later new
|
|
167
176
|
figure must not receive steps nobody is waiting for.
|
|
168
177
|
*/
|
|
169
|
-
|
|
178
|
+
type Settle = (result: { applied: boolean; error?: string; code?: string }) => void
|
|
179
|
+
let pending: { steps: FigureProposalStep[]; key?: string; settle: Settle; hold(): void; resume(): void } | undefined
|
|
170
180
|
|
|
171
181
|
export function pendNewFigure(steps: FigureProposalStep[], key?: string, ms = 10_000): Promise<{ applied: boolean; error?: string; code?: string }> {
|
|
172
182
|
pending?.settle({ applied: false, code: 'not-opened' })
|
|
173
183
|
return new Promise(resolve => {
|
|
184
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
185
|
+
const lapse = () => mine.settle({ applied: false, code: 'not-opened' })
|
|
174
186
|
const mine = {
|
|
175
187
|
steps,
|
|
176
188
|
key,
|
|
@@ -178,17 +190,36 @@ export function pendNewFigure(steps: FigureProposalStep[], key?: string, ms = 10
|
|
|
178
190
|
clearTimeout(timer)
|
|
179
191
|
if (pending === mine) pending = undefined
|
|
180
192
|
resolve(result)
|
|
193
|
+
},
|
|
194
|
+
hold: () => clearTimeout(timer),
|
|
195
|
+
resume: () => {
|
|
196
|
+
clearTimeout(timer)
|
|
197
|
+
timer = setTimeout(lapse, ms)
|
|
181
198
|
}
|
|
182
199
|
}
|
|
183
|
-
|
|
200
|
+
mine.resume()
|
|
184
201
|
pending = mine
|
|
185
202
|
})
|
|
186
203
|
}
|
|
187
204
|
|
|
205
|
+
/**
|
|
206
|
+
* For a page about to ask the person whether to leave unsaved edits: the new figure waiting to open stops its clock
|
|
207
|
+
* while they answer -- an answer is no absent page (figure audit 2026-10-16 N4). Give it the answer: leaving runs the
|
|
208
|
+
* clock again for the page that opens; staying says now that it was not opened. Nothing waiting: undefined.
|
|
209
|
+
*/
|
|
210
|
+
export function askingToLeave(): ((leaving: boolean) => void) | undefined {
|
|
211
|
+
const waiting = pending
|
|
212
|
+
if (!waiting) return undefined
|
|
213
|
+
waiting.hold()
|
|
214
|
+
return leaving => {
|
|
215
|
+
if (pending !== waiting) return
|
|
216
|
+
if (leaving) waiting.resume()
|
|
217
|
+
else waiting.settle({ applied: false, code: 'not-opened' })
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
188
221
|
/** For the page, on a new figure it has just started: the steps waiting for it, and where to say how it went. */
|
|
189
|
-
export function takePendingNewFigure():
|
|
190
|
-
| { steps: FigureProposalStep[]; key?: string; settle: (result: { applied: boolean; error?: string; code?: string }) => void }
|
|
191
|
-
| undefined {
|
|
222
|
+
export function takePendingNewFigure(): { steps: FigureProposalStep[]; key?: string; settle: Settle } | undefined {
|
|
192
223
|
const taken = pending
|
|
193
224
|
pending = undefined
|
|
194
225
|
return taken
|
|
@@ -5,7 +5,7 @@ import { create, Model, niceGridStep } from '@hatiolab/things-scene'
|
|
|
5
5
|
import type { GizmoReach } from './gizmo.js'
|
|
6
6
|
import type { FigureRenderPart } from './renderer.js'
|
|
7
7
|
import { DEFAULT_VIEW, sceneLook, WORKBENCH, type ViewSettings } from '@things-factory/figure-viewer/dist-client/figure-view.js'
|
|
8
|
-
import { boardFor, changedKeys, componentOf, gizmoResultOf, type Board, type ScaleAnchor } from './stage-model.js'
|
|
8
|
+
import { boardFor, changedKeys, componentOf, gizmoResultOf, needsRefit, type Board, type ScaleAnchor } from './stage-model.js'
|
|
9
9
|
import { JOINT_FAN } from './joint-fan.js'
|
|
10
10
|
import { SLOT_MARK } from './slot-layout-host.js'
|
|
11
11
|
import { PREVIEW, TARGET_FACE } from './command-preview.js'
|
|
@@ -48,6 +48,9 @@ interface SceneHandle {
|
|
|
48
48
|
root?: SceneLayer
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/* How long the figure stands still before the camera judges whether to follow it. */
|
|
52
|
+
const REFIT_SETTLE_MS = 400
|
|
53
|
+
|
|
51
54
|
/* The fan's colour when the figure's palette names no accent. */
|
|
52
55
|
const FAN_COLOUR = '#ff8a00'
|
|
53
56
|
|
|
@@ -103,6 +106,9 @@ export class FigureCanvas extends LitElement {
|
|
|
103
106
|
|
|
104
107
|
private scene?: SceneHandle
|
|
105
108
|
private board?: Board & { depth: number }
|
|
109
|
+
/** The figure's reach when the view was last fitted to it. */
|
|
110
|
+
private fittedFor?: Board & { depth: number }
|
|
111
|
+
private refitTimer?: ReturnType<typeof setTimeout>
|
|
106
112
|
private standingFor?: string
|
|
107
113
|
private watcher?: ResizeObserver
|
|
108
114
|
/** Set while this canvas writes to components, so the scene's echo of it is not taken for a drag. */
|
|
@@ -134,6 +140,7 @@ export class FigureCanvas extends LitElement {
|
|
|
134
140
|
|
|
135
141
|
disconnectedCallback() {
|
|
136
142
|
super.disconnectedCallback()
|
|
143
|
+
clearTimeout(this.refitTimer)
|
|
137
144
|
this.watcher?.disconnect()
|
|
138
145
|
this.watcher = undefined
|
|
139
146
|
this.teardown()
|
|
@@ -162,6 +169,7 @@ export class FigureCanvas extends LitElement {
|
|
|
162
169
|
|
|
163
170
|
/** The whole figure in view: the board's fit, then the 3D camera's own. */
|
|
164
171
|
fit() {
|
|
172
|
+
this.fittedFor = boardFor(this.parts)
|
|
165
173
|
this.scene?.fit?.('ratio')
|
|
166
174
|
this.scene?.root?.fitCameraToModel?.()
|
|
167
175
|
}
|
|
@@ -186,6 +194,12 @@ export class FigureCanvas extends LitElement {
|
|
|
186
194
|
*/
|
|
187
195
|
if (outgrown) this.grow(needed)
|
|
188
196
|
this.sync()
|
|
197
|
+
/*
|
|
198
|
+
Grown or shrunk far past the view (a size typed in): the camera follows, once the figure has settled. A value is
|
|
199
|
+
drawn as it is typed -- 「3」, 「30」, 「300」 on the way to 3000 -- so the reach is judged after the last change.
|
|
200
|
+
*/
|
|
201
|
+
clearTimeout(this.refitTimer)
|
|
202
|
+
if (this.parts.length) this.refitTimer = setTimeout(() => needsRefit(this.fittedFor, boardFor(this.parts)) && this.fit(), REFIT_SETTLE_MS)
|
|
189
203
|
return
|
|
190
204
|
}
|
|
191
205
|
this.teardown()
|
|
@@ -3,6 +3,7 @@ import { css } from 'lit'
|
|
|
3
3
|
/** The modeller editor's own styles: the bar's tools, the panes, the start card, the side's tabs and panels. */
|
|
4
4
|
export const EditorStyles = css`
|
|
5
5
|
:host {
|
|
6
|
+
position: relative;
|
|
6
7
|
display: flex;
|
|
7
8
|
flex-direction: column;
|
|
8
9
|
min-height: 0;
|
|
@@ -16,6 +17,7 @@ export const EditorStyles = css`
|
|
|
16
17
|
fit in the suite's places. What the editor puts in it - the mode switch, the status, save - is styled here. */
|
|
17
18
|
ox-canvas-bar button[mode] {
|
|
18
19
|
height: 28px;
|
|
20
|
+
border: 1px solid transparent;
|
|
19
21
|
padding: 0 10px;
|
|
20
22
|
border-radius: 999em;
|
|
21
23
|
border-color: transparent;
|
|
@@ -250,6 +252,63 @@ export const EditorStyles = css`
|
|
|
250
252
|
[off] {
|
|
251
253
|
display: none !important;
|
|
252
254
|
}
|
|
255
|
+
/* The part's own numbers inside a group the links draw (size, place): one column, the panel's gap. */
|
|
256
|
+
div[group-fields] {
|
|
257
|
+
display: grid;
|
|
258
|
+
gap: 8px;
|
|
259
|
+
}
|
|
260
|
+
/* A folded group (capability, 「더 보기」): its title is the fold's handle, at the title's size. */
|
|
261
|
+
section[more] > details > summary,
|
|
262
|
+
section[capability] > details > summary {
|
|
263
|
+
padding: 0;
|
|
264
|
+
}
|
|
265
|
+
section[more] > details > summary > h3,
|
|
266
|
+
section[capability] > details > summary > h3 {
|
|
267
|
+
margin: 0;
|
|
268
|
+
}
|
|
269
|
+
div[more-body] {
|
|
270
|
+
display: grid;
|
|
271
|
+
gap: 16px;
|
|
272
|
+
margin-top: 12px;
|
|
273
|
+
}
|
|
274
|
+
/*
|
|
275
|
+
The view settings and the release history, opened from the bar over the right of the work: neither is about the
|
|
276
|
+
part chosen, so neither takes a tab beside the part's (chief architect 2026-10-16).
|
|
277
|
+
*/
|
|
278
|
+
:host {
|
|
279
|
+
position: relative;
|
|
280
|
+
}
|
|
281
|
+
div[bar-overlay-panel] {
|
|
282
|
+
position: absolute;
|
|
283
|
+
top: var(--figure-bar-height, 44px);
|
|
284
|
+
right: 8px;
|
|
285
|
+
z-index: 5;
|
|
286
|
+
display: flex;
|
|
287
|
+
flex-direction: column;
|
|
288
|
+
width: min(360px, calc(100% - 16px));
|
|
289
|
+
max-height: calc(100% - var(--figure-bar-height, 44px) - 16px);
|
|
290
|
+
background: var(--md-sys-color-surface-container-lowest);
|
|
291
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
292
|
+
border-radius: 8px;
|
|
293
|
+
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.16);
|
|
294
|
+
}
|
|
295
|
+
div[overlay-head] {
|
|
296
|
+
display: flex;
|
|
297
|
+
align-items: center;
|
|
298
|
+
justify-content: space-between;
|
|
299
|
+
padding: 8px 8px 8px 12px;
|
|
300
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
301
|
+
}
|
|
302
|
+
div[overlay-head] h3 {
|
|
303
|
+
margin: 0;
|
|
304
|
+
}
|
|
305
|
+
div[overlay-head] button[quiet] {
|
|
306
|
+
margin-left: 0;
|
|
307
|
+
}
|
|
308
|
+
div[overlay-body] {
|
|
309
|
+
min-height: 0;
|
|
310
|
+
overflow: auto;
|
|
311
|
+
}
|
|
253
312
|
/* Which part the panel speaks of. */
|
|
254
313
|
div[subject] {
|
|
255
314
|
position: sticky;
|
|
@@ -277,19 +336,52 @@ export const EditorStyles = css`
|
|
|
277
336
|
div[subject-summary] small {
|
|
278
337
|
color: var(--md-sys-color-on-surface-variant);
|
|
279
338
|
}
|
|
339
|
+
/*
|
|
340
|
+
No rule here draws every button as a box: the side's buttons take the shared panel rules (SidePanelStyles), one
|
|
341
|
+
height with the fields beside them, and only the bar's publish keeps an outline (user 2026-10-16: 「버튼 박스도
|
|
342
|
+
과하게 사용되어서」 -- a bare 「button」 rule had put a 32px box round every button the editor drew).
|
|
343
|
+
*/
|
|
280
344
|
button {
|
|
281
345
|
font: inherit;
|
|
282
346
|
box-sizing: border-box;
|
|
347
|
+
color: inherit;
|
|
348
|
+
min-width: 0;
|
|
349
|
+
cursor: pointer;
|
|
350
|
+
}
|
|
351
|
+
/* What blocks release, beside 「발행」: one red word button, and its list under the bar. */
|
|
352
|
+
ox-canvas-bar button[release-blocked] {
|
|
353
|
+
display: inline-flex;
|
|
354
|
+
align-items: center;
|
|
355
|
+
gap: 4px;
|
|
283
356
|
height: 32px;
|
|
284
|
-
|
|
357
|
+
padding: 0 10px;
|
|
358
|
+
border: none;
|
|
285
359
|
border-radius: 6px;
|
|
360
|
+
color: var(--md-sys-color-on-error-container);
|
|
361
|
+
background: var(--md-sys-color-error-container);
|
|
362
|
+
}
|
|
363
|
+
ox-canvas-bar button[release-blocked] md-icon {
|
|
364
|
+
--md-icon-size: 18px;
|
|
365
|
+
}
|
|
366
|
+
div[release-blockers] {
|
|
367
|
+
position: absolute;
|
|
368
|
+
top: 46px;
|
|
369
|
+
right: 8px;
|
|
370
|
+
z-index: 6;
|
|
371
|
+
width: min(340px, calc(100% - 16px));
|
|
372
|
+
padding: 8px 10px 10px;
|
|
373
|
+
box-sizing: border-box;
|
|
286
374
|
background: var(--md-sys-color-surface);
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
375
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
376
|
+
border-radius: 8px;
|
|
377
|
+
box-shadow: var(--md-sys-elevation-2, 0 2px 8px #0003);
|
|
290
378
|
}
|
|
291
|
-
button {
|
|
292
|
-
|
|
379
|
+
ox-canvas-bar button[publish] {
|
|
380
|
+
height: 32px;
|
|
381
|
+
padding: 0 8px;
|
|
382
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
383
|
+
border-radius: 6px;
|
|
384
|
+
background: var(--md-sys-color-surface);
|
|
293
385
|
}
|
|
294
386
|
button:disabled {
|
|
295
387
|
opacity: 0.45;
|