Repository navigation
Add Library drag assignment to Dashboard panels and variables #428
Description
Status
Drag path shipped in PR #484 (merged 2026-07-27). The only unmet acceptance
bullet is 9 — the keyboard-accessible Add to dashboard… command — which was
deferred to #483 by owner decision the same day. So there is no remaining work
tracked in this issue: it stays open only as the umbrella for that deferred
bullet, and closes when #483 lands (or immediately, if the bullet is considered
#483's to own).
Assignment is therefore pointer-only on main today. That is the accessibility
gap #483 exists to close, and the reason bullet 9 was not silently dropped.
Required foundations have shipped:
- Introduce StoredWorkspaceV3 with a dashboard collection and no UI change #424 — multi-Dashboard storage collection;
- Add a Databases/Dashboards switcher and dashboard hierarchy tree #426 — Dashboard hierarchy tree;
- Separate Library queries from dashboard-owned query copies #427 — Library projection and one dedicated saved-query copy per panel tile;
- Replace curated Dashboard filters with inferred Variables and batched option queries #447 phases 1–2 — inferred Dashboard Variables and batched option execution;
- Remove the variable drawer and edit option SQL in a dedicated main-editor tab #457 — variable option SQL edited in dedicated main-editor tabs.
This issue owns one item of #447 phase 3 — the drag/drop of Library SQL onto
Variables rows. It does not own that phase. Corrected 2026-07-27; the original
wording ("owns #447 phase 3") over-claimed and would have read as though merging
#484 completed the phase.
#447 phase 3's other six items stay with #447: the paste/copy workflow in the
variable editor, rewritten shipped examples on inferred variables, import/export
coverage for variableConfigs, docs and E2E coverage, the final dead-code audit for
removed filter terminology, and reconciling or closing superseded #450.
#465 is not a prerequisite. It restores the two-String-column shape check on an individual variable tab's Run action; assignment here only copies SQL and preserves the current validation/runtime behaviour.
The old curated-filter/provider design no longer exists. There are no filter-role queries, persisted filter objects, source-query IDs, target lists, or filter configuration dialogs.
Goal
A Library query drag has three destination-dependent meanings:
Library query -> Dashboard row / Panels group -> create an independent panel-owned query copy and tile
Library query -> existing Variables row -> copy its SQL text into that Dashboard variable's option SQL
Library query -> main SQL editor -> insert the existing `( … )` subquery text at the drop position
The third path is already implemented by PR #40 and must remain behaviourally unchanged. This issue extends the same Library drag source with stable identity for Dashboard assignment without breaking the existing editor drop.
Drag payload and source rules
Only rows in the lower Library list may be assigned to Dashboards. A Library query has zero Dashboard owners according to buildQueryOwnershipIndex.
History rows remain draggable only for the existing editor-subquery insertion and are never Dashboard-assignment sources.
A Library drag must publish two independent payloads:
- the existing
SUBQUERY_MIMESQL snapshot used by the SQL editor; - a new internal identity payload used only by Dashboard/Variables targets.
Suggested identity payload:
interface LibraryQueryDragPayload {
kind: 'library-query';
workspaceId: string;
queryId: string;
}Requirements:
- Dashboard assignment must never trust SQL or a complete saved-query object from
dataTransfer; - Dashboard assignment re-resolves the query by ID from the latest committed workspace inside
mutateWorkspace; - editor insertion continues to use the SQL snapshot and
toSubquery(...)exactly as today; - initiating a drag must not open the Library query;
- normal click, star, rename, delete, and existing editor drop behaviour remain usable;
- workspace switch, missing source, or a source that is no longer a Library query cancels safely.
Do not cancel merely because the same workspace changed after drag start. Re-read the latest workspace and apply the semantic operation when the source and target still exist.
Supported destinations
Panel assignment
Accept on:
Dashboard row -> panel assignment
Panels group -> panel assignment
Both destinations have the same persistence semantics.
Variable option-SQL assignment
Accept on an individual inferred Variables row:
Variables
country : String <- valid destination
The Variables group itself is not a destination because it does not identify which variable receives the SQL.
An orphaned configuration that is no longer inferred is not a valid assignment destination. It can still be edited or deleted through its existing UI.
Existing editor insertion
The main SQL editor remains an accepted destination through SUBQUERY_MIME:
- insert at the actual drop position;
- wrap with the existing
( … )subquery formatting; - make the edit undoable;
- perform no workspace mutation.
Rejected destinations
Do not accept Dashboard assignment on:
- an individual panel row;
- the Variables group row;
- an orphan-only variable row;
- unrelated tree rows or schema objects;
- Dashboard tiles/canvas content;
- History rows as sources;
- Dashboard-owned query rows as sources.
Moving/copying members between Dashboards and tree reordering remain out of scope.
Drag feedback
During a Library drag:
- highlight eligible Dashboard rows, Panels groups, and inferred Variables rows;
- show a distinct active-target state on
dragenter/dragover; - do not present the Variables group itself as eligible;
- clear all assignment drag state on drop, cancellation, Escape, workspace switch, or drag end;
- preserve the editor's existing drop feedback;
- auto-expand a collapsed Dashboard after a short bounded hover delay so Panels and Variables rows become reachable;
- cancel the hover timer when leaving;
- auto-expansion is UI state only and must not navigate to the Dashboard or mutate workspace content;
- dropping must not trigger Dashboard-name navigation, row click actions, disclosure clicks, or trailing action menus.
Panel assignment
Dropping on a Dashboard row or Panels group performs one atomic mutation over the latest StoredWorkspaceV5.
Suggested application command:
copyLibraryQueryToPanel({
latest,
sourceQueryId,
dashboardId,
newQueryId,
newTileId,
}): StoredWorkspaceV5 | nullRequired transformation:
- verify the active/latest workspace has the payload's exact
workspaceId; - resolve the source query by exact ID;
- verify it still has zero Dashboard owners;
- resolve exactly one target Dashboard by stable ID;
- mint collision-free query and tile IDs against the latest document;
- clone with the shipped
cloneQueryForDashboardOwner({ source, newId })helper; - append the clone to
workspace.queries[]; - append
{ id: newTileId, queryId: newQueryId }to the target Dashboard'stiles[]; - do not create redundant tile title/description overrides;
- normalise the target Dashboard through the active layout implementation and regenerate fallback layout data where required;
- increment only the target Dashboard revision, exactly once;
- preserve every non-target Dashboard and the source Library query unchanged;
- validate the complete workspace candidate;
- commit through
app.mutateWorkspace(...).
The source remains in Library and is never modified, including its favourite state.
Repeated drops are allowed. Every successful drop creates a new independent query ID and tile ID, even when the same source is dropped repeatedly into one Dashboard.
After success:
- repaint Library and Dashboard tree projections;
- expand the target Dashboard and Panels group;
- focus/select the new panel row;
- do not automatically open Dashboard View/Edit or execute the query.
Owner revision (2026-07-27). The drop DOES now open the new panel's owned
query copy in the SQL editor, and focuses the new row in the tree. Landing on an
unchanged-looking Dashboard read as "did anything happen?". The Dashboard itself
is still never opened in View/Edit and nothing is executed.
Variable option-SQL assignment
Dropping on an inferred Variables row copies only the source query's SQL text into the target Dashboard's variableConfigs[variableName] entry.
Required semantics:
- identify the target by exact
(dashboardId, variableName); - re-derive the target variable from the latest target Dashboard's panel queries;
- require that the exact variable name is still inferred at commit time;
- preserve exact case-sensitive variable-name identity;
- copy SQL text only;
- create no saved-query clone, tile, role, source ID, owner, target list, or provider mapping;
- leave the Library query byte-for-byte/canonical-value unchanged;
- later edits to the Library query do not affect the copied variable SQL;
- increment only the target Dashboard revision, exactly once;
- commit through the existing
withVariableConfig/ Dashboard-variable-config mutation path or an equivalent id-addressed transform overmutateWorkspace.
A blank/whitespace-only Library query is not a deletion gesture: reject the drop as a no-op with a clear message. Removing variable option SQL remains the explicit blank-Save/delete behaviour of the variable editor.
Copy the SQL as authored. The existing local option-SQL rules remain authoritative:
- parameterised/cascading SQL is stored but diagnosed as unsupported;
- non-embeddable or otherwise locally invalid SQL is stored for correction but excluded from option execution;
- result-shape validation on an individual Run is tracked by Validate dashboard-variable option SQL on editor Run #465 and is not implemented by this issue;
- until Validate dashboard-variable option SQL on editor Run #465 lands, the existing batch/runtime failure behaviour remains unchanged.
No configuration dialog is required.
Open variable-tab interaction
A drop must never silently overwrite an unsaved variable-tab draft.
- no open tab: commit normally;
- matching clean variable tab: commit and reconcile it to the committed SQL;
- matching dirty variable tab: reject the assignment, keep the draft unchanged, and focus/select that tab with a clear message.
The variable tab is identified by (dashboardId, variableName), as shipped by #457.
After a successful variable assignment:
- repaint the Variables row/control state;
- keep the Dashboard tree expansion state;
- focus/select the target variable row;
- do not automatically switch to the Query surface or run the option SQL.
Owner revision (2026-07-27). The drop DOES now open that variable's
Variable: <name>tab, so the assigned option SQL is there to edit and run. The
option SQL is still not executed automatically.
Commit-window addendum (2026-07-27). The dirty-tab rule below is enforced
inside themutateWorkspacetransform, which closes the queue-and-load window.
It cannot close the window duringworkspace.commit(...)itself — the write is
already durable by then. That case is REPORTED rather than hidden: the outcome
carriesdraftDiverged, and the drop shows a non-dismissing toast offering
Discard draft. Refusing to adopt while reporting a clean success was the
failure mode this rule exists to prevent.
Existing editor-subquery path
The existing PR #40 behaviour is part of this issue's integration contract, not new implementation scope.
Adding the identity payload must not change:
- Library and History rows inserting into the main SQL editor;
SUBQUERY_MIMEhandling;- trailing semicolon/
FORMATcleanup performed bytoSubquery; - drop-position insertion;
- undo/redo behaviour;
- editor-only operation with no persistence side effects.
The same Library drag may therefore be interpreted differently by different targets: the editor consumes SQL text; Dashboard destinations consume stable identity.
Concurrency and stale-state rules
Use the current read-before-write model from #343:
enter mutateWorkspace queue
-> load latest committed workspace
-> re-resolve source and target
-> apply one semantic transform
-> validate whole candidate
-> atomically commit
-> project committed truth
Do not introduce or claim repository compare-and-swap. Simultaneous same-instant multi-tab commits remain outside the repository contract.
Abort without mutation when:
- the active workspace ID differs from the payload;
- the source query was deleted or became Dashboard-owned;
- the target Dashboard disappeared or has duplicate IDs;
- the target variable is no longer inferred;
- generated IDs cannot be made collision-free;
- validation or persistence fails;
- a dirty target variable tab would be overwritten.
Never commit an orphan panel query without its tile, a tile without its query, or a partial variable update.
Keyboard-accessible alternative
DEFERRED to #483 (owner decision, 2026-07-27). This section is the spec for
that issue; PR #484 implements the drag path only. Mobile is also out of scope
for the whole gesture, so noisMobilebranches were added — touch drag is
already a non-goal below.
Each Library row must expose an equivalent command:
Add to dashboard…
The chooser contains:
- Dashboard selection;
- destination kind: Panel or Variable;
- for Variable, one of that Dashboard's currently inferred exact variable names.
The chooser calls the same application commands as drag/drop and produces identical data.
Do not recreate the removed curated-filter configuration form. There is no parameter picker, target-panel picker, default picker, selection-mode picker, or provider mapping.
The existing editor-subquery drag remains separate from this assignment chooser.
Suggested boundaries
Pure/application mutation layer
copyLibraryQueryToPanelcopyLibraryQuerySqlToVariable
Both operate over an explicit latest workspace and return a complete candidate or a typed abort.
Library drag adapter
Publishes both MIME payloads, suppresses row opening during drag, and exposes the keyboard command.
Dashboard-tree drop controller
Owns eligible targets, hover expansion, visual state, payload decoding, and command dispatch. It never mutates workspace documents directly.
Existing SQL editor adapter
Remains unchanged except for regression coverage proving the second MIME payload does not interfere with SUBQUERY_MIME.
Tests
Payload/source
- Library rows publish both identity and subquery MIME payloads;
- History rows publish only the existing subquery payload;
- Dashboard-owned queries cannot start Library assignment;
- drag start does not open a query;
- star/rename/delete remain usable;
- stale/missing/no-longer-Library source cancels safely.
Targeting
- Dashboard row and Panels group accept panel assignment;
- inferred Variables rows accept SQL assignment;
- Variables group, orphan rows, member rows, and unrelated UI reject assignment;
- hover auto-expands without navigating;
- drop/cancel/Escape/workspace switch clears every visual state;
- drop does not trigger row navigation or action-menu handlers.
Panel mutation
- source remains unchanged and in Library;
- clone preserves SQL, Spec version, name, description, panel data, variants, time ranges, and extension fields;
- clone receives a new ID, panel role, and no favourite;
- exactly one new tile references it;
- layout/fallback normalisation is correct;
- target revision increments once;
- non-target Dashboards remain unchanged;
- repeated drops create independent copies;
- validation/persistence failure leaves the workspace unchanged.
Variable mutation
- exact inferred variable receives the source SQL;
- no query/tile/owner is created;
- source remains unchanged;
- Dashboard revision increments once;
- case-sensitive variable identity is preserved;
- orphan and disappeared variable targets abort;
- blank source SQL does not delete an existing configuration;
- parameterised or locally invalid SQL is preserved and follows existing diagnostics;
- result-shape behaviour remains unchanged pending Validate dashboard-variable option SQL on editor Run #465;
- clean open variable tab reconciles;
- dirty open variable tab is preserved and assignment is rejected.
Existing editor regression
- Library drop still inserts a
( … )subquery at the drop position; - History drop still works;
- editor insertion remains undoable;
- no Dashboard mutation occurs;
- the additional identity MIME does not alter editor behaviour.
Concurrency/accessibility
- same-workspace changes during drag are rebased over latest committed state;
- workspace switch aborts;
- source/target deletion aborts without partial writes;
- keyboard Add to dashboard… matches drag semantics for Panel and Variable destinations.
Acceptance criteria
- One Library drag supports the three destination semantics documented above.
- Dashboard/Panels drop creates one independent panel-owned query copy and tile.
- Variables-row drop copies SQL only into the exact Dashboard variable configuration.
- Main-editor drop retains the existing subquery-insertion behaviour.
- Source Library queries are never modified or removed from Library.
- Assignment uses stable IDs and the latest committed workspace, never names or stale serialized query objects.
- All persistence mutations are atomic, revision-correct, layout-safe, and fully validated.
- Dirty variable drafts cannot be overwritten by a drop.
- A keyboard-accessible Add to dashboard… command provides equivalent Panel/Variable assignment. — DEFERRED to Keyboard-accessible "Add to dashboard…" command for Library queries #483 (owner decision, 2026-07-27). The drag path ships without it, so assignment is pointer-only until Keyboard-accessible "Add to dashboard…" command for Library queries #483 lands.
Non-goals
- Reimplementing the existing editor-subquery drop.
- Restoring curated filters, filter-role queries, provider queries, or configuration dialogs.
- Creating a variable that is not already inferred from panel SQL.
- Moving/copying existing Dashboard-owned members.
- Sharing one panel query among members.
- Reordering Dashboards, Variables, or Panels by this drag.
- Dropping directly onto the Dashboard canvas or an individual tile.
- Touch-specific drag interaction.
- Automatically opening or executing the destination after assignment.
- Persisting source-copy lineage.
Activity
feat(#427): Dashboard-owned query copies and the Library projection (…
chore(ship): fold ship-phase into /ship and add the multi-phase model
[-]Add copy-on-drag assignment from Library to Dashboards[/-][+]Add Library drag assignment to Dashboard panels and variables[/+]BorisTyshkevich commentedon Jul 27, 2026
Ship log — #428
Single-phase issue (no
## Phases, no### Phase Nheadings). One branch offmain, one PR.Handoff
Landed (branch
feat/library-drag-assignment-428, commit20a28b8)New modules, matching the issue's own "Suggested boundaries":
src/core/library-drag.ts— the identity payload (LibraryQueryDragPayload,encode/decode) and the
LibraryDropTargetunion. Fail-closed decode: a foreignor malformed payload is one
null, not a parse guard per call site.src/dashboard/application/library-assignment.ts— the two pure transformscopyLibraryQueryToPanel/copyLibraryQuerySqlToVariable, each returning acomplete candidate or one of eight typed aborts.
src/application/library-assignment-service.ts— themutateWorkspaceplumbing, modelled on
dashboard-variable-config.ts, pluslibraryAssignmentMessage(the abort→sentence table, kept out of the view).src/ui/tabs.tsgainsreconcileVariableTab.DashboardTreeRow.dropTargetinapplication/dashboard-tree-model.ts;the drop controller in
ui/dashboard-tree.ts; the dual payload inui/saved-history.ts;LIBRARY_QUERY_MIMEinui/dnd-mime.ts.Owner decisions taken this run (NOT in the issue body)
Add to dashboard…chooser — is DEFERREDto Keyboard-accessible "Add to dashboard…" command for Library queries #483. This PR ships the drag path only. Until Keyboard-accessible "Add to dashboard…" command for Library queries #483 lands, assignment is
pointer-only, which is a real accessibility gap and is stated as such in Keyboard-accessible "Add to dashboard…" command for Library queries #483.
src/ui/library-assign-menu.tswas planned and then not built; Keyboard-accessible "Add to dashboard…" command for Library queries #483 carriesthe design (two-stage
openMenu) and the reminder to add that file totests/unit/typography-contract.test.js's source list.isMobilebranches anywhere. Touchdrag is already a stated non-goal, so no drag can start on touch and the drop
target needs no gate — adding one would be unreachable branches the coverage
config forbids.
draggablestays unconditional on Library AND History rows;removing it on mobile would have broken the shipped PR feat(editor): drag a Library/History query into the editor as a ( … ) subquery #40 editor drop, which
this issue's integration contract forbids changing.
there is regression coverage.
Decisions taken under ambiguity
row.dropTarget), resolved in the tree model,not a rule in the view.
ui/dashboard-tree.tsis at a 100/100/95/90 per-filegate and the model owns every other structural decision. A first draft put a
libraryDropTarget(row)function insrc/corewith a hand-redeclaredstructural row type; that re-typed
DashboardTreeInvalidas a bare string, sorenaming the literal in the model would have silently stopped rejecting orphans.
is a type disagreement between panels, orthogonal to where option SQL is
stored; an orphan has nothing to bind to.
mutateWorkspacetransform, viaan injected
readTabs(). A pre-dispatch check is a snapshot: the primitivequeues behind
serializeWritethen awaitsloadById, and a keystroke in thatwindow flips the tab dirty after the check.
src/applicationmay importsrc/state.ts(dashboard-variable-config.tsalready does), so this issatisfiable — an earlier plan wrongly argued it was not.
delivers
dragend; a key listener would be a branch no test could reach. Escapeis covered by
dragend, and there is a test saying so.data-droptargetmarkup revealed by onedash-draggingclass on the list; theactive target is re-applied from
app._dashTreeDropKeyafter any paint. AnAppStatesignal (the first plan) would have repainted on drag start and onevery hover expand — and
replaceChildren()destroys the row under the pointer.itself and both groups; the Variables group hover-expands while still
rejecting drops. Gating on eligibility left variable rows unreachable by drag.
pending timer, because
dragenteron the new target fires beforedragleaveon the old one.
endLibraryDragis wired intocancelDashboardTreeClicks, so a workspaceswitch / role change / dispose cancels a pending expansion. Without it a timer
armed before a refresh fires afterwards and re-adds an
expandedDashboardIdsentry the refresh just pruned.
applyCommand('add-query-instance'), not ahand-rolled
tiles.push+ normalize: only the canonical add path seedsplacement from
spec.dashboard.sizeHints, which grafana-grid@1 requires. Theas Extract<…,{ok:true}>cast is documented — that command's only failure is anunresolvable
queryId, and the resolver is built over the collection the clonewas just added to.
crypto.randomUUIDseam, notderiveOwnedQueryId.Derived
q-own-…ids keep migration/import idempotent; a drag is neither, and acontent-derived id would fight repeated drops. The
id-collisionabort stays.blank-sqlis checked LAST, after the Dashboard and variable resolve, so ablank query dropped on a deleted Dashboard does not tell the user to go fix a
tab on a Dashboard that no longer exists.
--bg-highlight. That tokenalready means "the current Dashboard/member"; reusing it made every eligible row
indistinguishable from the open one for the whole drag.
After a mouse gesture, yanking focus is worse than not.
Review findings folded in (independent read-only pass, no blockers): the
cancelDashboardTreeClicksteardown, the service reporting its committedsqlinstead of the UI re-deriving it from a projection, the hover-timer hand-over, the
blank-sqlordering, the eligible-highlight collision, dropping the unreachableondropguard, and four tests that asserted less than their names claimed(timer count, no-repaint, star/rename/delete actually invoked).
Deferred acceptance criteria — bullet 9 → #483. Bullets 1–8 claimed.
Reconciled:
CHANGELOG.md[Unreleased], an ADR-0003 addendum(#428, 2026-07-27), the
dash-emptycopy inui/dashboard.ts(which saiddragging a query onto a Dashboard "does not exist yet"), and #447's ship log —
its phase 3 is no longer blocked on this issue.
Follow-ups filed: #483 (
enhancement,accessibility— the keyboard chooser).Gate: 5753 unit tests / 178 files, per-file coverage met,
tsc --noEmit,check:archandcheck:schemasclean, build 1,829,855 bytes. e2e 281 passed onchromium + webkit (
tests/e2e/dashboard-tree.htmlneeded a third, UNTILED query —both seeded queries were tiled, so its Library was empty and there was no
.saved-rowto drag). The drag CSS is asserted against real computed styles inChromium/WebKit, since happy-dom sees no cascade at all.
Follow-up round (2026-07-27, commit
e7be508)Three items from owner review of the open PR.
1. CI was red — and the local gate could not have caught it.
tests/unit/dashboard-tree.test.tsdereferenced a.find()result withoutproving it exists;
tsc --noEmitfailed before vitest ran, so the build and e2ejobs never started.
The reason it shipped: this sandbox has npm
ignore-scripts=truesetglobally, so
npm testsilently skipspretest—check:schemas,check:archandcheck:typesnever run. A green localnpm testis NOT therepo's gate here, despite CLAUDE.md hard rule 1 describing tsc as "wired into the
preteststep" (true ofpackage.json, false of this machine). Run the threeexplicitly before pushing. Saved as a memory. Related trap:
npx tsc --noEmit | headreportshead's exit code, so a failure reads as a pass.2. A drop now opens what it created (owner request, REVERSES a spec line).
#428's "After success" said not to switch surfaces; landing on an
unchanged-looking Dashboard read as "did anything happen?". Now:
it —
renderDashboardTreerestores focus only when the tree already held it,and after a mouse drop it does not, so previously the row was the arrow-key
origin while off-screen;
(never the Library original — editing that would not touch the panel), for a
variable its
Variable: <name>tab.Still true: the Dashboard is never opened in View/Edit, and nothing is executed.
The issue body carries an "Owner revision" note on both affected bullets.
3. The dirty-variable-tab race was NOT closed by the in-transform gate.
The gate closes the queue-and-load window.
mutateWorkspacethen awaitsworkspace.commit(candidate), and a keystroke in that window passes everycheck.
reconcileVariableTabcorrectly refused to adopt — and the caller ignoredthe refusal and reported success. Result: committed SQL, a diverging dirty draft,
no signal, and the next Save silently reverting the assignment. Precisely the
state the PR claimed to prevent. A blocked or slow IndexedDB transaction widens
the window materially.
No check can close it: the commit is the repository's atomic write and UI state
cannot be held still across it. So the outcome carries
draftDiverged,re-read after the commit resolves, and the drop surfaces it — a toast that does
not auto-dismiss (an
actionsuppresses the timer) with a Discard draftaction wired to a new
discardVariableDraft.reconcileVariableTabstill refusesa dirty tab; nothing discards typing automatically.
Why the old test could not see it: it dirtied the tab before the transform
ran. The fake
mutateWorkspacenow has aduringCommithook, exercised at boththe service and drop-controller levels.
Gate: 5760 unit tests,
check:types/check:arch/check:schemasall runexplicitly and clean, build 1,830,478 bytes, e2e 281 passed on chromium + webkit.
Merged (2026-07-27)
PR #484 merged into
main. Final branch statebca4414, on a merge ofmainthrough #482 (
f2cb8cb) — the only conflict wasCHANGELOG.mdand was purelyadditive (both the #428 and #463 entries kept);
main's changes todashboard-tree.tsanddashboard.tswere comment-only. CI green on the newmerge base: 5812 unit tests,
check:types/check:arch/check:schemasclean,281 e2e on chromium + webkit.
Two late review fixes are in that final state:
draftDivergedmeans the draft DIFFERS, not merely that it is dirty(
bca4414). Typing and undoing back to the assigned text left the flag set withbyte-identical content — a false alarm about a Save that could not change
anything. The in-transform GATE deliberately still keys on plain dirtiness:
Add Library drag assignment to Dashboard panels and variables #428's own wording is "matching dirty variable tab: reject", and refusing there
discards nothing. The asymmetry is between "refuse to act" (conservative is
right) and "warn the user" (precision matters).
not leave the Dashboard" rule that item 2 of the follow-up round reversed.
#428 remains OPEN deliberately: acceptance bullet 9 (the keyboard
Add to dashboard…command) is unmet and tracked in #483. Until that lands,Library→Dashboard assignment is pointer-only. Close #428 only when #483 ships, or
close it now and let #483 carry the remaining scope — an owner call, not taken
here.
fix(#428): open what a drop creates, and report a draft that diverged…
fix(#428): draftDiverged means the draft DIFFERS, not merely that it …
Merge pull request #484 from Altinity/feat/library-drag-assignment-428
fix(#487): address ChatGPT review feedback
feat(Altinity#428): assign a Library query to a Dashboard by dragging it
feat(Altinity#429): split the Dashboard tree row into chevron, name a…
feat(Altinity#535): duplicate, widen and expand actions on a panel ti…
feat(Altinity#487): left navigation controller seam (phase 3, step 2)
fix(Altinity#487): resize-session must compare raw proposals, not cla…