feat(web): add Vue workspace with file tree, tabs, autosave and live sync

- Login and first-run setup screens
- File tree with inline create/rename, context menu, drag-and-drop
  move and trash with undo; all operations update the UI first and roll
  back on failure
- Tabs keep a CodeMirror state each; edits autosave after 500 ms with
  If-Match revisions, unsent edits persist in IndexedDB, and conflicts
  or remote deletions surface as banners with explicit choices
- WebSocket events sync external edits, moves and deletions into open
  tabs; reconnects trigger a full resync
- Pasted or dropped images show instantly and upload to note.assets/
- Responsive layout with a swipe-in sidebar drawer on phones
- Switch to TypeScript 6: vue-tsc does not support TypeScript 7 yet
This commit is contained in:
eezd committed 2026-10-16 18:37:14 +08:00
1 parent c4d2bd4636
commit 02d904056c
28 files changed
+2231 -398

No files matched your search

+27
View File
@@ -0,0 +1,27 @@
<script setup lang="ts">
import Icon from './Icon.vue'
import { noteTitle } from './paths'
import { closeTab, ws } from './workspace'
</script>
<template>
<nav class="rm-tabs" role="tablist">
<div
v-for="tab in ws.tabs"
:key="tab.path"
class="rm-tab"
:class="{ active: ws.active === tab.path }"
role="tab"
:aria-selected="ws.active === tab.path"
:title="tab.path"
@click="ws.active = tab.path"
@auxclick.prevent="$event.button === 1 && closeTab(tab.path)"
>
<span class="rm-tab-title">{{ noteTitle(tab.path) }}</span>
<button class="rm-tab-close" :class="{ dirty: tab.dirty }" title="关闭" @click.stop="closeTab(tab.path)">
<span class="rm-dirty-dot" />
<Icon name="close" :size="12" />
</button>
</div>
</nav>
</template>