Repository navigation
Feat/ledger docs site - #226
Open
barisakcam wants to merge 18 commits into
Open
barisakcam wants to merge 18 commits into
barisakcam wants to merge 18 commits into
Conversation
Reads a user-supplied SQLite ledger entirely in the browser via sql.js, with no backend endpoints involved. - Workspace selector separates Schema (Explorer/Insights) from Ledger - Left sidebar loads a ledger and summarises active/total counts per table - Raw Tables: table dropdown, search, kind and status filters, pagination - Explore: one search across every table, results grouped per table - Query: predefined queries plus editable read-only SQL, enforced by PRAGMA query_only - Details sidebar shows a record's Concept -> Revision -> Contract -> Binding context with expandable nodes and record-to-record navigation - Table order, level links and record identity are derived from the database's declared foreign keys
Add case-sensitive, whole-word and regex search across Raw Tables and Explore, backed by a registered SQLite regex function, plus kind and status filter dropdowns. Give the details pane record actions that navigate to any record a row references, and to that record's page in Raw Tables. Add a mermaid ER diagram of the ledger shape behind a button in the sidebar. Replace the query presets with simpler ones that exercise these paths. Fix chain resolution to link levels by foreign key rather than by table order or table name, so a sibling table or an irregular plural no longer hides a subtree, and bound the walk so a wide ledger cannot freeze the tab. Keep a loaded ledger when an import fails, report the reason, and close a superseded import instead of leaking it.
Show each table's columns and the relationship diagram in all three ledger views, not only in Query. Drop the collapsible wrapper, the empty-state text and the separator from the ledger file list, which holds one file at most. In the query toolbar, remove the read-only hint and move the selected preset's description below the selector, keeping its line reserved so the toolbar does not change height. Hide the Explorer and Insights tabs until a schema is loaded, replacing the prompt that was duplicated into both tab bodies. Fix the theme flashing to the system setting when switching workspaces: ThemeToggle is remounted each time and read its stored value one render too late.
Derive the ModL model from one declaration and fall back to it when the uploaded database declares no keys, so the chain, the table order and the page ordering survive a file exported without constraints. Take the shape from modl/ledger.py, and make the table icons and status tones exhaustive over it. Fix the correctness findings: a second ledger no longer inherits the first one's details, matches and query result; a failed import leaves the loaded ledger alone and says why; a superseded import closes its own database; chain levels link by the foreign key that declares them rather than by table order or name; search folds case for non-ASCII text in every mode; a repeated column name no longer highlights the wrong row. Split the four largest files along the seams their signatures already drew: introspect into sql, schema, rows, query and search; the slice and the saga into one per concern; chain into a pure derivation and a database walk. Extract the shared details-pane shell, the search input, the record list and actions, the table footer and filter selects, and the two help panels. Dissolve constants.ts into the modules that read them. Reduce the repeated work each read did: two queries per page instead of three, one count per table instead of two, and one pass over the chain instead of one per node. Style the record details as bordered rows with tinted column names, to match the insights detail views.
Match the ledger file row to the Schema file list: the same container padding and list spacing, and a placeholder in the leading slot so the name and the row height line up with a list that has a drag handle and a per-row remove button. Give the table list and the relationships button the same width as the file row, which the container's wider horizontal padding had held back. Drop the status badge from the details title, and move the tint in Record details from the column name to the value, which is what a reader scans for.
Two review findings whose earlier fixes had not landed: the quoted identifier was still built inside the columnPredicate call, and the per-column predicate was still bound as `one` in both the join and the loop.
Keep the scroll-into-view effect from firing on every render: the call sites build selectedValues inline, so the effect now keys on the values rather than the array's identity. Clear the rows on a failed reload, which the split had dropped, so the previous page's footer no longer sits under an error banner. Pass the profiled tables into searchLedger. Building the list there again skipped the ModL fallback, so Explore ordered a keyless file alphabetically while the sidebar showed chain order. Compare blob values by content rather than by length alone: two rows differing only in same-length blob content both highlighted. Also pass bodyKey so ledger navigation animates as Insights does, merge LedgerErrorBanner's className instead of replacing it, move a doc comment that stayed behind in schema.ts, drop one that stayed behind in RawTablesView, and name the two values in the row comparison.
The pane wrote its own centred, muted div for the "no card selected" state. LedgerDetailsPane and LedgerTab already render the shared EmptyState for the same thing, so this was the last of the three copies the ledger style review named. Drop the div's p-5 and text-center rather than passing them through className: EmptyState centres with flex already, and neither ledger site adds padding, so all three placeholders now look alike.
Move the ledger out of the playground and into a shared
src/s2dm/templates/ledger-ui workspace, alongside insights-ui, so the
scaffolded Docusaurus site can render it too. The data layer becomes
ledger-ui/data, the slices and sagas ledger-ui/state, and the portable
components ledger-ui/components; the playground keeps only its own
shell — the file list, the help button and the collapsible details pane.
Follow insights-ui's host contract rather than inventing one. Selectors
read a private LedgerRootState that each host's larger RootState
structurally satisfies, components use ledger-ui's own typed hooks, and
the fourteen things the shared code still needs from its host arrive
through the @/ alias, which resolves per host. docs-website gains its
own copies: nine ui primitives, an axios-free getErrorMessage, a
useTheme that reads Docusaurus's colour mode, and a Monaco editor
deferred behind BrowserOnly because every route here is prerendered.
Two host differences needed real seams. sql.js cannot resolve its own
wasm URL under two bundlers, so configureSqlJs takes it from the host —
Vite's ?url import in the playground, static/sql-wasm.wasm on the docs
site, copied out of node_modules after install. And openLedger now
carries { name, bytes } instead of a File, so a host that ships a
ledger can dispatch the same action as one that opens a file picker.
The ledger reaches the site the way the composed schema does: staged in
dist/ledger.db, turned into static/ledger.db by npm run doc, and
supplied either by the reusable workflow's new ledger_source input or by
preview-docs.sh. A model without a ledger still builds.
Three Docusaurus CSS gaps had to be closed for the shared components to
render. Infima styles every table on the site from unlayered rules that
utility classes cannot outrank; the site loads Tailwind's theme and
utilities but not its preflight, so a display utility beat [hidden] and
Radix laid out all three tab panels at once; and Tailwind only scans a
project's own directory, so both hosts need an @source line pointing at
each shared template.
Reading the file moved out of the saga in the previous commit, which dropped its error path: useFileImport calls onFilesSelected without awaiting, so a failed read became an unhandled rejection and the import banner stayed empty. Chain the read instead, report failures through openLedgerFailure, and keep a sequence ref so a superseded pick is discarded rather than winning by finishing last. Scope the docs site's portalled content by having it carry the class rather than the stylesheet reaching for every [role="dialog"] on the site, which would have stripped an adopter's search modal. Release the ledger when leaving the page: the database sits decoded in wasm memory, so the store now hands back a stop() alongside it. Declare tw-animate-css, which the copied dialog and select assume and the site did not have. Unify the danger tone (A19), now that both hosts sit in one layer: border-destructive/40 in all four files, with the two status-banner copies kept byte-identical. Write the tab-close rule once (A8b). Not as extraReducers as the review proposed — setWorkspace and setExploreTab live in the playground while both detail slices are templates, so neither can react to them — but in appSaga, leaving the tab handlers to dispatch only their state change.
Move the view switcher from a centered tab bar into the site's own docs sidebar, the way the insights page does it. That means a route per view, so the plugin now loops over a list of sidebar pages instead of hardcoding insights, sidebars.ts gains a ledgerSidebar, and the page moves to src/ledger/LedgerPage.tsx behind the same DocsSidebarProvider/DocRootLayout providers. The file-routed src/pages/ledger.tsx goes, since it would collide with /ledger. Add a Schema view carrying what the playground keeps in its left pane: the table and column summary and the relationships diagram button, held to a readable column rather than stretched across the page. The sidebar drives the view by URL while the store switches it from inside the app — "show in table" does — so the page reconciles them: whichever side moved since the last check wins. The session moves to module scope because each view is its own route and the page remounts on every click; it owns the download so switching view mid-load neither cancels it nor starts a second one, and reports failure through openLedgerFailure so the message outlives the switch. It is released on leaving the ledger rather than on unmount. Import monaco through edcore.main rather than the package entry: that drops the TypeScript, JSON, CSS and HTML language services and the other grammars a SQL box never uses, about 560 KB, while keeping the editor's own contributions.
Most of these are the missing Tailwind preflight showing up again. Borders carried a width and a style but no colour, so every uncoloured border fell back to currentColor and drew in the text colour — the playground sets border-border in its own base layer, and now so does this. The popover tokens were absent entirely, leaving the select menus with no background to resolve; and the table reset that cancels Infima's row stripes is unlayered, so it beat the row's own background classes and erased both selection and hover. Both are restated where they can win. Put the view switcher in the site's own sidebar order — Schema, Raw Tables, Explore, Query — with Schema on /ledger so the first entry is not a dead link. Strip the details chrome: this host shows details in the document flow beneath the workspace, where there is no pane to close and the page's own scroll is the way back, so the title, the back arrow and the close button go. When nothing is selected a centred line says so, and neither it nor the details appear under the schema view, which has nothing to select. Skip the ledger context for a query projection. It is not a record of any table, so no chain is ever resolved and the section waited on something that never arrived. Bring the details pane to the size insights uses — it was text-xs throughout against sixteen text-sm — and give the row selection a tint that reads on white; --accent is a 3% difference there, having been picked against a dark ground. Open dialogs below the navbar rather than centred over it: they ask for 90vh, which tucked their top edge behind sticky chrome. Scroll areas take the site's own thin-scrollbar treatment, which everything outside the ledger already had.
…l roots Details sit below the workspace on this host, so following "Show in Raw Tables" left the grid above the viewport. The grid does scroll the selected row into view, but with block: "nearest", which is satisfied as soon as the row is visible inside its own overflow box — the page is already "nearest" by then and never moves. Correct in the playground, where the grid fills a pane that is always on screen. The page now scrolls the workspace in itself, keyed on the rows: the action clears them through resetTableView and the saga loads a fresh set, so it fires even when the record is already in the table on screen, which keying on the table or the page would have missed. Also "nearest", so it stays a no-op whenever the grid is already visible. The border defaults reached descendants but not the scope roots. Radix portals the dialog, its overlay and the select content to the body, so those carry the class themselves rather than sitting inside it, and their own border utility was still drawing in currentColor. Also correct the details shell's comment, which explained the missing pane rather than the missing controls: following a chain node or a record action pushes onto the detail stack with nothing to pop it, so the stack is one-way here. That is the intended trade for keeping the header off, and the comment now says so.
Four tables were sharing the grid's bounded box, so the last one sat behind an internal scrollbar that read as the card ending. Only a grid of many rows needs to scroll inside a box.
|
We need to adapt the workflow to fetch the ledger from the releases instead of the repository itself |
Let the three search toggles combine. SearchOptions carried a single mode, so whole word and regex were mutually exclusive by construction; they are three independent flags now, and a pattern asked for as both is wrapped in the same lookarounds the whole-word mode already used. Also tint the active toggles like the grid rows and record values — they used --accent, which is barely a shade against a light ground. Pluralise "See N more bindings": the label is the table name, always plural, so a single hidden row read wrong. Give the schema view its own space. The Records line gains the border its table rows already had, the content fills its card instead of sitting capped at 40rem in the middle of one, and the relationships diagram moves out of the dialog into a card of its own below — extracted as LedgerErdDiagram, which scales to the width it is given. The playground keeps the dialog, since its overview lives in a narrow pane; LedgerOverview takes the affordance from its host and null now means "leave it out" rather than falling back to the button.
SQLite cannot be interrupted, so a query ran to the end on the thread painting the page. The database now lives on a worker, and Cancel or a 30s deadline ends that worker and reopens the ledger behind it, carrying over any other work in flight. Typing in the query box no longer redraws the results with it. The context tree keeps the record it was built for, past both the page limit and the node budget and under its own parent. A query result is a stored record only when the table holds one like it, and references read positional cells. An empty ledger says so rather than reading as still loading, and a loaded one survives a failed import. Each page's sidebar links, routes and rendered view read one definition. CI now scaffolds a site, type-checks it and builds it, and the template guide describes how the ledger reaches it.
barisakcam
marked this pull request as ready for review
September 15, 2026 14:05
The schema view showed the overview and the diagram stacked on one page, which made it the longest view and gave the diagram no address of its own. Give each a sidebar entry under a Schema category, and put that category last, after the views that read the ledger's records. Raw Tables takes the root in its place, since the sidebar's first entry is where /ledger now lands. Structure and Diagram sit under /ledger/schema, so the two halves share a prefix. A view carries the section it belongs to, and consecutive views sharing one become a category, so the order in LEDGER_VIEWS stays the only thing that decides what the sidebar shows.
The reduce mixed two styles: two branches returned a new array while the third pushed into the accumulator. Build it in place throughout, which drops the copying and leaves one return, and name the entry it looks back at rather than calling it last. Name the schema-view check once on the page instead of computing it at both sites that branch on it.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Host the ledger explorer on the docs website
Moves the ledger from
playground/into a sharedsrc/s2dm/templates/ledger-uiworkspace besideinsights-ui, so thescaffolded Docusaurus site renders it too. The playground keeps only its own
shell — file list, help button, collapsible details pane.
Two seams were needed:
configureSqlJstakes the wasm URL from the host,since sql.js can't resolve it under two bundlers, and
openLedgercarries{ name, bytes }instead of aFileso a host that ships a ledger dispatchesthe same action as one that opens a picker.
The ledger reaches the site the way the schema does — staged in
dist/ledger.db, turned intostatic/ledger.dbbynpm run doc, supplied bythe workflow's new
ledger_sourceinput orpreview-docs.sh. A model withoutone still builds. On the site it gets the docs sidebar, a route per view:
Schema, Raw Tables, Explore, Query.
Most of the styling work traces to one fact — the site loads Tailwind's theme
and utilities but not its preflight, and Infima's rules are unlayered, so
utility classes can't outrank them. Each affected default is restated in
custom.cssat a layer where it wins.