Rename Space to Project, add desktop document creation
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
import Icon from '@iconify/svelte';
|
||||
|
||||
let {
|
||||
spaceId,
|
||||
projectId,
|
||||
onClose
|
||||
}: {
|
||||
spaceId: string;
|
||||
projectId: string;
|
||||
onClose: () => void;
|
||||
} = $props();
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
const res = await fetch('/api/packages/publish', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ space_id: spaceId, version: version.trim() || undefined })
|
||||
body: JSON.stringify({ project_id: projectId, version: version.trim() || undefined })
|
||||
});
|
||||
if (!res.ok) {
|
||||
error = await res.text();
|
||||
@@ -45,7 +45,7 @@
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">
|
||||
Snapshots this space's files into an immutable package version, importable instance-wide as
|
||||
Snapshots this project's files into an immutable package version, importable instance-wide as
|
||||
<code class="font-mono text-xs bg-gray-100 dark:bg-white/10 px-1.5 py-0.5 rounded">@typstdrive/<name>:<version></code>.
|
||||
The name, version and entrypoint come from your <code class="font-mono text-xs bg-gray-100 dark:bg-white/10 px-1.5 py-0.5 rounded">typst.toml</code>.
|
||||
</p>
|
||||
|
||||
+11
-11
@@ -1,24 +1,24 @@
|
||||
<script lang="ts">
|
||||
import Icon from '@iconify/svelte';
|
||||
let { createSpace, onClose } = $props<{
|
||||
createSpace: (name: string) => void,
|
||||
let { createProject, onClose } = $props<{
|
||||
createProject: (name: string) => void,
|
||||
onClose: () => void
|
||||
}>();
|
||||
|
||||
let newSpaceName = $state('Untitled Space');
|
||||
let newProjectName = $state('Untitled Project');
|
||||
|
||||
function onSubmit(e: Event) {
|
||||
e.preventDefault();
|
||||
createSpace(newSpaceName);
|
||||
createProject(newProjectName);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4 animate-in fade-in duration-200" role="presentation" onclick={onClose}>
|
||||
<div tabindex="-1" class="bg-[var(--theme-bg)] rounded-xl shadow-2xl border border-gray-200 dark:border-white/10 w-full max-w-md overflow-hidden" role="dialog" aria-modal="true" aria-labelledby="create-space-title" onclick={(e) => e.stopPropagation()} onkeydown={(e) => { if (e.key === 'Escape') onClose(); }}>
|
||||
<div tabindex="-1" class="bg-[var(--theme-bg)] rounded-xl shadow-2xl border border-gray-200 dark:border-white/10 w-full max-w-md overflow-hidden" role="dialog" aria-modal="true" aria-labelledby="create-project-title" onclick={(e) => e.stopPropagation()} onkeydown={(e) => { if (e.key === 'Escape') onClose(); }}>
|
||||
<div class="flex justify-between items-center p-5 border-b border-gray-200 dark:border-white/10">
|
||||
<h2 id="create-space-title" class="text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<h2 id="create-project-title" class="text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<Icon icon="mdi:folder-multiple-plus" class="text-blue-500 text-xl" />
|
||||
Create Space
|
||||
Create Project
|
||||
</h2>
|
||||
<button onclick={onClose} aria-label="Close" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full p-1 transition-colors">
|
||||
<Icon icon="mdi:close" class="text-xl" />
|
||||
@@ -27,14 +27,14 @@
|
||||
|
||||
<form onsubmit={onSubmit} class="p-5 space-y-4">
|
||||
<div class="space-y-2">
|
||||
<label for="space-name-input" class="text-sm font-medium text-gray-700 dark:text-gray-300 block">Space Name</label>
|
||||
<label for="project-name-input" class="text-sm font-medium text-gray-700 dark:text-gray-300 block">Project Name</label>
|
||||
<input
|
||||
id="space-name-input"
|
||||
id="project-name-input"
|
||||
type="text"
|
||||
required
|
||||
bind:value={newSpaceName}
|
||||
bind:value={newProjectName}
|
||||
class="w-full bg-black/5 dark:bg-white/5 border border-gray-300 dark:border-white/20 text-gray-900 dark:text-white text-sm rounded-lg px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
|
||||
placeholder="Untitled Space"
|
||||
placeholder="Untitled Project"
|
||||
/>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">A multi-file workspace, seeded with a <code class="font-mono">typst.toml</code> and <code class="font-mono">main.typ</code>.</p>
|
||||
</div>
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
<div class="h-6 w-px bg-gray-300 dark:bg-white/20"></div>
|
||||
|
||||
<a href="/spaces" class="text-sm font-medium text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400 transition-colors bg-gray-100 hover:bg-gray-200 dark:bg-white/5 dark:hover:bg-white/10 px-3 py-2 rounded-lg flex items-center gap-2 border border-transparent dark:border-white/10" title="Spaces">
|
||||
<a href="/projects" class="text-sm font-medium text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400 transition-colors bg-gray-100 hover:bg-gray-200 dark:bg-white/5 dark:hover:bg-white/10 px-3 py-2 rounded-lg flex items-center gap-2 border border-transparent dark:border-white/10" title="Projects">
|
||||
<Icon icon="mdi:folder-multiple-outline" class="text-xl" />
|
||||
</a>
|
||||
<a href="/packages" class="text-sm font-medium text-gray-600 hover:text-purple-600 dark:text-gray-300 dark:hover:text-purple-400 transition-colors bg-gray-100 hover:bg-gray-200 dark:bg-white/5 dark:hover:bg-white/10 px-3 py-2 rounded-lg flex items-center gap-2 border border-transparent dark:border-white/10" title="Packages">
|
||||
|
||||
+18
-18
@@ -2,26 +2,26 @@
|
||||
import Icon from '@iconify/svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let { space, activeMenu, setActiveMenu, openInfo, openRename, deleteSpace } = $props<{
|
||||
space: any;
|
||||
let { project, activeMenu, setActiveMenu, openInfo, openRename, deleteProject } = $props<{
|
||||
project: any;
|
||||
activeMenu: string | null;
|
||||
setActiveMenu: (id: string | null) => void;
|
||||
openInfo: (space: any) => void;
|
||||
openInfo: (project: any) => void;
|
||||
openRename: (id: string, name: string) => void;
|
||||
deleteSpace: (id: string, name: string) => void;
|
||||
deleteProject: (id: string, name: string) => void;
|
||||
}>();
|
||||
|
||||
let dropUp = $state(false);
|
||||
|
||||
function toggleMenu(e: MouseEvent) {
|
||||
e.stopPropagation();
|
||||
if (activeMenu === space.id) {
|
||||
if (activeMenu === project.id) {
|
||||
setActiveMenu(null);
|
||||
} else {
|
||||
const button = e.currentTarget as HTMLElement;
|
||||
const rect = button.getBoundingClientRect();
|
||||
dropUp = window.innerHeight - rect.bottom < 200;
|
||||
setActiveMenu(space.id);
|
||||
setActiveMenu(project.id);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -30,13 +30,13 @@
|
||||
class="bg-white dark:bg-black/20 rounded-xl shadow-sm border border-gray-200 dark:border-white/10 flex flex-col hover:shadow-lg hover:border-blue-400 dark:hover:border-blue-500/50 transition-all duration-200 relative group transform hover:-translate-y-1 cursor-pointer overflow-visible"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick={() => goto(`/space/${space.id}`)}
|
||||
onkeydown={(e) => e.key === 'Enter' && goto(`/space/${space.id}`)}
|
||||
onclick={() => goto(`/project/${project.id}`)}
|
||||
onkeydown={(e) => e.key === 'Enter' && goto(`/project/${project.id}`)}
|
||||
>
|
||||
<div class="h-40 w-full bg-gray-50 dark:bg-black/40 rounded-t-xl overflow-hidden flex items-center justify-center border-b border-gray-100 dark:border-white/10 relative pointer-events-none">
|
||||
{#if space.thumbnail_svg}
|
||||
{#if project.thumbnail_svg}
|
||||
<div class="w-full h-full flex items-start justify-center bg-white transition-transform duration-300 group-hover:scale-110">
|
||||
<img src={`data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(space.thumbnail_svg)))}`} class="w-full h-auto shadow-sm border border-gray-200" alt="Thumbnail" draggable="false" />
|
||||
<img src={`data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(project.thumbnail_svg)))}`} class="w-full h-auto shadow-sm border border-gray-200" alt="Thumbnail" draggable="false" />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="p-4 bg-blue-50 dark:bg-blue-500/10 text-blue-600 dark:text-blue-400 rounded-full transition-transform duration-300 group-hover:scale-110">
|
||||
@@ -47,29 +47,29 @@
|
||||
|
||||
<div class="p-4 flex flex-col flex-grow">
|
||||
<div class="flex items-start justify-between">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white truncate pr-2 pointer-events-none" title={space.name}>{space.name}</h3>
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white truncate pr-2 pointer-events-none" title={project.name}>{project.name}</h3>
|
||||
|
||||
<div class="relative action-menu-container">
|
||||
<button aria-label="Space actions" onclick={toggleMenu} class="text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors p-1 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 pointer-events-auto">
|
||||
<button aria-label="Project actions" onclick={toggleMenu} class="text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors p-1 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 pointer-events-auto">
|
||||
<Icon icon="mdi:dots-vertical" class="text-xl" />
|
||||
</button>
|
||||
|
||||
{#if activeMenu === space.id}
|
||||
{#if activeMenu === project.id}
|
||||
<div class="absolute right-0 {dropUp ? 'bottom-full mb-1' : 'top-full mt-1'} w-48 bg-[var(--theme-bg)] rounded-xl shadow-xl border border-gray-200 dark:border-white/10 py-1 z-[100]">
|
||||
<button onclick={(e) => { e.stopPropagation(); openInfo(space); }} class="w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-black/5 dark:hover:bg-white/5 flex items-center gap-2">
|
||||
<button onclick={(e) => { e.stopPropagation(); openInfo(project); }} class="w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-black/5 dark:hover:bg-white/5 flex items-center gap-2">
|
||||
<Icon icon="mdi:information-outline" class="text-lg text-blue-500" />
|
||||
View Info
|
||||
</button>
|
||||
<button onclick={(e) => { e.stopPropagation(); openRename(space.id, space.name); }} class="w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-black/5 dark:hover:bg-white/5 flex items-center gap-2">
|
||||
<button onclick={(e) => { e.stopPropagation(); openRename(project.id, project.name); }} class="w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-black/5 dark:hover:bg-white/5 flex items-center gap-2">
|
||||
<Icon icon="mdi:pencil-outline" class="text-lg text-yellow-500" />
|
||||
Rename
|
||||
</button>
|
||||
<button onclick={(e) => { e.stopPropagation(); goto(`/space/${space.id}`); }} class="w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-black/5 dark:hover:bg-white/5 flex items-center gap-2">
|
||||
<button onclick={(e) => { e.stopPropagation(); goto(`/project/${project.id}`); }} class="w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-black/5 dark:hover:bg-white/5 flex items-center gap-2">
|
||||
<Icon icon="mdi:open-in-new" class="text-lg text-green-500" />
|
||||
Open
|
||||
</button>
|
||||
<div class="h-px bg-gray-200 dark:bg-white/10 my-1"></div>
|
||||
<button onclick={(e) => { e.stopPropagation(); deleteSpace(space.id, space.name); }} class="w-full text-left px-4 py-2 text-sm text-red-600 dark:text-red-400 hover:bg-red-500/10 flex items-center gap-2">
|
||||
<button onclick={(e) => { e.stopPropagation(); deleteProject(project.id, project.name); }} class="w-full text-left px-4 py-2 text-sm text-red-600 dark:text-red-400 hover:bg-red-500/10 flex items-center gap-2">
|
||||
<Icon icon="mdi:trash-can-outline" class="text-lg" />
|
||||
Delete
|
||||
</button>
|
||||
@@ -79,7 +79,7 @@
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 flex items-center gap-1 mt-2 pointer-events-none">
|
||||
<Icon icon="mdi:clock-outline" class="text-sm" />
|
||||
Edited {new Date(space.updated_at.endsWith('Z') ? space.updated_at : space.updated_at + 'Z').toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })}
|
||||
Edited {new Date(project.updated_at.endsWith('Z') ? project.updated_at : project.updated_at + 'Z').toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Icon from '@iconify/svelte';
|
||||
|
||||
interface SpaceFile {
|
||||
interface ProjectFile {
|
||||
id: string;
|
||||
path: string;
|
||||
kind: string;
|
||||
@@ -19,16 +19,16 @@
|
||||
onDelete,
|
||||
onSetEntry
|
||||
}: {
|
||||
files?: SpaceFile[];
|
||||
files?: ProjectFile[];
|
||||
activeFileId?: string;
|
||||
entrypoint?: string;
|
||||
readOnly?: boolean;
|
||||
onSelect: (file: SpaceFile) => void;
|
||||
onSelect: (file: ProjectFile) => void;
|
||||
onCreate: (path: string) => void;
|
||||
onUpload: (fileList: FileList) => void;
|
||||
onRename: (file: SpaceFile, path: string) => void;
|
||||
onDelete: (file: SpaceFile) => void;
|
||||
onSetEntry: (file: SpaceFile) => void;
|
||||
onRename: (file: ProjectFile, path: string) => void;
|
||||
onDelete: (file: ProjectFile) => void;
|
||||
onSetEntry: (file: ProjectFile) => void;
|
||||
} = $props();
|
||||
|
||||
let fileInput: HTMLInputElement = $state()!;
|
||||
@@ -48,7 +48,7 @@
|
||||
if (path && path.trim()) onCreate(path.trim());
|
||||
}
|
||||
|
||||
function handleRename(file: SpaceFile) {
|
||||
function handleRename(file: ProjectFile) {
|
||||
const path = prompt('Rename file to:', file.path);
|
||||
if (path && path.trim() && path.trim() !== file.path) onRename(file, path.trim());
|
||||
}
|
||||
+27
-27
@@ -9,14 +9,14 @@
|
||||
documentZoomStore,
|
||||
previewOpenStore
|
||||
} from '../../ts/store';
|
||||
import { exportSpace } from '../../ts/typst-api';
|
||||
import { exportProject } from '../../ts/typst-api';
|
||||
import ThemePicker from '../ThemePicker.svelte';
|
||||
import PageSettingsModal from '../PageSettingsModal.svelte';
|
||||
import PresentationMode from '../PresentationMode.svelte';
|
||||
|
||||
let {
|
||||
spaceName = 'Space',
|
||||
spaceId,
|
||||
projectName = 'Project',
|
||||
projectId,
|
||||
entrypoint = 'main.typ',
|
||||
role = 'owner',
|
||||
activeText = null,
|
||||
@@ -25,8 +25,8 @@
|
||||
onPublish,
|
||||
onFilesChanged
|
||||
}: {
|
||||
spaceName?: string;
|
||||
spaceId: string;
|
||||
projectName?: string;
|
||||
projectId: string;
|
||||
entrypoint?: string;
|
||||
role?: string;
|
||||
activeText?: any;
|
||||
@@ -55,10 +55,10 @@
|
||||
let showDeleteModal = $state(false);
|
||||
let renameName = $state('');
|
||||
|
||||
$effect(() => { renameName = spaceName; });
|
||||
$effect(() => { renameName = projectName; });
|
||||
|
||||
function safeName() {
|
||||
return spaceName.replace(/[^a-z0-9_-]/gi, '_');
|
||||
return projectName.replace(/[^a-z0-9_-]/gi, '_');
|
||||
}
|
||||
|
||||
function handleExport(format: 'pdf' | 'png' | 'svg' | 'typ') {
|
||||
@@ -73,7 +73,7 @@
|
||||
URL.revokeObjectURL(url);
|
||||
return;
|
||||
}
|
||||
exportSpace(spaceId, getAllText(), format, safeName()).catch((e) => {
|
||||
exportProject(projectId, getAllText(), format, safeName()).catch((e) => {
|
||||
console.error(`Export to ${format} failed:`, e);
|
||||
alert(`Failed to export as ${format.toUpperCase()}`);
|
||||
});
|
||||
@@ -83,7 +83,7 @@
|
||||
fetch(`/api/export/pdf`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ space_id: spaceId, files: getAllText() })
|
||||
body: JSON.stringify({ project_id: projectId, files: getAllText() })
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error('Print failed');
|
||||
@@ -225,7 +225,7 @@
|
||||
const file = target.files[0];
|
||||
const form = new FormData();
|
||||
form.append('file', file);
|
||||
fetch(`/api/spaces/${spaceId}/files/upload`, { method: 'POST', body: form })
|
||||
fetch(`/api/projects/${projectId}/files/upload`, { method: 'POST', body: form })
|
||||
.then((res) => res.json())
|
||||
.then(() => {
|
||||
const lower = file.name.toLowerCase();
|
||||
@@ -259,8 +259,8 @@
|
||||
|
||||
function submitRename(e: Event) {
|
||||
e.preventDefault();
|
||||
if (renameName && renameName !== spaceName) {
|
||||
fetch(`/api/spaces/${spaceId}`, {
|
||||
if (renameName && renameName !== projectName) {
|
||||
fetch(`/api/projects/${projectId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name: renameName })
|
||||
@@ -270,8 +270,8 @@
|
||||
}
|
||||
|
||||
function confirmDelete() {
|
||||
fetch(`/api/spaces/${spaceId}`, { method: 'DELETE' }).then((res) => {
|
||||
if (res.ok) goto('/spaces');
|
||||
fetch(`/api/projects/${projectId}`, { method: 'DELETE' }).then((res) => {
|
||||
if (res.ok) goto('/projects');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -289,14 +289,14 @@
|
||||
<header class="flex flex-col border-b border-[var(--theme-border)] bg-[var(--theme-bg)] text-[var(--theme-text)] select-none w-full relative z-[70]">
|
||||
<div class="flex items-center justify-between px-4 py-2.5">
|
||||
<div class="flex items-center gap-3">
|
||||
<button onclick={() => goto('/spaces')} class="p-1.5 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-white/10 transition-colors" title="Spaces">
|
||||
<button onclick={() => goto('/projects')} class="p-1.5 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-white/10 transition-colors" title="Projects">
|
||||
<Icon icon="mdi:arrow-left" class="text-xl" />
|
||||
</button>
|
||||
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon icon="mdi:folder-multiple-outline" class="text-blue-500 text-base" />
|
||||
<h1 class="text-[16px] font-semibold text-gray-900 dark:text-white tracking-tight truncate max-w-[200px] md:max-w-xs" title={spaceName}>{spaceName}</h1>
|
||||
<h1 class="text-[16px] font-semibold text-gray-900 dark:text-white tracking-tight truncate max-w-[200px] md:max-w-xs" title={projectName}>{projectName}</h1>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-0.5 text-[13px] font-medium text-gray-600 dark:text-gray-300 -ml-1 action-menu-container">
|
||||
@@ -304,11 +304,11 @@
|
||||
<button onclick={(e) => { e.stopPropagation(); activeMenu = activeMenu === 'file' ? null : 'file'; }} class="px-2 py-0.5 rounded transition-colors {activeMenu === 'file' ? 'bg-[var(--theme-border)]' : 'hover:bg-[var(--theme-border)]'}">File</button>
|
||||
{#if activeMenu === 'file'}
|
||||
<div class="absolute left-0 top-full mt-1 w-48 bg-[var(--theme-bg)] rounded-xl shadow-xl border border-[var(--theme-border)] py-1 z-[100] max-h-[calc(100vh-8rem)] overflow-y-auto">
|
||||
<button onclick={() => { activeMenu = null; goto('/spaces'); }} class="w-full text-left px-4 py-1.5 text-sm hover:bg-[var(--theme-border)]">All Spaces</button>
|
||||
<button onclick={() => { activeMenu = null; showInfoModal = true; }} class="w-full text-left px-4 py-1.5 text-sm hover:bg-[var(--theme-border)]">Space Info</button>
|
||||
<button onclick={() => { activeMenu = null; goto('/projects'); }} class="w-full text-left px-4 py-1.5 text-sm hover:bg-[var(--theme-border)]">All Projects</button>
|
||||
<button onclick={() => { activeMenu = null; showInfoModal = true; }} class="w-full text-left px-4 py-1.5 text-sm hover:bg-[var(--theme-border)]">Project Info</button>
|
||||
{#if !isViewer}
|
||||
<div class="h-px bg-[var(--theme-border)] my-1"></div>
|
||||
<button onclick={() => { activeMenu = null; renameName = spaceName; showRenameModal = true; }} class="w-full text-left px-4 py-1.5 text-sm hover:bg-[var(--theme-border)]">Rename</button>
|
||||
<button onclick={() => { activeMenu = null; renameName = projectName; showRenameModal = true; }} class="w-full text-left px-4 py-1.5 text-sm hover:bg-[var(--theme-border)]">Rename</button>
|
||||
<button onclick={() => { activeMenu = null; isPageSettingsOpen = true; }} class="w-full text-left px-4 py-1.5 text-sm hover:bg-[var(--theme-border)]">Page Settings</button>
|
||||
{#if role === 'owner'}
|
||||
<button onclick={() => { activeMenu = null; onPublish(); }} class="w-full text-left px-4 py-1.5 text-sm hover:bg-[var(--theme-border)]">Publish as Package</button>
|
||||
@@ -329,7 +329,7 @@
|
||||
<button onclick={() => { activeMenu = null; handlePandocExport('html'); }} class="w-full text-left px-4 py-1 text-sm hover:bg-[var(--theme-border)]">HTML (.html)</button>
|
||||
{#if role === 'owner'}
|
||||
<div class="h-px bg-[var(--theme-border)] my-1"></div>
|
||||
<button onclick={() => { activeMenu = null; showDeleteModal = true; }} class="w-full text-left px-4 py-1.5 text-sm text-red-500 hover:bg-red-500/10">Delete Space</button>
|
||||
<button onclick={() => { activeMenu = null; showDeleteModal = true; }} class="w-full text-left px-4 py-1.5 text-sm text-red-500 hover:bg-red-500/10">Delete Project</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -427,8 +427,8 @@
|
||||
<div class="w-px h-4 bg-gray-300 dark:bg-white/10"></div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<label for="space-font-select" class="text-[11px] font-semibold uppercase tracking-wider opacity-60">Font</label>
|
||||
<select id="space-font-select" onchange={(e) => insertTypstConfig('text', `font: "${e.currentTarget.value}"`)} class="bg-[var(--theme-bg)] text-[var(--theme-text)] border border-[var(--theme-border)] text-xs rounded shadow-sm block py-1 pl-2 pr-6 appearance-none cursor-pointer">
|
||||
<label for="project-font-select" class="text-[11px] font-semibold uppercase tracking-wider opacity-60">Font</label>
|
||||
<select id="project-font-select" onchange={(e) => insertTypstConfig('text', `font: "${e.currentTarget.value}"`)} class="bg-[var(--theme-bg)] text-[var(--theme-text)] border border-[var(--theme-border)] text-xs rounded shadow-sm block py-1 pl-2 pr-6 appearance-none cursor-pointer">
|
||||
<option value="New Computer Modern">Default (New CM)</option>
|
||||
<option value="Libertinus Serif">Libertinus Serif</option>
|
||||
<option value="PT Sans">PT Sans</option>
|
||||
@@ -478,10 +478,10 @@
|
||||
<div class="bg-[var(--theme-bg)] text-[var(--theme-text)] rounded-2xl shadow-2xl border border-gray-200 dark:border-white/10 w-full max-w-sm overflow-hidden" onclick={(e) => e.stopPropagation()} role="presentation">
|
||||
<div class="p-6 border-b border-gray-100 dark:border-white/10 flex items-center gap-3">
|
||||
<Icon icon="mdi:folder-multiple-outline" class="text-xl text-blue-500" />
|
||||
<h3 class="text-lg font-semibold flex-grow truncate">{spaceName}</h3>
|
||||
<h3 class="text-lg font-semibold flex-grow truncate">{projectName}</h3>
|
||||
</div>
|
||||
<div class="p-6 space-y-4 text-sm">
|
||||
<div><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-1">Type</p><p>Space (multi-file)</p></div>
|
||||
<div><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-1">Type</p><p>Project (multi-file)</p></div>
|
||||
<div><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-1">Entrypoint</p><p class="font-mono">{entrypoint}</p></div>
|
||||
<div><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-1">Your role</p><p class="capitalize">{role}</p></div>
|
||||
</div>
|
||||
@@ -496,7 +496,7 @@
|
||||
<div class="fixed inset-0 bg-black/60 backdrop-blur-sm z-[100] flex items-center justify-center p-4" onclick={() => showRenameModal = false} role="presentation">
|
||||
<div class="bg-[var(--theme-bg)] text-[var(--theme-text)] rounded-2xl shadow-2xl border border-gray-200 dark:border-white/10 w-full max-w-sm overflow-hidden" onclick={(e) => e.stopPropagation()} role="presentation">
|
||||
<form onsubmit={submitRename} class="p-6">
|
||||
<h3 class="text-lg font-semibold mb-4 flex items-center gap-2"><Icon icon="mdi:pencil-outline" class="text-yellow-500" /> Rename Space</h3>
|
||||
<h3 class="text-lg font-semibold mb-4 flex items-center gap-2"><Icon icon="mdi:pencil-outline" class="text-yellow-500" /> Rename Project</h3>
|
||||
<input type="text" required bind:value={renameName} class="w-full bg-transparent border border-gray-300 dark:border-white/20 text-sm rounded-lg px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-blue-500" />
|
||||
<div class="pt-6 flex justify-end gap-3">
|
||||
<button type="button" onclick={() => showRenameModal = false} class="px-4 py-2 text-sm font-medium hover:bg-gray-100 dark:hover:bg-white/10 rounded-lg">Cancel</button>
|
||||
@@ -511,8 +511,8 @@
|
||||
<div class="fixed inset-0 bg-black/60 backdrop-blur-sm z-[100] flex items-center justify-center p-4" onclick={() => showDeleteModal = false} role="presentation">
|
||||
<div class="bg-[var(--theme-bg)] text-[var(--theme-text)] rounded-2xl shadow-2xl border border-gray-200 dark:border-white/10 w-full max-w-sm overflow-hidden" onclick={(e) => e.stopPropagation()} role="presentation">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-semibold mb-4 flex items-center gap-2"><Icon icon="mdi:trash-can-outline" class="text-red-500" /> Delete Space</h3>
|
||||
<p class="text-gray-600 dark:text-gray-300 text-sm mb-6">Delete this space and all its files? This cannot be undone.</p>
|
||||
<h3 class="text-lg font-semibold mb-4 flex items-center gap-2"><Icon icon="mdi:trash-can-outline" class="text-red-500" /> Delete Project</h3>
|
||||
<p class="text-gray-600 dark:text-gray-300 text-sm mb-6">Delete this project and all its files? This cannot be undone.</p>
|
||||
<div class="flex justify-end gap-3">
|
||||
<button type="button" onclick={() => showDeleteModal = false} class="px-4 py-2 text-sm font-medium hover:bg-gray-100 dark:hover:bg-white/10 rounded-lg">Cancel</button>
|
||||
<button type="button" onclick={confirmDelete} class="bg-red-600 hover:bg-red-700 text-white px-5 py-2 rounded-lg text-sm font-medium">Delete</button>
|
||||
Reference in New Issue
Block a user