← Design · c125

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/ and app/utils/sequin.js at f5faf10. The kit is unchanged since 90dd408 (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/Step and StepWrapper are doc-page steps, not a wizard.
  • Replica: /pages/phase4/wizard-kit, built from app/components/replicas/wizard/* (ReplicasWizard*) and app/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.

The pieces

PieceWhat 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.
WizardStepsThe 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 overridesmode (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).
WizardEduHarnessThe body on the left and an #edu slot on the right. The slot styles h1, h2, p, .instruction, .tip and .tip.caution.
WizardToggleHarnessThe same split with a narrow 360 px column of choices.
WizardPingListenerA 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.
WizardFinalStepThe end slide: #image, title, sub, and a "Next Steps" link list.
UxLinkBack (app-local)The Back link in linear mode.

Where it shipped

FlowFile (app/pages/…)StepsShape
Install Qtaporgs/components/install/qtap/Index.vue, mounted by orgs/install.vueenvironment → installation → completelistener + block while waiting for the sensor to register, then end. Without waitForTraffic: environment → installation (form, Finish).
Install Qscanorgs/components/install/qscan/Index.vueenvironment → installationform, Finish
Activate data-risk scanningsecurity/components/data-risk/wizards/activate/Index.vue, inline in the NoneDetected empty stateStacks → Data Types → Configuration → Persistencetabs, Activate
Activate vulnerability scanningsecurity/components/vulnerabilities/wizards/activate/Index.vueSelect Categoriesone step, Activate
Create a label (views)orgs/components/settings/views/wizards/create/Index.vuename → filters → memberstabs, noNextBtn and noTitle (each step's own form advances), Complete
Configure MCP clientorgs/components/settings/mcp/Configure.vuechoose client → instructionsnoNextBtn (picking a client advances), Finish
Onboarding preferencesorgs/components/onboarding/preferences/Index.vuecapabilities → hostingend 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 + block holds 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. noNextBtn lets 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 adds components/wizard/Radar.vue and 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 adds bg-lines-human and 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-outline loses to UxButton's bg-grape-600: the layer's UxButton gained kind="stroke" for the outline look. So it reads grape on grape there. On the design site the CSS order lets the outline win.
  • The callBack prop is declared but never read; the code reads props.callback.
  • pingConfirm (the listener's listenConfirmCallback) is commented out. Confirmation un-blocks Continue but never advances on its own.
  • The /dev/wizards/install-qtap, connect-an-app and deploy-qproxy demos 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, so localhost:3300 hangs. Run it with --port 3301 (launch config app-qpoint-io).

Replica changes (everything else is verbatim)

  • UxLinkBack became ReplicasWizardLinkBack, and WizardPingListener became ReplicasWizardPingListener.
  • PingListener's images were copied to app/assets/images/wizard/, and its style block is now scoped.
  • sequin.js dropped its closing window.Sequin = Sequin, because the design site renders on the server.

Qpoint Brand Style Guide