prior-art-wizard-kit.md
Prior art: app.qpoint.io's linear wizard kit
c125 · 2026-10-27. Mark named this as the prior art: "a simple linear wizard for complex selection routines". It was documented first, then copied (Mark's ruling of 2026-10-27).
- Source:
../app.qpoint.io/app/components/wizard/andapp/utils/sequin.jsatf5faf10. The kit is unchanged since90dd408(2026-03-10). - Not in q-nuxt-layer. c23's pantry analysis listed all five components as app-local, and nobody extracted them since. The layer's
prose/StepandStepWrapperare doc-page steps, not a wizard. - Replica:
/pages/phase4/wizard-kit, built fromapp/components/replicas/wizard/*(ReplicasWizard*) andapp/utils/sequin.js. - Captures:
- thread
c125-wizard-kit(c125-wizard-steps-tabs-{a,c,d}), the original running on app.qpoint.io; - thread
c125-wizard-replica(c125-replica-*), the copy on the design site.
- thread
The pieces
| Piece | What it is |
|---|---|
Sequin (utils/sequin.js) | A cursor over an array: next, prev, isAtFirstItem, isAtLastItem, activateItemByParam('id', …), addItem, removeItemByParam. It clamps at the ends, and can loop on overshoot. |
WizardSteps | The container. It takes sequence, an array of steps { id, title, tab?, wizard? }. Header: tabs (tabs, centerTabs), or the step title plus Back and "Next : …". Then Continue, then Submit on the last step. It emits changeStep(id, step) and submit, and exposes nextStep, prevStep and submit. The parent renders the body with v-if on the active id. |
per-step wizard overrides | mode (form · listener · end), block (hold Continue until the listener confirms), listenConfirmation (a boolean or a function, read reactively), listenTxt, confirmTxt, continueTxt, submitTxt, submittingTxt, noNextBtn, noTitle, callback (fired on Continue). |
WizardEduHarness | The body on the left and an #edu slot on the right. The slot styles h1, h2, p, .instruction, .tip and .tip.caution. |
WizardToggleHarness | The same split with a narrow 360 px column of choices. |
WizardPingListener | A spinning radar with expanding rings, then the proxy icon and a check with confirmText when success is set. The header uses it in listener mode. |
WizardFinalStep | The end slide: #image, title, sub, and a "Next Steps" link list. |
UxLinkBack (app-local) | The Back link in linear mode. |
Where it shipped
| Flow | File (app/pages/…) | Steps | Shape |
|---|---|---|---|
| Install Qtap | orgs/components/install/qtap/Index.vue, mounted by orgs/install.vue | environment → installation → complete | listener + block while waiting for the sensor to register, then end. Without waitForTraffic: environment → installation (form, Finish). |
| Install Qscan | orgs/components/install/qscan/Index.vue | environment → installation | form, Finish |
| Activate data-risk scanning | security/components/data-risk/wizards/activate/Index.vue, inline in the NoneDetected empty state | Stacks → Data Types → Configuration → Persistence | tabs, Activate |
| Activate vulnerability scanning | security/components/vulnerabilities/wizards/activate/Index.vue | Select Categories | one step, Activate |
| Create a label (views) | orgs/components/settings/views/wizards/create/Index.vue | name → filters → members | tabs, noNextBtn and noTitle (each step's own form advances), Complete |
| Configure MCP client | orgs/components/settings/mcp/Configure.vue | choose client → instructions | noNextBtn (picking a client advances), Finish |
| Onboarding preferences | orgs/components/onboarding/preferences/Index.vue | capabilities → hosting | end on hosting. bob-wire c15 called these the "highest-leverage empty shell". |
What it hands c125
- A step is data. An id, a title and per-step overrides. A tour step can be the same shape plus a page, a target and a condition.
- Waiting is a mode.
listener+blockholds a step until the world confirms it. That is the mechanism for journey progress from real state rather than ticked boxes (J2 deploy: "N of M endpoints reporting"). - The edu slot is where the documentation already lived. Every step had a place for the why, so the kit already holds "the UI becomes the documentation" in small.
- It lives inside one container. Each flow mounts the wizard in a page region: an empty state, the install page, a settings panel. A tour must cross pages and point at things outside its box, and that is the gap c125 fills.
- Advancing from the content.
noNextBtnlets a step's own choice move the wizard. That is the right default for selection routines, and for tour steps that wait on the user doing the thing.
The richer radar (branch origin/mask-image)
Mark (2026-10-27): "I had a branch a looong time ago that added a more sophisticated radar." It's app.qpoint.io origin/mask-image, two unmerged commits on top of 82e68bf (Wizard Refactor / Radar improvement, #77):
d82388d(2024-06-05), EXPERIMENTAL: trying a low-fi image masking technique. It addscomponents/wizard/Radar.vueand five radar layers as SVG and @3x PNG:radar-bg-lines,radar-fat-lines,radar-mask,radar-mask-3,radar-mask-fat.0bfe673(2024-06-05), Full svg use. It addsbg-lines-humanand moves Radar to SVGs.
Radar is a black disc with two layers turning at different speeds: bg-lines-human every 12 s, under the radar-mask-3 sweep (arm, rings, centre dot) every 6 s. PingListener drops the single spinning radar.svg for <WizardRadar />, and its fourth ping moves from 1.35 s to 1.3 s.
In the replica, ReplicasWizardRadar is the branch tip copied verbatim except for its image paths, and ReplicasWizardPingListener carries the branch's change. All twelve branch assets are in app/assets/images/wizard/mask-image/ (SVG, plus @3x PNG in 3x/), so the unused masks and line sets are there to try. The shipped radar.svg stays in app/assets/images/wizard/ for comparison.
Found while copying
- In app.qpoint.io, Continue's
btn-outlineloses toUxButton'sbg-grape-600: the layer'sUxButtongainedkind="stroke"for the outline look. So it reads grape on grape there. On the design site the CSS order lets the outline win. - The
callBackprop is declared but never read; the code readsprops.callback. pingConfirm(the listener'slistenConfirmCallback) is commented out. Confirmation un-blocks Continue but never advances on its own.- The
/dev/wizards/install-qtap,connect-an-appanddeploy-qproxydemos no longer resolve their components (WizardsConnectApp,WizardsInstallQproxy). Only/dev/wizards(A–D) still runs, and the real flows sit behind sign-in. - app.qpoint.io's dev server conflicts with itself on :3300: the content watcher (
watch.port: 3300) holds the IPv6 listener, solocalhost:3300hangs. Run it with--port 3301(launch configapp-qpoint-io).
Replica changes (everything else is verbatim)
UxLinkBackbecameReplicasWizardLinkBack, andWizardPingListenerbecameReplicasWizardPingListener.- PingListener's images were copied to
app/assets/images/wizard/, and its style block is now scoped. sequin.jsdropped its closingwindow.Sequin = Sequin, because the design site renders on the server.