feat(ui): streamline workspace settings and remove sharing

Unify compact workspace controls and panel transitions, move directory actions into the context menu, and remove backlink UI.

Reorganize settings with searchable sections, per-setting resets, shortcut search, and accurate save feedback.

Remove sharing UI, APIs, authentication helpers, and storage code; provide a transactional cleanup script with retirement tests. Reject directories during orphan image cleanup and cover mixed-batch preservation.
This commit is contained in:
eezd committed 2026-10-24 12:32:23 +08:00
1 parent a716794e41
commit 19aab631ec
63 files changed
+1077 -3343

No files matched your search

+1 -30
View File
@@ -1,8 +1,6 @@
package api
import (
"encoding/json"
"io"
"net/http"
"os"
"path/filepath"
@@ -10,7 +8,7 @@ import (
"testing"
)
func TestThemeAndShareAppearanceBoundaries(t *testing.T) {
func TestThemeBoundaries(t *testing.T) {
f := newFixture(t)
dir := filepath.Join(filepath.Dir(f.root), "themes", "paper")
if err := os.MkdirAll(dir, 0o755); err != nil {
@@ -42,33 +40,6 @@ func TestThemeAndShareAppearanceBoundaries(t *testing.T) {
if !strings.Contains(theme["css"], "#faf6ed") {
t.Fatal("未读取到主题 CSS")
}
f.write("笔记.md", "正文")
f.do(f.admin, "PATCH", "/api/v1/settings", `{"theme":"paper","colorMode":"dark","fontSize":18,"uiFontSize":20,"privateSetting":"不公开","cssSnippets":[{"id":"a","name":"启用","css":"body { color: red }","enabled":true},{"id":"b","name":"停用","css":"不公开片段","enabled":false}]}`, nil)
sh := decode[shareView](t, f.do(f.admin, "POST", "/api/v1/shares", `{"path":"笔记.md","password":"访问密码"}`, nil))
base := "/api/v1/public/shares/" + sh.Token
if code := f.status(guest, "GET", base+"/appearance", "", nil); code != 401 {
t.Fatalf("未解锁不能读取外观,得到 %d", code)
}
f.do(guest, "POST", base+"/unlock", `{"password":"访问密码"}`, nil)
res := f.do(guest, "GET", base+"/appearance", "", nil)
raw, err := io.ReadAll(res.Body)
if err != nil {
t.Fatal(err)
}
var appearance map[string]any
if err := json.Unmarshal(raw, &appearance); err != nil {
t.Fatal(err)
}
if res.StatusCode != 200 || appearance["colorMode"] != "dark" || appearance["uiFontSize"] != float64(20) || appearance["fontSize"] != float64(18) || !strings.Contains(appearance["themeCSS"].(string), "#faf6ed") {
t.Fatalf("分享外观错误:%s", raw)
}
if strings.Contains(string(raw), "不公开") || len(appearance["cssSnippets"].([]any)) != 1 {
t.Fatalf("分享泄露了其他设置或停用片段:%s", raw)
}
f.do(f.admin, "DELETE", "/api/v1/shares/"+sh.Token, "", nil)
if code := f.status(guest, "GET", base+"/appearance", "", nil); code != 404 {
t.Fatalf("撤销分享后不能读取外观,得到 %d", code)
}
// CSS 符号链接不能读取主题目录之外的应用数据。
if err := os.Remove(filepath.Join(dir, "theme.css")); err != nil {
t.Fatal(err)