Skip to content

feat(dashboard): variable autocomplete, highlighting & live preview for {variable} fields#48

Closed
Abdulkhalek-1 wants to merge 27 commits into
fix/ui-review-findingsfrom
feat/dashboard-variable-fields
Closed

feat(dashboard): variable autocomplete, highlighting & live preview for {variable} fields#48
Abdulkhalek-1 wants to merge 27 commits into
fix/ui-review-findingsfrom
feat/dashboard-variable-fields

Conversation

@Abdulkhalek-1

Copy link
Copy Markdown
Member

Summary

Adds a reusable variable-field editing experience to every dashboard text field that supports {variable} placeholders, replacing the old static hint text with:

  • {-triggered autocomplete — an ARIA combobox that filters the field's variables as you type and inserts at the cursor
  • Searchable variable browser — an "Insert variable" popover with descriptions and examples
  • Inline highlighting — valid tokens render in accent, unknown/misspelled tokens in red with a "did you mean?" suggestion (Levenshtein)
  • Live Discord-style preview — message + embed bubble rendered with hybrid data: real guild/user where available, realistic samples otherwise

Rolled out to

Welcome / Farewell / DM · Custom Commands · Scheduled Messages · Leveling announce · TempVoice name template · Automation action fields (with event-scoped variables threaded through the workflow editor — {ban.reason} only shows for Member Banned, etc.)

Architecture

New module apps/dashboard/src/client/shared/ui/variable-field/: pure, DOM-free logic (tokenizer, caret math, filter, validation, registry, preview resolver) with node-env unit tests, plus three React components (VariableEditor, VariableBrowser, DiscordMessagePreview) and a usePreviewContext hook tested under jsdom. The editor uses an overlay-mirror technique (native field + aria-hidden highlight backdrop) to keep native caret/undo/IME while showing colored tokens. A registry drift guard asserts the dashboard's token names match the canonical @fluxcore/systems runtime tokens, so the UI can't silently diverge from bot-side substitution.

Design: spec · plan

Testing

  • 46 new client tests (11 variable-field suites + jsdom smoke), all passing
  • Full dashboard suite: 10 failed / 354 passed — the 10 failures are pre-existing and unrelated (auth/session/actions/starboard/anti-raid); this branch introduces zero new failures
  • Client typecheck clean (tsc -p tsconfig.client.json --noEmit)
  • Added the jsdom + @testing-library/react toolchain (no client test infra existed before) and regenerated pnpm-lock.yaml to match

Notes

  • No existing runtime tokens were renamed (would break saved content); each field surfaces its own token set.
  • No new backend endpoints — preview reuses already-loaded guild/user state; memberCount uses a sample (not client-available).

Follow-ups (non-blocking)

  • Variable browser inserts at end of field rather than at caret position
  • Markdown/mention styling inside the preview (currently plain resolved text)
  • Drift guard for tempvoice tokens once a canonical constant exists

🤖 Generated with Claude Code

Abdulkhalek-1 and others added 27 commits July 8, 2026 09:41
21 TDD tasks: pure logic foundations (tokenize, validation, caret, filter,
registry, automation vars, preview resolver), React components (VariableEditor,
VariableBrowser, DiscordMessagePreview, usePreviewContext), and rollout across
welcome/commands/scheduled/leveling/tempvoice/automation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Installs @testing-library/react, @testing-library/user-event, and jsdom as
devDependencies, wires up the @vitejs/plugin-react plugin in vitest.config.ts
so TSX files get the automatic JSX runtime, extends the include glob to cover
tests/**/*.test.tsx, adds the variable-field coverage path, and adds a smoke
component test (jsdom docblock, 1 test) that confirms the DOM render pipeline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Creates registry.ts with welcomeVariables, customCommandVariables,
levelingVariables, tempvoiceVariables descriptors, knownTokenSet,
buildRealData (CDN URL builder with fallbacks), and buildTokenValues.
Drift-guard tests assert token names match canonical @fluxcore/systems
sources (WELCOME_VARIABLES, TEMPLATE_VARIABLES).

Also adds ./customCommands/variables subpath export to
packages/systems/package.json so the drift test import resolves
(dist files already existed; the export map entry was missing).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…validation

Implements the VariableEditor React component: overlay-mirror token highlighting,
{-triggered ARIA combobox with ArrowUp/Down/Enter/Tab/Escape navigation, and
unknown-variable warning list with Levenshtein suggestions. Fixes import path
(../../lib/utils), synchronous caret-query detection on change event, and
active-index starting at -1 so ArrowDown selects index 0 first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements Task 10: a Popover-based variable browser with live filtering,
grouped token list, and onInsert callback; test stubs ResizeObserver for jsdom.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements usePreviewContext(guildId) which assembles PreviewRealData for
the live variable-field preview from useAuth + useGuilds query results,
memoized on [auth.data, guilds.data, guildId].

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace plain Input/Textarea fields in EmbedEditor with VariableEditor
(token highlighting, autocomplete), add VariableBrowser near description,
add DiscordMessagePreview below fields, thread PreviewRealData via
usePreviewContext into all three EmbedEditor instances (welcome/farewell/DM).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the plain Textarea + static hint in the level-up announcement
field with VariableEditor, VariableBrowser, and DiscordMessagePreview,
giving users inline token insertion and a live Discord-style preview.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Threads workflow eventType down through NodeDetailPanel → ActionFields
and swaps variable-bearing text/textarea inputs for VariableEditor with
per-event autocomplete. StepPanel action fields get an empty variable
list (not event-scoped in the current model).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ation

Thread eventType into StepPanelProps and compute buildAutomationVariables in
StepPanel so step action fields receive the real token set instead of [].
Fix VariableEditor id prop (text + textarea branches) to use sanitised fieldId
so <Label htmlFor> correctly associates with the input.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds DiscordMessagePreview below ActionFields in ActionPanel for
sendMessage, sendDM (content preview) and sendEmbed (embed preview),
using already-computed variables and usePreviewContext for live
guild/user resolution.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…highlight

The highlight backdrop and the text field each carried the full field style
(border + bg-surface-lowest + rounded), stacking two bordered boxes into a
doubled/corrupted border. The field also carried both text-text and
text-transparent, so twMerge could not dedupe the custom color and the field's
own text painted over the colored backdrop, hiding variable highlighting.

Move border/background/rounding/focus-ring to the container (focus-within), and
make both inner layers borderless + transparent-bg with identical padding: the
field text is transparent, the backdrop shows the accent/danger-colored tokens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add variables.* and variables.entries.* to common.json so VariableBrowser
and VariableEditor render human-readable descriptions for all registry tokens
(welcome/leveling/tempvoice top-level tokens + automation/command entries).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Task 1 added @testing-library/react, @testing-library/user-event, and jsdom to
apps/dashboard devDependencies, but the docker workflow (repo root not
bind-mounted) meant the root lockfile was never regenerated, so
`pnpm install --frozen-lockfile` (CI/clean clone) would fail. Regenerated from
the current manifests. Diff is additive (peer-dep hash re-resolution only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… forwarding

- vitest.config.ts: widen coverage include to *.{ts,tsx} so .tsx components are not silently excluded
- commands.tsx: gate DiscordMessagePreview content/embed by formResponseType to prevent both rendering at once
- VariableEditor: add aria-required prop and forward it to textarea and input elements
- ActionFields: pass aria-required={field.required} to both VariableEditor branches (text + textarea)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Abdulkhalek-1

Copy link
Copy Markdown
Member Author

Superseded by #49, which integrates all 7 open PRs (#42#48) into main in one verified branch — conflicts resolved, typecheck 14/14, and 1053 tests green. This PR's changes are fully included there; it will be closed as merged once #49 lands.

Abdulkhalek-1 added a commit that referenced this pull request Jul 8, 2026
Integrate all open PRs (#42#48) into main
@Abdulkhalek-1

Copy link
Copy Markdown
Member Author

Included in main via #49 (integration of #42#48). #48's commits are all in main now — closing. (GitHub didn't auto-mark this MERGED because its base was fix/ui-review-findings rather than main.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant