@synopackageland/sdk 0.49.5 → 0.50.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 CHANGED
@@ -17,7 +17,7 @@ App's `backend/go.mod`.
17
17
 
18
18
  `createDsmSession({read, write, fetch})` provides DSM session carriers for a
19
19
  desktop window or an Application Portal page. `ready()` returns the desktop's
20
- current token, or makes one shared, five-second, same-origin request to
20
+ current token, or makes one shared, bounded, same-origin request to
21
21
  `webman/login.cgi?enable_syno_token=yes`. `token()` reads the current carrier.
22
22
  The optional `write` updates DSM's in-memory session for its own WebAPI client.
23
23
  Nothing is persisted. Recovery failure leaves the backend's existing
@@ -25,6 +25,23 @@ Nothing is persisted. Recovery failure leaves the backend's existing
25
25
  both the cookie and token. The CLI embeds this factory from the selected SDK
26
26
  into every generated native window, including Go-only Apps.
27
27
 
28
+ An old page can retain a rejected token after another tab signs in again.
29
+ `refresh(rejectedToken)` forces same-origin recovery while sharing concurrent
30
+ requests. `createClient({sessionRetry: {session, operations}})` opts in to one
31
+ retry for trusted `effect: read`, `kind: rpc` operations only. Writes, AI,
32
+ streams and batches never replay; the default remains off because the client
33
+ cannot infer operation effects. Existing `ctx.call` is not automatically wrapped.
34
+ See [HTTPS and session recovery](../docs/https-and-session-recovery.md) for the
35
+ API, cancellation/deadline behavior and the CLI skill link.
36
+
37
+ Many Apps can use HTTP. Only Apps needing secure context, pairing or a strict
38
+ HTTPS Origin policy should require it. Opt-in Go `api.RequireSecureOrigin`
39
+ returns `https_required`; render `identityCopy(locale).httpsTitle` and
40
+ `errors.https_required` with instructions to reopen through HTTPS.
41
+ `readDsmHttpsURL(ctx, location.href)` discovers `dms_https` through
42
+ `readDsmPorts` only for an administrator. Ordinary/unknown users get no
43
+ PortInfo request and should see instructions only. No port is guessed.
44
+
28
45
  ## DSM WebAPI
29
46
 
30
47
  Every CLI-generated DSM window provides
@@ -48,7 +65,8 @@ native download form, which cannot set a header, puts it in its same-origin
48
65
  query.
49
66
 
50
67
  `readDsmPorts(ctx)` already prefers this channel and returns `[]` on refusal,
51
- unavailable data or its five-second deadline. It uses a same-origin token
68
+ unavailable data or its request deadline. Call it only for an administrator;
69
+ PortInfo can refuse ordinary sessions or close their DSM window. It uses a same-origin token
52
70
  request only without a window channel. `createNasProvider(ctx)` and executor
53
71
  scope selection also use `ctx.webApi` for File Station JSON calls, retaining
54
72
  App admission, validation, cancellation and deadlines. A channel refusal never
@@ -176,8 +194,7 @@ const ctx = createStandaloneContext({locale: 'zh-TW', call: (method, params) =>
176
194
 
177
195
  The default logo and app name return to the first entry in `pages` (including a
178
196
  hidden entry) through `v-model`. With no pages, activation changes no selection.
179
- This needs UI 0.14.3 or later; older supported
180
- UI versions keep their static brand. Pointer, Enter and Space share the same
197
+ Use a matching UI release with brand activation support. Pointer, Enter and Space share the same
181
198
  native button action; the phone drawer closes on activation.
182
199
 
183
200
  `@brand` receives the original `MouseEvent`. To replace the default selection,
@@ -195,13 +212,39 @@ This does not change the DSM window title bar or DSM's own Portal header.
195
212
  a `title` the App has already translated, and optionally an `icon`, a `group`
196
213
  label (pages with the same label sit together), a `badge`, a `component` to
197
214
  render while it is the current page, `hidden` (reachable through `v-model`
198
- but not in the sidebar), its own `description`, `placement: 'bottom'` for a
199
- page the sidebar lists last, after every group (an App's settings), and
200
- `header: false` for a page that draws its own header. `fill: true` gives the page's body the height under
201
- its header and notices; the page does not scroll, so the content scrolls
202
- itself (a treemap, a table paged by height, a log viewer). The shell then
203
- holds to `100dvh`, so a phone's address bar does not leave a gap. The built-in `'status'` and `'background'`
204
- may sit in the same list.
215
+ but not in the sidebar), `placement: 'bottom'` for a page the sidebar lists
216
+ last, after every group (an App's settings), `header` and `fill`. The built-in
217
+ `'status'` and `'background'` may sit in the same list.
218
+
219
+ ### Page title and actions
220
+
221
+ The sidebar already names the page, so a page shows no title of its own and no
222
+ subtitle. A visually hidden `h1` with the page's `title` still names it for
223
+ screen readers. The `#actions` slot, `PageActions`, the `#meta` slot and
224
+ `PageInfo` sit in a bar above the page: notes at the start, actions at the
225
+ end. The bar stays in place while the page scrolls and shows only while it
226
+ holds something. `header: true` shows the shell's `PageHeader` with the title
227
+ and puts the notes and actions on its line instead. `header: false` is for a
228
+ page that draws its own header: the shell adds no title, and its actions still
229
+ go to the bar.
230
+
231
+ ### Scrolling
232
+
233
+ The navigation, the page bar, a page's tabs and a settings footer stay in
234
+ place; only the content scrolls, so no scrollbar runs past them. A page
235
+ scrolls as a whole by default. A page that shows a `SettingsPage` or a
236
+ `SectionTabs fill` fills the height instead: the tab strip stays at the top,
237
+ the footer at the bottom edge of the window, and only the `SettingsPage` body
238
+ scrolls (a tab that is not a `SettingsPage` scrolls under the strip). This
239
+ works through the App's own wrappers, such as a template's
240
+ `.integration-app > [data-page]`, with no CSS in the App. A page that shows a
241
+ settings footer has no space under it, whether or not it sits in tabs.
242
+
243
+ `fill: true` makes any page fill: the page does not scroll and the content
244
+ scrolls itself (a treemap, a table paged by height, a log viewer). `fill: false`
245
+ keeps a page scrolling as a whole even when it shows a `SettingsPage`. A filling
246
+ shell holds to `100dvh`, so a phone's address bar does not leave a gap. On a
247
+ phone the same rules give one scrolling area, with the footer in view.
205
248
 
206
249
  ```vue
207
250
  <AppShell v-model="page" :ctx="ctx" :locale="ctx.locale" :pages="[
@@ -216,9 +259,10 @@ may sit in the same list.
216
259
  ```
217
260
 
218
261
  A page passed as `component` cannot fill the shell's `#actions` slot. It puts
219
- its own header buttons, such as its refresh, in `PageActions` anywhere in its
220
- template: they show in the page header before the App's `#actions` and leave
221
- with the page. Outside an `AppShell`, `PageActions` renders nothing.
262
+ its own buttons, such as its refresh, in `PageActions` anywhere in its
263
+ template: they show in the page bar (or the page header) before the App's
264
+ `#actions` and leave with the page. Outside an `AppShell`, `PageActions`
265
+ renders nothing.
222
266
 
223
267
  ```vue
224
268
  <!-- PackagesPage.vue -->
@@ -226,7 +270,8 @@ with the page. Outside an `AppShell`, `PageActions` renders nothing.
226
270
  ```
227
271
 
228
272
  A page's secondary notes take no row of their own: `PageInfo` puts them behind
229
- one info button on the title line, after the title and before the App's `#meta`.
273
+ one info button at the start of the page bar (on the title line with
274
+ `header: true`), before the App's `#meta`.
230
275
  The button is named "Notes (2)" in the shell's language (`label` names it
231
276
  otherwise); a click, or focus from the keyboard, opens the notes below it from
232
277
  its start, inside the App's window, and Escape closes them. The default slot
@@ -243,9 +288,8 @@ tested on its own provides `PageInfoTarget` as it does `PageActionsTarget`.
243
288
  horizontal bar in web and DSM window layouts. The `sidebar-footer` slot (or
244
289
  the account supplied by `ctx`) follows the navigation; mobile keeps its drawer.
245
290
  Omit `navigation` to keep the installed UI's default sidebar, or pass
246
- `navigation="sidebar"` explicitly. Top navigation requires
247
- **@synopackageland/ui 0.13.1 or later**; older supported UI peers keep their
248
- existing layout. Sidebar width and collapse preferences are unused in top mode.
291
+ `navigation="sidebar"` explicitly. Use a matching `@synopackageland/ui` release
292
+ with top navigation support. Sidebar width and collapse preferences are unused in top mode.
249
293
 
250
294
  ```vue
251
295
  <AppShell v-model="page" :locale="ctx.locale" :ctx="ctx" :pages="pages" navigation="top" />
@@ -263,22 +307,37 @@ surface at every width (web, DSM window and mobile):
263
307
  <AppShell v-model="page" :locale="ctx.locale" :ctx="ctx" :pages="pages" page-surface="none" />
264
308
  ```
265
309
 
266
- `none` requires **@synopackageland/ui 0.12.5 or later**. The SDK still accepts
267
- older UI peers and forwards the value unchanged, but those releases do not
268
- provide the no-surface/no-horizontal-gutter behavior. Other shell spacing,
310
+ `none` requires a matching `@synopackageland/ui` release with no-surface support.
311
+ Other shell spacing,
269
312
  including the body bottom padding, stays unchanged.
270
313
 
271
314
  Moving to another page starts it at the top; `keep-scroll` keeps the
272
- position. The `toolbar` slot sits above the page (in `AppLayout`'s toolbar)
273
- and `notices` in the page, under its header and inside the same padding, and
274
- scrolls with it. `activePage(current, pages)` keeps a selection that still exists and
315
+ position. The `toolbar` slot sits above the page (in `AppLayout`'s toolbar,
316
+ above the page bar) and `notices` at the top of the page, inside the same
317
+ padding, and scrolls with it. `activePage(current, pages)` keeps a selection that still exists and
275
318
  otherwise picks the first page the sidebar shows. A router drives the shell
276
319
  through `v-model`:
277
320
  `:model-value="route.name" @update:model-value="name => router.push({name})"`.
278
321
 
322
+ ### Moving an existing App to these defaults
323
+
324
+ - A page that should keep its visible title sets `header: true`. Pages carry no
325
+ subtitle: drop `description` from the page entries (it is no longer shown).
326
+ - An App that set `header: false` only to hide the title, and added its own
327
+ hidden `h1`, can drop both: the shell now names the page itself. Buttons it
328
+ moved into its own `#toolbar` `AppToolbar` can go back to `#actions`.
329
+ - Remove CSS that passed the height to a settings page, such as
330
+ `.integration-app > [data-page][data-fill] { flex: 1 1 0%; min-height: 0 }`
331
+ and the same rule for its child, and CSS that removed the space under a
332
+ settings footer (`.app-page-content:has(...) { padding-bottom: 0 }`).
333
+ - Remove `fill: true` from pages whose content is a `SettingsPage` or a
334
+ `SectionTabs fill`; keep it for pages that scroll their own content, such as
335
+ a split view or a list paged by height. A page that must keep scrolling as a
336
+ whole while it shows a `SettingsPage` sets `fill: false`.
337
+
279
338
  Render the shell immediately, with [`EntryGuard`](../docs/app-entry.md) checking
280
339
  session/admin access in the background, and pass `loading` while startup reads
281
- run. The sidebar and title show immediately; the page body shows
340
+ run. The sidebar and the page bar show immediately; the page body shows
282
341
  its loading area and the actions slot waits. With the current CLI/SDK, use
283
342
  `ctx.batch` for independent startup reads so they share one HTTP request and
284
343
  App process:
@@ -503,8 +562,8 @@ differ provides its adapter there, and the dialog reads as its settings
503
562
  page does.
504
563
 
505
564
  A full page, or a window DSM left open, keeps running the UI it loaded across
506
- a package upgrade until the page reloads (CLI 0.33.2+ windows opened after
507
- the upgrade load the new UI themselves). The CLI's DSM window (CLI 0.33.2+) gives `ctx.uiUpdate`: its `check()` reads the package's `ui/config`
565
+ a package upgrade until the page reloads; windows opened after
566
+ the upgrade load the new UI themselves. The CLI's DSM window gives `ctx.uiUpdate`: its `check()` reads the package's `ui/config`
508
567
  without the browser cache and answers whether the installed build replaced
509
568
  the window this page loaded. `AppShell` given that `ctx` shows
510
569
  `UiUpdateNotice` above every page once it answers yes: one line, Updated.