@kernhq/module-quire 0.13.1 → 0.15.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/dist/contract/models.d.ts +394 -0
- package/dist/contract/models.d.ts.map +1 -1
- package/dist/contract/models.js +284 -0
- package/dist/contract/models.js.map +1 -1
- package/dist/contract/permissions.d.ts +14 -0
- package/dist/contract/permissions.d.ts.map +1 -1
- package/dist/contract/permissions.js +114 -0
- package/dist/contract/permissions.js.map +1 -1
- package/dist/contract/properties.d.ts +7 -7
- package/dist/contract/router.d.ts +1124 -96
- package/dist/contract/router.d.ts.map +1 -1
- package/dist/contract/router.js +343 -1
- package/dist/contract/router.js.map +1 -1
- package/dist/server/_impl.d.ts +983 -1168
- package/dist/server/_impl.d.ts.map +1 -1
- package/dist/server/_impl.js +308 -0
- package/dist/server/_impl.js.map +1 -1
- package/dist/server/export/html.d.ts +67 -0
- package/dist/server/export/html.d.ts.map +1 -0
- package/dist/server/export/html.js +206 -0
- package/dist/server/export/html.js.map +1 -0
- package/dist/server/export/markdown.d.ts +51 -0
- package/dist/server/export/markdown.d.ts.map +1 -0
- package/dist/server/export/markdown.js +351 -0
- package/dist/server/export/markdown.js.map +1 -0
- package/dist/server/export/pdf.d.ts +20 -0
- package/dist/server/export/pdf.d.ts.map +1 -0
- package/dist/server/export/pdf.js +91 -0
- package/dist/server/export/pdf.js.map +1 -0
- package/dist/server/export/zip.d.ts +31 -0
- package/dist/server/export/zip.d.ts.map +1 -0
- package/dist/server/export/zip.js +158 -0
- package/dist/server/export/zip.js.map +1 -0
- package/dist/server/import/csv.d.ts +77 -0
- package/dist/server/import/csv.d.ts.map +1 -0
- package/dist/server/import/csv.js +263 -0
- package/dist/server/import/csv.js.map +1 -0
- package/dist/server/import/html.d.ts +52 -0
- package/dist/server/import/html.d.ts.map +1 -0
- package/dist/server/import/html.js +472 -0
- package/dist/server/import/html.js.map +1 -0
- package/dist/server/import/markdown.d.ts +63 -0
- package/dist/server/import/markdown.d.ts.map +1 -0
- package/dist/server/import/markdown.js +692 -0
- package/dist/server/import/markdown.js.map +1 -0
- package/dist/server/import/plan.d.ts +70 -0
- package/dist/server/import/plan.d.ts.map +1 -0
- package/dist/server/import/plan.js +761 -0
- package/dist/server/import/plan.js.map +1 -0
- package/dist/server/import/ydoc.d.ts +35 -0
- package/dist/server/import/ydoc.d.ts.map +1 -0
- package/dist/server/import/ydoc.js +91 -0
- package/dist/server/import/ydoc.js.map +1 -0
- package/dist/server/import/zip.d.ts +63 -0
- package/dist/server/import/zip.d.ts.map +1 -0
- package/dist/server/import/zip.js +308 -0
- package/dist/server/import/zip.js.map +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/render.d.ts +158 -0
- package/dist/server/render.d.ts.map +1 -1
- package/dist/server/render.js +240 -0
- package/dist/server/render.js.map +1 -1
- package/dist/server/schema.d.ts +704 -1
- package/dist/server/schema.d.ts.map +1 -1
- package/dist/server/schema.js +254 -1
- package/dist/server/schema.js.map +1 -1
- package/dist/server/services/databases.d.ts +10 -10
- package/dist/server/services/export.d.ts +176 -0
- package/dist/server/services/export.d.ts.map +1 -0
- package/dist/server/services/export.js +822 -0
- package/dist/server/services/export.js.map +1 -0
- package/dist/server/services/import.d.ts +109 -0
- package/dist/server/services/import.d.ts.map +1 -0
- package/dist/server/services/import.js +570 -0
- package/dist/server/services/import.js.map +1 -0
- package/dist/server/services/index.d.ts +32 -1
- package/dist/server/services/index.d.ts.map +1 -1
- package/dist/server/services/index.js +87 -4
- package/dist/server/services/index.js.map +1 -1
- package/dist/server/services/macros.d.ts +83 -0
- package/dist/server/services/macros.d.ts.map +1 -0
- package/dist/server/services/macros.js +488 -0
- package/dist/server/services/macros.js.map +1 -0
- package/dist/server/services/publications.d.ts +2 -1
- package/dist/server/services/publications.d.ts.map +1 -1
- package/dist/server/services/publications.js +43 -4
- package/dist/server/services/publications.js.map +1 -1
- package/dist/server/services/templates.d.ts +135 -0
- package/dist/server/services/templates.d.ts.map +1 -0
- package/dist/server/services/templates.js +897 -0
- package/dist/server/services/templates.js.map +1 -0
- package/dist/server/services/versions.d.ts +13 -1
- package/dist/server/services/versions.d.ts.map +1 -1
- package/dist/server/services/versions.js +3 -1
- package/dist/server/services/versions.js.map +1 -1
- package/migrations/0010_transfers.sql +154 -0
- package/migrations/0011_templates.sql +157 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +5 -5
- package/src/client/components/ExportDialog.svelte +685 -0
- package/src/client/components/ImportDialog.svelte +702 -0
- package/src/client/components/ImportReport.svelte +310 -0
- package/src/client/components/NewSpaceDialog.svelte +77 -8
- package/src/client/components/PageEditor.svelte +80 -0
- package/src/client/components/PagePicker.svelte +264 -0
- package/src/client/components/SaveAsTemplateDialog.svelte +502 -0
- package/src/client/components/SidebarSpaces.svelte +112 -1
- package/src/client/components/TemplatePicker.svelte +437 -0
- package/src/client/i18n.ts +941 -0
- package/src/client/index.ts +51 -0
- package/src/client/mock.ts +592 -0
- package/src/client/module.ts +36 -0
- package/src/client/pages/PageView.svelte +86 -0
- package/src/client/pages/SpacePage.svelte +20 -4
- package/src/client/pages/TransfersPage.svelte +570 -0
- package/src/client/permissions.ts +11 -0
- package/src/client/query.ts +40 -0
- package/src/client/transfers.ts +142 -0
- package/src/contract/models.ts +343 -0
- package/src/contract/permissions.ts +117 -0
- package/src/contract/router.ts +375 -0
package/src/client/module.ts
CHANGED
|
@@ -49,6 +49,27 @@ export const quireClientModule = defineClientModule({
|
|
|
49
49
|
component: () => import('./pages/SpacePage.svelte'),
|
|
50
50
|
permission: QUIRE_PERMISSIONS.spaceView,
|
|
51
51
|
},
|
|
52
|
+
/**
|
|
53
|
+
* Work on its way out and on its way in.
|
|
54
|
+
*
|
|
55
|
+
* Two literal segments, so it beats `/quire/:space` however the two are ordered — the shell
|
|
56
|
+
* scores a match as `literals * 1000 + segments`. Its place in this list is for a reader.
|
|
57
|
+
*
|
|
58
|
+
* **No `permission`, on purpose**, and this is the one route here without one. The screen has
|
|
59
|
+
* two halves behind two independent keys — `quire.page.export` is a member by default,
|
|
60
|
+
* `quire.page.import` is owner-and-admin — so a single gate would either hide it from somebody
|
|
61
|
+
* who may import or show it to somebody who may do neither; each half asks its own key instead.
|
|
62
|
+
* And a gate here would misbehave rather than 404: a route the shell refuses falls through to
|
|
63
|
+
* `/quire/:space`, which would tell the person "Space not found" about a space called
|
|
64
|
+
* *transfers*.
|
|
65
|
+
*/
|
|
66
|
+
{
|
|
67
|
+
path: '/quire/transfers',
|
|
68
|
+
component: () => import('./pages/TransfersPage.svelte'),
|
|
69
|
+
get title() {
|
|
70
|
+
return t('transfers')
|
|
71
|
+
},
|
|
72
|
+
},
|
|
52
73
|
/**
|
|
53
74
|
* Declared before `/quire/:space/:page` for a reader's benefit only — the shell resolves by
|
|
54
75
|
* specificity, not by order, so the two literal segments here beat the one in the page route
|
|
@@ -93,6 +114,21 @@ export const quireClientModule = defineClientModule({
|
|
|
93
114
|
permission: QUIRE_PERMISSIONS.spaceManage,
|
|
94
115
|
run: (ctx) => ctx.navigate('/quire?new=1'),
|
|
95
116
|
},
|
|
117
|
+
/*
|
|
118
|
+
* The palette is the second way to a running job, and the one that works from anywhere.
|
|
119
|
+
* Gated on `page.export` rather than left open: the command list is a menu, and an entry that
|
|
120
|
+
* leads to a screen with both halves empty is an entry that teaches people to ignore the list.
|
|
121
|
+
* Somebody who may import and not export reaches the screen from the import dialog instead.
|
|
122
|
+
*/
|
|
123
|
+
{
|
|
124
|
+
id: 'quire.transfers',
|
|
125
|
+
get label() {
|
|
126
|
+
return t('cmd_transfers')
|
|
127
|
+
},
|
|
128
|
+
icon: 'package',
|
|
129
|
+
permission: QUIRE_PERMISSIONS.pageExport,
|
|
130
|
+
run: (ctx) => ctx.navigate('/quire/transfers'),
|
|
131
|
+
},
|
|
96
132
|
],
|
|
97
133
|
|
|
98
134
|
sidebar: [
|
|
@@ -22,10 +22,12 @@ import { untrack } from 'svelte'
|
|
|
22
22
|
import { getQuireApi } from '../api-instance.js'
|
|
23
23
|
import CommentsPanel from '../components/CommentsPanel.svelte'
|
|
24
24
|
import ConfirmDialog from '../components/ConfirmDialog.svelte'
|
|
25
|
+
import ExportDialog from '../components/ExportDialog.svelte'
|
|
25
26
|
import FavoriteStar from '../components/FavoriteStar.svelte'
|
|
26
27
|
import PageEditor from '../components/PageEditor.svelte'
|
|
27
28
|
import PageLabels from '../components/PageLabels.svelte'
|
|
28
29
|
import PublishDialog from '../components/PublishDialog.svelte'
|
|
30
|
+
import SaveAsTemplateDialog from '../components/SaveAsTemplateDialog.svelte'
|
|
29
31
|
import VersionHistory from '../components/VersionHistory.svelte'
|
|
30
32
|
import { type CoreApi, toPerson } from '../core-api.js'
|
|
31
33
|
import DatabaseView from '../database/DatabaseView.svelte'
|
|
@@ -77,6 +79,19 @@ const membersQuery = createQuery(() => ({
|
|
|
77
79
|
queryFn: () => core.workspaces.members.list({ workspaceId, limit: 200 }),
|
|
78
80
|
}))
|
|
79
81
|
|
|
82
|
+
/*
|
|
83
|
+
* The space by name, for the one sentence that needs it: "offered only in Handbook".
|
|
84
|
+
*
|
|
85
|
+
* The same query key the sidebar already holds, so this is a cache read rather than a request —
|
|
86
|
+
* and the key is what the URL carries, which is not a thing to show somebody in a sentence.
|
|
87
|
+
*/
|
|
88
|
+
const spacesQuery = createQuery(() => ({
|
|
89
|
+
queryKey: quireKeys.spaces(workspaceId),
|
|
90
|
+
enabled: Boolean(workspaceId),
|
|
91
|
+
queryFn: () => api.spaces.list({ workspaceId, includeArchived: false }),
|
|
92
|
+
}))
|
|
93
|
+
const spaceName = $derived((spacesQuery.data ?? []).find((space) => space.key === spaceKey)?.name ?? spaceKey)
|
|
94
|
+
|
|
80
95
|
const query = createQuery(() => ({
|
|
81
96
|
queryKey: quireKeys.page(workspaceId, pageId),
|
|
82
97
|
enabled: Boolean(workspaceId && pageId),
|
|
@@ -266,6 +281,23 @@ async function revert() {
|
|
|
266
281
|
let shareOpen = $state(false)
|
|
267
282
|
const canPublish = $derived(canQuire('pagePublish'))
|
|
268
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Taking this page out as a file.
|
|
286
|
+
*
|
|
287
|
+
* Nothing is loaded for it here: the dialog is the only thing that knows what an export needs, and
|
|
288
|
+
* it asks for the space, the tree and any job already running the moment it opens. This screen's
|
|
289
|
+
* whole part in the feature is a flag and a menu entry, which is what an entry point should be.
|
|
290
|
+
*/
|
|
291
|
+
let exportOpen = $state(false)
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Saving this page as something to write with again.
|
|
295
|
+
*
|
|
296
|
+
* A flag and a menu entry, exactly like the export above: what a template needs to know is the
|
|
297
|
+
* dialog's business, and it reads the starters and the space list itself when it opens.
|
|
298
|
+
*/
|
|
299
|
+
let templateOpen = $state(false)
|
|
300
|
+
|
|
269
301
|
const publicationsQuery = createQuery(() => ({
|
|
270
302
|
queryKey: quireKeys.publications(workspaceId, doc?.spaceId ?? ''),
|
|
271
303
|
enabled: canPublish && Boolean(workspaceId && doc?.spaceId),
|
|
@@ -592,6 +624,42 @@ async function undoTrash(workspace: string, id: string, spaceId: string, title:
|
|
|
592
624
|
},
|
|
593
625
|
]
|
|
594
626
|
: []),
|
|
627
|
+
/*
|
|
628
|
+
* Every kind of page, not only a `page`. A live doc and a database both render through
|
|
629
|
+
* `renderPageDoc` like anything else — a database exports as the table it is — so the
|
|
630
|
+
* restriction that belongs on **Share to the web** does not belong here.
|
|
631
|
+
*
|
|
632
|
+
* `page.export` rather than `page.view`, matching what `exports.start` asks: reading a
|
|
633
|
+
* handbook a page at a time and walking out with the whole thing in a zip are different
|
|
634
|
+
* acts, and an administrator is allowed to think so.
|
|
635
|
+
*/
|
|
636
|
+
...(canQuire('pageExport')
|
|
637
|
+
? [
|
|
638
|
+
{
|
|
639
|
+
id: 'export',
|
|
640
|
+
label: t('export_menu'),
|
|
641
|
+
icon: 'download',
|
|
642
|
+
onSelect: () => (exportOpen = true),
|
|
643
|
+
},
|
|
644
|
+
]
|
|
645
|
+
: []),
|
|
646
|
+
/*
|
|
647
|
+
* `space.manage`, not `page.edit`, matching what `templates.createFromPage` asks — and the
|
|
648
|
+
* asymmetry is the point. A template changes what everybody in the space is offered the
|
|
649
|
+
* next time they make a page, which is the space's furniture rather than this page's
|
|
650
|
+
* content, exactly as a label is. Somebody who may write here is not thereby somebody who
|
|
651
|
+
* may add to everybody else's picker.
|
|
652
|
+
*/
|
|
653
|
+
...(canQuire('spaceManage')
|
|
654
|
+
? [
|
|
655
|
+
{
|
|
656
|
+
id: 'save-template',
|
|
657
|
+
label: t('template_menu'),
|
|
658
|
+
icon: 'clipboard-list',
|
|
659
|
+
onSelect: () => (templateOpen = true),
|
|
660
|
+
},
|
|
661
|
+
]
|
|
662
|
+
: []),
|
|
595
663
|
{
|
|
596
664
|
id: 'watch',
|
|
597
665
|
label: watching ? t('watch_stop') : t('watch'),
|
|
@@ -757,6 +825,24 @@ async function undoTrash(workspace: string, id: string, spaceId: string, title:
|
|
|
757
825
|
|
|
758
826
|
<PublishDialog bind:open={shareOpen} {workspaceId} spaceId={doc.spaceId} page={doc} />
|
|
759
827
|
|
|
828
|
+
<ExportDialog
|
|
829
|
+
bind:open={exportOpen}
|
|
830
|
+
{workspaceId}
|
|
831
|
+
spaceId={doc.spaceId}
|
|
832
|
+
page={{ id: doc.id, title: doc.title }}
|
|
833
|
+
/>
|
|
834
|
+
|
|
835
|
+
{#if canQuire('spaceManage')}
|
|
836
|
+
<SaveAsTemplateDialog
|
|
837
|
+
bind:open={templateOpen}
|
|
838
|
+
{workspaceId}
|
|
839
|
+
spaceId={doc.spaceId}
|
|
840
|
+
{spaceName}
|
|
841
|
+
pageId={doc.id}
|
|
842
|
+
pageTitle={doc.title}
|
|
843
|
+
/>
|
|
844
|
+
{/if}
|
|
845
|
+
|
|
760
846
|
<!--
|
|
761
847
|
The body says nothing about numbers until it knows them. Naming a count before the tree has
|
|
762
848
|
loaded would be the same silent lie in a smaller size — "it goes to the trash" for a page that
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Button, EmptyState, navigation, Skeleton, session } from '@kernhq/ui'
|
|
3
3
|
import { createQuery } from '@tanstack/svelte-query'
|
|
4
4
|
import { getQuireApi } from '../api-instance.js'
|
|
5
|
+
import TemplatePicker from '../components/TemplatePicker.svelte'
|
|
5
6
|
import { t } from '../i18n.js'
|
|
6
7
|
import { canQuire } from '../permissions.js'
|
|
7
8
|
import { quireKeys } from '../query.js'
|
|
@@ -57,6 +58,16 @@ const firstPageId = $derived(
|
|
|
57
58
|
)
|
|
58
59
|
|
|
59
60
|
let creating = $state(false)
|
|
61
|
+
/**
|
|
62
|
+
* The first page of an empty space is the one page most worth offering a template for — a handbook
|
|
63
|
+
* that starts as a how-to is a handbook somebody kept writing. The picker still opens on **Blank
|
|
64
|
+
* page**, so nothing about starting from nothing got slower.
|
|
65
|
+
*/
|
|
66
|
+
let pickerOpen = $state(false)
|
|
67
|
+
|
|
68
|
+
const open = (pageId: string) =>
|
|
69
|
+
void navigation.go(`/${workspaceSlug}/quire/${encodeURIComponent(spaceKey)}/${encodeURIComponent(pageId)}`)
|
|
70
|
+
|
|
60
71
|
async function createFirst() {
|
|
61
72
|
if (!space || creating) return
|
|
62
73
|
creating = true
|
|
@@ -70,9 +81,7 @@ async function createFirst() {
|
|
|
70
81
|
icon: null,
|
|
71
82
|
afterId: null,
|
|
72
83
|
})
|
|
73
|
-
|
|
74
|
-
`/${workspaceSlug}/quire/${encodeURIComponent(spaceKey)}/${encodeURIComponent(created.id)}`,
|
|
75
|
-
)
|
|
84
|
+
open(created.id)
|
|
76
85
|
} finally {
|
|
77
86
|
creating = false
|
|
78
87
|
}
|
|
@@ -96,11 +105,18 @@ async function createFirst() {
|
|
|
96
105
|
<EmptyState icon="file-text" title={t('space_empty')} description={t('space_empty_desc')}>
|
|
97
106
|
{#snippet actions()}
|
|
98
107
|
{#if canQuire('pageCreate')}
|
|
99
|
-
<Button
|
|
108
|
+
<Button aria-busy={creating} onclick={() => (pickerOpen = true)}>{t('new_page')}</Button>
|
|
100
109
|
{/if}
|
|
101
110
|
{/snippet}
|
|
102
111
|
</EmptyState>
|
|
103
112
|
</div>
|
|
113
|
+
<TemplatePicker
|
|
114
|
+
bind:open={pickerOpen}
|
|
115
|
+
{workspaceId}
|
|
116
|
+
spaceId={space.id}
|
|
117
|
+
onBlank={() => void createFirst()}
|
|
118
|
+
onMade={(result) => result.pageId && open(result.pageId)}
|
|
119
|
+
/>
|
|
104
120
|
{/if}
|
|
105
121
|
|
|
106
122
|
<style>
|