refactor!: align with the DSH plugin conventions and drop the npm publish workflow
Follow the cordis-plugin-development references instead of the ad-hoc choices the first version made. Client half: - register the row button under this package's own id instead of shadowing the shipped `archive` action at a lower priority, so the official hover buttons keep their cells and archiving stays where the harness put it - drop the synthetic `pointerout` dispatched at a `[data-row-key]` ancestor: a plugin does not read or drive another package's DOM, so the tooltip is now positioned from its own button alone - keep the self-rendered primitives, the `--dsw-*` token-only styling, and the modal focus/Escape behavior, and document why Host half: - resolve a conversation's descendants with `sessionQuery.traceSession()` instead of listing every stored header and re-deriving `parentSession` edges - decide liveness from the traced `SessionRecord.live` flag, which removes the `agents` and `sessions` dependencies from `inject` - build the orphan-sweep corpus from `sessionQuery.listSessions()`, which already merges live and persisted sessions - document that `sessionPersistence.locate()` is a JSONL-backend diagnostic hook, not part of the seam, and keep probing it explicitly - record only tree roots in the deferred-deletion ledger: the activation sweep removes a root with everything under it, so a separately recorded child was deleted twice or needed a pass that never runs - return the activation's sweep promise from `apply()` so a test can await it Manifest and docs: - version 2.0.0, `private`, `dsh.manifestVersion`, and `engines` - delete the Gitea npm publish workflow and every npm-publishing task: the package is distributed only through the Git repository and tags - rewrite the README around the current install paths and the plugin's limits
This commit is contained in:
@@ -1,14 +1,27 @@
|
||||
/**
|
||||
* Browser half of the session-delete bundle.
|
||||
*
|
||||
* One row in a conversation's "..." menu opens an irreversible-deletion dialog.
|
||||
* Confirming posts to the Host half's authenticated route; the Host owns every
|
||||
* policy decision and the Client only renders its answer.
|
||||
* One row in a conversation's "..." menu and one hover button on the
|
||||
* conversation's sidebar row open an irreversible-deletion dialog. Confirming
|
||||
* posts to the Host half's authenticated route; the Host owns every policy
|
||||
* decision and the Client only renders its answer.
|
||||
*
|
||||
* Harness Client packages may change without notice, so this bundle imports no
|
||||
* Harness Client module: its menu row and dialog copy the shipped primitives'
|
||||
* markup, stylesheet declarations, and focus/Escape behavior, keeping only
|
||||
* `--dsw-*` theme-token references. React comes from the page's module table.
|
||||
* Two client-authoring rules shape this file:
|
||||
*
|
||||
* - No Harness Client module is imported. Those packages change without notice,
|
||||
* a plain-JavaScript bundle has no type check, and a throwing component blanks
|
||||
* the whole slot entry, so the menu row, the row button, the dialog, and the
|
||||
* General-settings row re-implement the shipped markup, stylesheet
|
||||
* declarations, and focus/Escape behavior themselves. Class names are renamed
|
||||
* under `dsd-` and every color, radius, elevation, and motion value stays a
|
||||
* `--dsw-*` theme-token reference.
|
||||
* - Nothing reads another package's DOM. An earlier version nudged the row's
|
||||
* hover preview out of the way by dispatching a synthetic pointer event at a
|
||||
* `[data-row-key]` ancestor; that is exactly the kind of dependency on a
|
||||
* self-owned rendering surface a plugin must not take. The tooltip below is
|
||||
* positioned from its own button alone.
|
||||
*
|
||||
* @module @dsh-plugin/session-delete/client
|
||||
*/
|
||||
window.__ModuleLoader__.load({
|
||||
id: '@dsh-plugin/session-delete',
|
||||
@@ -18,14 +31,12 @@ window.__ModuleLoader__.load({
|
||||
const NS = 'session-delete'
|
||||
const ROUTE = 'api/plugin/session-delete/delete'
|
||||
const ORPHAN_ROUTE = 'api/plugin/session-delete/orphans'
|
||||
/** The shipped menu rows occupy 100–400; this one follows them. */
|
||||
const MENU_ORDER = 500
|
||||
/** The shipped hover button's id; reusing its id takes that cell over. */
|
||||
const ROW_ACTION_ID = 'archive'
|
||||
const ROW_ACTION_ORDER = 100
|
||||
/** Below the shipped entry's implicit 0: the lowest priority renders. */
|
||||
const ROW_ACTION_PRIORITY = -1
|
||||
/** Between the shipped `archive` (100) and `pin` (200) hover buttons. */
|
||||
const ROW_ACTION_ORDER = 150
|
||||
/** General-settings position: after the log-upload row, before the version. */
|
||||
const SETTINGS_ORDER = 95
|
||||
const SETTINGS_ORDER = 96
|
||||
const TOOLTIP_DELAY_MS = 500
|
||||
const TOOLTIP_GAP = 8
|
||||
/** Document base captured at bundle registration, before any routing. */
|
||||
@@ -83,8 +94,9 @@ window.__ModuleLoader__.load({
|
||||
* Menu-row declarations mirror `dsh-client-ui-primitives`' Menu.module.css
|
||||
* `.item/.itemIcon/.itemLabel`; dialog declarations mirror its
|
||||
* Modal.module.css and Button.module.css plus the workspace dialog's
|
||||
* destructive accent and secondary status lines. Class names are renamed
|
||||
* under `dsd-`, and every color, radius, elevation, and transition stays a
|
||||
* destructive accent and secondary status lines; the settings row copies
|
||||
* the General-section row pattern. Class names are renamed under `dsd-`,
|
||||
* and every color, radius, elevation, and transition stays a
|
||||
* `--dsw-*`/`--ds-*` token reference.
|
||||
*/
|
||||
const CSS = `
|
||||
@@ -172,8 +184,7 @@ window.__ModuleLoader__.load({
|
||||
/**
|
||||
* Icon paths are the shipped `IconTrashOutline` and `IconCloseOutline`
|
||||
* artwork verbatim: a 16-unit viewBox the glyph fills, stroked at the
|
||||
* regular weight of 1. Redrawing a smaller glyph in a 24-unit box is what
|
||||
* made an earlier version of this row look undersized.
|
||||
* regular weight of 1.
|
||||
*/
|
||||
function TrashIcon({ size = 16 }) {
|
||||
return h('svg', {
|
||||
@@ -244,42 +255,15 @@ window.__ModuleLoader__.load({
|
||||
}
|
||||
|
||||
/**
|
||||
* Withdraw the session row's hover preview while this button's own tooltip
|
||||
* is showing.
|
||||
* The row's hover button. It carries its own id in the row-action list, so
|
||||
* the shipped `archive` and `pin` buttons keep their cells instead of being
|
||||
* shadowed.
|
||||
*
|
||||
* The shipped row buttons get this for free: `Tooltip` reports itself
|
||||
* through the primitives' private `TooltipSuppression` context, and the
|
||||
* enclosing `HoverCard` — whose anchor is the whole row, so a button inside
|
||||
* it counts as hovering the row — hides its card while that bubble is up.
|
||||
* That context object belongs to another module instance and cannot be
|
||||
* reached from here, so this reproduces the same outcome through the DOM:
|
||||
* a `pointerout` on the card's anchor wrapper makes React deliver
|
||||
* `onPointerLeave` to that wrapper alone, which is exactly the cancel the
|
||||
* hover card performs on a real leave. Nothing else in the tree is affected
|
||||
* because the related target is the wrapper's own parent.
|
||||
*
|
||||
* @param button - the hovered row button.
|
||||
*/
|
||||
function withdrawRowPreview(button) {
|
||||
try {
|
||||
const wrapper = button.closest('[data-row-key]')?.parentElement
|
||||
if (wrapper === null || wrapper === undefined) return
|
||||
wrapper.dispatchEvent(new PointerEvent('pointerout', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
composed: true,
|
||||
pointerType: 'mouse',
|
||||
relatedTarget: wrapper.parentElement,
|
||||
}))
|
||||
} catch {
|
||||
/* an unchanged host simply keeps showing its card */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The row's hover button, in the cell the shipped archive action occupied.
|
||||
* Its tooltip copies the primitive's bubble: the same delay, the same
|
||||
* bottom/end placement, and the same fade.
|
||||
* Its tooltip copies the primitive's bubble — the same delay, the same
|
||||
* bottom/end placement, the same fade — and is placed from this button's own
|
||||
* rect alone. While the pointer rests on the row, the row's own hover
|
||||
* preview may appear alongside it; that is accepted rather than suppressed
|
||||
* by reaching into the row's DOM.
|
||||
*/
|
||||
function DeleteSessionRowButton(props) {
|
||||
const t = translate(props.t)
|
||||
@@ -289,7 +273,6 @@ window.__ModuleLoader__.load({
|
||||
const [anchor, setAnchor] = React.useState(null)
|
||||
|
||||
const show = () => {
|
||||
if (buttonRef.current !== null) withdrawRowPreview(buttonRef.current)
|
||||
clearTimeout(timerRef.current)
|
||||
timerRef.current = setTimeout(() => {
|
||||
const rect = buttonRef.current?.getBoundingClientRect()
|
||||
@@ -549,8 +532,11 @@ window.__ModuleLoader__.load({
|
||||
}
|
||||
|
||||
/**
|
||||
* Post to one of this package's Host routes. Routes sit behind the
|
||||
* connection's trust fence, so the page's own credentials apply.
|
||||
* Post to one of this package's Host routes. The registration path on the
|
||||
* Host is absolute while this one is document-relative, so the request is
|
||||
* built against the base captured at bundle registration and works under a
|
||||
* mounted prefix. Routes sit behind the connection's trust fence, so the
|
||||
* page's own credentials apply.
|
||||
* @param route - document-relative route path.
|
||||
* @param body - JSON body to send.
|
||||
* @returns the Host's result value.
|
||||
@@ -569,7 +555,9 @@ window.__ModuleLoader__.load({
|
||||
payload = null
|
||||
}
|
||||
if (!response.ok || payload?.ok === false) {
|
||||
throw new Error(payload?.error?.message ?? `HTTP ${response.status}`)
|
||||
const code = payload?.error?.code
|
||||
const detail = payload?.error?.message ?? `HTTP ${response.status}`
|
||||
throw new Error(code === undefined ? detail : `${detail} (${code})`)
|
||||
}
|
||||
return payload
|
||||
}
|
||||
@@ -611,15 +599,13 @@ window.__ModuleLoader__.load({
|
||||
inject: () => ({ requestSessionDelete }),
|
||||
}, DeleteSessionMenuItem))
|
||||
|
||||
// This entry reuses the shipped archive action's id, so the row keeps
|
||||
// one hover button and archiving stays available from the "..." menu.
|
||||
// Same id at the same priority is refused as a duplicate, and the
|
||||
// lowest priority renders, so this row must sit below the shipped 0.
|
||||
// This entry carries its own id, so the shipped `archive` and `pin`
|
||||
// hover buttons keep their cells and are not shadowed by a lower
|
||||
// priority. Archiving therefore stays exactly where the harness put it.
|
||||
ctx.slots.inject('sidebar.workspaces.session.row.action', () => ctx.slots.register({
|
||||
name: 'sidebar.workspaces.session.row.action',
|
||||
id: ROW_ACTION_ID,
|
||||
id: NS,
|
||||
order: ROW_ACTION_ORDER,
|
||||
priority: ROW_ACTION_PRIORITY,
|
||||
locale: NS,
|
||||
inject: () => ({ requestSessionDelete }),
|
||||
}, DeleteSessionRowButton))
|
||||
|
||||
Reference in New Issue
Block a user