You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Documentation sites may optionally add AI features such as Ask AI. When enabled, Ask AI should fit into the existing search flow; keeping it completely separate makes the overall interaction feel fragmented.
Problem
OINK owns the Command Palette result model, DOM, selection, activation, ARIA, and focus lifecycle. A downstream site can configure safe URL commands or executors for OINK's built-in actions, but it has no supported way to add a row that depends on the current search query.
The available workarounds all cross a private boundary:
observe and rebuild Palette DOM after every render;
fork the Palette;
patch private runtime state.
OINK needs one narrow runtime hook: trusted site JavaScript may contribute data-only action rows after native search results. OINK still renders and operates every row.
Current boundary
Verified against main@93ac292. The relevant files are unchanged from v1.0.0.
registerSearchTail() returns an idempotent unregister function. Duplicate extension IDs are rejected. Registration order determines extension order.
Registration or unregistration while the Palette is open schedules one OINK-owned render. It never patches the current DOM directly. Unregistering also aborts a pending activation owned by that extension.
The Palette bundle remains conditional. A site must feature-detect the API on pages where local search may be disabled.
Row contract
rows(context) is synchronous, pure, and safe to call more than once. OINK does not promise an exact callback count.
Each returned row is data:
{id: "ask-ai",// required, unique in this extension's current result settitle: "Ask AI",// requireddescription: "Ask about...",// optionalicon: "fa-solid fa-wand-magic-sparkles",// optional class tokensavailable: true,// optional, defaults to truedisabledReason: ""// optional}
OINK copies these allowed fields and renders strings as text. A title such as Array<T> stays text; it is not interpreted as HTML. Extensions can use the row ID to distinguish actions.
Row IDs need to be unique only within one extension's current result set. OINK creates its internal identity from the extension ID and row ID, for example extension:ask-ai:ask-ai.
If rows() throws or returns an invalid descriptor, OINK skips that extension for the current render. Native search and other extensions continue to work.
Search context
OINK calls rows(context) only after local search settles for a non-empty text query:
phase and pageResultCount describe the local page index. Native actions and extension rows do not change them. pageResultCount is the number of local page rows returned after the configured result limit.
OINK does not call extensions for:
an empty Palette;
a > command query;
choice mode;
index loading.
OINK first builds the same native view it builds today, including its current loading, empty, or index-unavailable message. It then appends extension rows to the existing Actions group, or creates that group with the existing localized label when needed. This keeps native page results first and avoids a new translation key.
The context used to create a row is stored with that row. Activation receives the same snapshot, plus its AbortSignal; it does not rebuild context from a newer input value.
Activation lifecycle
OINK never calls an extension's activate() while registering, evaluating, or rendering rows. It calls the callback only when that row enters the Palette's existing activation path.
This is a sequencing guarantee, not a security sandbox. An extension is trusted site code. It can call public APIs or perform network work on its own, so the site remains responsible for keeping rows() pure and for asking for any consent its provider requires.
For an extension row:
OINK blocks duplicate activation while the first activation is pending.
OINK invokes activate() and catches both synchronous throws and Promise rejection.
OINK ignores the callback's fulfillment value. A successful activation closes the Palette through the existing focus-restoration path.
A failure keeps the Palette open and announces the existing localized action-failed message.
Closing the Palette, starting a new Palette session, or unregistering the extension aborts the signal and makes late settlement a no-op.
OINK does not impose an activation timeout. The extension may implement one with the provided signal.
State behavior
Palette state
Extension behavior
Empty/browse
Not called
Command query (>...)
Not called
Choice mode
Not called
Search index loading
Not called; current loading behavior stays unchanged
Page results
Rows follow all native results and actions
No page results
Current native empty/action view stays; extension rows follow it
Index error
Current index-unavailable view and input-triggered retry stay; extension rows may follow
Acceptance checks
With no registered extension, current DOM order, announcements, focus behavior, and network behavior remain unchanged.
Repeated renders, index completion, failure, and retry never duplicate extension rows. rows() remains safe when called more than once.
Extension rows use the current native interactions: pointer, Arrow keys, Cmd/Ctrl+Home/End, and Enter. Space remains normal search input.
Display fields render as text. Invalid field types and unsafe or duplicate IDs are rejected without breaking native search.
A throwing rows() affects only that extension. A synchronous throw, rejected Promise, or aborted activation releases pending state and leaves the Palette usable.
Registration, unregistration, and late registration do not add duplicate listeners or retain stale rows. An old unregister handle cannot remove a later registration that reused the same ID.
Search-off and Print pages still omit the Palette bundle. Markdown and no-JavaScript behavior remain unchanged. Normal HTML keeps the same server-rendered Palette structure; its production script fingerprint will change.
Existing page, quick-link, action, command, and choice rows retain their current behavior.
Compatibility
The action manifest remains data-only and cannot register JavaScript. This runtime API is available only to trusted site code.
OINK still ships no remote search fallback or telemetry. By default, every query stays in the browser. A site extension may send a query after activation, or earlier if it violates the documented rows() contract; OINK cannot enforce extension behavior.
The accepted behavior must update the bilingual Shell contract and owning checker before implementation. The public contract should keep the default local-only behavior explicit.
Downstream reference
HugeGraph evaluated three downstream compositions. The selected composition keeps native documentation results first, appends Ask AI as the search tail, and provides a separate Floating Launcher for a blank assistant session. The other two images explain the UI choice; they are not additional OINK requirements.
Selected: Search Tail + Floating Launcher
Alternative: AI-first row
Alternative: persistent side panel
Native results remain primary; Ask AI follows them and the launcher remains available outside search.
Ask AI precedes local results and receives too much visual priority.
Search and assistant share the Palette and leave less room for documentation results.
HugeGraph can keep its compatibility wrapper until a tagged OINK release provides this contract. Upstream acceptance does not block the downstream deployment.
Note
Background
Documentation sites may optionally add AI features such as Ask AI. When enabled, Ask AI should fit into the existing search flow; keeping it completely separate makes the overall interaction feel fragmented.
Problem
OINK owns the Command Palette result model, DOM, selection, activation, ARIA, and focus lifecycle. A downstream site can configure safe URL commands or executors for OINK's built-in actions, but it has no supported way to add a row that depends on the current search query.
The available workarounds all cross a private boundary:
OINK needs one narrow runtime hook: trusted site JavaScript may contribute data-only action rows after native search results. OINK still renders and operates every row.
Current boundary
Verified against
main@93ac292. The relevant files are unchanged fromv1.0.0.clearRows()replaces the result DOM and internal row array.groupsFor()builds groups only from local pages and the Action Registry.renderGroups()owns row indices, selection, pointer activation, and ARIA relationships.runRow()supports page, quick-link, action, command, and choice rows.open,close,render,activate, androws(), but has no registration API.BUILTINSset.registerExecutor()rejects other IDs.This API should not widen the Action Registry. Static site configuration must remain inert.
Goals
Non-goals
rows().Proposed v1 API
The first contract should name the one supported slot instead of exposing a generic placement system:
registerSearchTail()returns an idempotent unregister function. Duplicate extension IDs are rejected. Registration order determines extension order.Registration or unregistration while the Palette is open schedules one OINK-owned render. It never patches the current DOM directly. Unregistering also aborts a pending activation owned by that extension.
The Palette bundle remains conditional. A site must feature-detect the API on pages where local search may be disabled.
Row contract
rows(context)is synchronous, pure, and safe to call more than once. OINK does not promise an exact callback count.Each returned row is data:
OINK copies these allowed fields and renders strings as text. A title such as
Array<T>stays text; it is not interpreted as HTML. Extensions can use the row ID to distinguish actions.Row IDs need to be unique only within one extension's current result set. OINK creates its internal identity from the extension ID and row ID, for example
extension:ask-ai:ask-ai.If
rows()throws or returns an invalid descriptor, OINK skips that extension for the current render. Native search and other extensions continue to work.Search context
OINK calls
rows(context)only after local search settles for a non-empty text query:phaseandpageResultCountdescribe the local page index. Native actions and extension rows do not change them.pageResultCountis the number of local page rows returned after the configured result limit.OINK does not call extensions for:
>command query;OINK first builds the same native view it builds today, including its current loading, empty, or index-unavailable message. It then appends extension rows to the existing
Actionsgroup, or creates that group with the existing localized label when needed. This keeps native page results first and avoids a new translation key.The context used to create a row is stored with that row. Activation receives the same snapshot, plus its
AbortSignal; it does not rebuild context from a newer input value.Activation lifecycle
OINK never calls an extension's
activate()while registering, evaluating, or rendering rows. It calls the callback only when that row enters the Palette's existing activation path.This is a sequencing guarantee, not a security sandbox. An extension is trusted site code. It can call public APIs or perform network work on its own, so the site remains responsible for keeping
rows()pure and for asking for any consent its provider requires.For an extension row:
activate()and catches both synchronous throws and Promise rejection.OINK does not impose an activation timeout. The extension may implement one with the provided signal.
State behavior
>...)Acceptance checks
rows()remains safe when called more than once.Cmd/Ctrl+Home/End, and Enter. Space remains normal search input.rows()affects only that extension. A synchronous throw, rejected Promise, or aborted activation releases pending state and leaves the Palette usable.Compatibility
The action manifest remains data-only and cannot register JavaScript. This runtime API is available only to trusted site code.
OINK still ships no remote search fallback or telemetry. By default, every query stays in the browser. A site extension may send a query after activation, or earlier if it violates the documented
rows()contract; OINK cannot enforce extension behavior.The accepted behavior must update the bilingual Shell contract and owning checker before implementation. The public contract should keep the default local-only behavior explicit.
Downstream reference
HugeGraph evaluated three downstream compositions. The selected composition keeps native documentation results first, appends Ask AI as the search tail, and provides a separate Floating Launcher for a blank assistant session. The other two images explain the UI choice; they are not additional OINK requirements.
HugeGraph can keep its compatibility wrapper until a tagged OINK release provides this contract. Upstream acceptance does not block the downstream deployment.