@godxjp/ui 31.20.0 → 31.21.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
@@ -30,6 +30,11 @@ Writes `.mcp.json`, `CLAUDE.md` and `.ai/rules/` into the consumer repo and wire
30
30
  **locked to the version on disk** — the failure it prevents is an agent being told a prop does not
31
31
  exist because the catalog was two minors behind (gh#789).
32
32
 
33
+ The package's postinstall runs this on every install, so an upgrade moves the MCP pin forward by
34
+ itself. **Where install scripts are off** (`npm config get ignore-scripts` is `true`, which some CI
35
+ images and machines set), it never runs: run `npx @godxjp/ui sync-rules` yourself after each upgrade,
36
+ or the agent keeps asking the catalog of the version you first installed.
37
+
33
38
  ### If your agent can only fetch URLs
34
39
 
35
40
  ChatGPT on the web, Claude.ai, or any client without a local process. The same data is served as
@@ -3,7 +3,7 @@
3
3
  You are about to write code against a design system you did not author. This file is the whole
4
4
  contract. Read it before you write JSX.
5
5
 
6
- **This catalog describes `@godxjp/ui` 31.20.0.** If the project you are editing has a different
6
+ **This catalog describes `@godxjp/ui` 31.21.0.** If the project you are editing has a different
7
7
  version in its `package.json`, read the pinned catalog for THAT version instead
8
8
  (`…/v<their-version>/agent/…`). A catalog newer than the installed package describes props that do
9
9
  not exist yet; older, and it hides props that do. Neither failure announces itself.
@@ -16,9 +16,9 @@
16
16
  "type": "string"
17
17
  },
18
18
  {
19
- "description": "antd shape. `false` = a plain, non-clickable picture that also leaves its group. `src` shows a different (larger) file in the preview. Defaults: scaleStep 0.5, minScale 1, maxScale 50.",
19
+ "description": "antd shape. `false` = a plain, non-clickable picture that also leaves its group. `src` shows a different (larger) file in the preview. Defaults: scaleStep 0.5, minScale 1, maxScale 50. `download` adds a Download action to the preview toolbar (gh#1122): `true` = a link to the picture on show (`<a href download>`; a cross-origin URL opens instead), a function `({ src, index }) => void` = a button for a signed URL or a blob. The same key works on `ImagePreviewGroup preview`.",
20
20
  "name": "preview",
21
- "type": "boolean | { visible?, onVisibleChange?(visible, prevVisible), src?, mask?, scaleStep?, minScale?, maxScale? }"
21
+ "type": "boolean | { visible?, onVisibleChange?(visible, prevVisible), src?, mask?, scaleStep?, minScale?, maxScale?, download? }"
22
22
  },
23
23
  {
24
24
  "description": "Shown when `src` fails. A failed picture does not open a preview (antd).",
@@ -78,7 +78,7 @@
78
78
  "type": "React.ReactNode"
79
79
  },
80
80
  {
81
- "description": "Fires with the current file list.",
81
+ "description": "Fires with the current file list. If it (or `previewFile` / `onRemove`) THROWS, Upload reports the error through `globalThis.reportError` — the console and the window `error` event — and shows a message in the upload area; it is never swallowed (gh#1123).",
82
82
  "name": "onValueChange",
83
83
  "type": "(items: UploadFileItemProp[]) => void"
84
84
  },
@@ -9895,7 +9895,7 @@
9895
9895
  "type": "React.ReactNode"
9896
9896
  },
9897
9897
  {
9898
- "description": "Fires with the current file list.",
9898
+ "description": "Fires with the current file list. If it (or `previewFile` / `onRemove`) THROWS, Upload reports the error through `globalThis.reportError` — the console and the window `error` event — and shows a message in the upload area; it is never swallowed (gh#1123).",
9899
9899
  "name": "onValueChange",
9900
9900
  "type": "(items: UploadFileItemProp[]) => void"
9901
9901
  },
@@ -16041,9 +16041,9 @@
16041
16041
  "type": "string"
16042
16042
  },
16043
16043
  {
16044
- "description": "antd shape. `false` = a plain, non-clickable picture that also leaves its group. `src` shows a different (larger) file in the preview. Defaults: scaleStep 0.5, minScale 1, maxScale 50.",
16044
+ "description": "antd shape. `false` = a plain, non-clickable picture that also leaves its group. `src` shows a different (larger) file in the preview. Defaults: scaleStep 0.5, minScale 1, maxScale 50. `download` adds a Download action to the preview toolbar (gh#1122): `true` = a link to the picture on show (`<a href download>`; a cross-origin URL opens instead), a function `({ src, index }) => void` = a button for a signed URL or a blob. The same key works on `ImagePreviewGroup preview`.",
16045
16045
  "name": "preview",
16046
- "type": "boolean | { visible?, onVisibleChange?(visible, prevVisible), src?, mask?, scaleStep?, minScale?, maxScale? }"
16046
+ "type": "boolean | { visible?, onVisibleChange?(visible, prevVisible), src?, mask?, scaleStep?, minScale?, maxScale?, download? }"
16047
16047
  },
16048
16048
  {
16049
16049
  "description": "Shown when `src` fails. A failed picture does not open a preview (antd).",
package/agent/index.json CHANGED
@@ -48,7 +48,7 @@
48
48
  "note": "Pin to the tag that matches the @godxjp/ui version you installed. A catalog newer than your package describes props you do not have; older, and it hides props you do.",
49
49
  "read": {
50
50
  "live": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json",
51
- "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.20.0/agent/index.json"
51
+ "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.21.0/agent/index.json"
52
52
  },
53
53
  "source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
54
54
  "start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
@@ -62,5 +62,5 @@
62
62
  "foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
63
63
  "semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
64
64
  },
65
- "version": "31.20.0"
65
+ "version": "31.21.0"
66
66
  }
package/agent/llms.txt CHANGED
@@ -1,10 +1,10 @@
1
1
  # @godxjp/ui
2
2
 
3
3
  > A Japanese-enterprise React design system: 181 components, 2129 design tokens,
4
- > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.20.0.
4
+ > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.21.0.
5
5
 
6
6
  If your client can run a process, do not read these files — run the MCP server instead
7
- (`npx @godxjp/ui-mcp@31.20.0`). It is searchable and version-locked. These files exist for agents
7
+ (`npx @godxjp/ui-mcp@31.21.0`). It is searchable and version-locked. These files exist for agents
8
8
  that can only fetch URLs.
9
9
 
10
10
  ## Start
@@ -26,7 +26,7 @@ that can only fetch URLs.
26
26
  ## Pinning
27
27
 
28
28
  Every URL above tracks `main`. To pin to the release a project actually installed, swap `main` for
29
- the tag: `.../godx-jp/godxjp-ui/v31.20.0/agent/...`. A catalog that does not match the installed
29
+ the tag: `.../godx-jp/godxjp-ui/v31.21.0/agent/...`. A catalog that does not match the installed
30
30
  package describes props that are absent, or hides props that are present, and says nothing either way.
31
31
 
32
32
  Pinned catalogs only exist for releases whose tag actually contains `agent/`. If `…/v<version>/agent/index.json` returns 404, that release predates this catalog: read `…/main/…` instead and compare `index.json` → `version` against the package you have, so you at least know which way it drifted.
@@ -6,6 +6,7 @@ import { Dialog as RacDialog, Modal, ModalOverlay } from "react-aria-components"
6
6
  import {
7
7
  ChevronLeft,
8
8
  ChevronRight,
9
+ Download,
9
10
  Eye,
10
11
  FlipHorizontal2,
11
12
  FlipVertical2,
@@ -70,7 +71,8 @@ function ImagePreviewDialog({
70
71
  fallback,
71
72
  scaleStep = DEFAULT_SCALE_STEP,
72
73
  minScale = DEFAULT_MIN_SCALE,
73
- maxScale = DEFAULT_MAX_SCALE
74
+ maxScale = DEFAULT_MAX_SCALE,
75
+ download
74
76
  }) {
75
77
  const { t } = useTranslation();
76
78
  const portalContainer = useOverlayPortalContainer();
@@ -296,27 +298,53 @@ function ImagePreviewDialog({
296
298
  ),
297
299
  /* @__PURE__ */ jsx("span", { className: "sr-only", "aria-live": "polite", children: t("dataDisplay.image.position", { current: index + 1, total }) })
298
300
  ] }) : null,
299
- /* @__PURE__ */ jsx(
301
+ /* @__PURE__ */ jsxs(
300
302
  "div",
301
303
  {
302
304
  role: "group",
303
305
  "aria-label": t("dataDisplay.image.toolbar"),
304
306
  "data-slot": "image-preview-toolbar",
305
307
  className: "ui-image-preview-toolbar",
306
- children: tools.map((tool) => /* @__PURE__ */ jsx(
307
- "button",
308
- {
309
- type: "button",
310
- "data-action": tool.key,
311
- className: "ui-image-preview-button ui-focus-ring",
312
- "aria-label": tool.label,
313
- title: tool.label,
314
- disabled: tool.off,
315
- onClick: tool.run,
316
- children: /* @__PURE__ */ jsx("span", { className: "ui-image-preview-icon", "aria-hidden": "true", children: tool.icon })
317
- },
318
- tool.key
319
- ))
308
+ children: [
309
+ download === true && entry ? /* @__PURE__ */ jsx(
310
+ "a",
311
+ {
312
+ href: entry.src,
313
+ download: "",
314
+ "data-action": "download",
315
+ className: "ui-image-preview-button ui-focus-ring",
316
+ "aria-label": t("dataDisplay.image.download"),
317
+ title: t("dataDisplay.image.download"),
318
+ children: /* @__PURE__ */ jsx("span", { className: "ui-image-preview-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Download, {}) })
319
+ }
320
+ ) : null,
321
+ typeof download === "function" && entry ? /* @__PURE__ */ jsx(
322
+ "button",
323
+ {
324
+ type: "button",
325
+ "data-action": "download",
326
+ className: "ui-image-preview-button ui-focus-ring",
327
+ "aria-label": t("dataDisplay.image.download"),
328
+ title: t("dataDisplay.image.download"),
329
+ onClick: () => download({ src: entry.src, index }),
330
+ children: /* @__PURE__ */ jsx("span", { className: "ui-image-preview-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Download, {}) })
331
+ }
332
+ ) : null,
333
+ tools.map((tool) => /* @__PURE__ */ jsx(
334
+ "button",
335
+ {
336
+ type: "button",
337
+ "data-action": tool.key,
338
+ className: "ui-image-preview-button ui-focus-ring",
339
+ "aria-label": tool.label,
340
+ title: tool.label,
341
+ disabled: tool.off,
342
+ onClick: tool.run,
343
+ children: /* @__PURE__ */ jsx("span", { className: "ui-image-preview-icon", "aria-hidden": "true", children: tool.icon })
344
+ },
345
+ tool.key
346
+ ))
347
+ ]
320
348
  }
321
349
  )
322
350
  ] })
@@ -381,7 +409,8 @@ function ImagePreviewGroup({
381
409
  fallback,
382
410
  scaleStep: config.scaleStep,
383
411
  minScale: config.minScale,
384
- maxScale: config.maxScale
412
+ maxScale: config.maxScale,
413
+ download: config.download
385
414
  }
386
415
  ) : null
387
416
  ] }) });
@@ -531,7 +560,8 @@ const ImageRoot = React.forwardRef(function Image({
531
560
  fallback,
532
561
  scaleStep: config.scaleStep,
533
562
  minScale: config.minScale,
534
- maxScale: config.maxScale
563
+ maxScale: config.maxScale,
564
+ download: config.download
535
565
  }
536
566
  )
537
567
  ] })
@@ -240,6 +240,14 @@ function Upload({
240
240
  );
241
241
  onReject?.({ file, reason, error });
242
242
  };
243
+ const reportCallbackError = (error, visible = true) => {
244
+ if (typeof globalThis.reportError === "function") globalThis.reportError(error);
245
+ else
246
+ setTimeout(() => {
247
+ throw error;
248
+ });
249
+ if (visible && mounted.current) setRejection(t("dataEntry.upload.callbackFailed"));
250
+ };
243
251
  const pickFiles = (fileList) => {
244
252
  if (!fileList?.length || current.current.disabled) return;
245
253
  const files = Array.from(fileList);
@@ -302,12 +310,10 @@ function Upload({
302
310
  (entry) => entry.uid === item2.uid ? { ...entry, previewUrl: url } : entry
303
311
  )
304
312
  );
305
- }).catch(() => {
306
- });
313
+ }).catch((error) => reportCallbackError(error, false));
307
314
  if (automatic) void startUpload(item2);
308
315
  }
309
- }).catch(() => {
310
- });
316
+ }).catch((error) => reportCallbackError(error));
311
317
  };
312
318
  const removeItem = async (uid) => {
313
319
  if (current.current.disabled) return;
@@ -315,7 +321,8 @@ function Upload({
315
321
  if (!target) return;
316
322
  try {
317
323
  if (await onRemove?.(target) === false) return;
318
- } catch {
324
+ } catch (error) {
325
+ reportCallbackError(error, false);
319
326
  return;
320
327
  }
321
328
  if (!mounted.current || current.current.disabled || !current.current.items.some((item2) => item2.uid === uid))
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
3
- "version": "31.20.0",
3
+ "version": "31.21.0",
4
4
  "targetSize": {
5
5
  "standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
6
6
  "min": 24,
@@ -92,7 +92,8 @@
92
92
  "rejectSize": "{name}: file is too large.",
93
93
  "rejectCount": "{name}: file limit reached.",
94
94
  "rejectBeforeUpload": "{name}: file validation failed.",
95
- "readFailed": "Could not read the selected folder."
95
+ "readFailed": "Could not read the selected folder.",
96
+ "callbackFailed": "The selected files could not be added."
96
97
  },
97
98
  "cascader": {
98
99
  "placeholder": "Select…",
@@ -407,7 +408,8 @@
407
408
  "rotateRight": "Rotate right",
408
409
  "zoomIn": "Zoom in",
409
410
  "zoomOut": "Zoom out",
410
- "reset": "Reset"
411
+ "reset": "Reset",
412
+ "download": "Download"
411
413
  }
412
414
  },
413
415
  "ui": {
@@ -92,7 +92,8 @@
92
92
  "rejectSize": "{name}: ファイルが大きすぎます。",
93
93
  "rejectCount": "{name}: ファイル数の上限です。",
94
94
  "rejectBeforeUpload": "{name}: ファイルの検証に失敗しました。",
95
- "readFailed": "選択したフォルダーを読み取れませんでした。"
95
+ "readFailed": "選択したフォルダーを読み取れませんでした。",
96
+ "callbackFailed": "選択したファイルを追加できませんでした。"
96
97
  },
97
98
  "cascader": {
98
99
  "placeholder": "選択…",
@@ -400,7 +401,8 @@
400
401
  "rotateRight": "右に回転",
401
402
  "zoomIn": "拡大",
402
403
  "zoomOut": "縮小",
403
- "reset": "リセット"
404
+ "reset": "リセット",
405
+ "download": "ダウンロード"
404
406
  }
405
407
  },
406
408
  "ui": {
@@ -92,7 +92,8 @@
92
92
  "rejectSize": "{name}: tệp quá lớn.",
93
93
  "rejectCount": "{name}: đã đủ số lượng tệp.",
94
94
  "rejectBeforeUpload": "{name}: kiểm tra tệp thất bại.",
95
- "readFailed": "Không thể đọc thư mục đã chọn."
95
+ "readFailed": "Không thể đọc thư mục đã chọn.",
96
+ "callbackFailed": "Không thể thêm các tệp đã chọn."
96
97
  },
97
98
  "cascader": {
98
99
  "placeholder": "Chọn…",
@@ -401,7 +402,8 @@
401
402
  "rotateRight": "Xoay phải",
402
403
  "zoomIn": "Phóng to",
403
404
  "zoomOut": "Thu nhỏ",
404
- "reset": "Đặt lại"
405
+ "reset": "Đặt lại",
406
+ "download": "Tải xuống"
405
407
  }
406
408
  },
407
409
  "ui": {
@@ -1273,6 +1273,16 @@ export type ImagePreviewConfigProp = {
1273
1273
  minScale?: number;
1274
1274
  /** Largest zoom. Default `50`. */
1275
1275
  maxScale?: number;
1276
+ /**
1277
+ * A Download action in the preview toolbar (gh#1122). `true`: a link to the picture on show
1278
+ * (`<a href download>` — a cross-origin URL opens instead, since browsers ignore `download`
1279
+ * there). A function: a button that calls it with the picture on show, for a signed URL or a
1280
+ * streamed blob. Off by default.
1281
+ */
1282
+ download?: boolean | ((info: {
1283
+ src: string;
1284
+ index: number;
1285
+ }) => void);
1276
1286
  };
1277
1287
  /** @see ImagePreviewGroup — the group's `preview` object (antd `PreviewGroup preview`). */
1278
1288
  export type ImagePreviewGroupConfigProp = Omit<ImagePreviewConfigProp, "src" | "mask"> & {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "31.20.0",
2
+ "version": "31.21.0",
3
3
  "generatedBy": "scripts/gen-style-layers.mjs — do not edit by hand; run `pnpm gen:style-layers`",
4
4
  "base": "base.css",
5
5
  "fonts": "fonts.css",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "31.20.0",
4
- "godxUiMcp": "31.20.0",
3
+ "version": "31.21.0",
4
+ "godxUiMcp": "31.21.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",