uniweb 1.2.0 → 1.3.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/package.json +8 -8
- package/partials/agents.md +59 -7
- package/src/backend/client.js +3 -1
- package/src/commands/status.js +93 -4
- package/src/framework-index.json +15 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
"js-yaml": "^4.1.0",
|
|
42
42
|
"prompts": "^2.4.2",
|
|
43
43
|
"tar": "^7.0.0",
|
|
44
|
-
"@uniweb/
|
|
45
|
-
"@uniweb/
|
|
46
|
-
"@uniweb/
|
|
47
|
-
"@uniweb/
|
|
44
|
+
"@uniweb/core": "~1.2.0",
|
|
45
|
+
"@uniweb/semantic-parser": "~1.6.0",
|
|
46
|
+
"@uniweb/schemas": "~1.1.0",
|
|
47
|
+
"@uniweb/content-writer": "~1.1.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@uniweb/
|
|
51
|
-
"@uniweb/
|
|
52
|
-
"@uniweb/
|
|
50
|
+
"@uniweb/semantic-parser": "~1.6.0",
|
|
51
|
+
"@uniweb/content-reader": "~1.3.0",
|
|
52
|
+
"@uniweb/build": "~1.2.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependenciesMeta": {
|
|
55
55
|
"@uniweb/build": {
|
package/partials/agents.md
CHANGED
|
@@ -540,12 +540,13 @@ Sizing and loading ride on the image: `{width=800 height=600 loading=lazy fit=co
|
|
|
540
540
|
|
|
541
541
|
**Separators are forgiving.** `:` works wherever `=` does, and pairs may be separated by whitespace, a comma, or both — `{role=banner width=1200}`, `{role:banner, width:1200}` and `{role:banner,width:1200}` are identical. `=` and spaces are canonical, and that is what gets written back on save. Two rules keep it unambiguous: the separator must **touch** the key (`{note:warning}` is one pair, `{note : warning}` is two flags), and a value containing a **comma** must be quoted (`{style="a, b"}`). A value containing a colon needs no quoting — only the first colon separates, so `{href:https://example.com}` is fine.
|
|
542
542
|
|
|
543
|
-
Standalone links (alone on a line)
|
|
543
|
+
Standalone links (alone on a line) are promoted to `content.links[]` — usually drawn as buttons, which is the component's choice. Inline links stay as `<a>` tags inside `content.paragraphs[]`. Multiple links sharing a paragraph are all promoted:
|
|
544
544
|
|
|
545
545
|
```markdown
|
|
546
546
|
[Primary](/start) ← standalone → content.links[0]
|
|
547
547
|
[One](/a) [Two](/b) ← links-only paragraph → both in content.links[]
|
|
548
548
|
Check out [this](/a) link. ← inline → stays in paragraphs as an <a> tag
|
|
549
|
+
[Go](/start){button} ← a link with role "button" (also `button:/start`), and a `variant`
|
|
549
550
|
```
|
|
550
551
|
|
|
551
552
|
### Inline text styling
|
|
@@ -557,12 +558,18 @@ This is [less important]{muted} context.
|
|
|
557
558
|
|
|
558
559
|
`accent` and `callout` (both accent-colored + bold) and `muted` (subtle) are built-in defaults that adapt to context. `--accent` is your brand color unless you declare a separate `colors.accent`, and resolves to the shade you authored — not a darkened one, since accent is decorative emphasis rather than body-size link text. Components receive HTML strings with spans applied: `<span accent="true">faster</span>`.
|
|
559
560
|
|
|
560
|
-
Sites can adjust these or add named styles in `theme.yml`'s `inline:` section. Overrides merge **property by property**, so declare only what differs (`accent: { font-weight: inherit }` keeps the default color); to drop a default property rather than change it, give it a neutral value (`initial`, `inherit`, `unset`).
|
|
561
|
+
Sites can adjust these or add named styles in `theme.yml`'s `inline:` section — a name is a letter, then letters, digits, `_` or `-`, and never starts with `on` (a handler's prefix, so it is dropped). Overrides merge **property by property**, so declare only what differs (`accent: { font-weight: inherit }` keeps the default color); to drop a default property rather than change it, give it a neutral value (`initial`, `inherit`, `unset`).
|
|
561
562
|
|
|
562
563
|
> **On a site synced with Uniweb Cloud, the built-in style names round-trip *editable*** — an author who opens a page containing `[Get started]{accent}` gets the editor's own styling control for it, and can change or remove it like any text they styled there.
|
|
563
564
|
>
|
|
564
565
|
> **A name the editor doesn't know is preserved exactly, but not editable** — a style you defined yourself in `inline:`, two names on one span, or a `class`/`id` set. Nothing is lost and nothing is approximated: it survives every edit and save untouched, the editor simply offers no control for it. That is a permanent property rather than a gap, because the name set is open by design — you can add styles the editor has no way to know about.
|
|
565
566
|
|
|
567
|
+
### HTML is not content
|
|
568
|
+
|
|
569
|
+
Content is markdown. Components decide how it renders, so a tag written in it — `<strong>`, `<span class=…>`, `<div>` — is not markup: it **shows as written**, and the build names the file. Use markdown's own forms instead: `**bold**`, a named style (`[text]{accent}`), a link.
|
|
570
|
+
|
|
571
|
+
The one exception is `<br/>`, a line break where markdown has no other spelling — inside a heading. For a headline split across lines, the multi-line heading above usually says it better.
|
|
572
|
+
|
|
566
573
|
### Fenced code: data blocks vs snippets
|
|
567
574
|
|
|
568
575
|
Fenced code serves three purposes depending on its info string: `yaml:`/`json:` for data, `md:` for a named kind of prose (see *Concept blocks*), and a bare language for a code sample.
|
|
@@ -1072,6 +1079,8 @@ import { H1, H2, H3, P, Span } from '@uniweb/kit'
|
|
|
1072
1079
|
<Span text={listItem.paragraphs[0]} className="text-subtle" />
|
|
1073
1080
|
```
|
|
1074
1081
|
|
|
1082
|
+
These strings are safe to render as HTML because the parser builds them from structure and escapes every character of the text. **A string from anywhere else is not** — a field a member wrote, a value an API returned. Render it as text: `{value}` in JSX, or `<PlainText text={value} />`.
|
|
1083
|
+
|
|
1075
1084
|
Kit provides `H1`–`H6`. These render their own HTML tag — **don't wrap them**: `<H2 text={…} />`, not `<h2><H2 text={…} /></h2>`.
|
|
1076
1085
|
|
|
1077
1086
|
**Full content rendering** (article/docs sections where the author controls the flow):
|
|
@@ -2509,12 +2518,14 @@ const { entity } = useEntity({ schema: '@/track', uuid: record.uuid })
|
|
|
2509
2518
|
const sessions = entity?.hydrated?.items.filter((item) => item.section === 'sessions')
|
|
2510
2519
|
```
|
|
2511
2520
|
|
|
2521
|
+
⛔ **What a member wrote is text.** Render a field as `{record.brief.name}` or `<PlainText text={…} />` — never `<P text>`, `<H1>`–`<H6>` or `SafeHtml`, which render HTML: a member who typed `<img src=x onerror=…>` would run it as whoever views the page. A rich-text field (a ProseMirror document) renders through kit's `Render`, which escapes its text.
|
|
2522
|
+
|
|
2512
2523
|
⛔ **A listed record is a summary, not the entity.** `record.brief` holds the brief
|
|
2513
2524
|
section's fields (`record.brief.name`, not `record.name`), and a record carries **no
|
|
2514
2525
|
items**. Read one entity for its items. Each item carries `section`, its section's name
|
|
2515
2526
|
(the word a write takes), and `id`, which is what the writer takes to update, move or
|
|
2516
2527
|
remove it. `scope: 'mine'` lists only the viewer's own records; without it, a list also
|
|
2517
|
-
holds what was shared with the viewer.
|
|
2528
|
+
holds what was shared with the viewer, and what a role of theirs reaches.
|
|
2518
2529
|
|
|
2519
2530
|
⭐ **`absent` and an empty `ready` are different answers, and confusing them is the
|
|
2520
2531
|
mistake to avoid.** `absent` = there is no live source (no `backend` service, or nobody signed
|
|
@@ -2549,9 +2560,11 @@ await writer.remove(itemId)
|
|
|
2549
2560
|
Gate your controls on what the viewer may do — a record's own `can_edit` from a
|
|
2550
2561
|
single-entity read — but **never rely on that for safety**. A foundation runs with
|
|
2551
2562
|
exactly the viewer's own authority, so hiding a button hides a button. ⛔ **Not on
|
|
2552
|
-
`viewer.roles
|
|
2553
|
-
|
|
2554
|
-
operator nothing
|
|
2563
|
+
`viewer.roles` alone:** on a site a session holds its roles as a reader's until it is
|
|
2564
|
+
elevated (*Acting as an administrator*, below), so a gate on `system_admin` shows the
|
|
2565
|
+
operator nothing until then — and a role never says what the session may do to one
|
|
2566
|
+
record. Draw a control that acts through a role from `useElevation().rolesInFull`, and
|
|
2567
|
+
let the record answer.
|
|
2555
2568
|
|
|
2556
2569
|
What protects content is the **entry**: a member writes their own entries and not
|
|
2557
2570
|
another's, and the store enforces it. **Models are open** — anyone signed in may
|
|
@@ -2570,6 +2583,44 @@ sections:
|
|
|
2570
2583
|
`append_only` holds against the item's own author. That is the difference between a
|
|
2571
2584
|
permission model and a CSS one.
|
|
2572
2585
|
|
|
2586
|
+
A schema can also open entries by relationship, and keep them from being shared:
|
|
2587
|
+
|
|
2588
|
+
```yaml
|
|
2589
|
+
# foundation/schemas/submission.yml
|
|
2590
|
+
grantable: false # no entry is shared one at a time
|
|
2591
|
+
fields:
|
|
2592
|
+
course: { ref: '@/course', readable_with: edit } # the course's editors read it
|
|
2593
|
+
```
|
|
2594
|
+
|
|
2595
|
+
`readable_with` — `read`, `edit` or `admin`, and only on a reference — opens the entry, to
|
|
2596
|
+
read and never to change, to whoever holds that level on the entry the reference points to:
|
|
2597
|
+
a submission's `course` at `edit` reaches the course's staff and none of its students. A row
|
|
2598
|
+
read that way comes back with `via: 'rbac'`. It is one step, and a backend refuses adding it
|
|
2599
|
+
to a field entries already fill. `grantable: false` keeps entries from being shared with a
|
|
2600
|
+
person or a team; omitted, they can be.
|
|
2601
|
+
|
|
2602
|
+
### Acting as an administrator
|
|
2603
|
+
|
|
2604
|
+
On a site, an administrator's session reads what their role reaches — the operator reads
|
|
2605
|
+
every member's records — and changes nothing through the role until they confirm it is
|
|
2606
|
+
them. Their own records they edit, delete and share as always.
|
|
2607
|
+
|
|
2608
|
+
```jsx
|
|
2609
|
+
const { available, elevated, until, elevate, lower, error } = useElevation()
|
|
2610
|
+
// available: the account holds a role that does more than read — offer it only then
|
|
2611
|
+
// elevate({ password, code }) — code when the account has a second factor; a refusal is error.kind === 'auth'
|
|
2612
|
+
// elevated, until: true while it lasts, and when it ends; lower() ends it early
|
|
2613
|
+
```
|
|
2614
|
+
|
|
2615
|
+
- It lasts as long as the backend decides; `elevated` turns false by itself at `until`,
|
|
2616
|
+
and elevating again starts the time again. Nothing says whether the account has a
|
|
2617
|
+
second factor, so offer the code field to whoever might.
|
|
2618
|
+
- While it lasts `viewer.roles` names the role, and every record read before is read
|
|
2619
|
+
again, so `entity.can_edit` answers for the session as it is now.
|
|
2620
|
+
- An account with no password: `elevateWithProvider(provider)` goes to the provider's
|
|
2621
|
+
sign-in and back to the page.
|
|
2622
|
+
- It does not open the backend's administration — the Admin link does.
|
|
2623
|
+
|
|
2573
2624
|
### The Admin link
|
|
2574
2625
|
|
|
2575
2626
|
```jsx
|
|
@@ -2581,7 +2632,8 @@ const { adminHref } = useSession()
|
|
|
2581
2632
|
the operator, and whoever else administers it — and is `null` for everyone else. It
|
|
2582
2633
|
takes them there already signed in. ⛔ **Navigate to it, never `fetch` it**: the answer is
|
|
2583
2634
|
a redirect carrying a one-time credential no script on the site is to hold. It decides
|
|
2584
|
-
what to offer, not what is allowed — on the site the operator acts
|
|
2635
|
+
what to offer, not what is allowed — on the site the operator acts through their role
|
|
2636
|
+
only while elevated.
|
|
2585
2637
|
|
|
2586
2638
|
### The links the backend mails
|
|
2587
2639
|
|
package/src/backend/client.js
CHANGED
|
@@ -843,7 +843,9 @@ export class BackendClient {
|
|
|
843
843
|
* shipped backend-side — collab backend↔framework):
|
|
844
844
|
* { published: boolean, last_pushed_at?: string, last_published_at?: string, draft_dirty?: boolean }
|
|
845
845
|
* `draft_dirty` = never-published, or the synced draft changed since the last
|
|
846
|
-
* publish ("pushed but not published").
|
|
846
|
+
* publish ("pushed but not published"). Since 2026-10-13 it also carries `editors`:
|
|
847
|
+
* who has the site open in the app right now, and on what — `status --remote` shows
|
|
848
|
+
* it (`describeEditors`, commands/status.js).
|
|
847
849
|
*
|
|
848
850
|
* ⭐ **The backend also serves a LIVE-SITE record here, and nothing in this CLI reads
|
|
849
851
|
* it yet** (shipped 2026-08-29; documented here so it is not lost twice):
|
package/src/commands/status.js
CHANGED
|
@@ -7,15 +7,17 @@
|
|
|
7
7
|
* `uniweb push` runs) — no auth, no backend round-trip.
|
|
8
8
|
*
|
|
9
9
|
* `--remote` adds the backend signals (may prompt for login, like `git fetch`):
|
|
10
|
-
* - whether the synced draft differs from what's live (publish needed),
|
|
11
|
-
* - whether a newer foundation version is registered than the site pins
|
|
10
|
+
* - whether the synced draft differs from what's live (publish needed),
|
|
11
|
+
* - whether a newer foundation version is registered than the site pins, and
|
|
12
|
+
* - who has the site open in the app right now, and on what (`describeEditors`).
|
|
12
13
|
* Those use ASSUMED endpoints (see kb shipping-verbs-and-freshness.md §6.5); until
|
|
13
14
|
* the backend exposes them, `--remote` degrades silently to the local view.
|
|
14
15
|
*
|
|
15
16
|
* Usage:
|
|
16
17
|
* uniweb status Sync identity + unpushed content + foundation ref (local)
|
|
17
18
|
* uniweb status --remote Also: whether the backend still holds the site, draft-vs-live,
|
|
18
|
-
*
|
|
19
|
+
* a newer-registered-foundation check, and who has it open in
|
|
20
|
+
* the app
|
|
19
21
|
* uniweb status --json One JSON line (adds a `remote` object under --remote, whose
|
|
20
22
|
* `site_state` is live / gone / unknown — `gone` only on the
|
|
21
23
|
* backend's own word that it holds no such site)
|
|
@@ -28,7 +30,7 @@ import { join } from 'node:path'
|
|
|
28
30
|
import yaml from 'js-yaml'
|
|
29
31
|
|
|
30
32
|
import { resolveSiteDir } from './deploy.js'
|
|
31
|
-
import { probeUnpushed } from '../backend/site-sync.js'
|
|
33
|
+
import { probeUnpushed, readItemUuids, unitFilesOf } from '../backend/site-sync.js'
|
|
32
34
|
import { resolveWorkspace } from '../backend/workspace.js'
|
|
33
35
|
import {
|
|
34
36
|
BackendClient,
|
|
@@ -75,6 +77,79 @@ function foundationRef(siteYml) {
|
|
|
75
77
|
return typeof f === 'string' ? f : f.ref || null
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
// What an item that this copy has not synced is called — by its Section on the site.
|
|
81
|
+
const ITEM_KIND = { pages: 'a page', page_sections: 'a section', layout_sections: 'a layout section' }
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Who has the site open in the app right now, and on what — the status's `editors`
|
|
85
|
+
* (backend, since 2026-10-13), as lines a developer can follow up on. Each tab's item is
|
|
86
|
+
* named by the file it is in this copy, and by its kind and route when this copy has not
|
|
87
|
+
* synced it (`fileByItemUuid`).
|
|
88
|
+
*
|
|
89
|
+
* ⛔ Two ways not to read it. `changed_at` is the item's last change by ANYONE — this
|
|
90
|
+
* author, another, a push — so it says when the item last changed, never what this
|
|
91
|
+
* person did. And it is presence, not a lock: nothing refuses or warns a push over it.
|
|
92
|
+
*
|
|
93
|
+
* @param {object[]} editors - `[{ person: { uuid, handle, brief }, tabs: [{ page, item }] }]`
|
|
94
|
+
* @param {object} [opts]
|
|
95
|
+
* @param {(uuid: string) => string|null} [opts.fileOf] - an item's file in this copy
|
|
96
|
+
* @param {number} [opts.now] - for "3 min ago"
|
|
97
|
+
* @returns {{ person: string, tabs: string[] }[]} a tab with nothing open is left out
|
|
98
|
+
*/
|
|
99
|
+
export function describeEditors(editors, { fileOf = () => null, now = Date.now() } = {}) {
|
|
100
|
+
return editors.map((editor) => ({
|
|
101
|
+
person: personLabel(editor?.person),
|
|
102
|
+
tabs: (editor?.tabs || []).map((tab) => tabLabel(tab, fileOf, now)).filter(Boolean)
|
|
103
|
+
}))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function personLabel(person) {
|
|
107
|
+
const name = person?.brief?.display_name
|
|
108
|
+
const handle = person?.handle ? `@${person.handle}` : null
|
|
109
|
+
if (name && handle) return `${name} (${handle})`
|
|
110
|
+
return name || handle || `someone (${person?.uuid || 'unnamed'})`
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function tabLabel(tab, fileOf, now) {
|
|
114
|
+
const item = tab?.item
|
|
115
|
+
if (!item?.uuid) return null
|
|
116
|
+
const route = tab.page?.route || null
|
|
117
|
+
const file = fileOf(item.uuid)
|
|
118
|
+
const what = file
|
|
119
|
+
? `${file}${route ? ` (${route})` : ''}`
|
|
120
|
+
: `${ITEM_KIND[item.section] || 'an item'} not in this copy${route ? ` (${route})` : ''}`
|
|
121
|
+
const when = sinceLabel(item.changed_at, now)
|
|
122
|
+
return when ? `${what} · changed ${when}` : what
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function sinceLabel(iso, now) {
|
|
126
|
+
const t = Date.parse(iso)
|
|
127
|
+
if (Number.isNaN(t)) return null
|
|
128
|
+
const min = Math.floor(Math.max(0, now - t) / 60_000)
|
|
129
|
+
if (min < 1) return 'just now'
|
|
130
|
+
if (min < 60) return `${min} min ago`
|
|
131
|
+
const h = Math.floor(min / 60)
|
|
132
|
+
if (h < 24) return `${h} h ago`
|
|
133
|
+
const d = Math.floor(h / 24)
|
|
134
|
+
return `${d} day${d === 1 ? '' : 's'} ago`
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* An item's file in this copy, from its uuid: `sync.json` keeps each unit's uuid on
|
|
139
|
+
* `backend` by its path (`items`, banked from every push and pull), and `unitFilesOf`
|
|
140
|
+
* finds the file the author keeps it in (`1-hero.md` for `hero.md`). Null for an item
|
|
141
|
+
* this copy has not synced — one added in the app since its last pull.
|
|
142
|
+
*/
|
|
143
|
+
export function fileByItemUuid(siteDir, backend) {
|
|
144
|
+
const unitOf = new Map(
|
|
145
|
+
Object.entries(readItemUuids(siteDir, backend)).map(([unit, uuid]) => [String(uuid).toLowerCase(), unit])
|
|
146
|
+
)
|
|
147
|
+
return (uuid) => {
|
|
148
|
+
const unit = unitOf.get(String(uuid).toLowerCase())
|
|
149
|
+
return unit ? (unitFilesOf(siteDir, unit)[0] ?? unit) : null
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
78
153
|
// A versioned registry ref `@org/name@1.2.3` → its scoped name `@org/name` and
|
|
79
154
|
// pinned version `1.2.3`. A bare/local/unversioned ref → nulls.
|
|
80
155
|
function splitFoundationRef(fnd) {
|
|
@@ -263,6 +338,20 @@ export async function status(args = []) {
|
|
|
263
338
|
)
|
|
264
339
|
}
|
|
265
340
|
}
|
|
341
|
+
// An array is the backend's answer, `[]` included; no key is a backend that does not
|
|
342
|
+
// say, and then neither do we.
|
|
343
|
+
if (Array.isArray(site?.editors)) {
|
|
344
|
+
if (!site.editors.length) {
|
|
345
|
+
say.dim('Nobody has this site open in the app.')
|
|
346
|
+
} else {
|
|
347
|
+
say.info('Open in the app now:')
|
|
348
|
+
const fileOf = fileByItemUuid(siteDir, probeBackend)
|
|
349
|
+
for (const { person, tabs } of describeEditors(site.editors, { fileOf })) {
|
|
350
|
+
console.log(` ${person}${tabs.length ? '' : ', with nothing open'}`)
|
|
351
|
+
for (const tab of tabs) console.log(` ${c.dim}${tab}${c.reset}`)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
266
355
|
if (
|
|
267
356
|
fdnLatest?.latest_version &&
|
|
268
357
|
fndVersion &&
|
package/src/framework-index.json
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-10-
|
|
3
|
+
"generatedAt": "2026-10-15T01:14:19.629Z",
|
|
4
4
|
"packages": {
|
|
5
5
|
"@uniweb/api": {
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.2.0"
|
|
7
7
|
},
|
|
8
8
|
"@uniweb/build": {
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.2.0"
|
|
10
10
|
},
|
|
11
11
|
"@uniweb/content-reader": {
|
|
12
|
-
"version": "1.
|
|
12
|
+
"version": "1.3.0"
|
|
13
13
|
},
|
|
14
14
|
"@uniweb/content-writer": {
|
|
15
|
-
"version": "1.0
|
|
15
|
+
"version": "1.1.0"
|
|
16
16
|
},
|
|
17
17
|
"@uniweb/core": {
|
|
18
|
-
"version": "1.
|
|
18
|
+
"version": "1.2.0"
|
|
19
19
|
},
|
|
20
20
|
"@uniweb/frame-bridge": {
|
|
21
21
|
"version": "1.0.2"
|
|
22
22
|
},
|
|
23
23
|
"@uniweb/icons": {
|
|
24
|
-
"version": "1.0.
|
|
24
|
+
"version": "1.0.3"
|
|
25
25
|
},
|
|
26
26
|
"@uniweb/kit": {
|
|
27
|
-
"version": "1.
|
|
27
|
+
"version": "1.2.0"
|
|
28
28
|
},
|
|
29
29
|
"@uniweb/loom": {
|
|
30
30
|
"version": "1.1.0"
|
|
31
31
|
},
|
|
32
32
|
"@uniweb/press": {
|
|
33
|
-
"version": "1.1.
|
|
33
|
+
"version": "1.1.1"
|
|
34
34
|
},
|
|
35
35
|
"@uniweb/projections": {
|
|
36
|
-
"version": "1.1.
|
|
36
|
+
"version": "1.1.1"
|
|
37
37
|
},
|
|
38
38
|
"@uniweb/runtime": {
|
|
39
|
-
"version": "1.1.
|
|
39
|
+
"version": "1.1.1"
|
|
40
40
|
},
|
|
41
41
|
"@uniweb/scene": {
|
|
42
42
|
"version": "1.0.1"
|
|
43
43
|
},
|
|
44
44
|
"@uniweb/schemas": {
|
|
45
|
-
"version": "1.0
|
|
45
|
+
"version": "1.1.0"
|
|
46
46
|
},
|
|
47
47
|
"@uniweb/scholar": {
|
|
48
48
|
"version": "1.1.0"
|
|
49
49
|
},
|
|
50
50
|
"@uniweb/semantic-parser": {
|
|
51
|
-
"version": "1.
|
|
51
|
+
"version": "1.6.0"
|
|
52
52
|
},
|
|
53
53
|
"@uniweb/snapshot": {
|
|
54
54
|
"version": "1.1.0"
|
|
@@ -57,10 +57,10 @@
|
|
|
57
57
|
"version": "1.1.0"
|
|
58
58
|
},
|
|
59
59
|
"@uniweb/theming": {
|
|
60
|
-
"version": "1.
|
|
60
|
+
"version": "1.2.0"
|
|
61
61
|
},
|
|
62
62
|
"@uniweb/unipress": {
|
|
63
|
-
"version": "1.1.
|
|
63
|
+
"version": "1.1.1"
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"templates": {
|