Repository navigation
Conversation
remko48
added this pull request to stack #12
October 12, 2026 06:47
Views get a type (table or grid), a grid of widgets and a slug, and a grid view can exist without a table. Applications get a slug and an ordered menu of items that open a view, a table or a link, an index page, and a detail page that renders the menu and lets the owner edit it. A grid view has an edit mode built on GridStack with header, description and table widgets, saved as one grid on the view. Assisted-by: ClaudeCode:claude-fable-5-1
The grid canvas now drives its column count from its own width, 12 on a wide canvas, 6 on a medium one and 1 on a phone, and restores when the window grows again; a reflow never writes back into the stored layout. The context scheme export carries the slug, the menu items with their targets as uuids, and the table-less views the menu points at; import recreates missing grid views and remaps menu and widget targets by uuid. The view uuid lookup no longer requires a table and returns null as it declares. A Playwright spec walks the demo path: application, menu item, grid view, two widgets, save, reload. Assisted-by: ClaudeCode:claude-fable-5-1
The page headings leave room for the navigation toggle, the application header and the applications index wrap on narrow screens, the menu editor starts below its floating label and its rows wrap inside the dialog, and a table widget no longer repeats the table's own title. Views without a table, and shared views, now survive a reload of the tables list that used to race with their own request. Assisted-by: ClaudeCode:claude-fable-5-1
Unit tests for the View entity (grid parsing, type default, ownership fallback, serialisation), the MenuItem entity, the ViewService create and update validation (type, slug, grid shape and colors, table-less rules), the ContextService menu replacement, slug handling, scheme export and import resolution, the controller's menu item sanitiser, the permission guards on a view without a table, and the activity manager skipping such a view. Assisted-by: ClaudeCode:claude-fable-5-1
Every widget type now carries the schema of its content on the server, in GridWidgetTypes, and exposes it on /grid/widget-types. The add and configure dialog renders one generic form from that schema, so a new widget type needs a schema and a renderer, not a dialog. The server validates and sanitises stored widget content against the same schema, filling defaults and dropping unknown properties. The table widget's target became one property; grids saved in the old shape still render and are upgraded on their next save. Assisted-by: ClaudeCode:claude-fable-5-1
… as navigation
Opened at /apps/tables/app/{slug} (the id still works), an application
shows its name and icon above its menu items in the left navigation and
starts on its first page; the resource overview stays reachable as the
last entry and keeps the per-user layout. The top-bar entry links to the
slug. Inside Tables the application page became a configuration page:
the address, the pages as cards with Design and Open actions, "Add page"
which creates a grid view with its menu entry, and the menu editor
inline instead of inside the application dialog. The app shell's
initial redirect now fires only from the root route, which also fixes
menu links inside a standalone application.
Assisted-by: ClaudeCode:claude-fable-5-1
… land Stored in component data, the instance became a reactive proxy; drag handlers armed through the proxy failed GridStack's own instance check against the raw instance its nodes point at, so every moved widget snapped back without an event and nothing was saved. markRaw keeps the instance raw. The spec now drags a widget, saves, reloads and asserts the position. The application slug lookup is scoped to the applications the user owns or was given, since slugs are not unique across users. Assisted-by: ClaudeCode:claude-fable-5-1
…and menu items Views already carry a technical name; the fork added a slug next to it. The slug is gone: applications and menu items get technical_name with the same pattern as columns and views, a menu item derives its name from its label unless the dialog sets one, and the standalone app shell routes on it. Assisted-by: ClaudeCode:claude-fable-5-1
… its own schema Title and show title move from the widget itself into its configuration, the widget type carries a configuration schema next to the content schema, and grids saved in the old shape read as the new one until saved again. Assisted-by: ClaudeCode:claude-fable-5-1
Two OCS routes next to the internal ones the frontend uses: the widget types a grid can hold, with both schemas, and the creation of a view without a table. Documented in the OpenAPI document with their types. Assisted-by: ClaudeCode:claude-fable-5-1
…migration dated ContextScheme gains a factory that validates the import request at the controller boundary, so the service takes one object instead of ten parameters. The migration carries today's date. Assisted-by: ClaudeCode:claude-fable-5-1
…kes no focus before the first interaction The menu target was loaded once and kept as a snapshot, so a grid saved through the store rendered its old widgets until a reload. The grid page now reads the store's current copy of the view. On load the shell focused the content area before any interaction, which drew the browser's focus ring as a black line along the content edge; focus now moves only after a first pointer or keyboard event. Assisted-by: ClaudeCode:claude-fable-5-1
The frontend called internal routes for both actions; the OCS routes on api/2 already offer them. Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Remko <remko@conduction.nl>
- Serve widget types from an injected service with labels translated on the server - Validate a grid in one place: layout items must belong to a widget, widget ids are unique, a layout item is identified by its widget, booleans and integers are not cast - Refuse a view without a table as the target of a data widget - Validate the grids of imported views - Reset the stored value for every parameter in ViewService::update, so a type sent with a sort is no longer overwritten - Reject a malformed type or grid in ViewUpdateInput - Remove the internal widget-types and standalone view routes Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Remko <remko@conduction.nl>
…and relations Such a view lists no rows or columns, and creating rows or columns in it or importing into it is refused as not found. The views without a table of a deleted user are deleted with the user. Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Remko <remko@conduction.nl>
- Add the views and tables a menu opens as nodes of the application, so the people it is shared with can open its pages - Validate menu items in a MenuItemInput value object: http(s) links only, integer ids, unique technical names per menu - Report a menu item whose target was deleted as broken - Make technical names unique per owner and link the applications of others by id - Load the menus of all applications in one query - Answer invalid menu items and technical names with 400, and update an application atomically - Export and import data widgets in their current target shape, create grid views before resolving nodes - Declare tableId nullable and name the grid shapes in the API - Show broken menu entries, say underscores in the technical name hints, keep pages out of the resource overview, use core heading sizes and drop the custom table styling - Check the resources of an application in the application itself in the e2e tests Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Remko <remko@conduction.nl>
…ents - Move and resize widgets from the widget menu, kept inside the grid - Pick colors with NcColorPicker - Show the labels the server translated instead of translating them again - Align header text to start and end, so it follows the reading direction - Use core heading sizes and keep the heading order Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Remko <remko@conduction.nl>
Table views are listed under their tables and pages are reached through their application. The first-interaction listeners are registered and removed in the App lifecycle. The grid e2e test uses a unique technical name per run and covers a move from the widget menu. Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Remko <remko@conduction.nl>
remko48
force-pushed
the
refactor/upstream-shape
branch
from
October 12, 2026 07:37
2476620 to
2bd9ae7
Compare
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.
This is a test for how stacked pr's work.
This is pr 1 of 3