@nanisoft/prism-llms 0.4.0 → 0.5.1
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/README.md +88 -0
- package/THIRD-PARTY-NOTICES.md +48 -0
- package/dist/data.json +4037 -386
- package/dist/demo-graph.d.ts +21 -12
- package/dist/demo-graph.js +37 -21
- package/dist/extractor.d.ts +24 -17
- package/dist/extractor.js +299 -156
- package/dist/index.d.ts +12 -7
- package/dist/index.js +9 -7
- package/dist/llms-full.txt +8248 -2013
- package/dist/llms.txt +98 -93
- package/dist/markdown.d.ts +69 -23
- package/dist/markdown.js +131 -72
- package/dist/md/blocks/app-shell-01.md +135 -0
- package/dist/md/blocks/auth-form-01.md +135 -0
- package/dist/md/blocks/cta-01.md +88 -0
- package/dist/md/blocks/data-table-01.md +196 -0
- package/dist/md/blocks/feature-grid-01.md +92 -0
- package/dist/md/blocks/hero-01.md +90 -0
- package/dist/md/blocks/instrument-panel-01.md +109 -0
- package/dist/md/blocks/logo-strip-01.md +97 -0
- package/dist/md/blocks/note-grid-01.md +98 -0
- package/dist/md/blocks/page-header-01.md +97 -0
- package/dist/md/blocks/pricing-01.md +91 -0
- package/dist/md/blocks/process-rail-01.md +106 -0
- package/dist/md/blocks/product-grid-01.md +107 -0
- package/dist/md/blocks/settings-panel-01.md +146 -0
- package/dist/md/blocks/site-footer.md +103 -11
- package/dist/md/blocks/site-header.md +107 -11
- package/dist/md/blocks/stack-grid-01.md +108 -0
- package/dist/md/blocks/stats-01.md +89 -0
- package/dist/md/blocks/status-ledger-01.md +121 -0
- package/dist/md/changelogs/prism-llms.md +244 -0
- package/dist/md/changelogs/prism-mcp-server.md +95 -0
- package/dist/md/changelogs/prism-tokens.md +126 -0
- package/dist/md/changelogs/prism-ui.md +467 -0
- package/dist/md/components/accordion.md +108 -0
- package/dist/md/components/alert.md +90 -7
- package/dist/md/components/avatar.md +89 -7
- package/dist/md/components/badge.md +74 -7
- package/dist/md/components/breadcrumb.md +84 -7
- package/dist/md/components/button.md +90 -7
- package/dist/md/components/card.md +90 -7
- package/dist/md/components/checkbox.md +101 -7
- package/dist/md/components/cta-link.md +98 -0
- package/dist/md/components/diagram.md +124 -0
- package/dist/md/components/dialog.md +128 -0
- package/dist/md/components/dropdown-menu.md +125 -0
- package/dist/md/components/fact-list.md +85 -0
- package/dist/md/components/field.md +116 -0
- package/dist/md/components/input.md +86 -7
- package/dist/md/components/kbd.md +77 -0
- package/dist/md/components/pagination.md +95 -7
- package/dist/md/components/popover.md +95 -7
- package/dist/md/components/product-mark.md +114 -0
- package/dist/md/components/product-switcher.md +88 -0
- package/dist/md/components/progress.md +88 -7
- package/dist/md/components/prose.md +91 -0
- package/dist/md/components/radio-group.md +111 -0
- package/dist/md/components/section.md +91 -0
- package/dist/md/components/select.md +143 -7
- package/dist/md/components/separator.md +77 -0
- package/dist/md/components/skeleton.md +71 -7
- package/dist/md/components/slider.md +90 -7
- package/dist/md/components/switch.md +104 -7
- package/dist/md/components/table.md +104 -7
- package/dist/md/components/tabs.md +105 -7
- package/dist/md/components/textarea.md +101 -0
- package/dist/md/components/tooltip.md +95 -7
- package/dist/md/components/typography.md +82 -7
- package/dist/md/content/about-content.md +36 -0
- package/dist/md/content/audience.md +43 -0
- package/dist/md/content/glossary.md +69 -0
- package/dist/md/content/product-vocabulary.md +53 -0
- package/dist/md/content/voice.md +52 -0
- package/dist/md/content/writing-mechanics.md +58 -0
- package/dist/md/foundation/colors.md +10 -0
- package/dist/md/foundation/iconography.md +14 -0
- package/dist/md/foundation/motion.md +10 -0
- package/dist/md/foundation/radii.md +9 -0
- package/dist/md/foundation/shadows.md +9 -0
- package/dist/md/foundation/spacings.md +8 -0
- package/dist/md/foundation/typography.md +9 -0
- package/dist/md/foundation/variables.md +9 -0
- package/dist/md/overview/architecture.md +76 -0
- package/dist/md/overview/brand.md +41 -0
- package/dist/md/overview/composition.md +77 -0
- package/dist/md/overview/quickstart.md +97 -0
- package/dist/md/overview/theming.md +89 -0
- package/dist/md/overview/using-llms.md +83 -0
- package/dist/md/pages/auth-page.md +149 -0
- package/dist/md/pages/blog-post-page.md +136 -0
- package/dist/md/pages/dashboard-page.md +241 -0
- package/dist/md/pages/docs-shell.md +210 -15
- package/dist/md/pages/marketing-page.md +199 -0
- package/dist/md/pages/not-found-page.md +105 -0
- package/dist/md/pages/settings-page.md +206 -0
- package/dist/prism-skill.md +50 -0
- package/dist/store.d.ts +203 -0
- package/dist/store.js +317 -0
- package/package.json +29 -26
- package/dist/demo-graph.d.ts.map +0 -1
- package/dist/demo-graph.js.map +0 -1
- package/dist/extractor.d.ts.map +0 -1
- package/dist/extractor.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/markdown.d.ts.map +0 -1
- package/dist/markdown.js.map +0 -1
- package/dist/md/blocks/component-demo.md +0 -19
- package/dist/md/blocks/page-header.md +0 -19
- package/dist/md/components/affix.md +0 -14
- package/dist/md/components/anchor.md +0 -14
- package/dist/md/components/auto-complete.md +0 -14
- package/dist/md/components/back-top.md +0 -14
- package/dist/md/components/border-beam.md +0 -14
- package/dist/md/components/calendar.md +0 -14
- package/dist/md/components/carousel.md +0 -14
- package/dist/md/components/cascader.md +0 -14
- package/dist/md/components/col.md +0 -14
- package/dist/md/components/collapse.md +0 -14
- package/dist/md/components/color-picker.md +0 -14
- package/dist/md/components/date-picker.md +0 -14
- package/dist/md/components/descriptions.md +0 -14
- package/dist/md/components/display-title.md +0 -20
- package/dist/md/components/divider.md +0 -14
- package/dist/md/components/drawer.md +0 -14
- package/dist/md/components/dropdown.md +0 -14
- package/dist/md/components/empty.md +0 -14
- package/dist/md/components/flex.md +0 -14
- package/dist/md/components/float-button.md +0 -14
- package/dist/md/components/form.md +0 -14
- package/dist/md/components/grid.md +0 -14
- package/dist/md/components/image.md +0 -14
- package/dist/md/components/input-number.md +0 -14
- package/dist/md/components/layout.md +0 -14
- package/dist/md/components/list.md +0 -14
- package/dist/md/components/listy.md +0 -14
- package/dist/md/components/masonry.md +0 -14
- package/dist/md/components/mentions.md +0 -14
- package/dist/md/components/menu.md +0 -14
- package/dist/md/components/modal.md +0 -14
- package/dist/md/components/popconfirm.md +0 -14
- package/dist/md/components/qrcode.md +0 -14
- package/dist/md/components/radio.md +0 -14
- package/dist/md/components/rate.md +0 -14
- package/dist/md/components/result.md +0 -14
- package/dist/md/components/row.md +0 -14
- package/dist/md/components/segmented.md +0 -14
- package/dist/md/components/space.md +0 -14
- package/dist/md/components/spin.md +0 -14
- package/dist/md/components/splitter.md +0 -14
- package/dist/md/components/statistic.md +0 -14
- package/dist/md/components/steps.md +0 -14
- package/dist/md/components/tag.md +0 -14
- package/dist/md/components/time-picker.md +0 -14
- package/dist/md/components/timeline.md +0 -14
- package/dist/md/components/tour.md +0 -14
- package/dist/md/components/transfer.md +0 -14
- package/dist/md/components/tree-select.md +0 -14
- package/dist/md/components/tree.md +0 -14
- package/dist/md/components/upload.md +0 -14
- package/dist/md/components/watermark.md +0 -14
- package/dist/md/docs/brand.md +0 -41
- package/dist/md/pages/blog-layout.md +0 -19
- package/dist/md/theme/blue-dark.md +0 -127
- package/dist/md/theme/blue-light.md +0 -129
- package/dist/md/theme/green-dark.md +0 -127
- package/dist/md/theme/green-light.md +0 -129
- package/dist/md/theme/lavender-dark.md +0 -127
- package/dist/md/theme/lavender-light.md +0 -129
- package/dist/md/theme/peach-dark.md +0 -127
- package/dist/md/theme/peach-light.md +0 -129
- package/dist/md/theme/rose-dark.md +0 -127
- package/dist/md/theme/rose-light.md +0 -129
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Theming
|
|
2
|
+
|
|
3
|
+
Packs and modes are two independent axes, delivered as markup attributes with no override path.
|
|
4
|
+
|
|
5
|
+
Prism themes are complete atmospheres, not a primary-colour swap. A pack changes
|
|
6
|
+
the page ground, the surfaces, the hairlines, the text bases and the focus
|
|
7
|
+
treatment together.
|
|
8
|
+
|
|
9
|
+
## Two axes
|
|
10
|
+
|
|
11
|
+
Theming is two independent axes:
|
|
12
|
+
|
|
13
|
+
- **Pack** selects the palette: `default`, `blush`, `mint`, `lavender`, `sky`
|
|
14
|
+
or `peach`.
|
|
15
|
+
- **Mode** selects the appearance: `light` or `dark`.
|
|
16
|
+
|
|
17
|
+
A **theme** is one pack in one mode, such as Blush dark. It is a fully resolved
|
|
18
|
+
token set, not an object and not an API. There is no theme factory.
|
|
19
|
+
|
|
20
|
+
| Pack | Character |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| `default` | The neutral base. `primary` reads as ink, not colour. |
|
|
23
|
+
| `blush` | Soft rose, with the softest radius of the set. |
|
|
24
|
+
| `mint` | Soft green, calm and low-energy. |
|
|
25
|
+
| `lavender` | Soft violet, the most saturated brand ramp. |
|
|
26
|
+
| `sky` | Soft blue, the crispest hue and the smallest radius. |
|
|
27
|
+
| `peach` | Soft orange, the warmest hue and the roundest corners. |
|
|
28
|
+
|
|
29
|
+
## Declarative selection
|
|
30
|
+
|
|
31
|
+
Put `data-pack` on any element to make it, and its subtree, a theme boundary.
|
|
32
|
+
Add the `dark` class for dark mode. The neutral base pack is the absence of
|
|
33
|
+
`data-pack`, so it is expressed by omitting the attribute.
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
<!-- the default pack, light -->
|
|
37
|
+
<html lang="en">
|
|
38
|
+
|
|
39
|
+
<!-- Lavender, dark -->
|
|
40
|
+
<html lang="en" data-pack="lavender" class="dark">
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This form is server-rendered and needs no JavaScript. It is also the cheapest
|
|
44
|
+
way to show several packs at once: any subtree can carry its own `data-pack`.
|
|
45
|
+
See the [themes page](/foundation/themes) for all packs side by side.
|
|
46
|
+
|
|
47
|
+
## Programmatic selection
|
|
48
|
+
|
|
49
|
+
When a user needs to switch and persist a choice, mount the optional provider
|
|
50
|
+
and read the hook.
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { PrismProvider, usePrismTheme } from '@nanisoft/prism-ui/provider'
|
|
54
|
+
|
|
55
|
+
function PackPicker() {
|
|
56
|
+
const { pack, setPack, mode, toggleMode } = usePrismTheme()
|
|
57
|
+
return (
|
|
58
|
+
<button onClick={() => setPack('peach')}>
|
|
59
|
+
{pack} / {mode}
|
|
60
|
+
</button>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function App({ children }: { children: React.ReactNode }) {
|
|
65
|
+
return <PrismProvider>{children}</PrismProvider>
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The provider's only outputs are the two document-element attributes and the
|
|
70
|
+
context. It has no override parameter, no token object and no merge.
|
|
71
|
+
|
|
72
|
+
## Flash-free first paint
|
|
73
|
+
|
|
74
|
+
Under a static export there is no server and no middleware, so a blocking inline
|
|
75
|
+
script in `<head>` is what honours a stored choice before first paint. The
|
|
76
|
+
provider's package includes `PrismThemeScript` for exactly this, and the root
|
|
77
|
+
element carries `suppressHydrationWarning` because the script may set the
|
|
78
|
+
attributes before React hydrates.
|
|
79
|
+
|
|
80
|
+
## There is no consumer theming
|
|
81
|
+
|
|
82
|
+
Prism does not expose `createPrismTheme`, a per-key merge or semantic overrides.
|
|
83
|
+
The supported path is to select a pack and a mode. A product that needs a
|
|
84
|
+
different palette, a new semantic token or a changed component should request it
|
|
85
|
+
upstream, so every consumer benefits from the change and no project carries a
|
|
86
|
+
private fork.
|
|
87
|
+
|
|
88
|
+
`className` is for layout composition only. Changing a Prism-owned visual
|
|
89
|
+
property through a class is not supported.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Using LLMs
|
|
2
|
+
|
|
3
|
+
Using LLMs against Prism: the corpus, the Markdown mirror and the read-only MCP endpoint, so an agent reads the same source the human documentation does.
|
|
4
|
+
|
|
5
|
+
Prism treats agents as first-class consumers. The site, the package declarations,
|
|
6
|
+
the demo sources, the Markdown mirror and the MCP endpoint all project the same
|
|
7
|
+
source, so an agent and a human cannot be told different things.
|
|
8
|
+
|
|
9
|
+
## Fast path
|
|
10
|
+
|
|
11
|
+
1. Read `llms.txt` for the catalogue and the page index.
|
|
12
|
+
2. Use `search_docs` when the item name is unknown.
|
|
13
|
+
3. Use `get_item_doc` for the assembled item documentation and its rules.
|
|
14
|
+
4. Use `get_item_props` for the public TypeScript surface.
|
|
15
|
+
5. Use `get_item_source` for the verbatim demo and the public import line.
|
|
16
|
+
6. Use `get_theme_doc` before choosing a pack or reading a token scale.
|
|
17
|
+
7. Use `get_changelog` before upgrading a dependency on Prism.
|
|
18
|
+
|
|
19
|
+
## The MCP endpoint
|
|
20
|
+
|
|
21
|
+
The endpoint is public and read-only. It returns Markdown and never writes to a
|
|
22
|
+
consumer project.
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"prism": {
|
|
28
|
+
"type": "http",
|
|
29
|
+
"url": "https://prism.nanisoft.com/mcp"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## The nine tools
|
|
36
|
+
|
|
37
|
+
| Tool | Use |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| `list_items` | Browse the catalogue, filtered by `kind` and `category` |
|
|
40
|
+
| `get_item_doc` | Read the assembled item documentation |
|
|
41
|
+
| `get_item_props` | Read the `## Props` subset of an item |
|
|
42
|
+
| `get_item_source` | Copy the verbatim demo plus the import line |
|
|
43
|
+
| `get_theme_doc` | Read the token contract and the bound scales |
|
|
44
|
+
| `list_pages` | Browse the non-item pages: Overview, Foundation, Content, Changelogs |
|
|
45
|
+
| `get_page` | Read one non-item page as Markdown |
|
|
46
|
+
| `search_docs` | Search the complete corpus and return references |
|
|
47
|
+
| `get_changelog` | Read a published package's changelog, optionally one version |
|
|
48
|
+
|
|
49
|
+
`list_items` is the one tool that exists because a kind exists; the other seven
|
|
50
|
+
exist because a retrieval pattern exists, and `get_changelog` exists because a
|
|
51
|
+
published breaking change has to be findable by an agent. Search returns
|
|
52
|
+
references, not page bodies, so an agent follows a hit with `get_item_doc` or
|
|
53
|
+
`get_page`.
|
|
54
|
+
|
|
55
|
+
`get_changelog` takes a package and an optional version, and returns that
|
|
56
|
+
package's own `CHANGELOG.md` as it shipped rather than a summary of it. The
|
|
57
|
+
Changelogs section of this site renders the same bytes, so an agent and a reader
|
|
58
|
+
cannot be told different things about what changed.
|
|
59
|
+
|
|
60
|
+
## Import contract for generated code
|
|
61
|
+
|
|
62
|
+
Generated applications import from `@nanisoft/prism-ui`. The internal Base UI
|
|
63
|
+
dependency is not a consumer dependency and must not appear in application
|
|
64
|
+
source.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { Button } from '@nanisoft/prism-ui/components/button'
|
|
68
|
+
import { Hero01 } from '@nanisoft/prism-ui/blocks/hero-01'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The internal shadcn registry never appears in generated code. It is an integrity
|
|
72
|
+
artifact inside the package, not an install lane.
|
|
73
|
+
|
|
74
|
+
## Truth rules
|
|
75
|
+
|
|
76
|
+
- The corpus is versioned with `prism-ui`. Compare the version echoed by
|
|
77
|
+
`list_items` with the installed package when behaviour looks inconsistent.
|
|
78
|
+
- Demos are single-source files: the same text is rendered live, copied by the
|
|
79
|
+
site, and returned by `get_item_source`.
|
|
80
|
+
- A generated item has exactly one demo. When an item has no demo, the tool says
|
|
81
|
+
so rather than fabricating one.
|
|
82
|
+
- Product claims belong to the product team. The corpus documents implementation
|
|
83
|
+
and usage, never invented customer evidence.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# AuthPage
|
|
2
|
+
|
|
3
|
+
A full authentication screen composing a sign-in form with an optional supporting card.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { AuthPage } from '@nanisoft/prism-ui/pages/auth-page'
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
AuthPage is a complete authentication screen. It composes a sign-in form with an
|
|
12
|
+
optional supporting card, and it owns the centering, the breakpoint and the
|
|
13
|
+
fallback column only. The form carries the credentials, the error slot and the
|
|
14
|
+
submit; the card carries product context beside it.
|
|
15
|
+
|
|
16
|
+
The page composes these parts:
|
|
17
|
+
|
|
18
|
+
| Region | Piece | Role |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| Form | AuthForm01 | the title, the credential fields, the remember row and the submit |
|
|
21
|
+
| Support | Card | an optional surface beside the form for context or reassurance |
|
|
22
|
+
| Footer | footer | a line under the form, such as a legal note or a link |
|
|
23
|
+
|
|
24
|
+
Every field, label, handler, error and supporting string is a prop. The page
|
|
25
|
+
fetches nothing and imports no router, so the consumer owns the request and the
|
|
26
|
+
links. When no `aside` is passed the layout is one centered column; with one it
|
|
27
|
+
is a two-column split from the large breakpoint up, and the card falls below the
|
|
28
|
+
form on a narrow screen.
|
|
29
|
+
|
|
30
|
+
## Guidelines
|
|
31
|
+
|
|
32
|
+
### When to use
|
|
33
|
+
|
|
34
|
+
Use AuthPage for a sign-in screen whose only task is to collect one email and one
|
|
35
|
+
secret. It works when the screen is the first thing a returning reader sees and
|
|
36
|
+
the supporting card answers a question they are likely to have at that moment.
|
|
37
|
+
|
|
38
|
+
### When not to use
|
|
39
|
+
|
|
40
|
+
Do not use it for registration, password reset or profile editing. Those have
|
|
41
|
+
different field sets, and a sign-in card with the wrong fields is a dead end;
|
|
42
|
+
compose a form from the same parts instead.
|
|
43
|
+
|
|
44
|
+
Do not use it when the screen must carry more than a few words of supporting
|
|
45
|
+
content. The card is context beside a form, not a second page; a longer story
|
|
46
|
+
belongs on a marketing route the reader saw before signing in.
|
|
47
|
+
|
|
48
|
+
### Best practices
|
|
49
|
+
|
|
50
|
+
- Set `type`, `autoComplete` and `required` on every field. Browsers use them to
|
|
51
|
+
fill and validate, so the typing is the whole reason the field is typed.
|
|
52
|
+
- Keep the error to one sentence and mark the failed fields. AuthForm01 sets
|
|
53
|
+
`aria-invalid` when an error is present, so the form and the message agree.
|
|
54
|
+
- Disable the submit control while a request is in flight and pass `pendingLabel`,
|
|
55
|
+
so the reader sees that work is happening.
|
|
56
|
+
- Omit `aside` when there is nothing true to say. An empty card is worse than a
|
|
57
|
+
single centered form.
|
|
58
|
+
- Keep the footer to one line, such as a legal note or a route to create an
|
|
59
|
+
account, and pass the link in as a node so the consumer owns it.
|
|
60
|
+
|
|
61
|
+
### Content guidelines
|
|
62
|
+
|
|
63
|
+
- The title is a short verb phrase in sentence case: "Sign in", "Continue".
|
|
64
|
+
- The description states the outcome, not a policy.
|
|
65
|
+
- An error says what to do next: "Enter your email and password to continue."
|
|
66
|
+
|
|
67
|
+
**The auth page, with a controlled form and a supporting card.**
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
'use client'
|
|
71
|
+
|
|
72
|
+
import { useState } from 'react'
|
|
73
|
+
|
|
74
|
+
import { AuthPage } from '@nanisoft/prism-ui/pages/auth-page'
|
|
75
|
+
|
|
76
|
+
/** The auth page, with a controlled form and a supporting card. */
|
|
77
|
+
export default function AuthPageDemo() {
|
|
78
|
+
const [email, setEmail] = useState('')
|
|
79
|
+
const [password, setPassword] = useState('')
|
|
80
|
+
const [error, setError] = useState<string>()
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<AuthPage
|
|
84
|
+
form={{
|
|
85
|
+
title: 'Sign in',
|
|
86
|
+
description: 'Welcome back. Enter your details to continue.',
|
|
87
|
+
error,
|
|
88
|
+
submitLabel: 'Sign in',
|
|
89
|
+
fields: [
|
|
90
|
+
{
|
|
91
|
+
id: 'auth-page-email',
|
|
92
|
+
label: 'Email',
|
|
93
|
+
type: 'email',
|
|
94
|
+
autoComplete: 'email',
|
|
95
|
+
placeholder: 'you@example.com',
|
|
96
|
+
value: email,
|
|
97
|
+
onChange: setEmail,
|
|
98
|
+
required: true,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'auth-page-password',
|
|
102
|
+
label: 'Password',
|
|
103
|
+
type: 'password',
|
|
104
|
+
autoComplete: 'current-password',
|
|
105
|
+
value: password,
|
|
106
|
+
onChange: setPassword,
|
|
107
|
+
required: true,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
remember: { id: 'auth-page-remember', label: 'Remember me for 30 days' },
|
|
111
|
+
onSubmit: (event) => {
|
|
112
|
+
event.preventDefault()
|
|
113
|
+
setError(
|
|
114
|
+
email && password ? undefined : 'Enter your email and password to continue.',
|
|
115
|
+
)
|
|
116
|
+
},
|
|
117
|
+
}}
|
|
118
|
+
aside={{
|
|
119
|
+
title: 'Everything in one workspace',
|
|
120
|
+
description: 'Projects, deployments and usage, without a second tool.',
|
|
121
|
+
children: (
|
|
122
|
+
<ul className="text-muted-foreground flex flex-col gap-2 text-sm">
|
|
123
|
+
<li>One place for every team</li>
|
|
124
|
+
<li>Usage that updates as you deploy</li>
|
|
125
|
+
<li>Roles from viewer to admin</li>
|
|
126
|
+
</ul>
|
|
127
|
+
),
|
|
128
|
+
}}
|
|
129
|
+
footer={<p>No account yet? Create one.</p>}
|
|
130
|
+
/>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Props
|
|
136
|
+
|
|
137
|
+
**`form`** `ComponentProps<typeof AuthForm01>` · required · default: — — The credential form.
|
|
138
|
+
**`aside`** `AuthPageAside` · optional · default: — — A supporting card beside the form. Omit it for a single centered column.
|
|
139
|
+
**`footer`** `ReactNode` · optional · default: — — A node under the form, such as a legal line or a link to another screen.
|
|
140
|
+
|
|
141
|
+
## Composition
|
|
142
|
+
|
|
143
|
+
**`exports`** `AuthPage` — the public runtime names this Page promises
|
|
144
|
+
**`source`** `packages/ui/src/pages/auth-page/index.tsx` — the file this Page is defined in
|
|
145
|
+
**`dependencies`** `AuthForm01, Card` — the public Prism exports this item composes
|
|
146
|
+
|
|
147
|
+
## Blocks
|
|
148
|
+
|
|
149
|
+
- [AuthForm01](https://prism.nanisoft.com/blocks/auth-form-01.md)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# BlogPostPage
|
|
2
|
+
|
|
3
|
+
A complete blog post screen: title, standfirst, byline with both the display and the machine date, the body at the measure and the trail to the neighbouring posts.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { BlogPostPage } from '@nanisoft/prism-ui/pages/blog-post-page'
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
BlogPostPage is a complete blog post screen: the title, the standfirst, the
|
|
12
|
+
byline, the body at the reading measure, and the trail to the neighbouring posts.
|
|
13
|
+
|
|
14
|
+
All four NaniSoft sites publish a blog, each with four posts and the same
|
|
15
|
+
frontmatter: a title, a description, a date and a list of tags. Each one then
|
|
16
|
+
rendered that frontmatter in its own component, with its own wrapper, its own
|
|
17
|
+
measure and its own treatment of the date, and each of them wrote the trail to the
|
|
18
|
+
neighbouring posts separately or not at all.
|
|
19
|
+
|
|
20
|
+
**The date is two props.** `date` is the words a reader sees and `dateTime` is the
|
|
21
|
+
machine value on the `time` element, and they are kept apart because parsing a
|
|
22
|
+
formatted date back into an ISO one is a guess about a locale. A wrong date in a
|
|
23
|
+
`datetime` attribute is a wrong date in a feed reader and in a search result, and
|
|
24
|
+
neither reader can see the string that was wrong.
|
|
25
|
+
|
|
26
|
+
## Guidelines
|
|
27
|
+
|
|
28
|
+
### When to use
|
|
29
|
+
|
|
30
|
+
Use BlogPostPage as the body of the route that renders one post, handed the
|
|
31
|
+
frontmatter your content pipeline produced.
|
|
32
|
+
|
|
33
|
+
Use it for every post. A blog whose posts each have their own wrapper is a blog
|
|
34
|
+
whose posts drift apart within a year.
|
|
35
|
+
|
|
36
|
+
### When not to use
|
|
37
|
+
|
|
38
|
+
Do not use it for a documentation page. Documentation is a set of short pages
|
|
39
|
+
about one thing; a post is a dated argument about one thing, and a trail to the
|
|
40
|
+
post before it is not a trail through a manual.
|
|
41
|
+
|
|
42
|
+
Do not use it for a landing page. `MarketingPage` is that composition.
|
|
43
|
+
|
|
44
|
+
### Best practices
|
|
45
|
+
|
|
46
|
+
- Pass `dateTime` in `YYYY-MM-DD`. It is the value a feed reader and a search
|
|
47
|
+
engine order by, and it is the one that is wrong silently.
|
|
48
|
+
- The body is `children`, held by `Prose`. Your pipeline owns the document; this
|
|
49
|
+
owns the measure and the rhythm. Do not parse it into an array of paragraphs to
|
|
50
|
+
hand over - that is a second representation of something your pipeline already
|
|
51
|
+
has.
|
|
52
|
+
- Render only the halves of the trail you have. A previous link on the oldest post
|
|
53
|
+
is a link to the first post a reader has already read, and a next link on the
|
|
54
|
+
newest is a promise the site cannot keep.
|
|
55
|
+
- `trailLabel` and `trailLabels` are required alongside a neighbour. They are words
|
|
56
|
+
a reader reads and hears, and a Page that ships no copy ships no reader-facing
|
|
57
|
+
copy either: a site that files posts newest-last says the opposite pair.
|
|
58
|
+
- Use `footer` for what comes after the trail: related posts, a newsletter form, a
|
|
59
|
+
closing call to action. It is a slot because every one of those is a Block, and a
|
|
60
|
+
Page that took a Block per prop would be a Page whose interface lists the whole
|
|
61
|
+
catalogue.
|
|
62
|
+
- Pass `indexLink` with the words and the route your archive lives at. The page
|
|
63
|
+
ships neither.
|
|
64
|
+
|
|
65
|
+
### Content guidelines
|
|
66
|
+
|
|
67
|
+
- Write the standfirst as one or two sentences a reader can stop at. It is what an
|
|
68
|
+
index, a feed and a search result show.
|
|
69
|
+
- Keep tags to nouns. A tag is a category a reader browses, not a sentence.
|
|
70
|
+
- Keep the title to one line at the largest viewport unless the post is the only
|
|
71
|
+
thing on the page.
|
|
72
|
+
|
|
73
|
+
**A post: the title, the standfirst, both dates, tags, the body and the trail.**
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { BlogPostPage } from '@nanisoft/prism-ui/pages/blog-post-page'
|
|
77
|
+
|
|
78
|
+
/** A post: the title, the standfirst, both dates, tags, the body and the trail. */
|
|
79
|
+
export default function BlogPostPageDemo() {
|
|
80
|
+
return (
|
|
81
|
+
<BlogPostPage
|
|
82
|
+
title="Every minute, the whole chain"
|
|
83
|
+
description="What live full-chain capture at index scale actually takes: the spike, then production, then Kubernetes."
|
|
84
|
+
date="21 September 2026"
|
|
85
|
+
dateTime="2026-09-21"
|
|
86
|
+
author="NaniSoft"
|
|
87
|
+
readingTime="6 min read"
|
|
88
|
+
tags={[{ label: 'market-data' }, { label: 'collector' }]}
|
|
89
|
+
trailLabels={{ previous: 'Previous', next: 'Next' }}
|
|
90
|
+
trailLabel="More posts"
|
|
91
|
+
previous={{ title: 'One feed to research them all', href: '#one-feed' }}
|
|
92
|
+
next={{ title: 'Multi-agent research, mapped', href: '#multi-agent' }}
|
|
93
|
+
indexLink={{ title: 'All posts', href: '#all' }}
|
|
94
|
+
>
|
|
95
|
+
<p>
|
|
96
|
+
The claim sounds like a feature. It is a measurement with a timestamp on
|
|
97
|
+
it, and it started as a suspicion that it was impossible.
|
|
98
|
+
</p>
|
|
99
|
+
<h2>The assumption that was backwards</h2>
|
|
100
|
+
<p>
|
|
101
|
+
Going in, full-chain-per-minute looked like a streaming problem. It was a
|
|
102
|
+
counting problem, and the count had to fit in a minute.
|
|
103
|
+
</p>
|
|
104
|
+
<ul>
|
|
105
|
+
<li>Every strike, call and put.</li>
|
|
106
|
+
<li>Open interest, implied volatility and the Greeks.</li>
|
|
107
|
+
<li>Index futures, index spot and the volatility index.</li>
|
|
108
|
+
</ul>
|
|
109
|
+
</BlogPostPage>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Props
|
|
115
|
+
|
|
116
|
+
**`title`** `string` · required · default: — — The post's title. It is the page's `h1`, so the page has exactly one and it is the post's name.
|
|
117
|
+
**`description`** `string` · optional · default: — — The standfirst: one or two sentences under the title that say what the post is about. It is a prop rather than the first paragraph of the body because a reader deciding whether to read needs it before the body starts, and because it is what an index, a feed and a search result show.
|
|
118
|
+
**`date`** `string` · required · default: — — The publication date, already formatted for display, and the machine value for the `time` element's `dateTime`. Both are props, and the machine one is required: `dateTime` is what a reader's browser, a feed reader and a search engine use to order posts, and a formatted string is none of those.
|
|
119
|
+
**`dateTime`** `string` · required · default: — — The date in a machine-readable form, `YYYY-MM-DD`, carried on the `time` element. Required, and deliberately not derived from `date`: parsing a formatted date back into an ISO one is a guess about a locale, and a guess in a `datetime` attribute is a wrong date in a feed.
|
|
120
|
+
**`author`** `ReactNode` · optional · default: — — Who wrote the post. A name, or a link to an author page.
|
|
121
|
+
**`tags`** `readonly BlogPostTag[]` · optional · default: — — The tags on the post, in the order a reader should meet them. Omit them for a post with none: an empty row of badges is a row of nothing.
|
|
122
|
+
**`readingTime`** `string` · optional · default: — — How long the post takes to read, in the consumer's own words.
|
|
123
|
+
**`children`** `ReactNode` · required · default: — — The post's body. Any flow content: paragraphs, headings, lists, code, images, tables. `Prose` holds it to the reading measure and to the block rhythm.
|
|
124
|
+
**`previous`** `BlogPostNeighbour` · optional · default: — — The post before this one and the post after it, for the trail at the foot of the page. Omit the half there is no neighbour for; a "previous" link with no previous post is a link to nothing.
|
|
125
|
+
**`next`** `BlogPostNeighbour` · optional · default: —
|
|
126
|
+
**`trailLabels`** `BlogPostTrailLabels` · required · default: — — The two words the trail renders above the neighbouring posts' titles. Required whenever either neighbour is passed, because they are words a reader reads and a Page that ships no copy ships no reader-facing copy either.
|
|
127
|
+
**`trailLabel`** `string` · required · default: — — The accessible name of the trail between the neighbouring posts. It is required whenever either neighbour is passed, because it is a word a reader hears and a Page that ships no copy ships no reader-facing copy either. "More posts" is the Page's own phrasing and a site that files posts by release calls the same landmark something else.
|
|
128
|
+
**`indexLink`** `BlogPostNeighbour` · optional · default: — — The way back to the archive the post is filed in, rendered as one call to action under the trail. A prop rather than a hardcoded "back to the index", because the words and the route belong to the site that publishes the post and a Page that held either would be a Page making a claim about a consumer's own site.
|
|
129
|
+
**`footer`** `ReactNode` · optional · default: — — A slot for what comes after the trail: a related-posts grid, a newsletter form, a call to action. A slot rather than a prop because every one of those is a Block, and a Page that took a Block per prop would be a Page whose interface lists the whole catalogue.
|
|
130
|
+
**`className`** `string` · optional · default: — — Layout only, exactly as on every Component. Changing a Prism-owned visual property from here is prohibited.
|
|
131
|
+
|
|
132
|
+
## Composition
|
|
133
|
+
|
|
134
|
+
**`exports`** `BlogPostPage` — the public runtime names this Page promises
|
|
135
|
+
**`source`** `packages/ui/src/pages/blog-post-page/index.tsx` — the file this Page is defined in
|
|
136
|
+
**`dependencies`** `Badge, CtaLink, Prose, Section` — the public Prism exports this item composes
|