fix: draw the settings on the plugin card, not a click deeper
The page was registered into `plugins.row.config`, keyed
`<package>#<row id>`. That seat puts a page behind the ROW's title button on the
bundle's card — the right home for one row of a bundle that declares several,
and the reasoning the previous commit wrote down. What it missed is that this
bundle declares exactly one row, so its settings became reachable only by
opening the card and noticing that the row title had turned into a button.
Reported from a real install as "it shows the plugin just no settings", while
the sibling plugin's settings were visible: that one registers into
`plugins.bundle.config`.
`plugins.bundle.config` is the same document, dispatched on the card itself:
`PackageDetail` gates a section on `ledger.bundles.has(pkg.name)` and renders
`renderSlot("plugins.bundle.config", { view: "page" }, { entryKey: pkg.name })`
between the description and the rows. So the key becomes the bundle's package
name, and the form draws where someone opening "dsh-recall" is already looking.
The section gets no `form` prop at this seat, unlike the row page — which is
fine, because the card reads its own form from `configForms` rather than
depending on the one handed in.
Verified by serving the bundle from a profile that links this checkout and
reading the artifact back: the served `client.js` carries
`plugins.bundle.config` at the registration and names the retired seat only in
prose. The tests pin the new key and now fail if either the seat or the key
drifts.
This commit is contained in:
1 parent
1f42d2a821
commit
233215e6c9
3 files changed
+55
-36
No files matched your search
@@ -81,9 +81,11 @@ a fact:
|
|||||||
embeddingApiBase: http://127.0.0.1:11434 # replace with your endpoint
|
embeddingApiBase: http://127.0.0.1:11434 # replace with your endpoint
|
||||||
```
|
```
|
||||||
|
|
||||||
Or edit every setting from the UI: **Plugins → dsh-recall → Configure**. The
|
Or edit every setting from the UI: **Plugins** in the sidebar → the
|
||||||
plugin ships **two halves**, and the page appears only when both are present and
|
**dsh-recall** card → its settings are on that card, below the description.
|
||||||
the deployment serves the namespace:
|
(Not Settings → *Built-in plugins*: that section is a read-only inventory of
|
||||||
|
what the deployment ships.) The plugin ships **two halves**, and the section
|
||||||
|
appears only when both are present and the deployment serves the namespace:
|
||||||
|
|
||||||
- the **host half** (`index.js`) exports a `Config` schema whose every field is
|
- the **host half** (`index.js`) exports a `Config` schema whose every field is
|
||||||
`.volatile()`. That schema *is* the settings page's description — the Harness
|
`.volatile()`. That schema *is* the settings page's description — the Harness
|
||||||
@@ -92,11 +94,16 @@ the deployment serves the namespace:
|
|||||||
a schema without those markers has no settings surface at all rather than an
|
a schema without those markers has no settings surface at all rather than an
|
||||||
empty one;
|
empty one;
|
||||||
- the **browser half** (`client/client.js`) renders that form into the Plugins
|
- the **browser half** (`client/client.js`) renders that form into the Plugins
|
||||||
panel's per-row configuration seat, `plugins.row.config`, keyed
|
panel's per-bundle configuration seat, `plugins.bundle.config`, keyed by the
|
||||||
`<bundle package>#<row id>` — here `dsh-recall#dsh-recall`. Registering it is
|
bundle's package name. Registering it is what draws the section; without it
|
||||||
what adds the **Configure** control to the plugin's row.
|
there is no settings UI however complete the schema is.
|
||||||
|
|
||||||
A served namespace with no page draws no control, and a page whose namespace is
|
The sibling seat `plugins.row.config` — keyed `<package>#<row id>` — draws one
|
||||||
|
row's configuration on that row's own page. That is the right home for one row
|
||||||
|
of a bundle that declares several; this bundle declares one, so its settings
|
||||||
|
belong on the card.
|
||||||
|
|
||||||
|
A served namespace with no page draws nothing, and a page whose namespace is
|
||||||
not served is deliberately not registered — so both are required. A restart is
|
not served is deliberately not registered — so both are required. A restart is
|
||||||
needed for a newly installed browser half to appear, because client bundle bytes
|
needed for a newly installed browser half to appear, because client bundle bytes
|
||||||
are snapshotted when the profile activates.
|
are snapshotted when the profile activates.
|
||||||
|
|||||||
+23
-16
@@ -12,10 +12,11 @@
|
|||||||
* binding `settingsScope` and registering into a `settings.plugin.item` slot.
|
* binding `settingsScope` and registering into a `settings.plugin.item` slot.
|
||||||
* Neither exists in 0.1.7. The generated settings document is keyed by the
|
* Neither exists in 0.1.7. The generated settings document is keyed by the
|
||||||
* plugin's own profile entry id, and a plugin that wants a page renders it as a
|
* plugin's own profile entry id, and a plugin that wants a page renders it as a
|
||||||
* contribution to the Plugins panel: `plugins.row.config`, keyed
|
* contribution to the Plugins panel — `plugins.bundle.config`, keyed by the
|
||||||
* `<bundle package>#<row id>`. That key is what puts a **configure** control on
|
* bundle's package name, which the page draws as a section on the plugin's own
|
||||||
* the plugin's row — without a contribution there is no control and no page,
|
* card. Without a contribution there is no section, however complete the schema
|
||||||
* however complete the schema is.
|
* is; the sibling seat `plugins.row.config` would instead put it one click
|
||||||
|
* deeper, on a single row's page.
|
||||||
*
|
*
|
||||||
* **The page is the framework's form, not a form of our own.** The card used to
|
* **The page is the framework's form, not a form of our own.** The card used to
|
||||||
* reproduce `PluginCard`'s chrome by hand — the `<li>` root, the disclosure
|
* reproduce `PluginCard`'s chrome by hand — the `<li>` root, the disclosure
|
||||||
@@ -70,13 +71,17 @@ window.__ModuleLoader__.load({
|
|||||||
/**
|
/**
|
||||||
* The seat this plugin's configuration page renders in.
|
* The seat this plugin's configuration page renders in.
|
||||||
*
|
*
|
||||||
* Keyed `<bundle package name>#<row id>`, both as the profile's patch
|
* `plugins.bundle.config`, keyed by the BUNDLE's package name: the Plugins
|
||||||
* declares them. For this bundle the two coincide, which is a coincidence
|
* page draws that contribution as a section on the plugin's own card,
|
||||||
* and not a rule — the constants are kept separate so that stays visible.
|
* between its description and its rows. That is where someone opening
|
||||||
|
* "dsh-recall" expects its settings — the alternative seat,
|
||||||
|
* `plugins.row.config` (keyed `<package>#<row id>`), draws them on a page
|
||||||
|
* one click deeper, behind the row's title button, which is the right home
|
||||||
|
* for one row of a bundle that declares several. This bundle declares one
|
||||||
|
* row, so the card is the honest place.
|
||||||
*/
|
*/
|
||||||
const BUNDLE_PACKAGE = 'dsh-recall'
|
const BUNDLE_PACKAGE = 'dsh-recall'
|
||||||
const BUNDLE_ROW = 'dsh-recall'
|
const BUNDLE_CONFIG_KEY = BUNDLE_PACKAGE
|
||||||
const ROW_CONFIG_KEY = `${BUNDLE_PACKAGE}#${BUNDLE_ROW}`
|
|
||||||
|
|
||||||
const TITLE = 'Recall'
|
const TITLE = 'Recall'
|
||||||
const DESCRIPTION =
|
const DESCRIPTION =
|
||||||
@@ -486,10 +491,12 @@ window.__ModuleLoader__.load({
|
|||||||
/**
|
/**
|
||||||
* The plugin's configuration page.
|
* The plugin's configuration page.
|
||||||
*
|
*
|
||||||
* The Plugins page asks a configuration entry twice: once for a one-liner to
|
* The seat asks for `page`, the form with its save control; the `summary`
|
||||||
* stand in for a missing description (`summary`), and once for the body of
|
* branch answers the other shape a configuration entry can be asked for —
|
||||||
* the plugin's own page (`page`). Only the second draws a save control —
|
* the one-liner standing in for a missing description — because a component
|
||||||
* and only one save writes, because the form discards its drafts on unmount.
|
* registered under a seat that only ever asks for `page` is also the natural
|
||||||
|
* thing to reuse where that shape arrives. Only a save writes, and the form
|
||||||
|
* discards its drafts on unmount.
|
||||||
*/
|
*/
|
||||||
function RecallSettingsCard(props) {
|
function RecallSettingsCard(props) {
|
||||||
if (props.view === 'summary') return DESCRIPTION
|
if (props.view === 'summary') return DESCRIPTION
|
||||||
@@ -705,11 +712,11 @@ window.__ModuleLoader__.load({
|
|||||||
scoped.effect(
|
scoped.effect(
|
||||||
() =>
|
() =>
|
||||||
scoped.configForms.whileServed([SETTINGS_ENTRY], () =>
|
scoped.configForms.whileServed([SETTINGS_ENTRY], () =>
|
||||||
scoped.slots.inject('plugins.row.config', () =>
|
scoped.slots.inject('plugins.bundle.config', () =>
|
||||||
scoped.slots.register(
|
scoped.slots.register(
|
||||||
{
|
{
|
||||||
name: 'plugins.row.config',
|
name: 'plugins.bundle.config',
|
||||||
key: ROW_CONFIG_KEY,
|
key: BUNDLE_CONFIG_KEY,
|
||||||
inject: () => card.inject(),
|
inject: () => card.inject(),
|
||||||
},
|
},
|
||||||
RecallSettingsCard,
|
RecallSettingsCard,
|
||||||
|
|||||||
+18
-13
@@ -7,10 +7,13 @@
|
|||||||
* leaving the page to be eyeballed in a running app, because the failures this
|
* leaving the page to be eyeballed in a running app, because the failures this
|
||||||
* file exists to catch are structural and invisible until someone opens Plugins:
|
* file exists to catch are structural and invisible until someone opens Plugins:
|
||||||
*
|
*
|
||||||
* - the configuration page is registered into `plugins.row.config` under
|
* - the configuration page is registered into `plugins.bundle.config` under
|
||||||
* `<bundle package>#<row id>`. The 0.1.5 seat (`settings.plugin.item`, bound
|
* the bundle's package name, which is the seat the Plugins page draws as a
|
||||||
* through `settingsScope`) does not exist in 0.1.7, so a page left in it
|
* section on the plugin's own card. The 0.1.5 seat (`settings.plugin.item`,
|
||||||
* registers against a slot nobody declares and simply never appears.
|
* bound through `settingsScope`) does not exist in 0.1.7, so a page left in
|
||||||
|
* it registers against a slot nobody declares and simply never appears; the
|
||||||
|
* sibling seat `plugins.row.config` renders one click deeper instead, on a
|
||||||
|
* single row's page, which is not where someone opening the card looks.
|
||||||
* - the right-sidebar tab needs a TYPE registered before its body means
|
* - the right-sidebar tab needs a TYPE registered before its body means
|
||||||
* anything: the keyed body slot is dispatched under the definition's `id`.
|
* anything: the keyed body slot is dispatched under the definition's `id`.
|
||||||
* - the field list here and the `Config` schema in index.js must name the same
|
* - the field list here and the `Config` schema in index.js must name the same
|
||||||
@@ -322,7 +325,7 @@ function mount(options = {}) {
|
|||||||
* does, so the component under test sees the props a real render would give it.
|
* does, so the component under test sees the props a real render would give it.
|
||||||
*/
|
*/
|
||||||
const card = (view) => {
|
const card = (view) => {
|
||||||
const item = byName.get('plugins.row.config')
|
const item = byName.get('plugins.bundle.config')
|
||||||
assert.ok(item !== undefined, 'no settings page was registered')
|
assert.ok(item !== undefined, 'no settings page was registered')
|
||||||
const props = { ...item.meta.inject(), view }
|
const props = { ...item.meta.inject(), view }
|
||||||
delete props.hooks
|
delete props.hooks
|
||||||
@@ -364,13 +367,15 @@ test('the client module claims the slot registry in its inject list', () => {
|
|||||||
assert.deepEqual(module.inject, ['slots'])
|
assert.deepEqual(module.inject, ['slots'])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('the configuration page claims the 0.1.7 seat, keyed as the patch declares it', () => {
|
test('the configuration page claims the seat the plugin card draws', () => {
|
||||||
const { registered } = mount()
|
const { registered } = mount()
|
||||||
const page = registered.find((item) => item.meta.name === 'plugins.row.config')
|
const page = registered.find((item) => item.meta.name === 'plugins.bundle.config')
|
||||||
assert.ok(page !== undefined, 'the page must register into plugins.row.config')
|
assert.ok(page !== undefined, 'the page must register into plugins.bundle.config')
|
||||||
// `<bundle package name>#<row id>`: the row id is the one this bundle's own
|
// Keyed by the BUNDLE's package name, which is what the Plugins page
|
||||||
// cordis.patch.yml declares, which is what the Plugins page dispatches by.
|
// dispatches this seat with (`entryKey: pkg.name`). The sibling seat,
|
||||||
assert.equal(page.meta.key, 'dsh-recall#dsh-recall')
|
// `plugins.row.config`, would be keyed `<package>#<row id>` and would render
|
||||||
|
// one click deeper, on the row's own page.
|
||||||
|
assert.equal(page.meta.key, 'dsh-recall')
|
||||||
assert.equal(typeof page.meta.inject, 'function')
|
assert.equal(typeof page.meta.inject, 'function')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -385,7 +390,7 @@ test('no page is registered for a namespace the Host does not serve', () => {
|
|||||||
// onto fields nothing would accept.
|
// onto fields nothing would accept.
|
||||||
const { registered } = mount({ served: ['some-other-plugin'] })
|
const { registered } = mount({ served: ['some-other-plugin'] })
|
||||||
assert.equal(
|
assert.equal(
|
||||||
registered.some((item) => item.meta.name === 'plugins.row.config'),
|
registered.some((item) => item.meta.name === 'plugins.bundle.config'),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -431,7 +436,7 @@ test('the browser half contributes three seats, none of them the retired 0.1.5 o
|
|||||||
const { registered } = mount()
|
const { registered } = mount()
|
||||||
assert.deepEqual(registered.map((item) => item.meta.name).sort(), [
|
assert.deepEqual(registered.map((item) => item.meta.name).sort(), [
|
||||||
'conversation.session.header.corner',
|
'conversation.session.header.corner',
|
||||||
'plugins.row.config',
|
'plugins.bundle.config',
|
||||||
'sidebar.right.pane.tab',
|
'sidebar.right.pane.tab',
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in new issue
Block a user