@synopackageland/ui 0.5.8 → 0.6.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.
@@ -0,0 +1,154 @@
1
+ # Declarative DSM UI (`@synopackageland/ui/json`)
2
+
3
+ The JSON entry point is a curated AI/declarative surface over the normal Vue
4
+ SDK. It is intentionally smaller than the full component library. Ordinary
5
+ application structure, settings, forms, dialogs, resource tables and state
6
+ bindings can stay declarative; domain-specific views and business logic remain
7
+ normal Vue/TypeScript extensions.
8
+
9
+ It also supports responsive `AppShell` and `ResponsiveView` alternatives:
10
+ one JSON spec renders as a DSM window or a phone view, and as a wide Portal
11
+ page when its `AppShell` sets `wideLayout: "web"`.
12
+ See [adaptive-layout.md](adaptive-layout.md).
13
+
14
+ ## Boundary
15
+
16
+ Use JSON for repeated application interaction patterns:
17
+
18
+ - DSM app shell, sidebar, toolbar, page header and tabs
19
+ - settings/forms and ordinary validation/state binding
20
+ - dialogs, callouts, empty/loading states and progress
21
+ - simple resource collections/tables
22
+ - action dispatch into host-owned backend functions
23
+
24
+ Keep these in application code and expose them as extension components/actions:
25
+
26
+ - domain-specific visualizations (treemap, topology, terminal, media canvas)
27
+ - rich table rows/cells with domain actions
28
+ - filesystem/NAS pickers or native DSM bridges
29
+ - polling, retries, transactions, authorization and other business workflows
30
+
31
+ This gives the declarative layer an escape hatch instead of expanding the DSL
32
+ until it becomes another programming language.
33
+
34
+ ## Basic renderer
35
+
36
+ ```vue
37
+ <script setup lang="ts">
38
+ import { SynoJsonRenderer } from '@synopackageland/ui/json'
39
+ import type { Spec } from '@synopackageland/ui/json'
40
+
41
+ const spec: Spec = {
42
+ root: 'root',
43
+ state: { enabled: true },
44
+ elements: {
45
+ root: { type: 'AppRoot', props: { modalScope: 'app' }, children: ['shell'] },
46
+ shell: { type: 'AppShell', props: { minHeight: '0' }, children: ['page'] },
47
+ page: { type: 'Page', props: { width: 'readable' }, children: ['settings'] },
48
+ settings: { type: 'SettingsSection', props: { title: 'General', collapsible: false }, children: ['enabled'] },
49
+ enabled: { type: 'SettingsField', props: { label: 'Enabled' }, children: ['switch'] },
50
+ switch: { type: 'Switch', props: { checked: { $bindState: '/enabled' } }, children: [] },
51
+ },
52
+ }
53
+ </script>
54
+
55
+ <template><SynoJsonRenderer :spec="spec" /></template>
56
+ ```
57
+
58
+ ## Real applications: extend, do not fork
59
+
60
+ An application normally declares its backend actions and only the special
61
+ renderers it needs. Spread the SDK definitions/renderers into the app catalog:
62
+
63
+ ```ts
64
+ import { h } from 'vue'
65
+ import {
66
+ defineCatalog,
67
+ defineRegistry,
68
+ jsonRenderVueSchema,
69
+ synoJsonComponentDefinitions,
70
+ synoJsonRenderers,
71
+ z,
72
+ } from '@synopackageland/ui/json'
73
+ import DownloadTaskTable from './DownloadTaskTable.vue'
74
+
75
+ export const catalog = defineCatalog(jsonRenderVueSchema, {
76
+ components: {
77
+ ...synoJsonComponentDefinitions,
78
+ DownloadTaskTable: {
79
+ props: z.object({ rows: z.array(z.unknown()) }),
80
+ description: 'Gopeed download rows with progress and per-row actions.',
81
+ },
82
+ },
83
+ actions: {
84
+ refreshDownloads: { description: 'Reload download tasks.' },
85
+ createDownload: {
86
+ params: z.object({ url: z.string(), folder: z.string(), filename: z.string().optional() }),
87
+ description: 'Create a download through the package backend.',
88
+ },
89
+ },
90
+ })
91
+
92
+ export const { registry } = defineRegistry(catalog, {
93
+ components: {
94
+ ...synoJsonRenderers,
95
+ DownloadTaskTable: ({ props }) => h(DownloadTaskTable, { rows: props.rows }),
96
+ },
97
+ actions: {
98
+ refreshDownloads: async () => { /* call existing app/store code */ },
99
+ createDownload: async (params) => { /* call existing app/store code */ },
100
+ },
101
+ })
102
+ ```
103
+
104
+ The same boundary works for Syno Reclaim: its app shell/settings/dialogs can be
105
+ declarative while `Treemap`, `ReclaimGrid`, `ProgressRing` and the scan/cleanup
106
+ state machines stay application-owned extensions.
107
+
108
+ ## AI tools
109
+
110
+ The package ships a `syno-ui` command (`npx @synopackageland/ui <command>`, or
111
+ `npx syno-ui` once installed) and the same functions from the JSON entry:
112
+
113
+ | Command | Purpose | Function |
114
+ | --- | --- | --- |
115
+ | `catalog [--json]` | Components, required props, slots, events | `listSynoJsonComponents()` |
116
+ | `describe <Component> [--json]` | Prop JSON Schemas and a minimal valid element | `describeSynoJsonComponent()` |
117
+ | `schema` | Spec JSON Schema (draft 2020-12) generated from the catalog | `synoJsonSchema()` |
118
+ | `prompt` | System prompt: catalog, rules and the self-check workflow | `synoJsonAiPrompt()` |
119
+ | `validate <file...>` | Every problem at once, with JSON Pointer `path` and a `hint` | `validateSynoJsonSpec()` |
120
+ | `render <file>` | Validate, screenshot 390/640/960/1350 px, report overflow by spec key | (Chrome, Node 22+) |
121
+
122
+ `validate` checks the spec shape, unknown components/props/slots/events (with
123
+ "did you mean"), required props, literal types and enum values, child
124
+ references and cycles, state pointers, `$item` outside `repeat`, actions,
125
+ `$computed` functions and i18n keys. Tell it what the host provides:
126
+
127
+ ```bash
128
+ syno-ui validate ui.json --json --actions save,refresh --functions formatBytes \
129
+ --state host-state.json --messages messages/en.json
130
+ ```
131
+
132
+ Exit codes: `0` valid, `1` spec errors (or warnings with `--strict`, or layout
133
+ issues from `render`), `2` usage, unreadable files or browser failure.
134
+
135
+ Localized text uses `{"$computed": "t", "args": {"key": "settings.title"}}`.
136
+ Register `t` through the renderer's `functions` prop; `--messages` checks every
137
+ key and warns about literal user-facing text.
138
+
139
+ ### Workflow for an AI
140
+
141
+ 1. Read `syno-ui prompt` once, then `syno-ui catalog` and `syno-ui describe` for
142
+ the components you plan to use.
143
+ 2. Write `{ "root", "elements", "state" }`, seeding every state path you bind.
144
+ 3. Run `syno-ui validate ui.json --json` with the host's actions, functions,
145
+ state and messages. Fix each error at its `path` using its `hint`; repeat.
146
+ 4. Run `syno-ui render ui.json --out shots`, look at the screenshots, and fix
147
+ every element named in an `outside_viewport` or `clipped_content` issue.
148
+ 5. Hand over the spec only when `validate` and `render` both exit 0.
149
+
150
+ Builds also write `dist/json-catalog.schema.json` and `dist/json-ai-prompt.md`
151
+ (exported as `@synopackageland/ui/json-catalog.schema.json` and
152
+ `@synopackageland/ui/json-ai-prompt.md`) for constrained-generation pipelines.
153
+ Give the model the prompt plus the application's own actions and extension
154
+ components, not the Vue source tree.
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.5.8",
2
+ "version": "0.6.0",
3
3
  "description": "Shared Vue 3 UI component library with a DSM 7 inspired application theme",
4
4
  "license": "MIT",
5
5
  "type": "module",
@@ -11,7 +11,10 @@
11
11
  "LICENSE",
12
12
  "LICENSES",
13
13
  "README.md",
14
- "docs/app-local-modal.md"
14
+ "docs/adaptive-layout.md",
15
+ "docs/app-local-modal.md",
16
+ "docs/component-defaults-0.5.7.md",
17
+ "docs/json-ui.md"
15
18
  ],
16
19
  "main": "./dist/index.js",
17
20
  "module": "./dist/index.js",
@@ -21,6 +24,12 @@
21
24
  "types": "./dist/index.d.ts",
22
25
  "import": "./dist/index.js"
23
26
  },
27
+ "./json": {
28
+ "types": "./dist/json.d.ts",
29
+ "import": "./dist/json.js"
30
+ },
31
+ "./json-catalog.schema.json": "./dist/json-catalog.schema.json",
32
+ "./json-ai-prompt.md": "./dist/json-ai-prompt.md",
24
33
  "./theme.css": "./dist/theme.css",
25
34
  "./themes/dsm7.css": "./dist/themes/dsm7.css",
26
35
  "./themes/dsm7-embedded.css": "./dist/themes/dsm7-embedded.css",
@@ -32,7 +41,10 @@
32
41
  },
33
42
  "dependencies": {
34
43
  "@internationalized/date": "3.12.4",
35
- "@lucide/vue": "1.48.0"
44
+ "@json-render/core": "0.21.0",
45
+ "@json-render/vue": "0.21.0",
46
+ "@lucide/vue": "1.48.0",
47
+ "zod": "^4.6.5"
36
48
  },
37
49
  "engines": {
38
50
  "node": ">=20"