diff --git a/db.sqlite b/db.sqlite index c0229c8..99213a8 100644 Binary files a/db.sqlite and b/db.sqlite differ diff --git a/error.vue b/error.vue index cab5788..a630b98 100644 --- a/error.vue +++ b/error.vue @@ -1,16 +1,19 @@ - - d[any] - Erreur {{ error?.statusCode }} - - - + + d[any] - Erreur {{ error?.statusCode }} + + + Une erreur est survenue. Erreur {{ error?.statusCode }}: {{ error?.message }} - Revenir en lieu sûr - + Revenir en lieu sûr + \ No newline at end of file diff --git a/pages/explore/edit/index.vue b/pages/explore/edit/index.vue index 7e4f9c3..8b3c3f8 100644 --- a/pages/explore/edit/index.vue +++ b/pages/explore/edit/index.vue @@ -86,7 +86,7 @@ function push() } onMounted(async () => { - if(tree.value && container.value && await Content.ready) + if(tree.value && container.value) { const load = loading('normal'); tree.value.appendChild(load); @@ -100,7 +100,7 @@ onMounted(async () => { editor = new Editor(); - tree.value.replaceChild(editor.tree.container, load); + Content.ready.then(() => tree.value!.replaceChild(editor.tree.container, load)); container.value.appendChild(editor.container); } }); diff --git a/shared/content.util.ts b/shared/content.util.ts index facc0a7..83599fc 100644 --- a/shared/content.util.ts +++ b/shared/content.util.ts @@ -3,7 +3,7 @@ import { Canvas, CanvasEditor } from "#shared/canvas.util"; import render from "#shared/markdown.util"; import { confirm, contextmenu, tooltip } from "#shared/floating.util"; import { cancelPropagation, dom, icon, text, type Node } from "#shared/dom.util"; -import { loading } from "#shared/components.util"; +import { async, loading } from "#shared/components.util"; import prose, { h1, h2 } from "#shared/proses"; import { getID, parsePath } from '#shared/general.util'; import { TreeDOM, type Recursive } from '#shared/tree'; @@ -302,8 +302,9 @@ export class Content const handle = await Content.root.getFileHandle(path, options); const file = await handle.getFile(); - const text = await file.text(); - return text; + //@ts-ignore + const response = await new Response(file.stream().pipeThrough(new DecompressionStream('gzip'))); + return await response.text(); } catch(e) { @@ -330,15 +331,18 @@ export class Content //Easy to use, but not very performant. private static async write(path: string, content: string, options?: FileSystemGetFileOptions): Promise { - const size = new TextEncoder().encode(content).byteLength; try { const parent = path.split('/').slice(0, -1).join('/'), basename = path.split('/').slice(-1).join('/'); const handle = await (await Content.goto(parent, { create: true }) ?? Content.root).getFileHandle(basename, options); const file = await handle.createWritable({ keepExistingData: false }); - await file.write(content); - await file.close(); + await new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode(content)); + controller.close(); + } + }).pipeThrough(new CompressionStream("gzip")).pipeTo(file); } catch(e) { @@ -373,26 +377,27 @@ export class Content return handlers[overview.type].fromString(content); } - static render(parent: HTMLElement, path: string): Omit | undefined + static async render(parent: HTMLElement, path: string): Promise | undefined> { + parent.appendChild(dom('div', { class: 'flex, flex-1 justify-center items-center' }, [loading('normal')])) + + await Content.ready; + const overview = Content.getFromPath(path); if(!!overview) { - const load = dom('div', { class: 'flex, flex-1 justify-center items-center' }, [loading('normal')]); - parent.appendChild(load); - function _render(content: LocalContent): void { const el = handlers[content.type].render(content); - el && parent.replaceChild(el, load); + el && parent.replaceChildren(el); } Content.getContent(overview.id).then(content => _render(content!)); } else { - parent.appendChild(dom('h2', { class: 'flex-1 text-center', text: "Impossible d'afficher le contenu demandé" })); + parent.replaceChildren(dom('h2', { class: 'flex-1 text-center', text: "Impossible d'afficher le contenu demandé" })); } return overview; @@ -514,7 +519,7 @@ const handlers: { [K in FileType]: ContentTypeHandler } = { //TODO: Edition link ]), ]), - render(content.content), + render(content.content, undefined, { class: 'pb-64' }), ]) }, renderEditor: (content) => { @@ -572,7 +577,7 @@ export const iconByType: Record = { export class Editor { - tree: TreeDOM; + tree!: TreeDOM; container: HTMLDivElement; selected?: Recursive; @@ -692,29 +697,31 @@ export class Editor }, }, () => { this.tree.tree.each(e => Content.set(e.id, e)); Content.save(); }); - this.tree = new TreeDOM((item, depth) => { - return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-left': `${depth / 2 - 0.5}em` } }, [dom('div', { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full cursor-pointer font-medium'], attributes: { 'data-private': item.private }, listeners: { contextmenu: (e) => this.contextmenu(e, item as LocalContent)} }, [ - icon('radix-icons:chevron-right', { class: 'h-4 w-4 transition-transform absolute group-data-[state=open]:rotate-90', style: { 'left': `${depth / 2 - 1.5}em` } }), - dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }), - tooltip(dom('span', { class: 'flex', listeners: { click: e => this.toggleNavigable(e, item as LocalContent) } }, [icon(item.navigable ? 'radix-icons:eye-open' : 'radix-icons:eye-none', { class: ['mx-1', { 'opacity-50': !item.navigable }] })]), 'Navigable', 'left'), - tooltip(dom('span', { class: 'flex', listeners: { click: e => this.togglePrivate(e, item as LocalContent) } }, [icon(item.private ? 'radix-icons:lock-closed' : 'radix-icons:lock-open-2', { class: ['mx-1', { 'opacity-50': !item.private }] })]), 'Privé', 'right'), - ])]); - }, (item, depth) => { - return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-left': `${depth / 2 - 0.5}em` } }, [dom('div', { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full'], attributes: { 'data-private': item.private }, listeners: { contextmenu: (e) => this.contextmenu(e, item as LocalContent), click: () => this.select(item as LocalContent) } }, [ - icon(iconByType[item.type], { class: 'w-5 h-5', width: 20, height: 20 }), - dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }), - tooltip(dom('span', { class: 'flex', listeners: { click: e => this.toggleNavigable(e, item as LocalContent) } }, [icon(item.navigable ? 'radix-icons:eye-open' : 'radix-icons:eye-none', { class: ['mx-1', { 'opacity-50': !item.navigable }] })]), 'Navigable', 'left'), - tooltip(dom('span', { class: 'flex', listeners: { click: e => this.togglePrivate(e, item as LocalContent) } }, [icon(item.private ? 'radix-icons:lock-closed' : 'radix-icons:lock-open-2', { class: ['mx-1', { 'opacity-50': !item.private }] })]), 'Privé', 'right'), - ])]); + Content.ready.then(() => { + this.tree = new TreeDOM((item, depth) => { + return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-left': `${depth / 2 - 0.5}em` } }, [dom('div', { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full cursor-pointer font-medium'], attributes: { 'data-private': item.private }, listeners: { contextmenu: (e) => this.contextmenu(e, item as LocalContent)} }, [ + icon('radix-icons:chevron-right', { class: 'h-4 w-4 transition-transform absolute group-data-[state=open]:rotate-90', style: { 'left': `${depth / 2 - 1.5}em` } }), + dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }), + tooltip(dom('span', { class: 'flex', listeners: { click: e => this.toggleNavigable(e, item as LocalContent) } }, [icon(item.navigable ? 'radix-icons:eye-open' : 'radix-icons:eye-none', { class: ['mx-1', { 'opacity-50': !item.navigable }] })]), 'Navigable', 'left'), + tooltip(dom('span', { class: 'flex', listeners: { click: e => this.togglePrivate(e, item as LocalContent) } }, [icon(item.private ? 'radix-icons:lock-closed' : 'radix-icons:lock-open-2', { class: ['mx-1', { 'opacity-50': !item.private }] })]), 'Privé', 'right'), + ])]); + }, (item, depth) => { + return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-left': `${depth / 2 - 0.5}em` } }, [dom('div', { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full'], attributes: { 'data-private': item.private }, listeners: { contextmenu: (e) => this.contextmenu(e, item as LocalContent), click: () => this.select(item as LocalContent) } }, [ + icon(iconByType[item.type], { class: 'w-5 h-5', width: 20, height: 20 }), + dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }), + tooltip(dom('span', { class: 'flex', listeners: { click: e => this.toggleNavigable(e, item as LocalContent) } }, [icon(item.navigable ? 'radix-icons:eye-open' : 'radix-icons:eye-none', { class: ['mx-1', { 'opacity-50': !item.navigable }] })]), 'Navigable', 'left'), + tooltip(dom('span', { class: 'flex', listeners: { click: e => this.togglePrivate(e, item as LocalContent) } }, [icon(item.private ? 'radix-icons:lock-closed' : 'radix-icons:lock-open-2', { class: ['mx-1', { 'opacity-50': !item.private }] })]), 'Privé', 'right'), + ])]); + }); + + this.select(this.tree.tree.find(useRouter().currentRoute.value.hash.substring(1)) as Recursive | undefined); + + this.cleanup = this.setupDnD(); }); this.instruction = dom('div', { class: 'absolute h-full w-full top-0 right-0 border-light-50 dark:border-dark-50' }); - this.cleanup = this.setupDnD(); - this.container = dom('div', { class: 'flex flex-1 flex-col items-start justify-start max-h-full relative' }, [dom('div', { class: 'py-4 flex-1 w-full max-h-full flex overflow-auto xl:px-12 lg:px-8 px-6 relative' })]); - - this.select(this.tree.tree.find(useRouter().currentRoute.value.hash.substring(1)) as Recursive | undefined); } private contextmenu(e: MouseEvent, item: Recursive) {
Erreur {{ error?.statusCode }}: {{ error?.message }}