@atelic-action/artifact 1.1.0 → 1.2.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/README.md CHANGED
@@ -24,7 +24,7 @@ Every export of the package, all from the one entry, `@atelic-action/artifact`.
24
24
  | `Writeup`, `WriteupProps` | The graded document an owner gets: the overview, the three tiers of section cards, the close | `/writeup` |
25
25
  | `ratingCell` | A star rating table cell for a writeup table, sorted by its figure | `/writeup` |
26
26
  | `WriteupBlock`, `WriteupTopic`, `WriteupPart`, `WriteupSheet`, `WriteupSummary`, `WriteupSummaryRow`, `WriteupMarkets`, `WriteupRented`, `WriteupMark`, `WriteupCell`, `WriteupTone`, and the rest of the `Writeup*` types | The shapes a writeup's content is written in | none |
27
- | `WriteupGrade`, `WriteupPerson`, `WriteupShot` | The grade letter, the sign off person, and the owner's screen, shared by every document (the first two are re exported from ui) | none |
27
+ | `WriteupGrade`, `WriteupPerson`, `WriteupShot` | The grade letter, the sign off person, and the owner's screen, shared by every document, all three re exported from ui | none |
28
28
  | `Read`, `ReadProps` | The baseline's grades explained by cause: the gist table; each section read top to bottom as where we looked (the tools, linked, at the top), what it showed (a short paragraph or two, a compact table of facts, lists of examples under their own headings, and any proof pictures), why (one sentence on a tinted panel), today and after (a two column table), and what we recommend (a checklist); then the plan in order. A beat with no data is left out, heading and all | `/read` |
29
29
  | `readLabels`, `ReadLabels` | The fixed words the read sets (Prepared for, the gist table's columns, Where We Looked, What It Showed, Why, Today, After, What We Recommend, the plan's heading), each overridable through `labels` | `/read` |
30
30
  | `ReadTier`, `ReadSection`, `ReadTool`, `ReadFact`, `ReadExamples`, `ReadChange`, `ReadStep`, `ReadPlan`, `ReadClose`, `ReadLink`, `ReadParagraph` | The shapes a read's content is written in, all plain data so a routine can write a read as JSON. A section's `tools` were its `sources` in 1.0.0; `sources` is still read, and rendered at the top as tools, and `ReadSource` names the same shape as `ReadTool` | none |
@@ -36,7 +36,7 @@ Every export of the package, all from the one entry, `@atelic-action/artifact`.
36
36
  | `normalizeEmail` | How a typed address becomes the key a payload opens with; the sealing command mirrors it | none |
37
37
  | `documents.css` | The documents' styles, at `@atelic-action/artifact/styles/documents.css` | every route |
38
38
 
39
- What a document is built from stays in `@atelic-action/ui`, and this package imports it rather than copying it: `GradeChip`, `gradeLabel`, `SignOff`, and `newTabProps` from `@atelic-action/ui/artifact`; `Callout`, `Checklist`, `ColumnChart`, `DataTable`, `Disclosure`, `StackedBar`, `StatRow`, `ThresholdScale`, and their types from `@atelic-action/ui/components`; `Button` from `@atelic-action/ui/primitives`. The shell around a page (`ArtifactShell`), the head (`buildPageHead`), the contract (`ArtifactConfig`), the reader helpers, the error boundary, the access wall, the Vite configuration, and the commands are ui's too, and a site imports them from there. Two small helpers live here unexported, because ui 1.1.0 does not export them: `Shot` (the owner's screen, in the writeup, the note, and the read) and `Paragraph` (the plain data paragraph of the note and the read).
39
+ What a document is built from stays in `@atelic-action/ui`, and this package imports it rather than copying it: `GradeChip`, `gradeLabel`, `SignOff`, `Shot` (the owner's screen), `Paragraph`, `Runs`, `isEmpty`, `ArtifactAnchor` (the plain data paragraph of the note and the read), and `newTabProps` from `@atelic-action/ui/artifact`; `Callout`, `Checklist`, `ColumnChart`, `DataTable`, `Disclosure`, `StackedBar`, `StatRow`, `ThresholdScale`, and their types from `@atelic-action/ui/components`; `Button` from `@atelic-action/ui/primitives`. The shell around a page (`ArtifactShell`), the head (`buildPageHead`), the contract (`ArtifactConfig`), the reader helpers, the error boundary, the access wall, the Vite configuration, and the commands are ui's too, and a site imports them from there.
40
40
 
41
41
  ## Install
42
42
 
@@ -44,7 +44,7 @@ What a document is built from stays in `@atelic-action/ui`, and this package imp
44
44
  bun add @atelic-action/artifact @atelic-action/ui
45
45
  ```
46
46
 
47
- Peer dependencies: `@atelic-action/ui` at `^1.1.0`, `react` at `^19.0.0`, and `lucide-react` at `^1.23.0`. The package ships TSX and CSS as source, compiled inside the site's own Vite like ui's; a site whose `vite.config.ts` is one `artifactConfig` call from `@atelic-action/ui/vite` needs nothing more, for the build, the prerender, and the dev server alike. The default labels a document sets live in the document: the read's `readLabels` and the sealed document's gate copy are overridden through props, and the monthly page's four questions are fixed on purpose, since they are its contract with the Statement of Work.
47
+ Peer dependencies: `@atelic-action/ui` at `^2.0.0`, `react` at `^19.0.0`, and `lucide-react` at `^1.23.0`. The package ships TSX and CSS as source, compiled inside the site's own Vite like ui's; a site whose `vite.config.ts` is one `artifactConfig` call from `@atelic-action/ui/vite` needs nothing more, for the build, the prerender, and the dev server alike. The default labels a document sets live in the document: the read's `readLabels` and the sealed document's gate copy are overridden through props, and the monthly page's four questions are fixed on purpose, since they are its contract with the Statement of Work.
48
48
 
49
49
  ```tsx
50
50
  import { Writeup } from "@atelic-action/artifact";
@@ -57,13 +57,13 @@ The documents' sheet goes after every ui sheet and before the site's `theme.css`
57
57
 
58
58
  1. The site's fonts (`fonts.css`)
59
59
  2. `@atelic-action/ui/styles/base.css`, `chrome.css`, `components.css`, `primitives.css`, and `gate.css`, in that order
60
- 3. `@atelic-action/ui/styles/artifact.css`, the pieces the documents share (the grade chip, the sign off, the shell's logo)
60
+ 3. `@atelic-action/ui/styles/artifact.css`, the pieces the documents share (the grade chip, the sign off, the shot, the shell's logo, and the shell's print rules that drop the header, the credit band, and the skip link); it sets no `@page`
61
61
  4. `@atelic-action/artifact/styles/documents.css`
62
62
  5. The site's `theme.css`, last
63
63
 
64
64
  Every rule in the sheet sits in the `atelic-ui` layer and under the `.atelic-ui` root class, exactly as ui's sheets do, so any rule a site writes outside a layer still wins over it. The sheet holds two `@page` rules, the monthly page's and then the writeup's, and the later one wins for every page a site prints; their order is load bearing.
65
65
 
66
- Under ui 1.x the overlap is deliberate: ui's `artifact.css` still carries the documents' rules beside the pieces, so a site importing both sheets carries those rules twice, identical and in the same order, until ui 2.0.0 drops them from its sheet (ATE-671, step 3). On the template that is about 39 KB of the built base sheet, 6 KB gzipped.
66
+ From 1.2.0 the two sheets do not overlap: ui 2.0.0's `artifact.css` holds only the pieces, and this sheet holds only the documents and both `@page` rules. A site on ui 1.x stays on artifact 1.1.x, which carries the shot's rules and the shell's print rules itself.
67
67
 
68
68
  ## Tech Stack
69
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atelic-action/artifact",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "The composed documents the Atelic practice hands or sends a business (the writeup, the read, the monthly page, the note, and the sealed document), built from the pieces in @atelic-action/ui",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,12 +37,12 @@
37
37
  "format": "biome format . --write"
38
38
  },
39
39
  "peerDependencies": {
40
- "@atelic-action/ui": "^1.1.0",
40
+ "@atelic-action/ui": "^2.0.0",
41
41
  "lucide-react": "^1.23.0",
42
42
  "react": "^19.0.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@atelic-action/ui": "^1.1.0",
45
+ "@atelic-action/ui": "^2.0.0",
46
46
  "@biomejs/biome": "^2.0.0",
47
47
  "@playwright/test": "^1.60.0",
48
48
  "@sentry/react": "^10.0.0",
@@ -1,13 +1,15 @@
1
- import { SignOff, type WriteupPerson } from "@atelic-action/ui/artifact";
2
- import { Callout } from "@atelic-action/ui/components";
3
1
  import {
4
2
  type ArtifactLink,
5
3
  type ArtifactParagraph,
6
4
  isEmpty,
7
5
  ArtifactAnchor as NoteAnchor,
8
6
  Paragraph,
9
- } from "./Paragraph";
10
- import { Shot, type WriteupShot } from "./Shot";
7
+ Shot,
8
+ SignOff,
9
+ type WriteupPerson,
10
+ type WriteupShot,
11
+ } from "@atelic-action/ui/artifact";
12
+ import { Callout } from "@atelic-action/ui/components";
11
13
 
12
14
  /**
13
15
  * The note: the first touch, for a business whose site publishes no email
@@ -1,14 +1,19 @@
1
1
  import {
2
+ type ArtifactLink,
3
+ type ArtifactParagraph,
2
4
  GradeChip,
3
5
  gradeLabel,
6
+ isEmpty,
4
7
  newTabProps,
8
+ Paragraph,
9
+ Runs,
10
+ Shot,
5
11
  SignOff,
6
12
  type WriteupGrade,
7
13
  type WriteupPerson,
14
+ type WriteupShot,
8
15
  } from "@atelic-action/ui/artifact";
9
16
  import { ArrowUpRight, Check } from "lucide-react";
10
- import { type ArtifactLink, type ArtifactParagraph, isEmpty, Paragraph, Runs } from "./Paragraph";
11
- import { Shot, type WriteupShot } from "./Shot";
12
17
 
13
18
  /**
14
19
  * The read: what a customer gets at the end of an engagement's first week,
@@ -2,9 +2,11 @@ import {
2
2
  GradeChip,
3
3
  gradeLabel,
4
4
  newTabProps,
5
+ Shot,
5
6
  SignOff,
6
7
  type WriteupGrade,
7
8
  type WriteupPerson,
9
+ type WriteupShot,
8
10
  } from "@atelic-action/ui/artifact";
9
11
  import {
10
12
  Callout,
@@ -25,10 +27,8 @@ import {
25
27
  } from "@atelic-action/ui/components";
26
28
  import { ArrowUpRight, ChevronRight, CircleAlert, CircleCheck, CircleX, Star } from "lucide-react";
27
29
  import type { ReactNode } from "react";
28
- import { Shot, type WriteupShot } from "./Shot";
29
30
 
30
- export type { WriteupGrade, WriteupPerson } from "@atelic-action/ui/artifact";
31
- export type { WriteupShot } from "./Shot";
31
+ export type { WriteupGrade, WriteupPerson, WriteupShot } from "@atelic-action/ui/artifact";
32
32
 
33
33
  /**
34
34
  * The writeup: the graded document an owner gets. It is a web page first: an
@@ -7,11 +7,14 @@
7
7
  writeup's stays the one that wins.
8
8
 
9
9
  The pieces the documents share stay in @atelic-action/ui: the grade chip
10
- (.op-grade), the sign off (.op-who, .op-sign), and the shell's logo rule,
11
- in its styles/artifact.css; the login screen's vocabulary the sealed stage
12
- reuses, in its styles/gate.css; the page components (the callout, the
13
- checklist, the table, the charts), in its styles/components.css. Import
14
- this sheet after all of them and before the site's theme.css.
10
+ (.op-grade), the sign off (.op-who, .op-sign), the shot (.op-shot), the
11
+ shell's logo rule, and the shell's print rules that drop the header, the
12
+ credit band, and the skip link on every artifact page, in its
13
+ styles/artifact.css, which sets no @page; the login screen's vocabulary
14
+ the sealed stage reuses, in its styles/gate.css; the page components (the
15
+ callout, the checklist, the table, the charts), in its
16
+ styles/components.css. Import this sheet after all of them and before the
17
+ site's theme.css. Both @page rules live only here.
15
18
 
16
19
  Every rule sits in the same atelic-ui layer as the package sheets, so any
17
20
  rule a site writes outside a layer still wins over it whatever its
@@ -521,7 +524,8 @@
521
524
  never reflowed, so a print of one sheet is the check.
522
525
 
523
526
  Tokens only (theme.css), except the printed sheet's white. The grade
524
- chip and the sign off are the shared classes in @atelic-action/ui's
527
+ chip, the sign off, and the print rule that drops the header and the
528
+ credit band are the shared classes in @atelic-action/ui's
525
529
  styles/artifact.css.
526
530
  ================================================================= */
527
531
  .atelic-ui .rp {
@@ -797,11 +801,6 @@
797
801
  padding: 0;
798
802
  background: none;
799
803
  }
800
- .atelic-ui:has(.rp) .nav,
801
- .atelic-ui:has(.rp) .skip-link,
802
- .atelic-ui:has(.rp) .credit-bar {
803
- display: none;
804
- }
805
804
  .atelic-ui .rp {
806
805
  padding: 0;
807
806
  background: none;
@@ -1345,33 +1344,6 @@
1345
1344
  gap: 14px;
1346
1345
  max-width: 340px;
1347
1346
  }
1348
- .atelic-ui .op-shot {
1349
- display: flex;
1350
- flex-direction: column;
1351
- gap: 6px;
1352
- margin: 0;
1353
- }
1354
- .atelic-ui .op-shot a,
1355
- .atelic-ui .op-shot-frame {
1356
- display: block;
1357
- width: 100%;
1358
- border-radius: var(--radius-sm);
1359
- overflow: hidden;
1360
- border: 1px solid var(--neutral-200);
1361
- background: var(--op-card);
1362
- }
1363
- .atelic-ui .op-shot a {
1364
- cursor: zoom-in;
1365
- }
1366
- /* The expand badge on a linked crop: off here, the note turns it on. */
1367
- .atelic-ui .op-shot-open {
1368
- display: none;
1369
- }
1370
- .atelic-ui .op-shot img {
1371
- width: 100%;
1372
- height: auto;
1373
- display: block;
1374
- }
1375
1347
  .atelic-ui .op-shot-brief {
1376
1348
  display: grid;
1377
1349
  place-items: center;
@@ -1384,11 +1356,6 @@
1384
1356
  padding: 12px;
1385
1357
  text-align: center;
1386
1358
  }
1387
- .atelic-ui .op-shot figcaption {
1388
- font-size: 12.5px;
1389
- line-height: 1.45;
1390
- color: var(--op-ink-faint);
1391
- }
1392
1359
 
1393
1360
  /* sources: the tools the readings came from */
1394
1361
  .atelic-ui .op-sources {
@@ -1715,9 +1682,6 @@
1715
1682
  html:has(.atelic-ui .op-doc) {
1716
1683
  zoom: 0.74;
1717
1684
  }
1718
- .atelic-ui .nav,
1719
- .atelic-ui .credit-bar,
1720
- .atelic-ui .skip-link,
1721
1685
  .atelic-ui .op-ov-go,
1722
1686
  .atelic-ui .op-topic h3 a svg {
1723
1687
  display: none;
@@ -1808,9 +1772,6 @@
1808
1772
  .atelic-ui .data-table thead {
1809
1773
  break-after: avoid;
1810
1774
  }
1811
- .atelic-ui .op-shot a {
1812
- cursor: auto;
1813
- }
1814
1775
  .atelic-ui .op-twin {
1815
1776
  display: flex;
1816
1777
  align-items: center;
@@ -1851,17 +1812,17 @@
1851
1812
  aligned; the screen's column sizes to the picture, a phone crop
1852
1813
  about 260px and a wide card up to 45% of the row. A finding
1853
1814
  with no screen takes the full width.
1854
- - Print: one letter sheet. The chrome steps aside (the writeup's print
1855
- rules already hide the header and the credit band on any artifact
1856
- page), the type comes down, and the screens shrink. The note sets
1815
+ - Print: one letter sheet. The chrome steps aside (ui's artifact.css
1816
+ hides the header and the credit band on any artifact page), the
1817
+ type comes down, and the screens shrink. The note sets
1857
1818
  no @page of its own: the writeup's, the later one in this sheet,
1858
1819
  sets letter paper for every artifact page.
1859
1820
 
1860
1821
  Tokens only. The finding is the shared Callout (@atelic-action/ui's components.css),
1861
- the screen the writeup's .op-shot, the sign off the shared .op-who;
1822
+ the screen the shared .op-shot, the sign off the shared .op-who;
1862
1823
  this sheet sets the letter around them and their sizes here.
1863
1824
  ================================================================= */
1864
- /* The borrowed .op-shot reads the writeup's own two tokens, mapped here as
1825
+ /* The shared .op-shot reads the writeup's own two tokens, mapped here as
1865
1826
  the writeup's .op root maps them. */
1866
1827
  .atelic-ui .nt {
1867
1828
  --op-card: var(--card);
@@ -1,65 +0,0 @@
1
- import { newTabProps } from "@atelic-action/ui/artifact";
2
-
3
- /**
4
- * Plain data text for the documents a routine can write as JSON (the note
5
- * and the read): a paragraph is a string, or runs of text and links in
6
- * order. A link opens in a new tab when it leaves the page, and in place
7
- * when it is an anchor or a mail link.
8
- */
9
-
10
- /** A link inside running text: the words the reader clicks and where they go. */
11
- export interface ArtifactLink {
12
- text: string;
13
- href: string;
14
- }
15
-
16
- /** A paragraph: a plain string, or runs of text and links in order. */
17
- export type ArtifactParagraph = string | Array<string | ArtifactLink>;
18
-
19
- /** A link opens in a new tab when it leaves the page, and in place when it is an anchor or a mail link. */
20
- export function ArtifactAnchor({ href, children }: { href: string; children: string }) {
21
- return (
22
- <a href={href} {...newTabProps(href, /^https?:/i.test(href))}>
23
- {children}
24
- </a>
25
- );
26
- }
27
-
28
- /** A paragraph with nothing to read: a blank string, or runs that are all blank strings. A link always counts. */
29
- export function isEmpty(paragraph: ArtifactParagraph): boolean {
30
- const runs = typeof paragraph === "string" ? [paragraph] : paragraph;
31
- return runs.every((run) => typeof run === "string" && run.trim() === "");
32
- }
33
-
34
- /** The runs alone, for a container that is not a paragraph (a list item, a table cell). */
35
- export function Runs({ paragraph }: { paragraph: ArtifactParagraph }) {
36
- const runs = typeof paragraph === "string" ? [paragraph] : paragraph;
37
- return (
38
- <>
39
- {runs.map((run, i) =>
40
- typeof run === "string" ? (
41
- run
42
- ) : (
43
- // biome-ignore lint/suspicious/noArrayIndexKey: runs are static content in a fixed order
44
- <ArtifactAnchor href={run.href} key={i}>
45
- {run.text}
46
- </ArtifactAnchor>
47
- ),
48
- )}
49
- </>
50
- );
51
- }
52
-
53
- export function Paragraph({
54
- paragraph,
55
- className,
56
- }: {
57
- paragraph: ArtifactParagraph;
58
- className?: string;
59
- }) {
60
- return (
61
- <p className={className}>
62
- <Runs paragraph={paragraph} />
63
- </p>
64
- );
65
- }
@@ -1,38 +0,0 @@
1
- import { newTabProps } from "@atelic-action/ui/artifact";
2
- import { Maximize2 } from "lucide-react";
3
-
4
- export interface WriteupShot {
5
- /** The crop shown on the page. */
6
- src: string;
7
- alt: string;
8
- /** The full screenshot the crop opens in a new tab; omit to render the crop alone. */
9
- href?: string;
10
- caption: string;
11
- }
12
-
13
- /**
14
- * The owner's own screen: the crop, opening the full screenshot in a new tab
15
- * when it has one, over its caption. The writeup and the note both wear it;
16
- * its styles (.op-shot) are the writeup's, and the note only sizes it. A
17
- * linked crop says it opens: a hover title, and an expand badge the note
18
- * shows on its thumbnails and the writeup keeps hidden.
19
- */
20
- export function Shot({ shot }: { shot: WriteupShot }) {
21
- return (
22
- <figure className="op-shot">
23
- {shot.href ? (
24
- <a href={shot.href} title="Open the full picture" {...newTabProps(shot.href, true)}>
25
- <img src={shot.src} alt={shot.alt} loading="lazy" />
26
- <span className="op-shot-open" aria-hidden="true">
27
- <Maximize2 size={12} />
28
- </span>
29
- </a>
30
- ) : (
31
- <span className="op-shot-frame">
32
- <img src={shot.src} alt={shot.alt} loading="lazy" />
33
- </span>
34
- )}
35
- <figcaption>{shot.caption}</figcaption>
36
- </figure>
37
- );
38
- }