* fix(web): align Home prompt overlay with textarea so caret lands on click
Picking a chip such as Slide deck or Image loaded a default prompt
into the Home textarea and rendered an overlay with `{{key}}`
placeholders as interactive `<input>` / `<select>` controls. The
overlay controls and the underlying textarea text were laid out
independently:
- Inputs declared `min-width: 8ch` and `Math.max(displayValue.length
+ 1, 10)ch` of width.
- Selects added 18px of right-padding for the dropdown arrow.
- The textarea kept the raw substituted string in a proportional
font.
The two layouts no longer matched column-for-column, so every slot
shifted the textarea text to the left of where it appeared in the
overlay, compounding across the line. Clicking on visible prose to
position the caret hit a different character offset in the textarea
and subsequent typing or deletion landed in the wrong place.
For example, the Slide deck template
Create a {{slideCount}}-slide {{deckType}} for {{audience}} about
{{topic}}. ...
renders with slideCount=10 (~2 ch in the textarea) under a slot
input forced to 10 ch in the overlay — clicking right after the
literal `-slide` placed the caret several characters into `pitch
deck`. The Image / Video / Audio chips with their pre-filled
subject, style, aspect values reproduced the same drift.
Render the inline pills as read-only `<span>`s carrying the exact
substring the textarea shows at that position, mark them
`aria-hidden` so the textarea remains the single labelled control,
and surface every plugin input field — including the ones referenced
inline — in `PluginInputsForm` underneath. Editing flows through the
form, the parent's `updateActiveInputs` already re-renders the
prompt, and the pills stay aligned with the textarea on every
keystroke.
Also drop the now-unused inline helpers (updatePluginInput,
getTemplateInputNames, shouldRenderSlotAsText, inlineFieldType,
fileInputLabel, fileMetadata) and the dead
`.home-hero__prompt-slot-control/input/select/toggle/file/text` CSS
rules.
Verified:
- pnpm --filter @open-design/web typecheck
- vitest run on HomeHero.plugin-picker, HomeHero.rail, and
HomeView.prefill (29/29 pass; tests updated to reflect the new
read-only span + always-on form contract)
- Manual click-to-edit on Slide deck and Image chips in the
pnpm tools-dev web runtime — caret now lands where the user
clicked.
* Implement home audio essential workflow
* Fix Home media composer review issues
* Guard stale Home media apply results
---------
Co-authored-by: hahaplus <zmjdll@gmail.com>
- Added support for file input fields in the PluginInputsForm, allowing users to upload files with serializable metadata.
- Updated the HomeHero component to improve the layout and interaction of input fields, enhancing user experience.
- Adjusted CSS styles for better visual representation of input fields and their states.
- Modified HomeView to reflect changes in authoring chip IDs for better clarity in plugin actions.
- Enhanced tests to cover new file input functionality and ensure correct behavior in various scenarios.
This update significantly improves the plugin input handling, enabling users to upload files seamlessly and enhancing the overall interaction model.
Plan §3.C1–§3.C4.
Web composer integration for the plugin system:
- apps/web/src/state/projects.ts gains:
* applyPlugin(pluginId, { inputs?, projectId?, grantCaps? }) — wraps
POST /api/plugins/:id/apply and returns the typed ApplyResult.
* listPlugins() — wraps GET /api/plugins.
* renderPluginBriefTemplate(template, inputs) — substitutes
{{var}} placeholders inside useCase.query as the user types so the
composer's brief textarea re-renders live.
- New components:
* InlinePluginsRail — the card strip that lives below the input box
on Home and inside ChatComposer. Supports 'wide' / 'strip' layouts
+ taskKind / mode filters.
* ContextChipStrip — typed ContextItem chips above the brief input.
Optional onRemove for clearing the applied plugin.
* PluginInputsForm — JSON-Schema-light form rendered between the
input and Send. Required fields gate Send via onValidityChange;
string/text/select/number/boolean field types are supported.
* GenUISurfaceRenderer — first-class confirmation + oauth-prompt
surfaces (form + choice fall back to a JSON Schema preview +
free-form textarea until Phase 2A.5).
* GenUIInbox — drawer that lists every persisted surface answer for
a project; revoke calls POST /api/projects/:id/genui/:sid/revoke.
- jsdom tests under apps/web/tests/components/:
* InlinePluginsRail (mount fetch, click → applyPlugin → onApplied,
taskKind filter)
* PluginInputsForm (validity gating, default hydration, select
options)
* GenUISurfaceRenderer (confirmation true/false branches; oauth
surface forwards { authorized, connectorId } per spec §10.3.1)
Web test suite: 567 → 575 (added 8 plugin component cases). The
NewProjectPanel / ChatComposer / ProjectView mounts will land in the
follow-up commit so this PR's diff stays reviewable.
Co-authored-by: Tom Huang <1043269994@qq.com>