feat(preview): layout preview inside the QBit: service, action and record widget (P6b); bound preview painting and pin the version 1 job frame #17

Merged
james.maes merged 7 commits from feat/preview-ui into develop 2026-11-12 20:50:56 +00:00
7 Commits
Author SHA1 Message Date
james.maesandClaude Fable 5.2 363e96f641 fix(preview): only a publication is previewed by revision; the action's widget request carries no record
maven / build (push) Successful in 12m0s
build / maven (push) Successful in 12m0s
Changes after the independent review of the layout preview.

A person who may not author a template cannot obtain the picture of an
unpublished draft by naming a revision. The revision table also holds the
frozen drafts that the retained preview writes; a named revision is now
resolved only when it is a publication, and any other row is answered
"Document definition not found.", with the type and the words of an identity
that does not exist, from the service, from the action before and after its
form, and from the widget. A draft is previewed through its template alone.

The widget of the "Preview layout" action is requested by the identifier of
the run and reads the person's choices from the state of that run. A client
sends every plain process value as a parameter of the widget request, and
the framework's widget route logs the parameters of a request that fails or
takes over a second, so the chosen record's identity and the sample's name
were logged for a slow preview. The check step now holds the accepted
choices as one structured process value, which is not sent, and the widget
no longer reads a revision, record, sample, stock or resolution from any
request parameter.

A source record is read within the channel and credential ceilings the print
path applies to the source table, asked before the record is read.

A label whose picture alone is refused (PREVIEW_TOO_LARGE and no other
refusal) is no longer said to be unprintable: "This label is too dense to
preview; printing is not affected."

The help of the form says that the longest values picture is drawn with
capital W, is indicative, and that the renderer's proof at publication
decides. The README records what a host must configure for label previews:
no-store for widget responses, which neither a widget renderer nor the
widget route sets, and what the request log holds.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 14:38:15 -06:00
james.maesandClaude Fable 5.2 1db82957c9 fix(bridge): a placeholder of no area draws nothing; name the answer table for what it is
ZplRaster.hatch returns at once for an area of no width or no height. Its
outline and diagonals would otherwise be placed one dot outside the area:
with no width the diagonal's column divides down to -1 and a stray line is
drawn. The guard that the refused page had around its call is now in the
painter itself, so every caller has it. An empty printer font text field did
not reach the painter before this change either: such a field is refused as
no field (RASTER_FIELD), and by the output policy before that; the existing
test for it is unchanged. ZplRaster stays at 100% lines.

ReleasedRendererAnswerTest is renamed VersionOneAnswerTableTest and its
header says what it is: 71 synthetic answers in the shape the released
renderer answers with, and the outcomes of today's bridge for them. They were
not captured at the released tag, unlike the job frame golden, so the table
holds today's behaviour still and does not show equality with the release.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 14:38:14 -06:00
james.maesandClaude Fable 5.2 6aba6ce8d4 docs(preview): the placeholder is outlined and crossed; record the bridge review findings
maven / build (push) Successful in 12m0s
build / maven (push) Successful in 12m0s
The widget's sentences, the authoring guide, the bridge README and the
design follow the bounded painting of the preview: a placeholder is an
outline with its two diagonals, and a page painted more than sixteen
times over is PREVIEW_TOO_LARGE. The design also records that the
version question is asked again after any RENDERER_TOO_OLD and that the
released version 1 job frame is pinned.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 13:49:54 -06:00
james.maesandClaude Fable 5.2 1828ae9c48 fix(bridge): ask the renderer's version again after any RENDERER_TOO_OLD
The comments said that nothing of a version 2 scene reaches a renderer
that cannot draw it. That is not so: the answer to the version question is
remembered, so after a renderer image is rolled back ONE job reaches the
old (0.1.0) renderer, scene and record values included, and is then
refused as RENDERER_TOO_OLD. The class, field, invoke and probe comments
now say exactly that.

The window stays one job in every case: a job that ends in
RENDERER_TOO_OLD, from render, validate or preview, forgets what was
remembered, so the next job that needs renderer 0.2.0 asks the version
first. An answer from 0.1.0 already did this; a refusal that renderer
0.2.0 names itself, as an error answer or among collected diagnostics, did
not.

FakeWorker can stand for an image that is replaced while a bridge uses it.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 13:49:06 -06:00
james.maesandClaude Fable 5.2 bbb4b60a1b test(bridge): golden of the released version 1 job frame and a table of released answers
Nothing held the version 1 print request to the released bytes, and the
0.1.0 answers were tested at the units and not through render().

- compat-v1/job-frame-render.json is the frame the bridge of tag
  v1.1.0-RC.2 (e9ffba484e) wrote to its
  exchange for one render() with fixed inputs. VersionOneJobFrameTest
  compiles unchanged at that tag and is its own capture tool; the .md
  beside the frame gives the inputs and the commands.
- The frame is compared byte for byte except for the order of the seven
  members of "stock": the released bridge writes them in the iteration
  order of an immutable Map.of, which the JVM changes from run to run (four
  captures at the tag gave four orders and were otherwise identical).
- ReleasedRendererAnswerTest is one row for each answer of
  qqq-renderer-0.1.0 to a version 1 job, accepted, refused by code or
  rejected, every row through render(), with the name the version rule
  gives the same frame.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 13:49:06 -06:00
james.maesandClaude Fable 5.2 9e5b16d686 fix(bridge): bound preview painting to sixteen times the page
ZplRaster painted a box and a placeholder by testing every dot of the
field's area. The output policy allows 6000 fields, so a label of
page-sized boxes was tens of thousands of millions of dots of work on the
host while an admission slot was held, and the page of a refused scene had
the same cost for each of up to 2048 diagnostic boxes.

- Every painter counts the dots it is about to paint, an overlap each
  time, and the field that would take the page past sixteen times its dots
  is refused as PREVIEW_TOO_LARGE. This is the renderer's own limit for a
  scene. A graphic counts eight dots for each byte.
- A box is the whole area when its border meets itself, else four bars
  that do not overlap, filled whole bytes at a time. The dots are the same
  as before.
- A placeholder (printer font text, a refused element's box) is an outline
  and its two diagonals, so it costs its perimeter and not its area. This
  changes its look: the diagonal hatch every six dots is gone.
- preview() returns the refusal as a result, like the page limit: not
  printable, no page, PREVIEW_TOO_LARGE with limit.paintedPercent 1600. For
  a refused scene it joins the diagnostics the worker sent.

Printing is not changed.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 13:49:05 -06:00
james.maesandClaude Fable 5.2 ed11495120 feat(preview): layout preview inside the QBit: service, action and record widget (P6b)
A person editing a label template sees the picture that would print, with
its refusals and warnings, before anything is printed, from the module's
own screens and with no host code.

- DocumentPreviewService: authorises by the existing permissions (a draft
  needs configure, a published revision needs view, a record needs
  generate and is read as the person, with their permissions and record
  locks, before any context is built), loads the draft or the published
  snapshot without writing, builds the context as generation does and
  calls IsolatedRenderer.preview. Data is a sample from the scene, the
  longest declared values, or one record. One log line, no content.
- "Preview layout" action on templates and published revisions: data,
  sample, record, stock and resolution; the result is the preview widget.
- "Label preview" section on the template record screen: the draft with
  sample data (the published revision for a person who may only view).
- The widget is fetched, never seeded from a process value: QQQ keeps the
  state of a finished process for hours (in a table with the shared
  provider), so the picture, the context and the diagnostics exist only
  in the answer to the widget request. The process state holds the
  person's choices only.
- Bounded answer: one page, a picture of at most 2 MiB, fifty diagnostics.
- The existing action is relabelled "Generate retained preview file"; its
  process name and behaviour are unchanged.
- The record selector asks for generate alone, so a person who may
  preview a published revision with a record can choose one.
- Help on every new process, step, field, section, widget and choice list.

Tests: DocumentPreviewTest (data sources, permission matrix, unreadable
record, nothing written, process state, PDF refusal, refused scene,
version 1 label, bounds, log line, help coverage, instance validation)
and LayoutPreviewServiceTest in bridges/renderer (through the service and
the bridge, the preview has the hash of the bytes the print path returns
at 203, 300 and 600 dpi). The registration and process tests are updated
for the new wiring and the relabel. bridges/renderer gains the RDBMS
module at test scope for that test.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 13:37:56 -06:00