@tristan2828/ui-foundation 4.2.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/conventions/docs/add-an-entity.md +33 -1
- package/conventions/docs/design-language.md +10 -6
- package/conventions/docs/entity-plan-template.md +10 -0
- package/dist/components/app/prose-look.d.ts +10 -5
- package/dist/components/app/prose-look.js +10 -5
- package/package.json +1 -1
- package/styles/index.css +1 -0
- package/styles/theme.css +6 -2
|
@@ -437,7 +437,8 @@ is a filled-in example while the Widgets demo is still here).
|
|
|
437
437
|
each value as its cell shows it), plus the sparse record showing its
|
|
438
438
|
"not set" labels and no blank value. Its flows: the table's title link
|
|
439
439
|
opens it, saving and cancelling the form return to it, creating opens
|
|
440
|
-
the new record's view, and Delete confirms then lands on the list
|
|
440
|
+
the new record's view, and Delete confirms then lands on the list
|
|
441
|
+
(a soft-deleted entity's toast also has an Undo that restores it).
|
|
441
442
|
Each quick action gets its own tests (copy `quick action: …` in
|
|
442
443
|
`e2e/widget-view.spec.ts`): the change shows and the PATCH carries it
|
|
443
444
|
(a list item sends the whole list), a server-side effect shows once the
|
|
@@ -518,3 +519,34 @@ is a filled-in example while the Widgets demo is still here).
|
|
|
518
519
|
capability they don't have (a new field-type widget, say), don't copy
|
|
519
520
|
one into `src/` to change it: raise it for the foundation (AGENTS.md
|
|
520
521
|
"Changing the Foundation") and build the screen once the release ships.
|
|
522
|
+
|
|
523
|
+
## Deleting
|
|
524
|
+
|
|
525
|
+
The plan's "Deleting" line says which of three. The Widget reference is the
|
|
526
|
+
soft one; copy it.
|
|
527
|
+
|
|
528
|
+
- **Retired by a status:** no delete endpoint, screen or action. The
|
|
529
|
+
status field and its filter do the job.
|
|
530
|
+
- **Soft-deleted:**
|
|
531
|
+
- *Model and migration:* a nullable `deleted_at timestamptz`, commented
|
|
532
|
+
in the migration (null means live; set by DELETE, cleared by restore).
|
|
533
|
+
It is never on the wire. `backend/app/models.py`,
|
|
534
|
+
`migrations/versions/0012_widget_soft_delete.py`.
|
|
535
|
+
- *Router:* `DELETE /<entity>/{id}` sets `deleted_at`; `POST
|
|
536
|
+
/<entity>/{id}/restore` clears it and returns the record (404 unless
|
|
537
|
+
it is deleted); the list leaves deleted records out unless asked
|
|
538
|
+
(`deleted=true` lists only them); get and update of a deleted record
|
|
539
|
+
are a 404. `backend/app/routers/widgets.py` (`_get_or_404`'s
|
|
540
|
+
`deleted` flag).
|
|
541
|
+
- *Contract, gateway, mocks:* `restore<Entity>` in `openapi.yaml`, the
|
|
542
|
+
gateway's `restore<Entity>`, a `useRestore<Entity>Mutation`, and mocks
|
|
543
|
+
that move the record aside (`deletedWidgets` in `src/mocks/data.ts`).
|
|
544
|
+
- *Screens:* the delete dialog says it can be undone, and the success
|
|
545
|
+
toast carries an Undo that restores (`delete-widget-action.tsx`).
|
|
546
|
+
- *MCP:* give the entity's `McpEntity` `delete` and `restore` (and add
|
|
547
|
+
`deleted` to its filters). Chats then get `delete(url)`, marked
|
|
548
|
+
destructive so the chat asks before each call, and `restore(url)`.
|
|
549
|
+
Both are logged with the whole record.
|
|
550
|
+
- **Hard-deleted:** `DELETE` removes the row. Give the entity no `delete`
|
|
551
|
+
in `mcp_entities.py`: a chat that read planted instructions would
|
|
552
|
+
destroy data that only a backup returns.
|
|
@@ -208,14 +208,18 @@ outline (that stays `type-page-title` and `type-section-title`):
|
|
|
208
208
|
|
|
209
209
|
| Class | For | Size / weight |
|
|
210
210
|
|---|---|---|
|
|
211
|
-
| `type-heading-1` | `#` in a note, with a
|
|
212
|
-
| `type-heading-2` |
|
|
213
|
-
| `type-heading-3` |
|
|
211
|
+
| `type-heading-1` | `#` in a note, with a 5px bar in `--heading-bar` | 26px / 700 |
|
|
212
|
+
| `type-heading-2` | `##`, with a 3px bar in `--heading-bar` | 20px / 600 |
|
|
213
|
+
| `type-heading-3` | `###`, with a 2px bar in `--rule` | 17px / 600 |
|
|
214
214
|
|
|
215
|
-
The
|
|
215
|
+
The bar runs down the heading's left edge and thins as the level drops,
|
|
216
|
+
so the level reads at a glance as well as from the size.
|
|
217
|
+
|
|
218
|
+
The same text has three colour tokens of its own: `--link` (`text-link`), a
|
|
216
219
|
link's colour, blue as well as underlined (5.59:1 on white, 8.90:1 on the
|
|
217
|
-
dark page)
|
|
218
|
-
|
|
220
|
+
dark page); `--heading-bar` (`border-heading-bar`), the same blue for the
|
|
221
|
+
bar beside `#` and `##`; and `--rule` (`bg-rule`), the 2px line a `---`
|
|
222
|
+
divider draws, stronger than `--border`'s hairline. A callout (`> [!NOTE]`) is a 10% wash
|
|
219
223
|
of its kind's tone behind ordinary text, with the icon in the tone's text
|
|
220
224
|
shade: note `info`, tip `success`, important category slot 2, warning
|
|
221
225
|
`warning`, caution `destructive`. The package draws all of it; an app
|
|
@@ -187,6 +187,16 @@ except the quick actions, if any.
|
|
|
187
187
|
rows").
|
|
188
188
|
- Ownership: **shared** (every signed-in user sees every record) or
|
|
189
189
|
**per-user** (each user sees only their own).
|
|
190
|
+
- Deleting: how a record goes away, one of
|
|
191
|
+
- **retired by a status**: no delete; a status (Archived, Finished)
|
|
192
|
+
takes it out of the way, and a filter brings it back;
|
|
193
|
+
- **soft-deleted** (the default for a record someone typed): delete
|
|
194
|
+
marks it (`deleted_at`, null means live) and it comes back with
|
|
195
|
+
restore. The view's delete offers Undo, lists leave deleted records
|
|
196
|
+
out, and AI chats (MCP) can delete and restore it;
|
|
197
|
+
- **hard-deleted**: gone for good, back only from a backup. AI chats get
|
|
198
|
+
no delete for it. Say why it must be.
|
|
199
|
+
(`docs/foundation/add-an-entity.md` "Deleting".)
|
|
190
200
|
|
|
191
201
|
## Open questions
|
|
192
202
|
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { type LucideIcon } from 'lucide-react';
|
|
2
2
|
import type { CalloutKind } from '../../lib/markdown-callout.js';
|
|
3
3
|
/**
|
|
4
|
-
* Classes for written text's own elements. No colours on the
|
|
4
|
+
* Classes for written text's own elements. No colours on the heading text:
|
|
5
5
|
* <Markdown> adds the text colour, and the editor's read-only text is
|
|
6
6
|
* muted as a whole.
|
|
7
|
+
*
|
|
8
|
+
* Each of the first three heading levels has a bar down its left edge that
|
|
9
|
+
* thins as the level drops: 5px in the heading-bar tone for `#`, 3px for
|
|
10
|
+
* `##`, 2px in the rule tone for `###`. A level reads from the bar
|
|
11
|
+
* as well as from the size.
|
|
7
12
|
*/
|
|
8
13
|
export declare const PROSE: {
|
|
9
|
-
/** `#`: the largest, with
|
|
10
|
-
readonly heading1: "type-heading-1 mt-4 border-
|
|
11
|
-
readonly heading2: "type-heading-2 mt-3 first:mt-0";
|
|
12
|
-
readonly heading3: "type-heading-3 mt-1 first:mt-0";
|
|
14
|
+
/** `#`: the largest, with the widest bar, so it reads as the top of the outline. */
|
|
15
|
+
readonly heading1: "type-heading-1 mt-4 border-l-[5px] border-heading-bar pl-3 first:mt-0";
|
|
16
|
+
readonly heading2: "type-heading-2 mt-3 border-l-[3px] border-heading-bar pl-3 first:mt-0";
|
|
17
|
+
readonly heading3: "type-heading-3 mt-1 border-l-2 border-rule pl-3 first:mt-0";
|
|
13
18
|
/** `####` and deeper: a label, as before. */
|
|
14
19
|
readonly heading4: "type-label mt-1 first:mt-0";
|
|
15
20
|
/** Coloured as well as underlined. */
|
|
@@ -7,15 +7,20 @@
|
|
|
7
7
|
// (what "Callout" inserts).
|
|
8
8
|
import { InfoIcon, LightbulbIcon, MessageSquareWarningIcon, OctagonAlertIcon, TriangleAlertIcon, } from 'lucide-react';
|
|
9
9
|
/**
|
|
10
|
-
* Classes for written text's own elements. No colours on the
|
|
10
|
+
* Classes for written text's own elements. No colours on the heading text:
|
|
11
11
|
* <Markdown> adds the text colour, and the editor's read-only text is
|
|
12
12
|
* muted as a whole.
|
|
13
|
+
*
|
|
14
|
+
* Each of the first three heading levels has a bar down its left edge that
|
|
15
|
+
* thins as the level drops: 5px in the heading-bar tone for `#`, 3px for
|
|
16
|
+
* `##`, 2px in the rule tone for `###`. A level reads from the bar
|
|
17
|
+
* as well as from the size.
|
|
13
18
|
*/
|
|
14
19
|
export const PROSE = {
|
|
15
|
-
/** `#`: the largest, with
|
|
16
|
-
heading1: 'type-heading-1 mt-4 border-
|
|
17
|
-
heading2: 'type-heading-2 mt-3 first:mt-0',
|
|
18
|
-
heading3: 'type-heading-3 mt-1 first:mt-0',
|
|
20
|
+
/** `#`: the largest, with the widest bar, so it reads as the top of the outline. */
|
|
21
|
+
heading1: 'type-heading-1 mt-4 border-l-[5px] border-heading-bar pl-3 first:mt-0',
|
|
22
|
+
heading2: 'type-heading-2 mt-3 border-l-[3px] border-heading-bar pl-3 first:mt-0',
|
|
23
|
+
heading3: 'type-heading-3 mt-1 border-l-2 border-rule pl-3 first:mt-0',
|
|
19
24
|
/** `####` and deeper: a label, as before. */
|
|
20
25
|
heading4: 'type-label mt-1 first:mt-0',
|
|
21
26
|
/** Coloured as well as underlined. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tristan2828/ui-foundation",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "Shared UI foundation for contract-first, database-backed React apps: shadcn primitives, DataTable/EntityForm composites, auth, the gateway error seam, design tokens, lint rules and the AI playbook.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
package/styles/index.css
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
--color-info-text: var(--info-text);
|
|
50
50
|
--color-link: var(--link);
|
|
51
51
|
--color-rule: var(--rule);
|
|
52
|
+
--color-heading-bar: var(--heading-bar);
|
|
52
53
|
--color-category-1: var(--category-1);
|
|
53
54
|
--color-category-2: var(--category-2);
|
|
54
55
|
--color-category-3: var(--category-3);
|
package/styles/theme.css
CHANGED
|
@@ -142,8 +142,8 @@
|
|
|
142
142
|
* Headings inside written text (a record's notes: <Markdown> and the
|
|
143
143
|
* rich-text editor), not the screen's own outline. Three clear steps
|
|
144
144
|
* over the 14px body, roughly Notion's proportions, so `#`, `##` and
|
|
145
|
-
* `###` never look alike.
|
|
146
|
-
*
|
|
145
|
+
* `###` never look alike. Where the roles are used, each also gets a
|
|
146
|
+
* bar down its left edge that thins by level (--heading-bar).
|
|
147
147
|
*/
|
|
148
148
|
--type-heading-1-size: 1.625rem;
|
|
149
149
|
--type-heading-1-leading: 2rem;
|
|
@@ -208,6 +208,9 @@
|
|
|
208
208
|
/* A thematic break (`---`) in written text: a 2px line meant to be
|
|
209
209
|
seen, stronger than --border's hairline (on white: 2.59). */
|
|
210
210
|
--rule: var(--gray-400);
|
|
211
|
+
/* The bar down the left of a `#` or `##` heading in written text, so
|
|
212
|
+
the level shows at a glance (on white: 5.59). `###` takes --rule. */
|
|
213
|
+
--heading-bar: var(--blue-700);
|
|
211
214
|
/* Categorical slots — a glyph tint, never a fill behind text. */
|
|
212
215
|
--category-1: var(--category-1-700);
|
|
213
216
|
--category-2: var(--category-2-700);
|
|
@@ -340,6 +343,7 @@
|
|
|
340
343
|
--info-text: var(--blue-400);
|
|
341
344
|
--link: var(--blue-400); /* on near-black: 8.90 */
|
|
342
345
|
--rule: var(--gray-600); /* on near-black: 2.54 */
|
|
346
|
+
--heading-bar: var(--blue-400); /* on near-black: 8.90 */
|
|
343
347
|
/* Categorical slots — lighter shades to read against the dark page. */
|
|
344
348
|
--category-1: var(--category-1-400);
|
|
345
349
|
--category-2: var(--category-2-400);
|