mirror of
https://github.com/narrrl/narl-workspace.git
synced 2026-11-03 16:41:27 +01:00
files: a node kind of their own, in the tree beside the notes
A file node points at one media row and owns it the way a document owns
what it embeds, so folder deletes, the media browser and exports keep
working. POST /api/nodes/{id}/files streams uploads into a folder;
imports turn non-markdown files into file nodes; exports put them at
their tree path. The browser previews images, video, audio and text,
offers the rest as a download, and takes files dropped on the list.
Migration 0010 widens the kind CHECK by editing the stored schema, as
SQLite documents for loosening a constraint: sqlx runs migrations in a
transaction, where foreign keys cannot be switched off for a rebuild.
This commit is contained in:
1 parent
3d6944c860
commit
2103edc299
15 files changed
+808
-94
No files matched your search
+15
-1
@@ -6,6 +6,7 @@
|
||||
import Board from './lib/Board.svelte'
|
||||
import CommandLine from './lib/CommandLine.svelte'
|
||||
import DocumentPane from './lib/DocumentPane.svelte'
|
||||
import FilePane from './lib/FilePane.svelte'
|
||||
import Help from './lib/Help.svelte'
|
||||
import Login from './lib/Login.svelte'
|
||||
import Overview from './lib/Overview.svelte'
|
||||
@@ -29,6 +30,7 @@
|
||||
let cmdline = $state<string | null>(null)
|
||||
let cmdGlobal = $state(false)
|
||||
let fileInput = $state<HTMLInputElement>()
|
||||
let treeInput = $state<HTMLInputElement>()
|
||||
let folderInput = $state<HTMLInputElement>()
|
||||
let importInput = $state<HTMLInputElement>()
|
||||
let shell = $state<HTMLDivElement>()
|
||||
@@ -43,6 +45,7 @@
|
||||
|
||||
$effect(() => {
|
||||
hooks.pickFiles = () => fileInput?.click()
|
||||
hooks.pickTreeFiles = () => treeInput?.click()
|
||||
hooks.pickFolder = () => folderInput?.click()
|
||||
hooks.pickArchive = () => importInput?.click()
|
||||
hooks.focusList = () => shell?.focus()
|
||||
@@ -118,6 +121,14 @@
|
||||
})
|
||||
}
|
||||
|
||||
async function put(event: Event) {
|
||||
const input = event.target as HTMLInputElement
|
||||
const files = Array.from(input.files ?? [])
|
||||
input.value = ''
|
||||
if (files.length === 0) return
|
||||
await workspace.guard(() => workspace.putFiles(files))
|
||||
}
|
||||
|
||||
/*
|
||||
* A folder upload is the same multipart body as any other, except that each
|
||||
* file carries the path it had inside the folder — which is what makes the
|
||||
@@ -644,7 +655,9 @@
|
||||
class:focused={!!workspace.open && workspace.pane === 'document'}
|
||||
onpointerdown={() => workspace.open && (workspace.pane = 'document')}
|
||||
>
|
||||
{#if workspace.open}
|
||||
{#if workspace.open?.kind === 'file'}
|
||||
<FilePane />
|
||||
{:else if workspace.open}
|
||||
<DocumentPane />
|
||||
{:else}
|
||||
<div class="placeholder">
|
||||
@@ -686,6 +699,7 @@
|
||||
{/if}
|
||||
|
||||
<input class="hidden" type="file" multiple bind:this={fileInput} onchange={attach} />
|
||||
<input class="hidden" type="file" multiple bind:this={treeInput} onchange={put} />
|
||||
<!-- `webkitdirectory` is set from script: it is the one attribute browsers
|
||||
agree on for picking a folder, and no typed attribute list carries it. -->
|
||||
<input class="hidden" type="file" multiple bind:this={folderInput} onchange={importFolder} />
|
||||
|
||||
Reference in new issue
Block a user