Compress stored content for improved caching size and speed. Add loading component on every Content ready awaiting to reduce first render time.
This commit is contained in:
parent
1c3211d28e
commit
fde752b6ed
17
error.vue
17
error.vue
|
|
@ -1,16 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<Head>
|
<Head>
|
||||||
<Title>d[any] - Erreur {{ error?.statusCode }}</Title>
|
<Title>d[any] - Erreur {{ error?.statusCode }}</Title>
|
||||||
</Head>
|
</Head>
|
||||||
<div class="text-light-100 dark:text-dark-100 flex bg-light-0 dark:bg-dark-0 h-screen overflow-hidden justify-center items-center flex-col gap-4">
|
<div class="text-light-100 dark:text-dark-100 flex bg-light-0 dark:bg-dark-0 h-screen overflow-hidden justify-center items-center flex-col gap-4">
|
||||||
<NuxtRouteAnnouncer/>
|
<NuxtRouteAnnouncer/>
|
||||||
<div class="flex gap-4 items-center">
|
<div class="flex gap-4 items-center">
|
||||||
<Icon icon="si:error-line" class="w-12 h-12 text-light-60 dark:text-dark-60"/>
|
<Icon icon="si:error-line" class="w-12 h-12 text-light-60 dark:text-dark-60"/>
|
||||||
<div class="text-3xl">Une erreur est survenue.</div>
|
<div class="text-3xl">Une erreur est survenue.</div>
|
||||||
</div>
|
</div>
|
||||||
<pre class="text-center text-wrap">Erreur {{ error?.statusCode }}: {{ error?.message }}</pre>
|
<pre class="text-center text-wrap">Erreur {{ error?.statusCode }}: {{ error?.message }}</pre>
|
||||||
<Button @click="handleError">Revenir en lieu sûr</Button>
|
<button class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow]
|
||||||
</div>
|
text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40
|
||||||
|
hover:bg-light-25 dark:hover:bg-dark-25 hover:border-light-50 dark:hover:border-dark-50
|
||||||
|
focus:bg-light-30 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 p-2" @click="handleError">Revenir en lieu sûr</button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
|
||||||
|
|
@ -56,35 +56,21 @@ import { Content, iconByType } from '#shared/content.util';
|
||||||
import { dom, icon } from '#shared/dom.util';
|
import { dom, icon } from '#shared/dom.util';
|
||||||
import { unifySlug } from '#shared/general.util';
|
import { unifySlug } from '#shared/general.util';
|
||||||
import { tooltip } from '#shared/floating.util';
|
import { tooltip } from '#shared/floating.util';
|
||||||
import { link } from '#shared/components.util';
|
import { link, loading } from '#shared/components.util';
|
||||||
|
|
||||||
const open = ref(false);
|
const open = ref(false);
|
||||||
|
let tree: TreeDOM | undefined;
|
||||||
const { loggedIn, user } = useUserSession();
|
const { loggedIn, user } = useUserSession();
|
||||||
|
|
||||||
const route = useRouter().currentRoute;
|
const route = useRouter().currentRoute;
|
||||||
const path = computed(() => route.value.params.path ? decodeURIComponent(unifySlug(route.value.params.path)) : undefined);
|
const path = computed(() => route.value.params.path ? decodeURIComponent(unifySlug(route.value.params.path)) : undefined);
|
||||||
|
|
||||||
await Content.init();
|
|
||||||
const tree = new TreeDOM((item, depth) => {
|
|
||||||
return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-inline-start': `${depth / 1.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 } }, [
|
|
||||||
icon('radix-icons:chevron-right', { class: 'h-4 w-4 transition-transform absolute group-data-[state=open]:rotate-90', style: { 'left': `${depth / 1.5 - 1}em` } }),
|
|
||||||
dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }),
|
|
||||||
item.private ? tooltip(icon('radix-icons:lock-closed', { class: 'mx-1' }), 'Privé', 'right') : undefined,
|
|
||||||
])]);
|
|
||||||
}, (item, depth) => {
|
|
||||||
return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-inline-start': `${depth / 1.5}em` } }, [link([
|
|
||||||
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 } }),
|
|
||||||
item.private ? tooltip(icon('radix-icons:lock-closed', { class: 'mx-1' }), 'Privé', 'right') : undefined,
|
|
||||||
], { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full'], attributes: { 'data-private': item.private }, active: 'text-accent-blue' }, item.path ? { name: 'explore-path', params: { path: item.path } } : undefined )]);
|
|
||||||
}, (item) => item.navigable);
|
|
||||||
(path.value?.split('/').map((e, i, a) => a.slice(0, i).join('/')) ?? []).forEach(e => tree.toggle(tree.tree.search('path', e)[0], true));
|
|
||||||
|
|
||||||
const unmount = useRouter().afterEach((to, from, failure) => {
|
const unmount = useRouter().afterEach((to, from, failure) => {
|
||||||
if(failure)
|
if(failure)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
to.name === 'explore-path' && (unifySlug(to.params.path ?? '').split('/').map((e, i, a) => a.slice(0, i).join('/')) ?? []).forEach(e => tree.toggle(tree.tree.search('path', e)[0], true));
|
to.name === 'explore-path' && (unifySlug(to.params.path ?? '').split('/').map((e, i, a) => a.slice(0, i).join('/')) ?? []).forEach(e => tree?.toggle(tree.tree.search('path', e)[0], true));
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(route, () => {
|
watch(route, () => {
|
||||||
|
|
@ -94,7 +80,27 @@ watch(route, () => {
|
||||||
const treeParent = useTemplateRef('treeParent');
|
const treeParent = useTemplateRef('treeParent');
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if(treeParent.value)
|
if(treeParent.value)
|
||||||
treeParent.value.appendChild(tree.container);
|
{
|
||||||
|
treeParent.value.replaceChildren(loading('normal'));
|
||||||
|
Content.ready.then(() => {
|
||||||
|
tree = new TreeDOM((item, depth) => {
|
||||||
|
return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-inline-start': `${depth / 1.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 } }, [
|
||||||
|
icon('radix-icons:chevron-right', { class: 'h-4 w-4 transition-transform absolute group-data-[state=open]:rotate-90', style: { 'left': `${depth / 1.5 - 1}em` } }),
|
||||||
|
dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }),
|
||||||
|
item.private ? tooltip(icon('radix-icons:lock-closed', { class: 'mx-1' }), 'Privé', 'right') : undefined,
|
||||||
|
])]);
|
||||||
|
}, (item, depth) => {
|
||||||
|
return dom('div', { class: 'group flex items-center ps-2 outline-none relative cursor-pointer', style: { 'padding-inline-start': `${depth / 1.5}em` } }, [link([
|
||||||
|
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 } }),
|
||||||
|
item.private ? tooltip(icon('radix-icons:lock-closed', { class: 'mx-1' }), 'Privé', 'right') : undefined,
|
||||||
|
], { class: ['flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full'], attributes: { 'data-private': item.private }, active: 'text-accent-blue' }, item.path ? { name: 'explore-path', params: { path: item.path } } : undefined )]);
|
||||||
|
}, (item) => item.navigable);
|
||||||
|
(path.value?.split('/').map((e, i, a) => a.slice(0, i).join('/')) ?? []).forEach(e => tree?.toggle(tree.tree.search('path', e)[0], true));
|
||||||
|
|
||||||
|
treeParent.value!.replaceChildren(tree.container);
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
unmount();
|
unmount();
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ const route = useRouter().currentRoute;
|
||||||
const path = computed(() => unifySlug(route.value.params.path ?? ''));
|
const path = computed(() => unifySlug(route.value.params.path ?? ''));
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if(element.value && path.value && await Content.ready)
|
if(element.value && path.value)
|
||||||
{
|
{
|
||||||
overview.value = Content.render(element.value, path.value);
|
overview.value = await Content.render(element.value, path.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -86,7 +86,7 @@ function push()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if(tree.value && container.value && await Content.ready)
|
if(tree.value && container.value)
|
||||||
{
|
{
|
||||||
const load = loading('normal');
|
const load = loading('normal');
|
||||||
tree.value.appendChild(load);
|
tree.value.appendChild(load);
|
||||||
|
|
@ -100,7 +100,7 @@ onMounted(async () => {
|
||||||
|
|
||||||
editor = new Editor();
|
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);
|
container.value.appendChild(editor.container);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { Canvas, CanvasEditor } from "#shared/canvas.util";
|
||||||
import render from "#shared/markdown.util";
|
import render from "#shared/markdown.util";
|
||||||
import { confirm, contextmenu, tooltip } from "#shared/floating.util";
|
import { confirm, contextmenu, tooltip } from "#shared/floating.util";
|
||||||
import { cancelPropagation, dom, icon, text, type Node } from "#shared/dom.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 prose, { h1, h2 } from "#shared/proses";
|
||||||
import { getID, parsePath } from '#shared/general.util';
|
import { getID, parsePath } from '#shared/general.util';
|
||||||
import { TreeDOM, type Recursive } from '#shared/tree';
|
import { TreeDOM, type Recursive } from '#shared/tree';
|
||||||
|
|
@ -302,8 +302,9 @@ export class Content
|
||||||
const handle = await Content.root.getFileHandle(path, options);
|
const handle = await Content.root.getFileHandle(path, options);
|
||||||
const file = await handle.getFile();
|
const file = await handle.getFile();
|
||||||
|
|
||||||
const text = await file.text();
|
//@ts-ignore
|
||||||
return text;
|
const response = await new Response(file.stream().pipeThrough(new DecompressionStream('gzip')));
|
||||||
|
return await response.text();
|
||||||
}
|
}
|
||||||
catch(e)
|
catch(e)
|
||||||
{
|
{
|
||||||
|
|
@ -330,15 +331,18 @@ export class Content
|
||||||
//Easy to use, but not very performant.
|
//Easy to use, but not very performant.
|
||||||
private static async write(path: string, content: string, options?: FileSystemGetFileOptions): Promise<void>
|
private static async write(path: string, content: string, options?: FileSystemGetFileOptions): Promise<void>
|
||||||
{
|
{
|
||||||
const size = new TextEncoder().encode(content).byteLength;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const parent = path.split('/').slice(0, -1).join('/'), basename = path.split('/').slice(-1).join('/');
|
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 handle = await (await Content.goto(parent, { create: true }) ?? Content.root).getFileHandle(basename, options);
|
||||||
const file = await handle.createWritable({ keepExistingData: false });
|
const file = await handle.createWritable({ keepExistingData: false });
|
||||||
|
|
||||||
await file.write(content);
|
await new ReadableStream({
|
||||||
await file.close();
|
start(controller) {
|
||||||
|
controller.enqueue(new TextEncoder().encode(content));
|
||||||
|
controller.close();
|
||||||
|
}
|
||||||
|
}).pipeThrough(new CompressionStream("gzip")).pipeTo(file);
|
||||||
}
|
}
|
||||||
catch(e)
|
catch(e)
|
||||||
{
|
{
|
||||||
|
|
@ -373,26 +377,27 @@ export class Content
|
||||||
return handlers[overview.type].fromString(content);
|
return handlers[overview.type].fromString(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
static render(parent: HTMLElement, path: string): Omit<LocalContent, 'content'> | undefined
|
static async render(parent: HTMLElement, path: string): Promise<Omit<LocalContent, 'content'> | undefined>
|
||||||
{
|
{
|
||||||
|
parent.appendChild(dom('div', { class: 'flex, flex-1 justify-center items-center' }, [loading('normal')]))
|
||||||
|
|
||||||
|
await Content.ready;
|
||||||
|
|
||||||
const overview = Content.getFromPath(path);
|
const overview = Content.getFromPath(path);
|
||||||
|
|
||||||
if(!!overview)
|
if(!!overview)
|
||||||
{
|
{
|
||||||
const load = dom('div', { class: 'flex, flex-1 justify-center items-center' }, [loading('normal')]);
|
|
||||||
parent.appendChild(load);
|
|
||||||
|
|
||||||
function _render<T extends FileType>(content: LocalContent<T>): void
|
function _render<T extends FileType>(content: LocalContent<T>): void
|
||||||
{
|
{
|
||||||
const el = handlers[content.type].render(content);
|
const el = handlers[content.type].render(content);
|
||||||
el && parent.replaceChild(el, load);
|
el && parent.replaceChildren(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
Content.getContent(overview.id).then(content => _render(content!));
|
Content.getContent(overview.id).then(content => _render(content!));
|
||||||
}
|
}
|
||||||
else
|
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;
|
return overview;
|
||||||
|
|
@ -514,7 +519,7 @@ const handlers: { [K in FileType]: ContentTypeHandler<K> } = {
|
||||||
//TODO: Edition link
|
//TODO: Edition link
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
render(content.content),
|
render(content.content, undefined, { class: 'pb-64' }),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
renderEditor: (content) => {
|
renderEditor: (content) => {
|
||||||
|
|
@ -572,7 +577,7 @@ export const iconByType: Record<FileType, string> = {
|
||||||
|
|
||||||
export class Editor
|
export class Editor
|
||||||
{
|
{
|
||||||
tree: TreeDOM;
|
tree!: TreeDOM;
|
||||||
container: HTMLDivElement;
|
container: HTMLDivElement;
|
||||||
|
|
||||||
selected?: Recursive<LocalContent & { element?: HTMLElement }>;
|
selected?: Recursive<LocalContent & { element?: HTMLElement }>;
|
||||||
|
|
@ -692,29 +697,31 @@ export class Editor
|
||||||
},
|
},
|
||||||
}, () => { this.tree.tree.each(e => Content.set(e.id, e)); Content.save(); });
|
}, () => { this.tree.tree.each(e => Content.set(e.id, e)); Content.save(); });
|
||||||
|
|
||||||
this.tree = new TreeDOM((item, depth) => {
|
Content.ready.then(() => {
|
||||||
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)} }, [
|
this.tree = new TreeDOM((item, depth) => {
|
||||||
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` } }),
|
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)} }, [
|
||||||
dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }),
|
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` } }),
|
||||||
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'),
|
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.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'),
|
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) } }, [
|
}, (item, depth) => {
|
||||||
icon(iconByType[item.type], { class: 'w-5 h-5', width: 20, height: 20 }),
|
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) } }, [
|
||||||
dom('div', { class: 'pl-1.5 py-1.5 flex-1 truncate', text: item.title, attributes: { title: item.title } }),
|
icon(iconByType[item.type], { class: 'w-5 h-5', width: 20, height: 20 }),
|
||||||
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'),
|
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.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'),
|
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<LocalContent & { element?: HTMLElement }> | 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.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.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<LocalContent & { element?: HTMLElement }> | undefined);
|
|
||||||
}
|
}
|
||||||
private contextmenu(e: MouseEvent, item: Recursive<LocalContent>)
|
private contextmenu(e: MouseEvent, item: Recursive<LocalContent>)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue