Finish PreviewContent and add ProseA icon for none markdown files
25
app.vue
|
|
@ -1,8 +1,25 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
const layout = ref();
|
||||||
|
const toggled = ref(false);
|
||||||
|
|
||||||
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="published-container">
|
<div class="published-container" :class="{'has-navigation': layout?.layoutRef?.navigation ?? false}">
|
||||||
<NuxtLayout>
|
<div class="site-nav-bar">
|
||||||
<NuxtPage></NuxtPage>
|
<div>
|
||||||
</NuxtLayout>
|
<div class="gapx-3 flex align-stretch">
|
||||||
|
<NuxtLink class="site-nav-bar-text" aria-label="Accueil" :to="{ path: '/', force: true }"><ThemeIcon icon="logo" :width=40 :height=40 /></NuxtLink>
|
||||||
|
<NuxtLink class="site-nav-bar-text mobile-hidden" aria-label="Projets" :to="{ path: `/explorer`, force: true }" active-class="mod-active">Projets</NuxtLink>
|
||||||
|
<NuxtLink class="site-nav-bar-text mobile-hidden" aria-label="Editeur" :to="{ path: '/editing', force: true }" :class="{ 'mod-active': $route.path.startsWith('/editing') }">Editeur</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mobile-bigger"><SearchView /></div>
|
||||||
|
<div class="ps-1 gapx-1 flex align-center">
|
||||||
|
<ThemeSwitch class="mobile-hidden" />
|
||||||
|
<NuxtLink class="site-login" :to="{ path: '/user/profile', force: true }"><ThemeIcon icon="user" :width=32 :height=32 /></NuxtLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<NuxtLayout ref="layout"><NuxtPage/></NuxtLayout>
|
||||||
<div class="site-footer">
|
<div class="site-footer">
|
||||||
<p>Copyright Peaceultime - 2024</p>
|
<p>Copyright Peaceultime - 2024</p>
|
||||||
<NuxtLink :to="{ path: '/third-party', force: true }">Applications tierces et crédits</NuxtLink>
|
<NuxtLink :to="{ path: '/third-party', force: true }">Applications tierces et crédits</NuxtLink>
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,15 @@ html.light-mode .light-block {
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading:after {
|
||||||
|
content: '';
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
border: 4px solid var(--color-purple);
|
border: 4px solid var(--color-purple);
|
||||||
|
|
@ -371,3 +380,8 @@ html.light-mode .light-block {
|
||||||
.arrow-group .arrow-group-item:hover {
|
.arrow-group .arrow-group-item:hover {
|
||||||
background-color: var(--background-primary-alt);
|
background-color: var(--background-primary-alt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link-icon {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: sub;
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,8 @@ defineProps<Prop>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<img :src="`/icons/${icon}.light.svg`" class="dark-hidden light-block" :width="width" :height="height" />
|
<span>
|
||||||
<img :src="`/icons/${icon}.dark.svg`" class="dark-block light-hidden" :width="width" :height="height" />
|
<img :src="`/icons/${icon}.light.svg`" class="dark-hidden light-block" :width="width" :height="height" />
|
||||||
|
<img :src="`/icons/${icon}.dark.svg`" class="dark-block light-hidden" :width="width" :height="height" />
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -36,14 +36,16 @@ if(props.node.color !== undefined)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="canvas-node" :class="classes" :style="{transform: `translate(${node.x}px, ${node.y}px)`, width: `${node.width}px`, height: `${node.height}px`, '--canvas-node-width': `${node.width}px`, '--canvas-node-height': `${node.height}px`, '--canvas-color': node?.color?.startsWith('#') ? hexToRgb(node.color) : undefined}">
|
<div class="canvas-node" :class="classes"
|
||||||
|
:style="{transform: `translate(${node.x}px, ${node.y}px)`, width: `${node.width}px`, height: `${node.height}px`, '--canvas-node-width': `${node.width}px`, '--canvas-node-height': `${node.height}px`, '--canvas-color': node?.color?.startsWith('#') ? hexToRgb(node.color) : undefined}">
|
||||||
<div class="canvas-node-container">
|
<div class="canvas-node-container">
|
||||||
<template v-if="node.type === 'group' || zoom > Math.min(0.38, 1000 / size)">
|
<template v-if="node.type === 'group' || zoom > Math.min(0.38, 1000 / size)">
|
||||||
<div class="canvas-node-content markdown-embed">
|
<div class="canvas-node-content markdown-embed">
|
||||||
<div v-if="node.text?.length > 0" class="markdown-embed-content node-insert-event" style="">
|
<div v-if="node.text?.length > 0" class="markdown-embed-content node-insert-event" style="">
|
||||||
<div class="markdown-preview-view markdown-rendered node-insert-event show-indentation-guide allow-fold-headings allow-fold-lists">
|
<div
|
||||||
|
class="markdown-preview-view markdown-rendered node-insert-event show-indentation-guide allow-fold-headings allow-fold-lists">
|
||||||
<div class="markdown-preview-sizer markdown-preview-section">
|
<div class="markdown-preview-sizer markdown-preview-section">
|
||||||
<Markdown v-model="node.text"/>
|
<Markdown v-model="node.text" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -52,7 +54,9 @@ if(props.node.color !== undefined)
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="canvas-node-placeholder">
|
<div class="canvas-node-placeholder">
|
||||||
<div class="canvas-icon-placeholder">
|
<div class="canvas-icon-placeholder">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-align-left">
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
class="svg-icon lucide-align-left">
|
||||||
<line x1="21" y1="6" x2="3" y2="6"></line>
|
<line x1="21" y1="6" x2="3" y2="6"></line>
|
||||||
<line x1="15" y1="12" x2="3" y2="12"></line>
|
<line x1="15" y1="12" x2="3" y2="12"></line>
|
||||||
<line x1="17" y1="18" x2="3" y2="18"></line>
|
<line x1="17" y1="18" x2="3" y2="18"></line>
|
||||||
|
|
@ -61,6 +65,8 @@ if(props.node.color !== undefined)
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="node.type === 'group' && node.label !== undefined" class="canvas-group-label" :class="{'mod-foreground-dark': darken(getColor(node?.color ?? '')), 'mod-foreground-light': !darken(getColor(node?.color ?? ''))}">{{ node.label }}</div>
|
<div v-if="node.type === 'group' && node.label !== undefined" class="canvas-group-label"
|
||||||
|
:class="{ 'mod-foreground-dark': darken(getColor(props.node?.color ?? '')), 'mod-foreground-light': !darken(getColor(props.node?.color ?? ''))}">
|
||||||
|
{{ node.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -19,6 +19,8 @@ let lastPinchLength = 0;
|
||||||
let _minX = +Infinity, _minY = +Infinity, _maxX = -Infinity, _maxY = -Infinity;
|
let _minX = +Infinity, _minY = +Infinity, _maxX = -Infinity, _maxY = -Infinity;
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
props.canvas.nodes.forEach((e) => {
|
props.canvas.nodes.forEach((e) => {
|
||||||
_minX = Math.min(_minX, e.x);
|
_minX = Math.min(_minX, e.x);
|
||||||
_minY = Math.min(_minY, e.y);
|
_minY = Math.min(_minY, e.y);
|
||||||
|
|
@ -47,10 +49,10 @@ const onResize = (event?: Event) => {
|
||||||
maxX.value = _maxX = _maxX + 32;
|
maxX.value = _maxX = _maxX + 32;
|
||||||
maxY.value = _maxY = _maxY + 32;
|
maxY.value = _maxY = _maxY + 32;
|
||||||
|
|
||||||
minZoom.value = Math.min((canvas.value?.clientWidth ?? 0) / (_maxX - _minX), (canvas.value?.clientHeight ?? 0) / (_maxY - _minY)) * 0.9;
|
minZoom.value = Math.min((canvas.value?.clientWidth ?? 1920) / (_maxX - _minX), (canvas.value?.clientHeight ?? 1080) / (_maxY - _minY)) * 0.9;
|
||||||
zoom.value = clamp(zoom.value, minZoom.value, 3);
|
zoom.value = clamp(zoom.value, minZoom.value, 3);
|
||||||
|
|
||||||
bbox.value = canvas.value?.getBoundingClientRect();
|
bbox.value = (canvas.value ?? document.getElementById('canvas'))?.getBoundingClientRect();
|
||||||
|
|
||||||
centerX.value = (bbox.value?.width ?? 0) / 2;
|
centerX.value = (bbox.value?.width ?? 0) / 2;
|
||||||
centerY.value = (bbox.value?.height ?? 0) / 2;
|
centerY.value = (bbox.value?.height ?? 0) / 2;
|
||||||
|
|
@ -213,62 +215,71 @@ function getCenter(n: { x: number, y: number }, i: { x: number, y: number }, r:
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="canvas" @pointerdown="onPointerDown" @wheel.passive="onWheel" @touchstart.passive="onTouchStart" @dragstart.prevent=""
|
<Suspense>
|
||||||
class="canvas-wrapper node-insert-event mod-zoomed-out"
|
<template #default>
|
||||||
:style="{ '--zoom-multiplier': (1 / Math.pow(zoom, 0.7)) }">
|
<div id="canvas" ref="canvas" @pointerdown="onPointerDown" @wheel.passive="onWheel" @touchstart.passive="onTouchStart"
|
||||||
<div class="canvas-controls" style="z-index: 999;">
|
@dragstart.prevent="" class="canvas-wrapper node-insert-event mod-zoomed-out"
|
||||||
<div class="canvas-control-group">
|
:style="{ '--zoom-multiplier': (1 / Math.pow(zoom, 0.7)) }">
|
||||||
<div @click="zoom = clamp(zoom * 1.1, minZoom, 3)" class="canvas-control-item" aria-label="Zoom in"
|
<div class="canvas-controls" style="z-index: 999;">
|
||||||
data-tooltip-position="left">
|
<div class="canvas-control-group">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
<div @click="zoom = clamp(zoom * 1.1, minZoom, 3)" class="canvas-control-item"
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
aria-label="Zoom in" data-tooltip-position="left">
|
||||||
class="svg-icon lucide-plus">
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
<path d="M5 12h14"></path>
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
<path d="M12 5v14"></path>
|
stroke-linejoin="round" class="svg-icon lucide-plus">
|
||||||
</svg>
|
<path d="M5 12h14"></path>
|
||||||
|
<path d="M12 5v14"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div @click="zoom = 1" class="canvas-control-item" aria-label="Reset zoom"
|
||||||
|
data-tooltip-position="left">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" class="svg-icon lucide-rotate-cw">
|
||||||
|
<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"></path>
|
||||||
|
<path d="M21 3v5h-5"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div @click="reset" class="canvas-control-item" aria-label="Zoom to fit"
|
||||||
|
data-tooltip-position="left">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" class="svg-icon lucide-maximize">
|
||||||
|
<path d="M8 3H5a2 2 0 0 0-2 2v3"></path>
|
||||||
|
<path d="M21 8V5a2 2 0 0 0-2-2h-3"></path>
|
||||||
|
<path d="M3 16v3a2 2 0 0 0 2 2h3"></path>
|
||||||
|
<path d="M16 21h3a2 2 0 0 0 2-2v-3"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div @click="zoom = clamp(zoom * 0.9, minZoom, 3)" class="canvas-control-item"
|
||||||
|
aria-label="Zoom out" data-tooltip-position="left">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
|
||||||
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" class="svg-icon lucide-minus">
|
||||||
|
<path d="M5 12h14"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div @click="zoom = 1" class="canvas-control-item" aria-label="Reset zoom" data-tooltip-position="left">
|
<div class="canvas"
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
:style="{transform: `translate(${centerX}px, ${centerY}px) scale(${zoom}) translate(${dispX}px, ${dispY}px)`}">
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
<svg class="canvas-edges">
|
||||||
class="svg-icon lucide-rotate-cw">
|
<CanvasEdge v-for="edge of props.canvas.edges" :key="edge.id"
|
||||||
<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"></path>
|
:path="path(getNode(edge.fromNode)!, edge.fromSide, getNode(edge.toNode)!, edge.toSide)"
|
||||||
<path d="M21 3v5h-5"></path>
|
:color="edge.color" :label="edge.label" />
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div @click="reset" class="canvas-control-item" aria-label="Zoom to fit" data-tooltip-position="left">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
class="svg-icon lucide-maximize">
|
|
||||||
<path d="M8 3H5a2 2 0 0 0-2 2v3"></path>
|
|
||||||
<path d="M21 8V5a2 2 0 0 0-2-2h-3"></path>
|
|
||||||
<path d="M3 16v3a2 2 0 0 0 2 2h3"></path>
|
|
||||||
<path d="M16 21h3a2 2 0 0 0 2-2v-3"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div @click="zoom = clamp(zoom * 0.9, minZoom, 3)" class="canvas-control-item" aria-label="Zoom out"
|
|
||||||
data-tooltip-position="left">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
class="svg-icon lucide-minus">
|
|
||||||
<path d="M5 12h14"></path>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
<CanvasNode v-for="node of props.canvas.nodes" :key="node.id" :node="node" :zoom="zoom" />
|
||||||
|
<template v-for="edge of props.canvas.edges">
|
||||||
|
<div :key="edge.id" v-if="edge.label" class="canvas-path-label-wrapper"
|
||||||
|
:style="{ transform: labelCenter(getNode(edge.fromNode)!, edge.fromSide, getNode(edge.toNode)!, edge.toSide) }">
|
||||||
|
<div class="canvas-path-label">{{ edge.label }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
<div class="canvas"
|
<template #fallback>
|
||||||
:style="{transform: `translate(${centerX}px, ${centerY}px) scale(${zoom}) translate(${dispX}px, ${dispY}px)`}">
|
<div class="loading"></div>
|
||||||
<svg class="canvas-edges">
|
</template>
|
||||||
<CanvasEdge v-for="edge of props.canvas.edges" :key="edge.id"
|
</Suspense>
|
||||||
:path="path(getNode(edge.fromNode)!, edge.fromSide, getNode(edge.toNode)!, edge.toSide)"
|
|
||||||
:color="edge.color" :label="edge.label" />
|
|
||||||
</svg>
|
|
||||||
<CanvasNode v-for="node of props.canvas.nodes" :key="node.id" :node="node" :zoom="zoom" />
|
|
||||||
<template v-for="edge of props.canvas.edges">
|
|
||||||
<div :key="edge.id" v-if="edge.label" class="canvas-path-label-wrapper"
|
|
||||||
:style="{ transform: labelCenter(getNode(edge.fromNode)!, edge.fromSide, getNode(edge.toNode)!, edge.toSide) }">
|
|
||||||
<div class="canvas-path-label">{{ edge.label }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const project = computed(() => parseInt(Array.isArray(route.params.projectId) ? '0' : route.params.projectId));
|
const project = computed(() => parseInt(route.params.projectId as string));
|
||||||
|
|
||||||
const { data: navigation, execute, status, error } = await useFetch(() => `/api/project/${project.value}/navigation`, {
|
const { data: navigation, refresh } = await useLazyFetch(() => `/api/project/${project.value}/navigation`, { immediate: false });
|
||||||
immediate: false,
|
|
||||||
});
|
if(!isNaN(project.value))
|
||||||
|
{
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
<template>
|
|
||||||
<Teleport to="#teleports">
|
|
||||||
<div class="popover hover-popover" :class="{'is-loaded': pending === false && content !== ''}" :style="{'top': (pos?.y ?? 0) - 4, 'left': (pos?.y ?? 0) + 4}">
|
|
||||||
<template v-if="display">
|
|
||||||
<div v-if="pending" class="loading"></div>
|
|
||||||
<div class="markdown-embed" v-else-if="content !== ''">
|
|
||||||
<div class="markdown-preview-view markdown-rendered">
|
|
||||||
<div class="markdown-embed-content">
|
|
||||||
<h1>{{ title }}</h1>
|
|
||||||
<Markdown v-model="content"></Markdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="not-found-container" v-else>
|
|
||||||
<div class="not-found-image"></div>
|
|
||||||
<div class="not-found-title">Impossible d'afficher (ou vide)</div>
|
|
||||||
<div class="not-found-description">Cette page est actuellement vide ou impossible à traiter</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</Teleport>
|
|
||||||
<span ref="el" @mouseenter="show" @mouseleave="hide">
|
|
||||||
<slot></slot>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useDebounceFn as debounce } from '@vueuse/core';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
project: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
path: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
anchor: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const content = ref(''), title = ref(''), display = ref(false), pending = ref(false);
|
|
||||||
const el = ref(), pos = ref<DOMRect>();
|
|
||||||
watch(display, () => display.value && !pending.value && content.value === '' && fetch());
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if(el && el.value)
|
|
||||||
{
|
|
||||||
pos.value = (el.value as HTMLDivElement).getBoundingClientRect();
|
|
||||||
debugger;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
async function fetch()
|
|
||||||
{
|
|
||||||
pending.value = true;
|
|
||||||
|
|
||||||
const data = await $fetch(`/api/project/${props.project}/file`, {
|
|
||||||
method: 'get',
|
|
||||||
query: {
|
|
||||||
path: props.path,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
pending.value = false;
|
|
||||||
|
|
||||||
if(data && data[0])
|
|
||||||
{
|
|
||||||
content.value = data[0].content;
|
|
||||||
title.value = data[0].title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const show = debounce(() => display.value = true, 250), hide = debounce(() => display.value = false, 250);
|
|
||||||
</script>
|
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
<template>
|
||||||
|
<Teleport to="#teleports" v-if="display && (!fetched || loaded)">
|
||||||
|
<div class="popover hover-popover" :class="{'is-loaded': fetched}" :style="pos"
|
||||||
|
@mouseenter="debounce(show, 250)" @mouseleave="debounce(() => display = false, 250)">
|
||||||
|
<div v-if="pending" class="loading"></div>
|
||||||
|
<template v-else-if="content !==''">
|
||||||
|
<div v-if="type === 'Markdown'" class=" markdown-embed">
|
||||||
|
<div class="markdown-embed-content">
|
||||||
|
<div class="markdown-preview-view markdown-rendered node-insert-event hide-title">
|
||||||
|
<div class="markdown-preview-sizer markdown-preview-section">
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
<Markdown v-model="content"></Markdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="type === 'Canvas'" class="canvas-embed is-loaded">
|
||||||
|
<CanvasRenderer :canvas="JSON.parse(content) " />
|
||||||
|
</div>
|
||||||
|
<div class="not-found-container" v-else>
|
||||||
|
<div class="not-found-image"></div>
|
||||||
|
<div class="not-found-title">Fichier vide</div>
|
||||||
|
<div class="not-found-description">Cette page est vide</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="not-found-container" v-else>
|
||||||
|
<div class="not-found-image"></div>
|
||||||
|
<div class="not-found-title">Impossible d'afficher</div>
|
||||||
|
<div class="not-found-description">Cette page est impossible à traiter</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
<span ref="el" @mouseenter="debounce(show, 250)" @mouseleave="debounce(() => display = false, 250)">
|
||||||
|
<slot></slot>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
project: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
anchor: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const content = ref(''), title = ref(''), type = ref(''), display = ref(false), pending = ref(false), fetched = ref(false);
|
||||||
|
const el = ref(), pos = ref<Record<string, string>>();
|
||||||
|
const loaded = computed(() => !pending.value && fetched.value && content.value !== '');
|
||||||
|
|
||||||
|
async function fetch()
|
||||||
|
{
|
||||||
|
fetched.value = true;
|
||||||
|
pending.value = true;
|
||||||
|
|
||||||
|
const data = await $fetch(`/api/project/${props.project}/file`, {
|
||||||
|
method: 'get',
|
||||||
|
query: {
|
||||||
|
path: props.path,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
pending.value = false;
|
||||||
|
|
||||||
|
if(data && data[0])
|
||||||
|
{
|
||||||
|
content.value = data[0].content;
|
||||||
|
title.value = data[0].title;
|
||||||
|
type.value = data[0].type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function show()
|
||||||
|
{
|
||||||
|
if(display.value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!fetched.value)
|
||||||
|
await fetch();
|
||||||
|
|
||||||
|
const rect = (el.value as HTMLDivElement)?.getBoundingClientRect();
|
||||||
|
|
||||||
|
if(!rect)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const r: Record<string, string> = {};
|
||||||
|
if (rect.bottom + 450 < window.innerHeight)
|
||||||
|
r.top = (rect.bottom + 4) + "px";
|
||||||
|
else
|
||||||
|
r.bottom = (window.innerHeight - rect.top + 4) + "px";
|
||||||
|
if (rect.right + 550 < window.innerWidth)
|
||||||
|
r.left = rect.left + "px";
|
||||||
|
else
|
||||||
|
r.right = (window.innerWidth - rect.right + 4) + "px";
|
||||||
|
|
||||||
|
pos.value = r;
|
||||||
|
display.value = true;
|
||||||
|
}
|
||||||
|
let debounceFn: () => void, timeout: NodeJS.Timeout;
|
||||||
|
function debounce(fn: () => void, ms: number)
|
||||||
|
{
|
||||||
|
debounceFn = fn;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(debounceFn, ms);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<NuxtLink v-if="data && data[0] && status !== 'pending'" :to="{ path: `/explorer/${project}${data[0].path}`, hash: hash }" :class="class">
|
<NuxtLink class="preview-link" v-if="data && data[0] && status !== 'pending'"
|
||||||
|
:to="{ path: `/explorer/${project}${data[0].path}`, hash: hash }" :class="class">
|
||||||
<PreviewContent :project="project" :path="data[0].path" :anchor="hash">
|
<PreviewContent :project="project" :path="data[0].path" :anchor="hash">
|
||||||
<slot v-bind="$attrs"></slot>
|
<slot v-bind="$attrs"></slot>
|
||||||
|
<ThemeIcon class="link-icon" v-if="data && data[0] && data[0].type !== 'Markdown'" :height="20" :width="20"
|
||||||
|
:icon="`link-${data[0].type.toLowerCase()}`" />
|
||||||
</PreviewContent>
|
</PreviewContent>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink v-else-if="href" :to="{ path: href }" :class="class">
|
<NuxtLink v-else-if="href" :to="{ path: href }" :class="class">
|
||||||
<slot v-bind="$attrs"></slot>
|
<slot v-bind="$attrs"></slot>
|
||||||
|
<ThemeIcon class="link-icon" v-if="data && data[0] && data[0].type !== 'Markdown'" :height="20" :width="20"
|
||||||
|
:icon="`link-${data[0].type.toLowerCase()}`" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<slot :class="class" v-else v-bind="$attrs"></slot>
|
<slot :class="class" v-else v-bind="$attrs"></slot>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -32,7 +37,7 @@ const { data, status } = await useFetch(`/api/project/${project.value}/file`, {
|
||||||
query: {
|
query: {
|
||||||
search: `%${pathname}`
|
search: `%${pathname}`
|
||||||
},
|
},
|
||||||
transform: (data) => data?.map(e => ({ path: e.path })),
|
transform: (data) => data?.map(e => ({ path: e.path, type: e.type })),
|
||||||
key: `file:${project.value}:%${pathname}`,
|
key: `file:${project.value}:%${pathname}`,
|
||||||
dedupe: 'defer'
|
dedupe: 'defer'
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,68 +1,10 @@
|
||||||
<script lang="ts">
|
|
||||||
let icon: HTMLDivElement | null;
|
|
||||||
function toggleLeftPanel(_: Event): void {
|
|
||||||
document.querySelector('.published-container')?.classList.toggle('is-left-column-open');
|
|
||||||
}
|
|
||||||
function hideLeftPanel(_: Event): void {
|
|
||||||
document.querySelector('.published-container')?.classList.remove('is-left-column-open');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { id: project, home, get } = useProject();
|
defineExpose({
|
||||||
|
navigation: false,
|
||||||
if(project.value !== 0 && home.value === null)
|
|
||||||
{
|
|
||||||
await useAsyncData(`project:get:${project}`, get);
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggled = ref(false);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
icon = document.querySelector('.site-nav-bar .clickable-icon');
|
|
||||||
icon?.removeEventListener('click', toggleLeftPanel);
|
|
||||||
icon?.addEventListener('click', toggleLeftPanel);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="site-nav-bar">
|
|
||||||
<div>
|
|
||||||
<div class="clickable-icon">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
class="svg-icon lucide-menu">
|
|
||||||
<line x1="4" y1="12" x2="20" y2="12"></line>
|
|
||||||
<line x1="4" y1="6" x2="20" y2="6"></line>
|
|
||||||
<line x1="4" y1="18" x2="20" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="gapx-3 flex align-stretch">
|
|
||||||
<NuxtLink @click="hideLeftPanel" class="site-nav-bar-text" aria-label="Accueil" :to="{ path: '/', force: true }">
|
|
||||||
<ThemeIcon icon="logo" :width=40 :height=40 />
|
|
||||||
</NuxtLink>
|
|
||||||
<div class="site-nav-bar-text mobile-hidden">
|
|
||||||
<NuxtLink aria-label="Projet" :to="{ path: project === 0 ? `/explorer` : `/explorer/${project}${home}`, force: true }"
|
|
||||||
:class="{'mod-active': $route.path.startsWith('/explorer')}">Projet</NuxtLink>
|
|
||||||
<div class="arrow-down" :class="{active: toggled}" @click="toggled = !toggled"></div>
|
|
||||||
<div class="arrow-group" @click="toggled = false">
|
|
||||||
<NuxtLink class="arrow-group-item" aria-label="Projets" :to="{ path: '/explorer', force: true }">Liste des projets</NuxtLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<NuxtLink class="site-nav-bar-text mobile-hidden" aria-label="Editeur" :to="{ path: '/editing', force: true }"
|
|
||||||
:class="{'mod-active': $route.path.startsWith('/editing')}">Editeur</NuxtLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mobile-bigger">
|
|
||||||
<SearchView />
|
|
||||||
</div>
|
|
||||||
<div class="ps-1 gapx-1 flex align-center">
|
|
||||||
<ThemeSwitch class="mobile-hidden" />
|
|
||||||
<NuxtLink class="site-login" :to="{ path: '/user/profile', force: true }">
|
|
||||||
<ThemeIcon icon="user" :width=32 :height=32 />
|
|
||||||
</NuxtLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="site-body">
|
<div class="site-body">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,70 +1,12 @@
|
||||||
<script lang="ts">
|
|
||||||
let icon: HTMLDivElement | null;
|
|
||||||
function toggleLeftPanel(_: Event): void {
|
|
||||||
document.querySelector('.published-container')?.classList.toggle('is-left-column-open');
|
|
||||||
}
|
|
||||||
function hideLeftPanel(_: Event): void {
|
|
||||||
document.querySelector('.published-container')?.classList.remove('is-left-column-open');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { id: project, home, get } = useProject();
|
defineExpose({
|
||||||
|
navigation: true,
|
||||||
if(project.value !== 0 && home.value === null)
|
|
||||||
{
|
|
||||||
await useAsyncData(`project:get:${project}`, get);
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggled = ref(false);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
icon = document.querySelector('.site-nav-bar .clickable-icon');
|
|
||||||
icon?.removeEventListener('click', toggleLeftPanel);
|
|
||||||
icon?.addEventListener('click', toggleLeftPanel);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="site-nav-bar">
|
|
||||||
<div>
|
|
||||||
<div class="clickable-icon">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
class="svg-icon lucide-menu">
|
|
||||||
<line x1="4" y1="12" x2="20" y2="12"></line>
|
|
||||||
<line x1="4" y1="6" x2="20" y2="6"></line>
|
|
||||||
<line x1="4" y1="18" x2="20" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="gapx-3 flex align-stretch">
|
|
||||||
<NuxtLink @click="hideLeftPanel" class="site-nav-bar-text" aria-label="Accueil" :to="{ path: '/', force: true }">
|
|
||||||
<ThemeIcon icon="logo" :width=40 :height=40 />
|
|
||||||
</NuxtLink>
|
|
||||||
<div class="site-nav-bar-text mobile-hidden">
|
|
||||||
<NuxtLink aria-label="Projet" :to="{ path: project === 0 ? `/explorer` : `/explorer/${project}${home}`, force: true }"
|
|
||||||
:class="{'mod-active': $route.path.startsWith('/explorer')}">Projet</NuxtLink>
|
|
||||||
<div class="arrow-down" :class="{active: toggled}" @click="toggled = !toggled"></div>
|
|
||||||
<div class="arrow-group" @click="toggled = false">
|
|
||||||
<NuxtLink class="arrow-group-item" aria-label="Projets" :to="{ path: '/explorer', force: true }">Liste des projets</NuxtLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<NuxtLink class="site-nav-bar-text mobile-hidden" aria-label="Editeur" :to="{ path: '/editing', force: true }"
|
|
||||||
:class="{'mod-active': $route.path.startsWith('/editing')}">Editeur</NuxtLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mobile-bigger">
|
|
||||||
<SearchView />
|
|
||||||
</div>
|
|
||||||
<div class="ps-1 gapx-1 flex align-center">
|
|
||||||
<ThemeSwitch class="mobile-hidden" />
|
|
||||||
<NuxtLink class="site-login" :to="{ path: '/user/profile', force: true }">
|
|
||||||
<ThemeIcon icon="user" :width=32 :height=32 />
|
|
||||||
</NuxtLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="site-body">
|
<div class="site-body">
|
||||||
<ExplorerNavigation></ExplorerNavigation>
|
<ExplorerNavigation></ExplorerNavigation>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -9,11 +9,18 @@ export default defineNuxtConfig({
|
||||||
password: '699c46bd-9aaa-4364-ad01-510ee4fe7013'
|
password: '699c46bd-9aaa-4364-ad01-510ee4fe7013'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
app: {
|
||||||
|
pageTransition: false,
|
||||||
|
layoutTransition: false
|
||||||
|
},
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
path: '~/components',
|
path: '~/components',
|
||||||
pathPrefix: false,
|
pathPrefix: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
security: {
|
||||||
|
rateLimiter: false
|
||||||
|
},
|
||||||
compatibilityDate: '2024-07-25'
|
compatibilityDate: '2024-07-25'
|
||||||
})
|
})
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><path fill="white" d="M200 156a27.87 27.87 0 0 0-19.4 7.84l-28.28-22A27.8 27.8 0 0 0 156 128a28 28 0 0 0-.45-5l19.45-6.45a28.07 28.07 0 1 0-2.53-7.58L153 115.45A28 28 0 0 0 128 100a27.7 27.7 0 0 0-7.6 1.06l-9.5-21.37A28 28 0 1 0 96 84a27.7 27.7 0 0 0 7.6-1.06l9.5 21.37a27.95 27.95 0 0 0-8.46 39.1L74 170.61a28 28 0 1 0 5.32 6l30.6-27.2a27.92 27.92 0 0 0 37.44-1.23l28.28 22A28 28 0 1 0 200 156m0-72a20 20 0 1 1-20 20a20 20 0 0 1 20-20M76 56a20 20 0 1 1 20 20a20 20 0 0 1-20-20M56 212a20 20 0 1 1 20-20a20 20 0 0 1-20 20m72-64a20 20 0 1 1 20-20a20 20 0 0 1-20 20m72 56a20 20 0 1 1 20-20a20 20 0 0 1-20 20"/></svg>
|
||||||
|
After Width: | Height: | Size: 700 B |
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><path fill="black" d="M200 156a27.87 27.87 0 0 0-19.4 7.84l-28.28-22A27.8 27.8 0 0 0 156 128a28 28 0 0 0-.45-5l19.45-6.45a28.07 28.07 0 1 0-2.53-7.58L153 115.45A28 28 0 0 0 128 100a27.7 27.7 0 0 0-7.6 1.06l-9.5-21.37A28 28 0 1 0 96 84a27.7 27.7 0 0 0 7.6-1.06l9.5 21.37a27.95 27.95 0 0 0-8.46 39.1L74 170.61a28 28 0 1 0 5.32 6l30.6-27.2a27.92 27.92 0 0 0 37.44-1.23l28.28 22A28 28 0 1 0 200 156m0-72a20 20 0 1 1-20 20a20 20 0 0 1 20-20M76 56a20 20 0 1 1 20 20a20 20 0 0 1-20-20M56 212a20 20 0 1 1 20-20a20 20 0 0 1-20 20m72-64a20 20 0 1 1 20-20a20 20 0 0 1-20 20m72 56a20 20 0 1 1 20-20a20 20 0 0 1-20 20"/></svg>
|
||||||
|
After Width: | Height: | Size: 700 B |
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><path fill="white" d="m210.83 85.17l-56-56A4 4 0 0 0 152 28H56a12 12 0 0 0-12 12v176a12 12 0 0 0 12 12h144a12 12 0 0 0 12-12V88a4 4 0 0 0-1.17-2.83M156 41.65L198.34 84H156ZM200 220H56a4 4 0 0 1-4-4V40a4 4 0 0 1 4-4h92v52a4 4 0 0 0 4 4h52v124a4 4 0 0 1-4 4m-36-84a4 4 0 0 1-4 4H96a4 4 0 0 1 0-8h64a4 4 0 0 1 4 4m0 32a4 4 0 0 1-4 4H96a4 4 0 0 1 0-8h64a4 4 0 0 1 4 4"/></svg>
|
||||||
|
After Width: | Height: | Size: 459 B |
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><path fill="black" d="m210.83 85.17l-56-56A4 4 0 0 0 152 28H56a12 12 0 0 0-12 12v176a12 12 0 0 0 12 12h144a12 12 0 0 0 12-12V88a4 4 0 0 0-1.17-2.83M156 41.65L198.34 84H156ZM200 220H56a4 4 0 0 1-4-4V40a4 4 0 0 1 4-4h92v52a4 4 0 0 0 4 4h52v124a4 4 0 0 1-4 4m-36-84a4 4 0 0 1-4 4H96a4 4 0 0 1 0-8h64a4 4 0 0 1 4 4m0 32a4 4 0 0 1-4 4H96a4 4 0 0 1 0-8h64a4 4 0 0 1 4 4"/></svg>
|
||||||
|
After Width: | Height: | Size: 459 B |
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><path fill="white" d="M241.72 113a11.88 11.88 0 0 0-9.73-5H212V88a12 12 0 0 0-12-12h-70.67l-28.8-21.6a12.05 12.05 0 0 0-7.2-2.4H40a12 12 0 0 0-12 12v144a4 4 0 0 0 4 4h179.09a4 4 0 0 0 3.79-2.74l28.49-85.47a11.86 11.86 0 0 0-1.65-10.79M40 60h53.33a4 4 0 0 1 2.4.8l29.87 22.4a4 4 0 0 0 2.4.8h72a4 4 0 0 1 4 4v20H69.76a12 12 0 0 0-11.38 8.21L36 183.35V64a4 4 0 0 1 4-4m195.78 61.26L208.2 204H37.55L66 118.74a4 4 0 0 1 3.76-2.74H232a4 4 0 0 1 3.79 5.26Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 545 B |
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><path fill="black" d="M241.72 113a11.88 11.88 0 0 0-9.73-5H212V88a12 12 0 0 0-12-12h-70.67l-28.8-21.6a12.05 12.05 0 0 0-7.2-2.4H40a12 12 0 0 0-12 12v144a4 4 0 0 0 4 4h179.09a4 4 0 0 0 3.79-2.74l28.49-85.47a11.86 11.86 0 0 0-1.65-10.79M40 60h53.33a4 4 0 0 1 2.4.8l29.87 22.4a4 4 0 0 0 2.4.8h72a4 4 0 0 1 4 4v20H69.76a12 12 0 0 0-11.38 8.21L36 183.35V64a4 4 0 0 1 4-4m195.78 61.26L208.2 204H37.55L66 118.74a4 4 0 0 1 3.76-2.74H232a4 4 0 0 1 3.79 5.26Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 545 B |
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><path fill="currentColor" d="M128 28a100 100 0 1 0 100 100A100.11 100.11 0 0 0 128 28M68.87 198.42a68 68 0 0 1 118.26 0a91.8 91.8 0 0 1-118.26 0m124.3-5.55a75.6 75.6 0 0 0-44.51-34a44 44 0 1 0-41.32 0a75.6 75.6 0 0 0-44.51 34a92 92 0 1 1 130.34 0M128 156a36 36 0 1 1 36-36a36 36 0 0 1-36 36"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 386 B |
|
|
@ -19,10 +19,7 @@ export default defineEventHandler(async (e) => {
|
||||||
|
|
||||||
const content = db.query(`SELECT * FROM explorer_comments WHERE ${where.join(" and ")}`).all(criteria) as Comment[];
|
const content = db.query(`SELECT * FROM explorer_comments WHERE ${where.join(" and ")}`).all(criteria) as Comment[];
|
||||||
|
|
||||||
if(content.length > 0)
|
return content;
|
||||||
{
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setResponseStatus(e, 404);
|
setResponseStatus(e, 404);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import useDatabase from '~/composables/useDatabase';
|
import useDatabase from '~/composables/useDatabase';
|
||||||
import type { File } from '~/types/api';
|
import type { File } from '~/types/api';
|
||||||
|
|
||||||
export default defineEventHandler(async (e) => {
|
export default defineCachedEventHandler(async (e) => {
|
||||||
const project = getRouterParam(e, "projectId");
|
const project = getRouterParam(e, "projectId");
|
||||||
const query = getQuery(e);
|
const query = getQuery(e);
|
||||||
|
|
||||||
|
|
@ -11,6 +11,8 @@ export default defineEventHandler(async (e) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(query);
|
||||||
|
|
||||||
const where = ["project = $project"];
|
const where = ["project = $project"];
|
||||||
const criteria: Record<string, any> = { $project: project };
|
const criteria: Record<string, any> = { $project: project };
|
||||||
|
|
||||||
|
|
@ -48,4 +50,7 @@ export default defineEventHandler(async (e) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
setResponseStatus(e, 404);
|
setResponseStatus(e, 404);
|
||||||
});
|
}, {
|
||||||
|
maxAge: 60*60*24,
|
||||||
|
getKey: (e) => `${getRouterParam(e, "projectId")}-${JSON.stringify(getQuery(e))}`
|
||||||
|
});
|
||||||