@things-factory/figure-ui 10.1.196 → 10.1.198

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.
@@ -30,6 +30,8 @@ export declare class RefusalTeller {
30
30
  constructor(of: RefusalHost);
31
31
  /** After the editor's update: what changed in its refusal and notice, and the session's own error, said once. */
32
32
  tell(changed: PropertyValues): void;
33
+ /** What cannot be done, said once as a notice: nothing went wrong, and there is no other way to offer. */
34
+ say(message: string): void;
33
35
  /**
34
36
  * What cannot be done this way, with the way it can, as a notice: nothing went wrong, so not the error's red
35
37
  * (user 2026-10-19, the size drag on a part attached turned). Given more time than a plain notice, for its button.
@@ -31,6 +31,10 @@ export class RefusalTeller {
31
31
  notify({ level: 'error', message: viewError });
32
32
  this.toldViewError = viewError;
33
33
  }
34
+ /** What cannot be done, said once as a notice: nothing went wrong, and there is no other way to offer. */
35
+ say(message) {
36
+ notify({ level: 'info', message });
37
+ }
34
38
  /**
35
39
  * What cannot be done this way, with the way it can, as a notice: nothing went wrong, so not the error's red
36
40
  * (user 2026-10-19, the size drag on a part attached turned). Given more time than a plain notice, for its button.
@@ -1 +1 @@
1
- {"version":3,"file":"refusal-teller.js","sourceRoot":"","sources":["../../client/modeller/refusal-teller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PropertyValues } from 'lit'\nimport { i18next } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup'\nimport type { FigureAuthoringAction, FigureClipLoss } from '@hatiolab/figure-model'\nimport type { FigureProductSession } from './product-session.js'\nimport { clipLossOf } from './refusals.js'\n\n/** The same link offered again with the size range narrowed, after a link the declared range could not hold. */\nexport type Retry = { label: string; action?: FigureAuthoringAction; run?: () => void }\n\n/** What the teller reads of the editor, and what it asks of it. */\nexport interface RefusalHost {\n session(): FigureProductSession | undefined\n error(): string\n notice(): string\n retry(): Retry | undefined\n /** Said as the editor's refusal. */\n refuse(message: string): void\n /** A narrowed link, confirmed: run as the editor runs a command. */\n command(action: FigureAuthoringAction): void\n}\n\n/**\n * Refusals and notices go to the app's shared surfaces, as every screen's do (user, 2026-09-27: 「왜 모두가 사용하는\n * notification 오류 표현 방법을 사용하지 않고」 · 「혹은 oxprompt 에서 확인하고 진행하던지」). A refusal with a way on --\n * delete and fit the space again, narrow the range -- asks in the app's prompt and goes on when confirmed; any\n * other refusal is the app's notification, which stays until it is closed.\n */\nexport class RefusalTeller {\n private toldViewError = ''\n\n constructor(private readonly of: RefusalHost) {}\n\n /** After the editor's update: what changed in its refusal and notice, and the session's own error, said once. */\n tell(changed: PropertyValues) {\n const error = this.of.error()\n if (changed.has('error') && error) {\n const retry = this.of.retry()\n if (retry) this.ask(error, retry)\n else notify({ level: 'error', message: error })\n }\n const notice = this.of.notice()\n if (changed.has('notice') && notice) notify({ level: 'info', message: notice })\n const viewError = this.of.session()?.read()?.error ?? ''\n if (viewError && viewError !== this.toldViewError) notify({ level: 'error', message: viewError })\n this.toldViewError = viewError\n }\n\n /**\n * What cannot be done this way, with the way it can, as a notice: nothing went wrong, so not the error's red\n * (user 2026-10-19, the size drag on a part attached turned). Given more time than a plain notice, for its button.\n */\n point(message: string, way: { label: string; run: () => void }) {\n notify({ level: 'info', message, option: { action: { label: way.label, callback: way.run }, timer: 8000 } })\n }\n\n /*\n An edit that would stop clips is refused by figure-model with the clips named (CLIPS_DROPPED). The person is told\n which, and why, before anything is written; agreed, the same edit is made again letting exactly those go, as one\n step undo brings back (designer 2026-09-27).\n */\n askClipLoss(error: unknown, again: () => void): boolean {\n const clips = clipLossOf(error)\n if (!clips) return false\n /* The consent is for this edit, on this figure as it is now: held with it while the person reads. */\n const session = this.of.session()!\n const { context, revision } = session.read()\n void this.confirmClipLoss(clips, again, { session, context: context!, revision: revision! })\n return true\n }\n\n private async confirmClipLoss(clips: FigureClipLoss[], again: () => void, asked: { session: FigureProductSession; context: string; revision: number }) {\n const agreed = await OxPrompt.open({\n type: 'warning',\n text: i18next.t('figure.text.clips-would-go', { clips: clips.map(c => i18next.t(`figure.text.clip-${c.why}`, { clip: c.clip })).join(', ') }),\n confirmButton: { text: i18next.t('figure.button.let-clips-go') },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n })\n if (!agreed) return\n /* Changed while the person read -- another figure, or an edit in between: the consent was not for this; nothing is applied. */\n const now = this.of.session() === asked.session ? asked.session.read() : undefined\n if (!now || now.context !== asked.context || now.revision !== asked.revision) {\n this.of.refuse(i18next.t('figure.text.clips-consent-stale'))\n return\n }\n asked.session.withConsent({ dropClips: clips.map(c => c.id), context: asked.context, revision: asked.revision }, again)\n }\n\n private async ask(message: string, retry: Retry) {\n const agreed = await OxPrompt.open({\n type: 'question',\n text: message,\n confirmButton: { text: retry.label },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n })\n if (!agreed) return\n if (retry.run) retry.run()\n else this.of.command(retry.action!)\n }\n}\n"]}
1
+ {"version":3,"file":"refusal-teller.js","sourceRoot":"","sources":["../../client/modeller/refusal-teller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAGzC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAiB1C;;;;;GAKG;AACH,MAAM,OAAO,aAAa;IAGxB,YAA6B,EAAe;QAAf,OAAE,GAAF,EAAE,CAAa;QAFpC,kBAAa,GAAG,EAAE,CAAA;IAEqB,CAAC;IAEhD,iHAAiH;IACjH,IAAI,CAAC,OAAuB;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAA;QAC7B,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAA;YAC7B,IAAI,KAAK;gBAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;;gBAC5B,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAA;QACjD,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAA;QAC/B,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,MAAM;YAAE,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;QAC/E,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,IAAI,EAAE,CAAA;QACxD,IAAI,SAAS,IAAI,SAAS,KAAK,IAAI,CAAC,aAAa;YAAE,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAA;QACjG,IAAI,CAAC,aAAa,GAAG,SAAS,CAAA;IAChC,CAAC;IAED,0GAA0G;IAC1G,GAAG,CAAC,OAAe;QACjB,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;IACpC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAe,EAAE,GAAuC;QAC5D,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IAC9G,CAAC;IAED;;;;MAIE;IACF,WAAW,CAAC,KAAc,EAAE,KAAiB;QAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;QAC/B,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAA;QACxB,qGAAqG;QACrG,MAAM,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,EAAG,CAAA;QAClC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;QAC5C,KAAK,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAQ,EAAE,QAAQ,EAAE,QAAS,EAAE,CAAC,CAAA;QAC5F,OAAO,IAAI,CAAA;IACb,CAAC;IAEO,KAAK,CAAC,eAAe,CAAC,KAAuB,EAAE,KAAiB,EAAE,KAA2E;QACnJ,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC;YACjC,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7I,aAAa,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAC,EAAE;YAChE,YAAY,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAC,EAAE;SAChE,CAAC,CAAA;QACF,IAAI,CAAC,MAAM;YAAE,OAAM;QACnB,+HAA+H;QAC/H,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;QAClF,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC7E,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAA;YAC5D,OAAM;QACR,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAA;IACzH,CAAC;IAEO,KAAK,CAAC,GAAG,CAAC,OAAe,EAAE,KAAY;QAC7C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC;YACjC,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,OAAO;YACb,aAAa,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE;YACpC,YAAY,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAC,EAAE;SAChE,CAAC,CAAA;QACF,IAAI,CAAC,MAAM;YAAE,OAAM;QACnB,IAAI,KAAK,CAAC,GAAG;YAAE,KAAK,CAAC,GAAG,EAAE,CAAA;;YACrB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,MAAO,CAAC,CAAA;IACrC,CAAC;CACF","sourcesContent":["import type { PropertyValues } from 'lit'\nimport { i18next } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup'\nimport type { FigureAuthoringAction, FigureClipLoss } from '@hatiolab/figure-model'\nimport type { FigureProductSession } from './product-session.js'\nimport { clipLossOf } from './refusals.js'\n\n/** The same link offered again with the size range narrowed, after a link the declared range could not hold. */\nexport type Retry = { label: string; action?: FigureAuthoringAction; run?: () => void }\n\n/** What the teller reads of the editor, and what it asks of it. */\nexport interface RefusalHost {\n session(): FigureProductSession | undefined\n error(): string\n notice(): string\n retry(): Retry | undefined\n /** Said as the editor's refusal. */\n refuse(message: string): void\n /** A narrowed link, confirmed: run as the editor runs a command. */\n command(action: FigureAuthoringAction): void\n}\n\n/**\n * Refusals and notices go to the app's shared surfaces, as every screen's do (user, 2026-09-27: 「왜 모두가 사용하는\n * notification 오류 표현 방법을 사용하지 않고」 · 「혹은 oxprompt 에서 확인하고 진행하던지」). A refusal with a way on --\n * delete and fit the space again, narrow the range -- asks in the app's prompt and goes on when confirmed; any\n * other refusal is the app's notification, which stays until it is closed.\n */\nexport class RefusalTeller {\n private toldViewError = ''\n\n constructor(private readonly of: RefusalHost) {}\n\n /** After the editor's update: what changed in its refusal and notice, and the session's own error, said once. */\n tell(changed: PropertyValues) {\n const error = this.of.error()\n if (changed.has('error') && error) {\n const retry = this.of.retry()\n if (retry) this.ask(error, retry)\n else notify({ level: 'error', message: error })\n }\n const notice = this.of.notice()\n if (changed.has('notice') && notice) notify({ level: 'info', message: notice })\n const viewError = this.of.session()?.read()?.error ?? ''\n if (viewError && viewError !== this.toldViewError) notify({ level: 'error', message: viewError })\n this.toldViewError = viewError\n }\n\n /** What cannot be done, said once as a notice: nothing went wrong, and there is no other way to offer. */\n say(message: string) {\n notify({ level: 'info', message })\n }\n\n /**\n * What cannot be done this way, with the way it can, as a notice: nothing went wrong, so not the error's red\n * (user 2026-10-19, the size drag on a part attached turned). Given more time than a plain notice, for its button.\n */\n point(message: string, way: { label: string; run: () => void }) {\n notify({ level: 'info', message, option: { action: { label: way.label, callback: way.run }, timer: 8000 } })\n }\n\n /*\n An edit that would stop clips is refused by figure-model with the clips named (CLIPS_DROPPED). The person is told\n which, and why, before anything is written; agreed, the same edit is made again letting exactly those go, as one\n step undo brings back (designer 2026-09-27).\n */\n askClipLoss(error: unknown, again: () => void): boolean {\n const clips = clipLossOf(error)\n if (!clips) return false\n /* The consent is for this edit, on this figure as it is now: held with it while the person reads. */\n const session = this.of.session()!\n const { context, revision } = session.read()\n void this.confirmClipLoss(clips, again, { session, context: context!, revision: revision! })\n return true\n }\n\n private async confirmClipLoss(clips: FigureClipLoss[], again: () => void, asked: { session: FigureProductSession; context: string; revision: number }) {\n const agreed = await OxPrompt.open({\n type: 'warning',\n text: i18next.t('figure.text.clips-would-go', { clips: clips.map(c => i18next.t(`figure.text.clip-${c.why}`, { clip: c.clip })).join(', ') }),\n confirmButton: { text: i18next.t('figure.button.let-clips-go') },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n })\n if (!agreed) return\n /* Changed while the person read -- another figure, or an edit in between: the consent was not for this; nothing is applied. */\n const now = this.of.session() === asked.session ? asked.session.read() : undefined\n if (!now || now.context !== asked.context || now.revision !== asked.revision) {\n this.of.refuse(i18next.t('figure.text.clips-consent-stale'))\n return\n }\n asked.session.withConsent({ dropClips: clips.map(c => c.id), context: asked.context, revision: asked.revision }, again)\n }\n\n private async ask(message: string, retry: Retry) {\n const agreed = await OxPrompt.open({\n type: 'question',\n text: message,\n confirmButton: { text: retry.label },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n })\n if (!agreed) return\n if (retry.run) retry.run()\n else this.of.command(retry.action!)\n }\n}\n"]}