diff --git a/backend/internal/admin/api.go b/backend/internal/admin/api.go
index f4b5beb..1d99990 100644
--- a/backend/internal/admin/api.go
+++ b/backend/internal/admin/api.go
@@ -29,6 +29,7 @@ func (a *API) Routes() http.Handler {
mux.HandleFunc("/api/admin/logout", a.logout)
mux.HandleFunc("/api/admin/me", a.guard(a.me))
+ mux.HandleFunc("/api/admin/dashboard", a.guard(a.dashboard))
mux.HandleFunc("/api/admin/posts", a.guard(a.listPosts))
mux.HandleFunc("/api/admin/posts/", a.guard(a.postByID))
mux.HandleFunc("/api/admin/tags", a.guard(a.listTags))
@@ -128,12 +129,13 @@ func (a *API) listPosts(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
o := store.ListOptions{
- Kind: httpx.QueryString(r, "kind"),
- Tag: httpx.QueryString(r, "tag"),
- Query: httpx.QueryString(r, "q"),
- Status: httpx.QueryString(r, "status"),
- Page: httpx.QueryInt(r, "page", 1),
- Size: httpx.QueryInt(r, "size", 20),
+ Kind: httpx.QueryString(r, "kind"),
+ Tag: httpx.QueryString(r, "tag"),
+ Query: httpx.QueryString(r, "q"),
+ Status: httpx.QueryString(r, "status"),
+ OrderBy: httpx.QueryString(r, "order"),
+ Page: httpx.QueryInt(r, "page", 1),
+ Size: httpx.QueryInt(r, "size", 20),
}
if o.Status == "" {
o.Status = "any"
@@ -150,7 +152,7 @@ func (a *API) listPosts(w http.ResponseWriter, r *http.Request) {
httpx.BadRequest(w, "invalid body")
return
}
- in.Status = normalizeStatus(in.Status)
+ in.Status = store.NormalizeStatus(in.Status)
p, err := a.Store.Create(in)
if err != nil {
httpx.ServerError(w, err)
@@ -162,21 +164,16 @@ func (a *API) listPosts(w http.ResponseWriter, r *http.Request) {
}
}
-func normalizeStatus(s string) string {
- switch strings.ToLower(strings.TrimSpace(s)) {
- case model.StatusDraft, model.StatusPublished:
- return strings.ToLower(strings.TrimSpace(s))
- default:
- return model.StatusDraft
- }
-}
-
func (a *API) postByID(w http.ResponseWriter, r *http.Request) {
rest := strings.Trim(strings.TrimPrefix(r.URL.Path, "/api/admin/posts/"), "/")
if rest == "" {
a.listPosts(w, r)
return
}
+ if rest == "bulk" {
+ a.bulkPosts(w, r)
+ return
+ }
id, err := parseInt(rest)
if err != nil {
httpx.BadRequest(w, "bad post id")
@@ -193,7 +190,7 @@ func (a *API) postByID(w http.ResponseWriter, r *http.Request) {
return
}
if in.Status != "" {
- in.Status = normalizeStatus(in.Status)
+ in.Status = store.NormalizeStatus(in.Status)
}
p, err := a.Store.Update(id, in)
writeOne(w, p, err)
@@ -264,21 +261,27 @@ func (a *API) listTags(w http.ResponseWriter, r *http.Request) {
func (a *API) tagByID(w http.ResponseWriter, r *http.Request) {
rest := strings.Trim(strings.TrimPrefix(r.URL.Path, "/api/admin/tags/"), "/")
- id, err := parseInt(rest)
+ parts := strings.SplitN(rest, "/", 2)
+ id, err := parseInt(parts[0])
if err != nil {
httpx.BadRequest(w, "bad tag id")
return
}
+ if len(parts) == 2 && parts[1] == "merge" {
+ a.mergeTag(w, r, id)
+ return
+ }
switch r.Method {
case http.MethodPut, http.MethodPatch:
var in struct {
- Name string `json:"name"`
+ Name string `json:"name"`
+ Color string `json:"color"`
}
if err := httpx.Decode(r, &in); err != nil {
httpx.BadRequest(w, "invalid body")
return
}
- t, err := a.Store.RenameTag(id, in.Name)
+ t, err := a.Store.UpdateTag(id, in.Name, in.Color)
writeTag(w, t, err)
case http.MethodDelete:
if err := a.Store.DeleteTag(id); err != nil {
@@ -291,6 +294,26 @@ func (a *API) tagByID(w http.ResponseWriter, r *http.Request) {
}
}
+func (a *API) mergeTag(w http.ResponseWriter, r *http.Request, fromID int64) {
+ if r.Method != http.MethodPost {
+ httpx.Error(w, http.StatusMethodNotAllowed, "POST required")
+ return
+ }
+ var in struct {
+ ToID int64 `json:"to_id"`
+ }
+ if err := httpx.Decode(r, &in); err != nil {
+ httpx.BadRequest(w, "invalid body")
+ return
+ }
+ if in.ToID == 0 {
+ httpx.BadRequest(w, "to_id required")
+ return
+ }
+ t, err := a.Store.MergeTags(fromID, in.ToID)
+ writeTag(w, t, err)
+}
+
func writeTag(w http.ResponseWriter, t model.Tag, err error) {
if err != nil {
if errors.Is(err, store.ErrNotFound) {
@@ -348,3 +371,60 @@ func parseInt(s string) (int64, error) {
}
return n, nil
}
+
+// ---------- dashboard ----------
+
+func (a *API) dashboard(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ httpx.Error(w, http.StatusMethodNotAllowed, "GET required")
+ return
+ }
+ d, err := a.Store.Dashboard()
+ if err != nil {
+ httpx.ServerError(w, err)
+ return
+ }
+ httpx.OK(w, d)
+}
+
+// ---------- bulk posts ----------
+
+type bulkPostsRequest struct {
+ IDs []int64 `json:"ids"`
+ Action string `json:"action"` // "publish" | "draft" | "delete"
+}
+
+func (a *API) bulkPosts(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ httpx.Error(w, http.StatusMethodNotAllowed, "POST required")
+ return
+ }
+ var in bulkPostsRequest
+ if err := httpx.Decode(r, &in); err != nil {
+ httpx.BadRequest(w, "invalid body")
+ return
+ }
+ if len(in.IDs) == 0 {
+ httpx.BadRequest(w, "ids required")
+ return
+ }
+ switch in.Action {
+ case "publish", "draft":
+ n, err := a.Store.BulkUpdateStatus(in.IDs, store.NormalizeStatus(in.Action))
+ if err != nil {
+ httpx.ServerError(w, err)
+ return
+ }
+ httpx.OK(w, map[string]any{"ok": true, "updated": n})
+ case "delete":
+ var failed int
+ for _, id := range in.IDs {
+ if err := a.Store.Delete(id); err != nil {
+ failed++
+ }
+ }
+ httpx.OK(w, map[string]any{"ok": true, "deleted": len(in.IDs) - failed, "failed": failed})
+ default:
+ httpx.BadRequest(w, "action must be one of publish|draft|delete")
+ }
+}
diff --git a/backend/internal/model/model.go b/backend/internal/model/model.go
index 547d49e..e5309aa 100644
--- a/backend/internal/model/model.go
+++ b/backend/internal/model/model.go
@@ -18,6 +18,7 @@ type Post struct {
Title string `json:"title"`
Slug string `json:"slug"`
Summary string `json:"summary"`
+ CoverURL string `json:"cover_url"`
ContentMd string `json:"content_md,omitempty"`
ContentHTML string `json:"content_html"`
Status string `json:"status"`
@@ -33,6 +34,7 @@ type PostInput struct {
Title string `json:"title"`
Slug string `json:"slug"`
Summary string `json:"summary"`
+ CoverURL string `json:"cover_url"`
ContentMd string `json:"content_md"`
Status string `json:"status"`
PublishedAt string `json:"published_at"`
@@ -44,6 +46,7 @@ type Tag struct {
ID int64 `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
+ Color string `json:"color"`
Count int `json:"count"`
}
@@ -65,6 +68,26 @@ type Page struct {
Size int `json:"size"`
}
+// Dashboard is the snapshot rendered on /admin (homepage).
+type Dashboard struct {
+ TotalPosts int `json:"total_posts"`
+ PublishedPosts int `json:"published_posts"`
+ DraftPosts int `json:"draft_posts"`
+ ShortPosts int `json:"short_posts"`
+ LongPosts int `json:"long_posts"`
+ TotalTags int `json:"total_tags"`
+ TotalWords int `json:"total_words"`
+ RecentPosts []Post `json:"recent_posts"`
+ RecentDrafts []Post `json:"recent_drafts"`
+ TopTags []Tag `json:"top_tags"`
+ PublishedByMonth []MonthBucket `json:"published_by_month"`
+}
+
+type MonthBucket struct {
+ Month string `json:"month"` // "YYYY-MM"
+ Count int `json:"count"`
+}
+
type Settings struct {
SiteTitle string `json:"site_title"`
SiteDesc string `json:"site_desc"`
@@ -73,4 +96,13 @@ type Settings struct {
FooterNote string `json:"footer_note"`
ICPLicense string `json:"icp"`
PostsPerPage int `json:"posts_per_page"`
+ // LightSkinID is the front-end skin used when the client (or system)
+ // prefers light. Valid values: paper / sage / rose.
+ // Dark side is fixed to ink for now — kept implicit so we can add
+ // dark variants later without breaking clients.
+ LightSkinID string `json:"light_skin_id"`
+ // ThemeID is kept for backward compatibility with older clients that
+ // only know about a single skin. settingsFromMap falls back to it
+ // when LightSkinID is empty.
+ ThemeID string `json:"theme_id,omitempty"`
}
diff --git a/backend/internal/store/store.go b/backend/internal/store/store.go
index 0efa27f..b852338 100644
--- a/backend/internal/store/store.go
+++ b/backend/internal/store/store.go
@@ -44,6 +44,7 @@ func (s *Store) migrate() error {
title TEXT NOT NULL DEFAULT '',
slug TEXT NOT NULL,
summary TEXT NOT NULL DEFAULT '',
+ cover_url TEXT NOT NULL DEFAULT '',
content_md TEXT NOT NULL DEFAULT '',
content_html TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'draft',
@@ -53,9 +54,10 @@ func (s *Store) migrate() error {
reading_minutes INTEGER NOT NULL DEFAULT 1
)`, ai),
fmt.Sprintf(`CREATE TABLE IF NOT EXISTS tags (
- id %s,
- name TEXT NOT NULL,
- slug TEXT NOT NULL
+ id %s,
+ name TEXT NOT NULL,
+ slug TEXT NOT NULL,
+ color TEXT NOT NULL DEFAULT ''
)`, ai),
`CREATE TABLE IF NOT EXISTS post_tags (
post_id INTEGER NOT NULL,
@@ -73,6 +75,20 @@ func (s *Store) migrate() error {
}
}
+ // Lightweight column-add migrations for older databases. SQLite supports
+ // ADD COLUMN; Postgres doesn't support IF NOT EXISTS on column add, so we
+ // swallow "already exists" errors either way.
+ columnAdds := []string{
+ `ALTER TABLE posts ADD COLUMN cover_url TEXT NOT NULL DEFAULT ''`,
+ `ALTER TABLE tags ADD COLUMN color TEXT NOT NULL DEFAULT ''`,
+ }
+ for _, q := range columnAdds {
+ if _, err := s.db.Exec(s.db.Q(q)); err != nil && !strings.Contains(err.Error(), "already exists") &&
+ !strings.Contains(err.Error(), "duplicate column") {
+ return fmt.Errorf("migrate column: %w", err)
+ }
+ }
+
// Indexes/unique constraints need dialect-specific "IF NOT EXISTS" support.
indexes := []struct{ name, ddl string }{
{"idx_posts_slug", `CREATE UNIQUE INDEX IF NOT EXISTS idx_posts_slug ON posts(slug)`},
@@ -144,6 +160,18 @@ func settingsFromMap(m map[string]string) model.Settings {
ICPLicense: m["icp"],
PostsPerPage: 10,
}
+ // LightSkinID is the new canonical name; fall back to legacy theme_id
+ // for clients that haven't been updated, and finally to "paper".
+ st.LightSkinID = m["light_skin_id"]
+ if st.LightSkinID == "" {
+ st.LightSkinID = m["theme_id"]
+ }
+ if st.LightSkinID == "" {
+ st.LightSkinID = "paper"
+ }
+ // Mirror the value into ThemeID so legacy API consumers still see it
+ // in the JSON response.
+ st.ThemeID = st.LightSkinID
if n := atoi(m["posts_per_page"]); n > 0 {
st.PostsPerPage = n
}
@@ -161,10 +189,21 @@ func atoi(v string) int {
return n
}
+// ValidLightSkins is the whitelist of light-side skin ids the API accepts.
+// Anything outside this set falls back to "paper" on write.
+var ValidLightSkins = map[string]bool{
+ "paper": true,
+ "sage": true,
+ "rose": true,
+}
+
func (s *Store) UpdateSettings(st model.Settings) error {
if st.PostsPerPage <= 0 {
st.PostsPerPage = 10
}
+ if !ValidLightSkins[st.LightSkinID] {
+ st.LightSkinID = "paper"
+ }
sets := map[string]string{
"site_title": st.SiteTitle,
"site_desc": st.SiteDesc,
@@ -173,6 +212,9 @@ func (s *Store) UpdateSettings(st model.Settings) error {
"footer_note": st.FooterNote,
"icp": st.ICPLicense,
"posts_per_page": fmt.Sprint(st.PostsPerPage),
+ "light_skin_id": st.LightSkinID,
+ // Mirror to theme_id so any older client still sees something.
+ "theme_id": st.LightSkinID,
}
for k, v := range sets {
if s.db.Dialect == db.Postgres {
@@ -202,13 +244,13 @@ type ListOptions struct {
OrderBy string
}
-const postCols = `id, kind, title, slug, summary, content_md, content_html, status,
+const postCols = `id, kind, title, slug, summary, cover_url, content_md, content_html, status,
published_at, created_at, updated_at, reading_minutes`
func scanPost(rows interface{ Scan(...any) error }) (model.Post, error) {
var p model.Post
- err := rows.Scan(&p.ID, &p.Kind, &p.Title, &p.Slug, &p.Summary, &p.ContentMd,
- &p.ContentHTML, &p.Status, &p.PublishedAt, &p.CreatedAt, &p.UpdatedAt, &p.ReadingMinutes)
+ err := rows.Scan(&p.ID, &p.Kind, &p.Title, &p.Slug, &p.Summary, &p.CoverURL,
+ &p.ContentMd, &p.ContentHTML, &p.Status, &p.PublishedAt, &p.CreatedAt, &p.UpdatedAt, &p.ReadingMinutes)
p.Tags = []string{}
return p, err
}
@@ -340,8 +382,8 @@ func (s *Store) GetBySlug(slug string) (model.Post, error) {
}
func scanPostInto(row *sql.Row, p *model.Post) error {
- err := row.Scan(&p.ID, &p.Kind, &p.Title, &p.Slug, &p.Summary, &p.ContentMd,
- &p.ContentHTML, &p.Status, &p.PublishedAt, &p.CreatedAt, &p.UpdatedAt, &p.ReadingMinutes)
+ err := row.Scan(&p.ID, &p.Kind, &p.Title, &p.Slug, &p.Summary, &p.CoverURL,
+ &p.ContentMd, &p.ContentHTML, &p.Status, &p.PublishedAt, &p.CreatedAt, &p.UpdatedAt, &p.ReadingMinutes)
if err == sql.ErrNoRows {
return ErrNotFound
}
@@ -386,11 +428,12 @@ func (s *Store) uniqueSlug(base string, excludeID int64) string {
func (s *Store) Create(in model.PostInput) (model.Post, error) {
p := model.Post{
- Kind: in.Kind,
- Title: strings.TrimSpace(in.Title),
- Slug: in.Slug,
- Status: in.Status,
- Tags: []string{},
+ Kind: in.Kind,
+ Title: strings.TrimSpace(in.Title),
+ Slug: in.Slug,
+ Status: in.Status,
+ CoverURL: strings.TrimSpace(in.CoverURL),
+ Tags: []string{},
}
if p.Kind == "" {
p.Kind = model.KindLong
@@ -428,17 +471,17 @@ func (s *Store) Create(in model.PostInput) (model.Post, error) {
}
var id int64
- q := s.db.Q(`INSERT INTO posts (kind,title,slug,summary,content_md,content_html,status,
+ q := s.db.Q(`INSERT INTO posts (kind,title,slug,summary,cover_url,content_md,content_html,status,
published_at,created_at,updated_at,reading_minutes)
- VALUES (?,?,?,?,?,?,?,?,?,?,?)`)
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`)
if s.db.Dialect == db.Postgres {
- err := s.db.QueryRow(q, p.Kind, p.Title, p.Slug, p.Summary, p.ContentMd, p.ContentHTML,
+ err := s.db.QueryRow(q, p.Kind, p.Title, p.Slug, p.Summary, p.CoverURL, p.ContentMd, p.ContentHTML,
p.Status, p.PublishedAt, p.CreatedAt, p.UpdatedAt, p.ReadingMinutes).Scan(&id)
if err != nil {
return p, err
}
} else {
- res, err := s.db.Exec(q, p.Kind, p.Title, p.Slug, p.Summary, p.ContentMd, p.ContentHTML,
+ res, err := s.db.Exec(q, p.Kind, p.Title, p.Slug, p.Summary, p.CoverURL, p.ContentMd, p.ContentHTML,
p.Status, p.PublishedAt, p.CreatedAt, p.UpdatedAt, p.ReadingMinutes)
if err != nil {
return p, err
@@ -483,6 +526,10 @@ func (s *Store) Update(id int64, in model.PostInput) (model.Post, error) {
if in.PublishedAt != "" {
p.PublishedAt = in.PublishedAt
}
+ // CoverURL is taken verbatim from input. The frontend always sends the
+ // current value (empty string included), so any update round-trips with
+ // whatever the user last saved.
+ p.CoverURL = strings.TrimSpace(in.CoverURL)
p.UpdatedAt = now()
if in.ReadingMinutes != nil && *in.ReadingMinutes > 0 {
p.ReadingMinutes = *in.ReadingMinutes
@@ -490,9 +537,9 @@ func (s *Store) Update(id int64, in model.PostInput) (model.Post, error) {
p.ReadingMinutes = readingMinutes(in.ContentMd)
}
- if _, err := s.db.Exec(s.db.Q(`UPDATE posts SET kind=?,title=?,slug=?,summary=?,content_md=?,
+ if _, err := s.db.Exec(s.db.Q(`UPDATE posts SET kind=?,title=?,slug=?,summary=?,cover_url=?,content_md=?,
content_html=?,status=?,published_at=?,updated_at=?,reading_minutes=? WHERE id=?`),
- p.Kind, p.Title, p.Slug, p.Summary, p.ContentMd, p.ContentHTML, p.Status,
+ p.Kind, p.Title, p.Slug, p.Summary, p.CoverURL, p.ContentMd, p.ContentHTML, p.Status,
p.PublishedAt, p.UpdatedAt, p.ReadingMinutes, id); err != nil {
return p, err
}
@@ -504,6 +551,37 @@ func (s *Store) Update(id int64, in model.PostInput) (model.Post, error) {
return s.Get(id)
}
+// BulkUpdateStatus flips the status of many posts in a single transaction.
+func (s *Store) BulkUpdateStatus(ids []int64, status string) (int, error) {
+ if len(ids) == 0 {
+ return 0, nil
+ }
+ status = NormalizeStatus(status)
+ placeholders := strings.TrimSuffix(strings.Repeat("?,", len(ids)), ",")
+ args := []any{status, now()}
+ for _, id := range ids {
+ args = append(args, id)
+ }
+ q := s.db.Q(`UPDATE posts SET status=?, updated_at=? WHERE id IN (` + placeholders + `)`)
+ res, err := s.db.Exec(q, args...)
+ if err != nil {
+ return 0, err
+ }
+ n, _ := res.RowsAffected()
+ return int(n), nil
+}
+
+// NormalizeStatus coerces an arbitrary input string into a known post status
+// value, defaulting to draft when nothing matches.
+func NormalizeStatus(s string) string {
+ switch strings.ToLower(strings.TrimSpace(s)) {
+ case model.StatusDraft, model.StatusPublished:
+ return strings.ToLower(strings.TrimSpace(s))
+ default:
+ return model.StatusDraft
+ }
+}
+
func (s *Store) Delete(id int64) error {
if _, err := s.db.Exec(s.db.Q(`DELETE FROM post_tags WHERE post_id = ?`), id); err != nil {
return err
@@ -568,9 +646,9 @@ func (s *Store) upsertTag(name string) (int64, error) {
}
func (s *Store) ListTags() ([]model.Tag, error) {
- q := s.db.Q(`SELECT t.id, t.name, t.slug, COUNT(pt.post_id) AS c
+ q := s.db.Q(`SELECT t.id, t.name, t.slug, t.color, COUNT(pt.post_id) AS c
FROM tags t LEFT JOIN post_tags pt ON pt.tag_id = t.id
- GROUP BY t.id, t.name, t.slug ORDER BY c DESC, t.name`)
+ GROUP BY t.id, t.name, t.slug, t.color ORDER BY c DESC, t.name`)
rows, err := s.db.Query(q)
if err != nil {
return nil, err
@@ -579,7 +657,7 @@ func (s *Store) ListTags() ([]model.Tag, error) {
out := []model.Tag{}
for rows.Next() {
var t model.Tag
- if err := rows.Scan(&t.ID, &t.Name, &t.Slug, &t.Count); err != nil {
+ if err := rows.Scan(&t.ID, &t.Name, &t.Slug, &t.Color, &t.Count); err != nil {
return nil, err
}
out = append(out, t)
@@ -599,6 +677,59 @@ func (s *Store) CreateTag(name string) (model.Tag, error) {
return model.Tag{ID: id, Name: name, Slug: Slugify(name)}, nil
}
+// CreateTagFull is used by the admin API when color is supplied.
+func (s *Store) CreateTagFull(name, color string) (model.Tag, error) {
+ name = strings.TrimSpace(name)
+ if name == "" {
+ return model.Tag{}, errors.New("tag name required")
+ }
+ slug := Slugify(name)
+ if slug == "" {
+ return model.Tag{}, errors.New("tag slug required")
+ }
+ var id int64
+ err := s.db.QueryRow(s.db.Q(`SELECT id FROM tags WHERE slug = ?`), slug).Scan(&id)
+ if err != nil && err != sql.ErrNoRows {
+ return model.Tag{}, err
+ }
+ if err == nil {
+ // already exists — update color and return
+ if _, err := s.db.Exec(s.db.Q(`UPDATE tags SET color=? WHERE id=?`), color, id); err != nil {
+ return model.Tag{}, err
+ }
+ return s.getTag(id)
+ }
+ color = strings.TrimSpace(color)
+ if s.db.Dialect == db.Postgres {
+ err := s.db.QueryRow(s.db.Q(`INSERT INTO tags(name,slug,color) VALUES (?,?,?) RETURNING id`),
+ name, slug, color).Scan(&id)
+ if err != nil {
+ return model.Tag{}, err
+ }
+ } else {
+ res, err := s.db.Exec(s.db.Q(`INSERT INTO tags(name,slug,color) VALUES (?,?,?)`), name, slug, color)
+ if err != nil {
+ return model.Tag{}, err
+ }
+ id, err = res.LastInsertId()
+ if err != nil {
+ return model.Tag{}, err
+ }
+ }
+ return s.getTag(id)
+}
+
+func (s *Store) getTag(id int64) (model.Tag, error) {
+ var t model.Tag
+ err := s.db.QueryRow(s.db.Q(`SELECT t.id, t.name, t.slug, t.color, COUNT(pt.post_id)
+ FROM tags t LEFT JOIN post_tags pt ON pt.tag_id = t.id
+ WHERE t.id = ? GROUP BY t.id`), id).Scan(&t.ID, &t.Name, &t.Slug, &t.Color, &t.Count)
+ if err == sql.ErrNoRows {
+ return t, ErrNotFound
+ }
+ return t, err
+}
+
func (s *Store) RenameTag(id int64, name string) (model.Tag, error) {
name = strings.TrimSpace(name)
slug := Slugify(name)
@@ -608,13 +739,49 @@ func (s *Store) RenameTag(id int64, name string) (model.Tag, error) {
if _, err := s.db.Exec(s.db.Q(`UPDATE tags SET name=?, slug=? WHERE id=?`), name, slug, id); err != nil {
return model.Tag{}, err
}
- var t model.Tag
- err := s.db.QueryRow(s.db.Q(`SELECT id, name, slug, 0 FROM tags WHERE id = ?`), id).
- Scan(&t.ID, &t.Name, &t.Slug, &t.Count)
- if err == sql.ErrNoRows {
- return t, ErrNotFound
+ return s.getTag(id)
+}
+
+// UpdateTag is the combined rename + recolor used by the admin UI.
+func (s *Store) UpdateTag(id int64, name, color string) (model.Tag, error) {
+ name = strings.TrimSpace(name)
+ slug := Slugify(name)
+ if name == "" {
+ return model.Tag{}, errors.New("tag name required")
}
- return t, err
+ color = strings.TrimSpace(color)
+ if _, err := s.db.Exec(s.db.Q(`UPDATE tags SET name=?, slug=?, color=? WHERE id=?`), name, slug, color, id); err != nil {
+ return model.Tag{}, err
+ }
+ return s.getTag(id)
+}
+
+// MergeTags moves every post from fromID to toID, then deletes fromID. It is a
+// no-op if the ids are equal or either doesn't exist.
+func (s *Store) MergeTags(fromID, toID int64) (model.Tag, error) {
+ if fromID == toID {
+ return model.Tag{}, errors.New("source and target are the same tag")
+ }
+ if _, err := s.getTag(fromID); err != nil {
+ return model.Tag{}, err
+ }
+ if _, err := s.getTag(toID); err != nil {
+ return model.Tag{}, err
+ }
+ // De-duplicate before the move so we don't end up with two rows pointing
+ // at the same post.
+ if _, err := s.db.Exec(s.db.Q(`DELETE FROM post_tags
+ WHERE post_id IN (SELECT post_id FROM post_tags WHERE tag_id = ?)
+ AND tag_id = ?`), toID, toID); err != nil {
+ return model.Tag{}, err
+ }
+ if _, err := s.db.Exec(s.db.Q(`UPDATE post_tags SET tag_id=? WHERE tag_id=?`), toID, fromID); err != nil {
+ return model.Tag{}, err
+ }
+ if _, err := s.db.Exec(s.db.Q(`DELETE FROM tags WHERE id=?`), fromID); err != nil {
+ return model.Tag{}, err
+ }
+ return s.getTag(toID)
}
func (s *Store) DeleteTag(id int64) error {
@@ -667,3 +834,133 @@ func splitDate(rfc3339 string) (string, string) {
}
return rfc3339[0:4], rfc3339[5:7]
}
+
+// ---------- dashboard ----------
+
+// Dashboard produces the snapshot rendered on the admin home page.
+func (s *Store) Dashboard() (model.Dashboard, error) {
+ d := model.Dashboard{
+ RecentPosts: []model.Post{},
+ RecentDrafts: []model.Post{},
+ TopTags: []model.Tag{},
+ PublishedByMonth: []model.MonthBucket{},
+ }
+
+ // status / kind counts --------------------------------------------------
+ rows, err := s.db.Query(s.db.Q(`SELECT status, kind, COUNT(*) FROM posts GROUP BY status, kind`))
+ if err != nil {
+ return d, err
+ }
+ for rows.Next() {
+ var status, kind string
+ var n int
+ if err := rows.Scan(&status, &kind, &n); err != nil {
+ rows.Close()
+ return d, err
+ }
+ d.TotalPosts += n
+ switch status {
+ case model.StatusPublished:
+ d.PublishedPosts += n
+ case model.StatusDraft:
+ d.DraftPosts += n
+ }
+ switch kind {
+ case model.KindShort:
+ d.ShortPosts += n
+ case model.KindLong:
+ d.LongPosts += n
+ }
+ }
+ rows.Close()
+
+ // total tag count -------------------------------------------------------
+ if err := s.db.QueryRow(s.db.Q(`SELECT COUNT(*) FROM tags`)).Scan(&d.TotalTags); err != nil {
+ return d, err
+ }
+
+ // total word count: sum of content_md length ----------------------------
+ if err := s.db.QueryRow(s.db.Q(`SELECT COALESCE(SUM(LENGTH(content_md)),0) FROM posts`)).Scan(&d.TotalWords); err != nil {
+ return d, err
+ }
+
+ // recent published posts (5) --------------------------------------------
+ if err := s.scanPostsInto(s.db.Q(`SELECT `+postCols+` FROM posts
+ WHERE status=? ORDER BY published_at DESC LIMIT 5`), []any{model.StatusPublished}, &d.RecentPosts); err != nil {
+ return d, err
+ }
+
+ // recent drafts (5) -----------------------------------------------------
+ if err := s.scanPostsInto(s.db.Q(`SELECT `+postCols+` FROM posts
+ WHERE status=? ORDER BY updated_at DESC LIMIT 5`), []any{model.StatusDraft}, &d.RecentDrafts); err != nil {
+ return d, err
+ }
+
+ // top tags (10) ---------------------------------------------------------
+ if err := s.db.QueryRow(s.db.Q(`SELECT COUNT(*) FROM tags`)).Scan(&d.TotalTags); err != nil {
+ return d, err
+ }
+ tagRows, err := s.db.Query(s.db.Q(`SELECT t.id, t.name, t.slug, t.color, COUNT(pt.post_id) AS c
+ FROM tags t LEFT JOIN post_tags pt ON pt.tag_id = t.id
+ GROUP BY t.id, t.name, t.slug, t.color ORDER BY c DESC, t.name LIMIT 10`))
+ if err != nil {
+ return d, err
+ }
+ for tagRows.Next() {
+ var t model.Tag
+ if err := tagRows.Scan(&t.ID, &t.Name, &t.Slug, &t.Color, &t.Count); err != nil {
+ tagRows.Close()
+ return d, err
+ }
+ d.TopTags = append(d.TopTags, t)
+ }
+ tagRows.Close()
+
+ // published_by_month — last 12 months -----------------------------------
+ monthRows, err := s.db.Query(s.db.Q(`SELECT substr(published_at,1,7) AS m, COUNT(*) AS c
+ FROM posts WHERE status=? AND length(published_at) >= 7
+ GROUP BY m ORDER BY m DESC LIMIT 12`), model.StatusPublished)
+ if err != nil {
+ return d, err
+ }
+ defer monthRows.Close()
+ for monthRows.Next() {
+ var b model.MonthBucket
+ if err := monthRows.Scan(&b.Month, &b.Count); err != nil {
+ return d, err
+ }
+ d.PublishedByMonth = append(d.PublishedByMonth, b)
+ }
+ // reverse so we can render left-to-right on the chart
+ for i, j := 0, len(d.PublishedByMonth)-1; i < j; i, j = i+1, j-1 {
+ d.PublishedByMonth[i], d.PublishedByMonth[j] = d.PublishedByMonth[j], d.PublishedByMonth[i]
+ }
+
+ return d, nil
+}
+
+// scanPostsInto is a small helper that runs a query and scans rows into the
+// given slice, attaching tags at the end.
+func (s *Store) scanPostsInto(query string, args []any, dst *[]model.Post) error {
+ rows, err := s.db.Query(query, args...)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ out := []model.Post{}
+ for rows.Next() {
+ p, err := scanPost(rows)
+ if err != nil {
+ return err
+ }
+ out = append(out, p)
+ }
+ if err := rows.Err(); err != nil {
+ return err
+ }
+ if err := s.attachTags(out); err != nil {
+ return err
+ }
+ *dst = out
+ return nil
+}
diff --git a/backend/internal/store/store_test.go b/backend/internal/store/store_test.go
index 026fcaf..c317e0b 100644
--- a/backend/internal/store/store_test.go
+++ b/backend/internal/store/store_test.go
@@ -40,3 +40,227 @@ func TestPostInputDefaults(t *testing.T) {
t.Errorf("empty status means draft is applied by the API layer, got %q", in.Status)
}
}
+
+func TestNormalizeStatus(t *testing.T) {
+ cases := map[string]string{
+ "": model.StatusDraft,
+ "draft": model.StatusDraft,
+ "published": model.StatusPublished,
+ " Published ": model.StatusPublished,
+ "pending": model.StatusDraft, // unknown → draft
+ }
+ for in, want := range cases {
+ if got := NormalizeStatus(in); got != want {
+ t.Errorf("NormalizeStatus(%q) = %q, want %q", in, got, want)
+ }
+ }
+}
+
+func TestRebindBulk(t *testing.T) {
+ // BulkUpdateStatus uses an IN clause with len(ids) placeholders + 1 for status.
+ d := &db.DB{Dialect: db.Postgres}
+ q := d.Q(`UPDATE posts SET status=? WHERE id IN (?,?,?)`)
+ if got := strings.Count(q, "$"); got != 4 {
+ t.Errorf("expected 4 placeholders, got %d", got)
+ }
+}
+
+func TestSettingsThemeID(t *testing.T) {
+ s := openTestStore(t)
+
+ st, err := s.GetSettings()
+ if err != nil {
+ t.Fatalf("get: %v", err)
+ }
+ if st.LightSkinID != "paper" {
+ t.Errorf("default light_skin_id should be 'paper', got %q", st.LightSkinID)
+ }
+
+ st.LightSkinID = "sage"
+ st.SiteTitle = "ONE"
+ if err := s.UpdateSettings(st); err != nil {
+ t.Fatalf("update: %v", err)
+ }
+ got, err := s.GetSettings()
+ if err != nil {
+ t.Fatalf("get again: %v", err)
+ }
+ if got.LightSkinID != "sage" {
+ t.Errorf("light_skin_id should persist, got %q", got.LightSkinID)
+ }
+ if got.ThemeID != "sage" {
+ t.Errorf("legacy theme_id should mirror light_skin_id, got %q", got.ThemeID)
+ }
+ if got.SiteTitle != "ONE" {
+ t.Errorf("site_title should persist, got %q", got.SiteTitle)
+ }
+}
+
+func TestSettingsLightSkinFallback(t *testing.T) {
+ s := openTestStore(t)
+ // Simulate an older DB that only has theme_id set.
+ if _, err := s.db.Exec(s.db.Q(`INSERT INTO settings(key,value) VALUES (?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value`),
+ "theme_id", "rose"); err != nil {
+ t.Fatalf("seed theme_id: %v", err)
+ }
+ st, err := s.GetSettings()
+ if err != nil {
+ t.Fatalf("get: %v", err)
+ }
+ if st.LightSkinID != "rose" {
+ t.Errorf("expected fallback to 'rose' from legacy theme_id, got %q", st.LightSkinID)
+ }
+}
+
+// TestStoreSQLite runs a small integration pass on top of an in-memory SQLite
+// database to exercise the new fields (cover_url, tag color) and the merge
+// + bulk + dashboard paths.
+func TestStoreSQLite(t *testing.T) {
+ s := openTestStore(t)
+
+ p1, err := s.Create(model.PostInput{
+ Kind: model.KindLong,
+ Title: "第一篇",
+ Slug: "first",
+ Summary: "first summary",
+ CoverURL: "https://example.com/a.png",
+ ContentMd: "# hi\n这是一段正文。",
+ Status: model.StatusPublished,
+ Tags: []string{"Go", "博客"},
+ })
+ if err != nil {
+ t.Fatalf("create p1: %v", err)
+ }
+ if p1.CoverURL == "" {
+ t.Errorf("cover_url not persisted")
+ }
+ if len(p1.Tags) != 2 {
+ t.Errorf("expected 2 tags, got %v", p1.Tags)
+ }
+
+ p2, err := s.Create(model.PostInput{
+ Kind: model.KindShort, ContentMd: "碎片想法。", Status: model.StatusDraft,
+ Tags: []string{"碎片"},
+ })
+ if err != nil {
+ t.Fatalf("create p2: %v", err)
+ }
+
+ // Update cover_url and tags. Frontend sends the current CoverURL back so
+ // the value is preserved across updates.
+ upd, err := s.Update(p1.ID, model.PostInput{
+ Kind: model.KindLong, Title: "第一篇",
+ CoverURL: p1.CoverURL,
+ ContentMd: "# hi\n这是新版本。",
+ Tags: []string{"Go"},
+ })
+ if err != nil {
+ t.Fatalf("update: %v", err)
+ }
+ if upd.CoverURL != p1.CoverURL {
+ t.Errorf("cover_url not preserved when echoed back, got %q", upd.CoverURL)
+ }
+
+ // An empty CoverURL is treated as an explicit clear.
+ upd2, err := s.Update(p1.ID, model.PostInput{
+ Kind: model.KindLong, Title: "第一篇", CoverURL: "",
+ Tags: []string{"Go"},
+ })
+ if err != nil {
+ t.Fatalf("update clear: %v", err)
+ }
+ if upd2.CoverURL != "" {
+ t.Errorf("empty cover_url should clear, got %q", upd2.CoverURL)
+ }
+ // restore for downstream tests
+ if _, err := s.Update(p1.ID, model.PostInput{
+ Kind: model.KindLong, Title: "第一篇", CoverURL: p1.CoverURL,
+ Tags: []string{"Go"},
+ }); err != nil {
+ t.Fatalf("restore: %v", err)
+ }
+
+ if len(upd.Tags) != 1 || upd.Tags[0] != "Go" {
+ t.Errorf("tags after update: %v", upd.Tags)
+ }
+
+ // BulkUpdateStatus flips drafts to published
+ n, err := s.BulkUpdateStatus([]int64{p2.ID}, model.StatusPublished)
+ if err != nil {
+ t.Fatalf("bulk: %v", err)
+ }
+ if n != 1 {
+ t.Errorf("expected 1 updated, got %d", n)
+ }
+
+ // Tag merge: create two distinct slug tags, attach both to p1, merge.
+ if _, err := s.UpdateTag(0, "", ""); err == nil {
+ t.Errorf("expected error for empty name")
+ }
+ tags, err := s.ListTags()
+ if err != nil {
+ t.Fatalf("list tags: %v", err)
+ }
+ var goID int64
+ for _, tg := range tags {
+ if tg.Name == "Go" {
+ goID = tg.ID
+ }
+ }
+ if goID == 0 {
+ t.Fatalf("Go tag not found")
+ }
+ // Give the surviving target tag a color so we can confirm it sticks.
+ if _, err := s.UpdateTag(goID, "Go", "#3d7f9c"); err != nil {
+ t.Fatalf("color target: %v", err)
+ }
+ // CreateTagFull uses upsertTag so "GoLang" with its own slug is a fresh row.
+ golang, err := s.CreateTagFull("GoLang", "")
+ if err != nil {
+ t.Fatalf("create golang: %v", err)
+ }
+ if _, err := s.db.Exec(s.db.Q(`INSERT INTO post_tags(post_id, tag_id) VALUES (?, ?)`), p1.ID, golang.ID); err != nil {
+ t.Fatalf("attach golang: %v", err)
+ }
+ merged, err := s.MergeTags(golang.ID, goID)
+ if err != nil {
+ t.Fatalf("merge: %v", err)
+ }
+ if merged.ID != goID {
+ t.Errorf("merge should return target tag, got %d want %d", merged.ID, goID)
+ }
+ if merged.Color != "#3d7f9c" {
+ t.Errorf("target tag color lost, got %q", merged.Color)
+ }
+
+ // Dashboard
+ d, err := s.Dashboard()
+ if err != nil {
+ t.Fatalf("dashboard: %v", err)
+ }
+ if d.TotalPosts != 2 {
+ t.Errorf("total_posts = %d", d.TotalPosts)
+ }
+ if d.PublishedPosts != 2 {
+ t.Errorf("published_posts = %d", d.PublishedPosts)
+ }
+ if len(d.RecentPosts) == 0 {
+ t.Errorf("recent posts empty")
+ }
+ if len(d.TopTags) == 0 {
+ t.Errorf("top tags empty")
+ }
+}
+
+func openTestStore(t *testing.T) *Store {
+ t.Helper()
+ d, err := db.Open("sqlite", ":memory:")
+ if err != nil {
+ t.Fatalf("open sqlite: %v", err)
+ }
+ s, err := New(d)
+ if err != nil {
+ t.Fatalf("new store: %v", err)
+ }
+ return s
+}
diff --git a/frontend/index.html b/frontend/index.html
index 91b6ffa..5aa9198 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -3,6 +3,8 @@
+
+
ONE · 一个博客
diff --git a/frontend/package.json b/frontend/package.json
index 35c1002..a094bd8 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -13,6 +13,7 @@
"dependencies": {
"@egoist/tailwindcss-icons": "^1.8.0",
"@iconify-json/mingcute": "^1.1.17",
+ "@milkdown/crepe": "^7.22.1",
"dompurify": "^3.1.6",
"marked": "^12.0.2",
"vue": "^3.4.0",
@@ -28,6 +29,8 @@
"vue-tsc": "^2.1.0"
},
"pnpm": {
- "onlyBuiltDependencies": ["esbuild"]
+ "onlyBuiltDependencies": [
+ "esbuild"
+ ]
}
}
diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml
index c5ede1b..a3b00d4 100644
--- a/frontend/pnpm-lock.yaml
+++ b/frontend/pnpm-lock.yaml
@@ -14,6 +14,9 @@ importers:
'@iconify-json/mingcute':
specifier: ^1.1.17
version: 1.2.7
+ '@milkdown/crepe':
+ specifier: ^7.22.1
+ version: 7.22.1(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.4)(typescript@5.9.3)
dompurify:
specifier: ^3.1.6
version: 3.4.12
@@ -75,6 +78,99 @@ packages:
resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
engines: {node: '>=6.9.0'}
+ '@codemirror/autocomplete@6.20.3':
+ resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==}
+
+ '@codemirror/commands@6.11.1':
+ resolution: {integrity: sha512-O/4hG3SC1YwcmQ0d2UVNDs+AsaNWd1iHVxbTeEBuqH+6bExAiPK3iS/BvpY6rZGURALv4ZD3sIgcCmRvw3ehBg==}
+
+ '@codemirror/lang-angular@0.1.4':
+ resolution: {integrity: sha512-oap+gsltb/fzdlTQWD6BFF4bSLKcDnlxDsLdePiJpCVNKWXSTAbiiQeYI3UmES+BLAdkmIC1WjyztC1pi/bX4g==}
+
+ '@codemirror/lang-cpp@6.0.3':
+ resolution: {integrity: sha512-URM26M3vunFFn9/sm6rzqrBzDgfWuDixp85uTY49wKudToc2jTHUrKIGGKs+QWND+YLofNNZpxcNGRynFJfvgA==}
+
+ '@codemirror/lang-css@6.3.1':
+ resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==}
+
+ '@codemirror/lang-go@6.0.1':
+ resolution: {integrity: sha512-7fNvbyNylvqCphW9HD6WFnRpcDjr+KXX/FgqXy5H5ZS0eC5edDljukm/yNgYkwTsgp2busdod50AOTIy6Jikfg==}
+
+ '@codemirror/lang-html@6.4.12':
+ resolution: {integrity: sha512-pw2ReWKUqSkbvh76RAT4NYxiogRu+PWkR2ukAwO9uOgrm8uipkzjtKKtNpyeAQwHOqxEeSvAXZ6vr3AfyB9y/w==}
+
+ '@codemirror/lang-java@6.0.2':
+ resolution: {integrity: sha512-m5Nt1mQ/cznJY7tMfQTJchmrjdjQ71IDs+55d1GAa8DGaB8JXWsVCkVT284C3RTASaY43YknrK2X3hPO/J3MOQ==}
+
+ '@codemirror/lang-javascript@6.2.5':
+ resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==}
+
+ '@codemirror/lang-jinja@6.0.1':
+ resolution: {integrity: sha512-P5kyHLObzjtbGj16h+hyvZTxJhSjBEeSx4wMjbnAf3b0uwTy2+F0zGjMZL4PQOm/mh2eGZ5xUDVZXgwP783Nsw==}
+
+ '@codemirror/lang-json@6.0.2':
+ resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==}
+
+ '@codemirror/lang-less@6.0.2':
+ resolution: {integrity: sha512-EYdQTG22V+KUUk8Qq582g7FMnCZeEHsyuOJisHRft/mQ+ZSZ2w51NupvDUHiqtsOy7It5cHLPGfHQLpMh9bqpQ==}
+
+ '@codemirror/lang-liquid@6.3.2':
+ resolution: {integrity: sha512-6PDVU3ZnfeYyz1at1E/ttorErZvZFXXt1OPhtfe1EZJ2V2iDFa0CwPqPgG5F7NXN0yONGoBogKmFAafKTqlwIw==}
+
+ '@codemirror/lang-markdown@6.5.2':
+ resolution: {integrity: sha512-AwBOdkWYuA//WcM0xO5PfHPUcmz/O2i5o0Nsg1U69SII/loCJlFI1Romd9xp2HYb1kYJRGZotyqRghuHH5n8Kw==}
+
+ '@codemirror/lang-php@6.0.2':
+ resolution: {integrity: sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA==}
+
+ '@codemirror/lang-python@6.2.1':
+ resolution: {integrity: sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==}
+
+ '@codemirror/lang-rust@6.0.2':
+ resolution: {integrity: sha512-EZaGjCUegtiU7kSMvOfEZpaCReowEf3yNidYu7+vfuGTm9ow4mthAparY5hisJqOHmJowVH3Upu+eJlUji6qqA==}
+
+ '@codemirror/lang-sass@6.0.2':
+ resolution: {integrity: sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==}
+
+ '@codemirror/lang-sql@6.10.0':
+ resolution: {integrity: sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==}
+
+ '@codemirror/lang-vue@0.1.3':
+ resolution: {integrity: sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==}
+
+ '@codemirror/lang-wast@6.0.2':
+ resolution: {integrity: sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==}
+
+ '@codemirror/lang-xml@6.1.0':
+ resolution: {integrity: sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==}
+
+ '@codemirror/lang-yaml@6.1.3':
+ resolution: {integrity: sha512-AZ8DJBuXGVHybpBQhmZtgew5//4hv3tdkXnr3vDmOUMJRuB6vn/uuwtmTOTlqEaQFg3hQSVeA90NmvIQyUV6FQ==}
+
+ '@codemirror/language-data@6.5.2':
+ resolution: {integrity: sha512-CPkWBKrNS8stYbEU5kwBwTf3JB1kghlbh4FSAwzGW2TEscdeHHH4FGysREW86Mqnj3Qn09s0/6Ea/TutmoTobg==}
+
+ '@codemirror/language@6.12.4':
+ resolution: {integrity: sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==}
+
+ '@codemirror/legacy-modes@6.5.4':
+ resolution: {integrity: sha512-/cZr6qZyl08iYNLGsJ862CXXNI51LryRFRE40ejgoIjXZz0C1rGkD3/Ek5jM/8w1ceRjqtt4qx/KLMh4zBTgew==}
+
+ '@codemirror/lint@6.9.7':
+ resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==}
+
+ '@codemirror/search@6.7.2':
+ resolution: {integrity: sha512-gUYkYhT2+n/+VGZ+8EzE5WFkYZUZYm1VOKDudIsNqh42uRVQJ0a6Yss9sdKT3MeOYfuL1N6AZA57oza0Oyr0LA==}
+
+ '@codemirror/state@6.7.5':
+ resolution: {integrity: sha512-QjLbZmY1Au3JiRrDVYFLRD0BZ3SOKS9pR3yjIkd7u27YY8TFD9/Q9fhPnLV5l1mHFSo3hHU/N31vpwEJOx4owQ==}
+
+ '@codemirror/theme-one-dark@6.1.3':
+ resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==}
+
+ '@codemirror/view@6.43.12':
+ resolution: {integrity: sha512-Nv0vxQ19NAqvB/c2pFzjIzFlzzJl7jmdtNkwOwGbn0Ks9mFAzibvumz7cQem5cRsFA2cEw2fg+uHZGbcHupLQQ==}
+
'@egoist/tailwindcss-icons@1.9.2':
resolution: {integrity: sha512-I6XsSykmhu2cASg5Hp/ICLsJ/K/1aXPaSKjgbWaNp2xYnb4We/arWMmkhhV+9CglOFCUbqx0A3mM2kWV32ZIhw==}
peerDependencies:
@@ -218,6 +314,15 @@ packages:
cpu: [x64]
os: [win32]
+ '@floating-ui/core@1.8.0':
+ resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==}
+
+ '@floating-ui/dom@1.8.0':
+ resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==}
+
+ '@floating-ui/utils@0.2.12':
+ resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==}
+
'@iconify-json/mingcute@1.2.7':
resolution: {integrity: sha512-bp4z6F53KAQp99aSL7qPCHL7HWUNGMNKIhfo/dx9pwVHiad/WusUbTOFXEZfpA44syJPzSOxa3O6Pwiq7qZz1g==}
@@ -240,6 +345,133 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+ '@lezer/common@1.5.2':
+ resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==}
+
+ '@lezer/cpp@1.1.6':
+ resolution: {integrity: sha512-vh9gWWJOXFVY8HBHK3Twzq8MgwG2iN4GSyzBP9sCGTe37P15x2R14VaBQk0VA0ezTRN1KHYBBsHhvpGZ2Xy/pA==}
+
+ '@lezer/css@1.3.6':
+ resolution: {integrity: sha512-YJE78Wcg+zX8f10hiHWQ4Az48Qr/c13eId0VtRQYLBpxHDmDeSrXIlkbl+fJGW42rWC/uoUco9mhBZeVWP/A1g==}
+
+ '@lezer/go@1.0.1':
+ resolution: {integrity: sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==}
+
+ '@lezer/highlight@1.2.3':
+ resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==}
+
+ '@lezer/html@1.3.13':
+ resolution: {integrity: sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==}
+
+ '@lezer/java@1.1.4':
+ resolution: {integrity: sha512-gQylJ2xHV2xXsVqtnAcJrcROvLZJaQiC0tR//+q0k961BxqvyYttusPkJ8d9T1fPDJKHJW2etUH8XbIn9jhVUg==}
+
+ '@lezer/javascript@1.5.5':
+ resolution: {integrity: sha512-sWg4yX1J6XW67AaAynVt0iwF0M5c+np36TEu+P2ifAJ8haRYvHnWDV28r1jdwnJehWCwXECutAUy56K4RBZIyg==}
+
+ '@lezer/json@1.0.3':
+ resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==}
+
+ '@lezer/lr@1.4.10':
+ resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==}
+
+ '@lezer/markdown@1.7.2':
+ resolution: {integrity: sha512-iTkYvoVcKt3WkeL7qUDyXHONZEwLio4wj8KTNi2dnjQEXBZKMV63BpQrPqfsM+OkvuRbiSTAcycYAsQzLhRNoQ==}
+
+ '@lezer/php@1.0.6':
+ resolution: {integrity: sha512-QJ2xNXPmsm/Z3IpThq8fnJrEIVpxhsIoj6GZBW0JYEd/l9zxpJZW216X4fzqQY4vgpdGIumypvI4uQjd4tnYtw==}
+
+ '@lezer/python@1.1.19':
+ resolution: {integrity: sha512-MhQIURHRytsNzP/YXnqpYKW6la6voAH3kyplTOOiCdjyFY6cWWGFVmYVdHIPrElqSDf4iCDktQCockB9FxuhzQ==}
+
+ '@lezer/rust@1.0.3':
+ resolution: {integrity: sha512-XxErOjZzQ7yJt1agUT4fu9qQvESZ3acgoxpPaPTPOiUx+duCjaVAtZGFIgphkHxlN05djdVAIOy/wItShMEjqQ==}
+
+ '@lezer/sass@1.1.0':
+ resolution: {integrity: sha512-3mMGdCTUZ/84ArHOuXWQr37pnf7f+Nw9ycPUeKX+wu19b7pSMcZGLbaXwvD2APMBDOGxPmpK/O6S1v1EvLoqgQ==}
+
+ '@lezer/xml@1.0.6':
+ resolution: {integrity: sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==}
+
+ '@lezer/yaml@1.0.4':
+ resolution: {integrity: sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==}
+
+ '@marijn/find-cluster-break@1.0.4':
+ resolution: {integrity: sha512-Wy0V7+SGUjnF9/TkiM1hKVDPj7jKXduPNboMVtHTA8dySMURWqfg/JZ9E2Sq8JgSJmkl7k7Qe9FLeMSrSraWmQ==}
+
+ '@milkdown/components@7.22.1':
+ resolution: {integrity: sha512-6IA8fcFcBTm/x1X1typz73yUoT1JuN4srmifGAIeWEtCnayEwRjFxpQOoQrfvMgBYx4DSHcPVLhJUlR/xbBtxg==}
+ peerDependencies:
+ '@codemirror/language': ^6
+ '@codemirror/state': ^6
+ '@codemirror/view': ^6
+
+ '@milkdown/core@7.22.1':
+ resolution: {integrity: sha512-X4O3al2aYugpFP/Do7VoPgvVEftjJVi+6UhZmz9fSdqMtpU70AitJ8R/erUA18hay2hWvNO9m3pxIJoR6no6KQ==}
+
+ '@milkdown/crepe@7.22.1':
+ resolution: {integrity: sha512-0BZTGBRV8lnlKsA3vmPK5Qyomjqd952493AIzfnbhiYwzHpluX03iMtOgHC6E2UcpMXP86P5CXhPUWjzDu+JZQ==}
+
+ '@milkdown/ctx@7.22.1':
+ resolution: {integrity: sha512-6+LJdo3DcrkO1lzGcje0yWqaG6ZKgTR8kDxyDrrm7iocBjq3M+bkw04f99ZG/BLp1Rbc4yRyin2mUy6m4SEVaA==}
+
+ '@milkdown/exception@7.22.1':
+ resolution: {integrity: sha512-wXUfn+fpWy0jeJ6AVEjKPieouLfJIDLQ4IRqyEL3d5yZDiZTtKu1Va0+RprrvqANpGJ1YBRQ1uCgDE6sJgTEjQ==}
+
+ '@milkdown/kit@7.22.1':
+ resolution: {integrity: sha512-gXLMhjqe0j8XRSUe97LBXbpZ0sDi5EM6nnt1zXewUaGDS3mleMjNntSCWrp9ln40kz8tGs0ol50kSAr1n/JBnQ==}
+
+ '@milkdown/plugin-block@7.22.1':
+ resolution: {integrity: sha512-R0gOuKRqR1DyFULD75Oic0+57DXTthagUZbbB9lsHM59aQmxKJ1nRmujptPeMfDlDfEod2ao5rsow7LO8p+ZWQ==}
+
+ '@milkdown/plugin-clipboard@7.22.1':
+ resolution: {integrity: sha512-H1meyLEj2fOnFWSopeQ2H7hl6VXM+OQZoU9H7GrlHFM5GKtBFBErFFV4Cl5tUSVFY+xYy/4rEPOA23Ba1u6OfA==}
+
+ '@milkdown/plugin-cursor@7.22.1':
+ resolution: {integrity: sha512-3tJzpQl5cyn+8vzsHkPBYsW6QacEN7yP2O5GkyUjrNLvMIQ5vVi7GUuztLeoouJKlOc1vjIq3h7ARXCgHAKe8g==}
+
+ '@milkdown/plugin-diff@7.22.1':
+ resolution: {integrity: sha512-qjuCMx11HwhMlTJuybqbFZT8PQkasSi0qICxq2HdYqH0WQuLO99I2mb17UAJz1aF+JM9rp1StHWr4bxC0ofamA==}
+
+ '@milkdown/plugin-history@7.22.1':
+ resolution: {integrity: sha512-SRD6emVhfVmA6mrcCsCiWrONN/2Kp+VT5LLojIQxNc/spzjWw15PtkA3r4nUe9cZmNN5rE7XvrPaA0WnjJIyeQ==}
+
+ '@milkdown/plugin-indent@7.22.1':
+ resolution: {integrity: sha512-s/wmqxaJpIKT01gEXsC9gCkzA3Clm+MNioZLzNCYD8xI/4Hf3AhmIEoEqf3LIQ9w10Zs2yhIxIrknNWfCOgsEw==}
+
+ '@milkdown/plugin-listener@7.22.1':
+ resolution: {integrity: sha512-6k8JMDrdAL0E0WygpDX1YOR0Q9Wc4pia8pIMN5nN5liEeAyNaM6LwAxXJY+awQJ0OstDKHTfjkh7WzJo4QiXOA==}
+
+ '@milkdown/plugin-slash@7.22.1':
+ resolution: {integrity: sha512-eFKCjfgXfAQusTSOyzhz4nASquOApdiBy9hnGkPB/SJ6oltTC8SonB3nIxF/X0wPZHfn09vuz/JBqh9cyDtXJA==}
+
+ '@milkdown/plugin-streaming@7.22.1':
+ resolution: {integrity: sha512-CR0lujyc7ae0sbjNfscvS+b+cUP7b6XJ90ioTor605UV+F8EKh5t4PMJM79IVzSUCeob51a+rtXA2LAA1b/GyA==}
+
+ '@milkdown/plugin-tooltip@7.22.1':
+ resolution: {integrity: sha512-drdWA/7WlrDr2B+ABYf4tY9xTiwg/CJMUCydfD05dR2d8wOkaF6oOHZwJbWnkWLMtAJWdieOYgfXGPhQRwXxCg==}
+
+ '@milkdown/plugin-trailing@7.22.1':
+ resolution: {integrity: sha512-Buy8IX3I7wwq0QPfJRep77QnvlkBeg6BvDHkbh5eTMrgnwSiWhN+tV9RVCYpdWXM7QhQZpSjU2SejluLuTyVCg==}
+
+ '@milkdown/plugin-upload@7.22.1':
+ resolution: {integrity: sha512-vV0bZzdh/PhM+Jk9Nk5cKHDXHY5jt+ePcaitTfvESDYfg83YZ9AglOhw/YclPWklMvswrnCGZZFTZD4daqc4hg==}
+
+ '@milkdown/preset-commonmark@7.22.1':
+ resolution: {integrity: sha512-it+G0YUG5MDXt0qLB6W083ss5i6tnWAXWCW8SgnmsehGxntkN5//gNM/8vh3rqwl8RvnBvjLWpJ6TgHSlBPhlw==}
+
+ '@milkdown/preset-gfm@7.22.1':
+ resolution: {integrity: sha512-UPMdHRdMHlVourOOTiwsp3qHu614pDFWlWtVzxe8fyl8uKZUk6IOuewH5ubhqD/opos8OEB+zgEomUGfee1oTw==}
+
+ '@milkdown/prose@7.22.1':
+ resolution: {integrity: sha512-fqgTHl94G7oDPY94cPYBm6ARxEoLQ10ubkTNLD1nzAlTUfJLULZkIEMSMy4CECncw2go3ExW32CZPpXLJVSxnQ==}
+
+ '@milkdown/transformer@7.22.1':
+ resolution: {integrity: sha512-rU1IBtxezNg7TSWaq+RP59/t6tfD3jIPGrwAtMKPIGuYEax+8d1fyGisp98TeI3l0VQEIgMpF+KlNDV+Y1HJVA==}
+
+ '@milkdown/utils@7.22.1':
+ resolution: {integrity: sha512-ye0LGy/Ez8fjtcYyYj93wp4XfH00JYpCiy1IDHYB+9HVIpnbr3ed7PvY4s2000xmDWe0Vlx07TOyv96yvFrnjQ==}
+
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -252,6 +484,9 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
+ '@ocavue/utils@1.8.0':
+ resolution: {integrity: sha512-BD1ro+6Kllkmr/at+o32kvnLrVooUbk9gw3JAPaoAZXx+A5GhZ6Laa+UA38ifhG3vaamx+jcgEwX22BmTSMUVA==}
+
'@rollup/rollup-android-arm-eabi@4.62.3':
resolution: {integrity: sha512-c0wdcekXtQvvn5Tsrk/+op/gUArrbWaFduBnTLP2l1cKLSQs4diMWjJw3m6A0DdzT8dAAX95KpkJ3qynCePbmw==}
cpu: [arm]
@@ -390,12 +625,36 @@ packages:
cpu: [x64]
os: [win32]
+ '@types/debug@4.1.13':
+ resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
+
'@types/estree@1.0.9':
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+ '@types/hast@3.0.5':
+ resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==}
+
+ '@types/katex@0.16.8':
+ resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==}
+
+ '@types/lodash-es@4.17.12':
+ resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
+
+ '@types/lodash@4.17.25':
+ resolution: {integrity: sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==}
+
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+ '@types/ms@2.1.0':
+ resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
+ '@types/unist@3.0.3':
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
'@vitejs/plugin-vue@5.2.4':
resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -473,6 +732,9 @@ packages:
peerDependencies:
postcss: ^8.1.0
+ bail@2.0.2:
+ resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
+
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@@ -504,14 +766,38 @@ packages:
caniuse-lite@1.0.30001806:
resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==}
+ ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+ character-entities@2.0.2:
+ resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ codemirror@6.0.2:
+ resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==}
+
+ commander@15.0.0:
+ resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==}
+ engines: {node: '>=22.12.0'}
+
commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
+ commander@8.3.0:
+ resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
+ engines: {node: '>= 12'}
+
+ crelt@1.0.7:
+ resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==}
+
cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
@@ -523,6 +809,25 @@ packages:
de-indent@1.0.2:
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decode-named-character-reference@1.3.0:
+ resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
@@ -552,9 +857,16 @@ packages:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
+ escape-string-regexp@5.0.0:
+ resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+ engines: {node: '>=12'}
+
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+ extend@3.0.2:
+ resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+
fast-glob@3.3.3:
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
engines: {node: '>=8.6.0'}
@@ -625,10 +937,22 @@ packages:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
+ is-plain-obj@4.1.0:
+ resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+ engines: {node: '>=12'}
+
jiti@1.21.7:
resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
hasBin: true
+ katex@0.16.47:
+ resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==}
+ hasBin: true
+
+ katex@0.18.7:
+ resolution: {integrity: sha512-h+UCwkZ+4Jz8WQ7MLGfj7UVFrRCizGb912fwF4luGdYsC5paYG1vx+jy+KRcC/XkpjGva/P7nAWuxNnPzRvzHw==}
+ hasBin: true
+
lilconfig@3.1.3:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'}
@@ -636,18 +960,153 @@ packages:
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+ lodash-es@4.18.1:
+ resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
+
+ longest-streak@3.1.0:
+ resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+ markdown-table@3.0.4:
+ resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+
marked@12.0.2:
resolution: {integrity: sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==}
engines: {node: '>= 18'}
hasBin: true
+ mdast-util-definitions@6.0.0:
+ resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==}
+
+ mdast-util-find-and-replace@3.0.2:
+ resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
+
+ mdast-util-from-markdown@2.0.3:
+ resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==}
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
+
+ mdast-util-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
+
+ mdast-util-gfm-table@2.0.0:
+ resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+
+ mdast-util-gfm@3.1.0:
+ resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
+
+ mdast-util-math@3.0.0:
+ resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==}
+
+ mdast-util-phrasing@4.1.0:
+ resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
+
+ mdast-util-to-markdown@2.1.2:
+ resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==}
+
+ mdast-util-to-string@4.0.0:
+ resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
+ micromark-core-commonmark@2.0.3:
+ resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
+
+ micromark-extension-gfm-footnote@2.1.0:
+ resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
+
+ micromark-extension-gfm-table@2.1.2:
+ resolution: {integrity: sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==}
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
+
+ micromark-extension-gfm@3.0.0:
+ resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+
+ micromark-extension-math@3.1.0:
+ resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==}
+
+ micromark-factory-destination@2.0.1:
+ resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
+
+ micromark-factory-label@2.0.1:
+ resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
+
+ micromark-factory-space@2.0.1:
+ resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==}
+
+ micromark-factory-title@2.0.1:
+ resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==}
+
+ micromark-factory-whitespace@2.0.1:
+ resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==}
+
+ micromark-util-character@2.1.1:
+ resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
+
+ micromark-util-chunked@2.0.1:
+ resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==}
+
+ micromark-util-classify-character@2.0.1:
+ resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==}
+
+ micromark-util-combine-extensions@2.0.1:
+ resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==}
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==}
+
+ micromark-util-decode-string@2.0.1:
+ resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==}
+
+ micromark-util-encode@2.0.1:
+ resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
+
+ micromark-util-html-tag-name@2.0.1:
+ resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
+
+ micromark-util-normalize-identifier@2.0.1:
+ resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==}
+
+ micromark-util-resolve-all@2.0.1:
+ resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==}
+
+ micromark-util-sanitize-uri@2.0.1:
+ resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
+
+ micromark-util-subtokenize@2.1.0:
+ resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==}
+
+ micromark-util-symbol@2.0.1:
+ resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
+
+ micromark-util-types@2.0.2:
+ resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
+
+ micromark@4.0.2:
+ resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==}
+
micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
@@ -656,6 +1115,9 @@ packages:
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
engines: {node: '>=16 || 14 >=14.17'}
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
@@ -667,6 +1129,11 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ nanoid@6.0.1:
+ resolution: {integrity: sha512-3wVS3i51pE2pi1k5FFL/95BGfVS0kSsvDVuGXHOtxox/TywUmtgq+3qiTOTbs9J7KfHaXPiN171k/A6dBnaXFw==}
+ engines: {node: ^22 || ^24 || >=26}
+ hasBin: true
+
node-releases@2.0.51:
resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==}
engines: {node: '>=18'}
@@ -683,6 +1150,9 @@ packages:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
+ orderedmap@2.1.1:
+ resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
+
package-manager-detector@1.8.0:
resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==}
@@ -758,6 +1228,65 @@ packages:
resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==}
engines: {node: ^10 || ^12 || >=14}
+ prosemirror-changeset@2.4.3:
+ resolution: {integrity: sha512-J8jqUZ1xybXKY44v3K0c8m8ZTIqJPVT34t2UVyxv4VEWoNd5C9t5Y52GNuMriRJ3IpNxOSE2R60vN4qBrTMcyQ==}
+
+ prosemirror-commands@1.7.2:
+ resolution: {integrity: sha512-q6Q6szxqdu9Xd6EcdKsqXghu5nQdZTpB4Q9yd04WRc7/jt763e/rT60Owh0L1GYY+T46o5rD+9lEN36dZS43tw==}
+
+ prosemirror-drop-indicator@0.1.4:
+ resolution: {integrity: sha512-YaRB1pZmU5GCorPVWbc9dbhbwqr4iMBO/AjPu4BTKHCUzxEDUXje2dUyoxOHib/z4uyPUZTJz64h7mHDJZeSzA==}
+
+ prosemirror-dropcursor@1.8.3:
+ resolution: {integrity: sha512-FoYbsJR8gK+DGlqhNoE29Loa38eIZPzQRIb1VMaDNBoo4OLP6vVof/jR8qFY/6XvUd6Dhug8MDCHl2a/h8RTfQ==}
+
+ prosemirror-gapcursor@1.4.1:
+ resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==}
+
+ prosemirror-history@1.5.0:
+ resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==}
+
+ prosemirror-inputrules@1.5.1:
+ resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==}
+
+ prosemirror-keymap@1.2.3:
+ resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==}
+
+ prosemirror-model@1.25.11:
+ resolution: {integrity: sha512-QWg9RhnpLlogAmp3p96uEFrE5txQpFynd4vhBAELkwgOCWQs/X0yCzB3/hrHqiPwf91RG5KyWq6553zs9JqIOQ==}
+
+ prosemirror-safari-ime-span@1.0.2:
+ resolution: {integrity: sha512-QJqD8s1zE/CuK56kDsUhndh5hiHh/gFnAuPOA9ytva2s85/ZEt2tNWeALTJN48DtWghSKOmiBsvVn2OlnJ5H2w==}
+
+ prosemirror-schema-list@1.5.1:
+ resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==}
+
+ prosemirror-state@1.4.4:
+ resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==}
+
+ prosemirror-tables@1.8.5:
+ resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==}
+
+ prosemirror-transform@1.12.1:
+ resolution: {integrity: sha512-t4F5615FycnCqsX7ShTUs8+jfnwcf46kuFRvSl/3qFx2QTZ4DjgowesLHQXcFP7G//TjesqZG3WtgL7vdyVJyA==}
+
+ prosemirror-view@1.42.4:
+ resolution: {integrity: sha512-H/LErnE8Vms1GYkvhfj6G3K9rc2p+o5EHGmTwvPOl0f21wPPxlMVRB8ICOseH+COb2oPKFEoufbgY6yet/dR4w==}
+
+ prosemirror-virtual-cursor@0.4.2:
+ resolution: {integrity: sha512-pUMKnIuOhhnMcgIJUjhIQTVJruBEGxfMBVQSrK0g2qhGPDm1i12KdsVaFw15dYk+29tZcxjMeR7P5VDKwmbwJg==}
+ peerDependencies:
+ prosemirror-model: ^1.0.0
+ prosemirror-state: ^1.0.0
+ prosemirror-view: ^1.0.0
+ peerDependenciesMeta:
+ prosemirror-model:
+ optional: true
+ prosemirror-state:
+ optional: true
+ prosemirror-view:
+ optional: true
+
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@@ -768,6 +1297,24 @@ packages:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
+ remark-gfm@4.0.1:
+ resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
+
+ remark-inline-links@7.0.0:
+ resolution: {integrity: sha512-4uj1pPM+F495ySZhTIB6ay2oSkTsKgmYaKk/q5HIdhX2fuyLEegpjWa0VdJRJ01sgOqAFo7MBKdDUejIYBMVMQ==}
+
+ remark-math@6.0.0:
+ resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==}
+
+ remark-parse@11.0.0:
+ resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
+
+ remark-stringify@11.0.0:
+ resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
+
+ remark@15.0.1:
+ resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==}
+
resolve@1.22.12:
resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==}
engines: {node: '>= 0.4'}
@@ -782,6 +1329,9 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
+ rope-sequence@1.3.4:
+ resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
+
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -789,6 +1339,9 @@ packages:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
+ style-mod@4.1.4:
+ resolution: {integrity: sha512-XXWIQt633/EpAFx8aZDOTjBzrCaGmhvEQlQo6MVPfa2OzO2cWo+4hV9h+6UkHYlXGfy+ODXKUdP7Pthmcu5ATw==}
+
sucrase@3.35.1:
resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -822,6 +1375,9 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
+ trough@2.2.0:
+ resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
+
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
@@ -830,6 +1386,24 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
+ unified@11.0.5:
+ resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
+
+ unist-util-is@6.0.1:
+ resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
+
+ unist-util-remove-position@5.0.0:
+ resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==}
+
+ unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+ unist-util-visit-parents@6.0.2:
+ resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==}
+
+ unist-util-visit@5.1.0:
+ resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==}
+
update-browserslist-db@1.2.3:
resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
hasBin: true
@@ -839,6 +1413,12 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+ vfile-message@4.0.3:
+ resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
+
+ vfile@6.0.3:
+ resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+
vite@5.4.21:
resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -892,6 +1472,12 @@ packages:
typescript:
optional: true
+ w3c-keyname@2.2.8:
+ resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
+
+ zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
snapshots:
'@alloc/quick-lru@5.2.0': {}
@@ -914,6 +1500,264 @@ snapshots:
'@babel/helper-string-parser': 7.29.7
'@babel/helper-validator-identifier': 7.29.7
+ '@codemirror/autocomplete@6.20.3':
+ dependencies:
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ '@lezer/common': 1.5.2
+
+ '@codemirror/commands@6.11.1':
+ dependencies:
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ '@lezer/common': 1.5.2
+
+ '@codemirror/lang-angular@0.1.4':
+ dependencies:
+ '@codemirror/lang-html': 6.4.12
+ '@codemirror/lang-javascript': 6.2.5
+ '@codemirror/language': 6.12.4
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@codemirror/lang-cpp@6.0.3':
+ dependencies:
+ '@codemirror/language': 6.12.4
+ '@lezer/cpp': 1.1.6
+
+ '@codemirror/lang-css@6.3.1':
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@lezer/common': 1.5.2
+ '@lezer/css': 1.3.6
+
+ '@codemirror/lang-go@6.0.1':
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@lezer/common': 1.5.2
+ '@lezer/go': 1.0.1
+
+ '@codemirror/lang-html@6.4.12':
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/lang-css': 6.3.1
+ '@codemirror/lang-javascript': 6.2.5
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ '@lezer/common': 1.5.2
+ '@lezer/css': 1.3.6
+ '@lezer/html': 1.3.13
+
+ '@codemirror/lang-java@6.0.2':
+ dependencies:
+ '@codemirror/language': 6.12.4
+ '@lezer/java': 1.1.4
+
+ '@codemirror/lang-javascript@6.2.5':
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/language': 6.12.4
+ '@codemirror/lint': 6.9.7
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ '@lezer/common': 1.5.2
+ '@lezer/javascript': 1.5.5
+
+ '@codemirror/lang-jinja@6.0.1':
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/lang-html': 6.4.12
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@codemirror/lang-json@6.0.2':
+ dependencies:
+ '@codemirror/language': 6.12.4
+ '@lezer/json': 1.0.3
+
+ '@codemirror/lang-less@6.0.2':
+ dependencies:
+ '@codemirror/lang-css': 6.3.1
+ '@codemirror/language': 6.12.4
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@codemirror/lang-liquid@6.3.2':
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/lang-html': 6.4.12
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@codemirror/lang-markdown@6.5.2':
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/lang-html': 6.4.12
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ '@lezer/common': 1.5.2
+ '@lezer/markdown': 1.7.2
+
+ '@codemirror/lang-php@6.0.2':
+ dependencies:
+ '@codemirror/lang-html': 6.4.12
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@lezer/common': 1.5.2
+ '@lezer/php': 1.0.6
+
+ '@codemirror/lang-python@6.2.1':
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@lezer/common': 1.5.2
+ '@lezer/python': 1.1.19
+
+ '@codemirror/lang-rust@6.0.2':
+ dependencies:
+ '@codemirror/language': 6.12.4
+ '@lezer/rust': 1.0.3
+
+ '@codemirror/lang-sass@6.0.2':
+ dependencies:
+ '@codemirror/lang-css': 6.3.1
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@lezer/common': 1.5.2
+ '@lezer/sass': 1.1.0
+
+ '@codemirror/lang-sql@6.10.0':
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@codemirror/lang-vue@0.1.3':
+ dependencies:
+ '@codemirror/lang-html': 6.4.12
+ '@codemirror/lang-javascript': 6.2.5
+ '@codemirror/language': 6.12.4
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@codemirror/lang-wast@6.0.2':
+ dependencies:
+ '@codemirror/language': 6.12.4
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@codemirror/lang-xml@6.1.0':
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ '@lezer/common': 1.5.2
+ '@lezer/xml': 1.0.6
+
+ '@codemirror/lang-yaml@6.1.3':
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+ '@lezer/yaml': 1.0.4
+
+ '@codemirror/language-data@6.5.2':
+ dependencies:
+ '@codemirror/lang-angular': 0.1.4
+ '@codemirror/lang-cpp': 6.0.3
+ '@codemirror/lang-css': 6.3.1
+ '@codemirror/lang-go': 6.0.1
+ '@codemirror/lang-html': 6.4.12
+ '@codemirror/lang-java': 6.0.2
+ '@codemirror/lang-javascript': 6.2.5
+ '@codemirror/lang-jinja': 6.0.1
+ '@codemirror/lang-json': 6.0.2
+ '@codemirror/lang-less': 6.0.2
+ '@codemirror/lang-liquid': 6.3.2
+ '@codemirror/lang-markdown': 6.5.2
+ '@codemirror/lang-php': 6.0.2
+ '@codemirror/lang-python': 6.2.1
+ '@codemirror/lang-rust': 6.0.2
+ '@codemirror/lang-sass': 6.0.2
+ '@codemirror/lang-sql': 6.10.0
+ '@codemirror/lang-vue': 0.1.3
+ '@codemirror/lang-wast': 6.0.2
+ '@codemirror/lang-xml': 6.1.0
+ '@codemirror/lang-yaml': 6.1.3
+ '@codemirror/language': 6.12.4
+ '@codemirror/legacy-modes': 6.5.4
+
+ '@codemirror/language@6.12.4':
+ dependencies:
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+ style-mod: 4.1.4
+
+ '@codemirror/legacy-modes@6.5.4':
+ dependencies:
+ '@codemirror/language': 6.12.4
+
+ '@codemirror/lint@6.9.7':
+ dependencies:
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ crelt: 1.0.7
+
+ '@codemirror/search@6.7.2':
+ dependencies:
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ crelt: 1.0.7
+
+ '@codemirror/state@6.7.5':
+ dependencies:
+ '@marijn/find-cluster-break': 1.0.4
+
+ '@codemirror/theme-one-dark@6.1.3':
+ dependencies:
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ '@lezer/highlight': 1.2.3
+
+ '@codemirror/view@6.43.12':
+ dependencies:
+ '@codemirror/state': 6.7.5
+ crelt: 1.0.7
+ style-mod: 4.1.4
+ w3c-keyname: 2.2.8
+
'@egoist/tailwindcss-icons@1.9.2(tailwindcss@3.4.19)':
dependencies:
'@iconify/utils': 3.1.4
@@ -988,6 +1832,17 @@ snapshots:
'@esbuild/win32-x64@0.21.5':
optional: true
+ '@floating-ui/core@1.8.0':
+ dependencies:
+ '@floating-ui/utils': 0.2.12
+
+ '@floating-ui/dom@1.8.0':
+ dependencies:
+ '@floating-ui/core': 1.8.0
+ '@floating-ui/utils': 0.2.12
+
+ '@floating-ui/utils@0.2.12': {}
+
'@iconify-json/mingcute@1.2.7':
dependencies:
'@iconify/types': 2.0.0
@@ -1014,6 +1869,384 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
+ '@lezer/common@1.5.2': {}
+
+ '@lezer/cpp@1.1.6':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/css@1.3.6':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/go@1.0.1':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/highlight@1.2.3':
+ dependencies:
+ '@lezer/common': 1.5.2
+
+ '@lezer/html@1.3.13':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/java@1.1.4':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/javascript@1.5.5':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/json@1.0.3':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/lr@1.4.10':
+ dependencies:
+ '@lezer/common': 1.5.2
+
+ '@lezer/markdown@1.7.2':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+
+ '@lezer/php@1.0.6':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/python@1.1.19':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/rust@1.0.3':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/sass@1.1.0':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/xml@1.0.6':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@lezer/yaml@1.0.4':
+ dependencies:
+ '@lezer/common': 1.5.2
+ '@lezer/highlight': 1.2.3
+ '@lezer/lr': 1.4.10
+
+ '@marijn/find-cluster-break@1.0.4': {}
+
+ '@milkdown/components@7.22.1(@codemirror/language@6.12.4)(@codemirror/state@6.7.5)(@codemirror/view@6.43.12)(typescript@5.9.3)':
+ dependencies:
+ '@codemirror/language': 6.12.4
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+ '@floating-ui/dom': 1.8.0
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/exception': 7.22.1
+ '@milkdown/plugin-diff': 7.22.1
+ '@milkdown/plugin-tooltip': 7.22.1
+ '@milkdown/preset-commonmark': 7.22.1
+ '@milkdown/preset-gfm': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/transformer': 7.22.1
+ '@milkdown/utils': 7.22.1
+ '@types/lodash-es': 4.17.12
+ clsx: 2.1.1
+ dompurify: 3.4.12
+ lodash-es: 4.18.1
+ nanoid: 6.0.1
+ unist-util-visit: 5.1.0
+ vue: 3.5.40(typescript@5.9.3)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@milkdown/core@7.22.1':
+ dependencies:
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/exception': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/transformer': 7.22.1
+ remark-parse: 11.0.0
+ remark-stringify: 11.0.0
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/crepe@7.22.1(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.4)(typescript@5.9.3)':
+ dependencies:
+ '@codemirror/commands': 6.11.1
+ '@codemirror/language': 6.12.4
+ '@codemirror/language-data': 6.5.2
+ '@codemirror/state': 6.7.5
+ '@codemirror/theme-one-dark': 6.1.3
+ '@codemirror/view': 6.43.12
+ '@milkdown/kit': 7.22.1(@codemirror/language@6.12.4)(@codemirror/state@6.7.5)(@codemirror/view@6.43.12)(typescript@5.9.3)
+ '@types/lodash-es': 4.17.12
+ clsx: 2.1.1
+ codemirror: 6.0.2
+ dompurify: 3.4.12
+ katex: 0.18.7
+ lodash-es: 4.18.1
+ prosemirror-virtual-cursor: 0.4.2(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.4)
+ remark-math: 6.0.0
+ unist-util-visit: 5.1.0
+ vue: 3.5.40(typescript@5.9.3)
+ transitivePeerDependencies:
+ - prosemirror-model
+ - prosemirror-state
+ - prosemirror-view
+ - supports-color
+ - typescript
+
+ '@milkdown/ctx@7.22.1':
+ dependencies:
+ '@milkdown/exception': 7.22.1
+
+ '@milkdown/exception@7.22.1': {}
+
+ '@milkdown/kit@7.22.1(@codemirror/language@6.12.4)(@codemirror/state@6.7.5)(@codemirror/view@6.43.12)(typescript@5.9.3)':
+ dependencies:
+ '@milkdown/components': 7.22.1(@codemirror/language@6.12.4)(@codemirror/state@6.7.5)(@codemirror/view@6.43.12)(typescript@5.9.3)
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/exception': 7.22.1
+ '@milkdown/plugin-block': 7.22.1
+ '@milkdown/plugin-clipboard': 7.22.1
+ '@milkdown/plugin-cursor': 7.22.1
+ '@milkdown/plugin-diff': 7.22.1
+ '@milkdown/plugin-history': 7.22.1
+ '@milkdown/plugin-indent': 7.22.1
+ '@milkdown/plugin-listener': 7.22.1
+ '@milkdown/plugin-slash': 7.22.1
+ '@milkdown/plugin-streaming': 7.22.1
+ '@milkdown/plugin-tooltip': 7.22.1
+ '@milkdown/plugin-trailing': 7.22.1
+ '@milkdown/plugin-upload': 7.22.1
+ '@milkdown/preset-commonmark': 7.22.1
+ '@milkdown/preset-gfm': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/transformer': 7.22.1
+ '@milkdown/utils': 7.22.1
+ transitivePeerDependencies:
+ - '@codemirror/language'
+ - '@codemirror/state'
+ - '@codemirror/view'
+ - supports-color
+ - typescript
+
+ '@milkdown/plugin-block@7.22.1':
+ dependencies:
+ '@floating-ui/dom': 1.8.0
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/utils': 7.22.1
+ '@types/lodash-es': 4.17.12
+ lodash-es: 4.18.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/plugin-clipboard@7.22.1':
+ dependencies:
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/utils': 7.22.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/plugin-cursor@7.22.1':
+ dependencies:
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/utils': 7.22.1
+ prosemirror-drop-indicator: 0.1.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/plugin-diff@7.22.1':
+ dependencies:
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/transformer': 7.22.1
+ '@milkdown/utils': 7.22.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/plugin-history@7.22.1':
+ dependencies:
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/utils': 7.22.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/plugin-indent@7.22.1':
+ dependencies:
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/utils': 7.22.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/plugin-listener@7.22.1':
+ dependencies:
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@types/lodash-es': 4.17.12
+ lodash-es: 4.18.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/plugin-slash@7.22.1':
+ dependencies:
+ '@floating-ui/dom': 1.8.0
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/utils': 7.22.1
+ '@types/lodash-es': 4.17.12
+ lodash-es: 4.18.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/plugin-streaming@7.22.1':
+ dependencies:
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/plugin-diff': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/utils': 7.22.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/plugin-tooltip@7.22.1':
+ dependencies:
+ '@floating-ui/dom': 1.8.0
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/utils': 7.22.1
+ '@types/lodash-es': 4.17.12
+ lodash-es: 4.18.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/plugin-trailing@7.22.1':
+ dependencies:
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/utils': 7.22.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/plugin-upload@7.22.1':
+ dependencies:
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/exception': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/utils': 7.22.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/preset-commonmark@7.22.1':
+ dependencies:
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/exception': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/transformer': 7.22.1
+ '@milkdown/utils': 7.22.1
+ remark-inline-links: 7.0.0
+ unist-util-visit: 5.1.0
+ unist-util-visit-parents: 6.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/preset-gfm@7.22.1':
+ dependencies:
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/exception': 7.22.1
+ '@milkdown/preset-commonmark': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/transformer': 7.22.1
+ '@milkdown/utils': 7.22.1
+ prosemirror-safari-ime-span: 1.0.2
+ remark-gfm: 4.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/prose@7.22.1':
+ dependencies:
+ '@milkdown/exception': 7.22.1
+ prosemirror-changeset: 2.4.3
+ prosemirror-commands: 1.7.2
+ prosemirror-dropcursor: 1.8.3
+ prosemirror-gapcursor: 1.4.1
+ prosemirror-history: 1.5.0
+ prosemirror-inputrules: 1.5.1
+ prosemirror-keymap: 1.2.3
+ prosemirror-model: 1.25.11
+ prosemirror-schema-list: 1.5.1
+ prosemirror-state: 1.4.4
+ prosemirror-tables: 1.8.5
+ prosemirror-transform: 1.12.1
+ prosemirror-view: 1.42.4
+
+ '@milkdown/transformer@7.22.1':
+ dependencies:
+ '@milkdown/exception': 7.22.1
+ '@milkdown/prose': 7.22.1
+ remark: 15.0.1
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@milkdown/utils@7.22.1':
+ dependencies:
+ '@milkdown/core': 7.22.1
+ '@milkdown/ctx': 7.22.1
+ '@milkdown/exception': 7.22.1
+ '@milkdown/prose': 7.22.1
+ '@milkdown/transformer': 7.22.1
+ nanoid: 6.0.1
+ transitivePeerDependencies:
+ - supports-color
+
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -1026,6 +2259,8 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.20.1
+ '@ocavue/utils@1.8.0': {}
+
'@rollup/rollup-android-arm-eabi@4.62.3':
optional: true
@@ -1101,11 +2336,35 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.62.3':
optional: true
+ '@types/debug@4.1.13':
+ dependencies:
+ '@types/ms': 2.1.0
+
'@types/estree@1.0.9': {}
+ '@types/hast@3.0.5':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/katex@0.16.8': {}
+
+ '@types/lodash-es@4.17.12':
+ dependencies:
+ '@types/lodash': 4.17.25
+
+ '@types/lodash@4.17.25': {}
+
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/ms@2.1.0': {}
+
'@types/trusted-types@2.0.7':
optional: true
+ '@types/unist@3.0.3': {}
+
'@vitejs/plugin-vue@5.2.4(vite@5.4.21)(vue@3.5.40(typescript@5.9.3))':
dependencies:
vite: 5.4.21
@@ -1217,6 +2476,8 @@ snapshots:
postcss: 8.5.25
postcss-value-parser: 4.2.0
+ bail@2.0.2: {}
+
balanced-match@1.0.2: {}
baseline-browser-mapping@2.11.8: {}
@@ -1243,6 +2504,10 @@ snapshots:
caniuse-lite@1.0.30001806: {}
+ ccount@2.0.1: {}
+
+ character-entities@2.0.2: {}
+
chokidar@3.6.0:
dependencies:
anymatch: 3.1.3
@@ -1255,14 +2520,46 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
+ clsx@2.1.1: {}
+
+ codemirror@6.0.2:
+ dependencies:
+ '@codemirror/autocomplete': 6.20.3
+ '@codemirror/commands': 6.11.1
+ '@codemirror/language': 6.12.4
+ '@codemirror/lint': 6.9.7
+ '@codemirror/search': 6.7.2
+ '@codemirror/state': 6.7.5
+ '@codemirror/view': 6.43.12
+
+ commander@15.0.0: {}
+
commander@4.1.1: {}
+ commander@8.3.0: {}
+
+ crelt@1.0.7: {}
+
cssesc@3.0.0: {}
csstype@3.2.3: {}
de-indent@1.0.2: {}
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ decode-named-character-reference@1.3.0:
+ dependencies:
+ character-entities: 2.0.2
+
+ dequal@2.0.3: {}
+
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
didyoumean@1.2.2: {}
dlv@1.1.3: {}
@@ -1305,8 +2602,12 @@ snapshots:
escalade@3.2.0: {}
+ escape-string-regexp@5.0.0: {}
+
estree-walker@2.0.2: {}
+ extend@3.0.2: {}
+
fast-glob@3.3.3:
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -1366,20 +2667,357 @@ snapshots:
is-number@7.0.0: {}
+ is-plain-obj@4.1.0: {}
+
jiti@1.21.7: {}
+ katex@0.16.47:
+ dependencies:
+ commander: 8.3.0
+
+ katex@0.18.7:
+ dependencies:
+ commander: 15.0.0
+
lilconfig@3.1.3: {}
lines-and-columns@1.2.4: {}
+ lodash-es@4.18.1: {}
+
+ longest-streak@3.1.0: {}
+
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
+ markdown-table@3.0.4: {}
+
marked@12.0.2: {}
+ mdast-util-definitions@6.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ unist-util-visit: 5.1.0
+
+ mdast-util-find-and-replace@3.0.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ escape-string-regexp: 5.0.0
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
+ mdast-util-from-markdown@2.0.3:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ mdast-util-to-string: 4.0.0
+ micromark: 4.0.2
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-decode-string: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+ unist-util-stringify-position: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-autolink-literal@2.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ ccount: 2.0.1
+ devlop: 1.1.0
+ mdast-util-find-and-replace: 3.0.2
+ micromark-util-character: 2.1.1
+
+ mdast-util-gfm-footnote@2.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ micromark-util-normalize-identifier: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-strikethrough@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-table@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ markdown-table: 3.0.4
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm-task-list-item@2.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-gfm@3.1.0:
+ dependencies:
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-gfm-autolink-literal: 2.0.1
+ mdast-util-gfm-footnote: 2.1.0
+ mdast-util-gfm-strikethrough: 2.0.0
+ mdast-util-gfm-table: 2.0.0
+ mdast-util-gfm-task-list-item: 2.0.0
+ mdast-util-to-markdown: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-math@3.0.0:
+ dependencies:
+ '@types/hast': 3.0.5
+ '@types/mdast': 4.0.4
+ devlop: 1.1.0
+ longest-streak: 3.1.0
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-to-markdown: 2.1.2
+ unist-util-remove-position: 5.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ mdast-util-phrasing@4.1.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ unist-util-is: 6.0.1
+
+ mdast-util-to-markdown@2.1.2:
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 3.0.3
+ longest-streak: 3.1.0
+ mdast-util-phrasing: 4.1.0
+ mdast-util-to-string: 4.0.0
+ micromark-util-classify-character: 2.0.1
+ micromark-util-decode-string: 2.0.1
+ unist-util-visit: 5.1.0
+ zwitch: 2.0.4
+
+ mdast-util-to-string@4.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+
merge2@1.4.1: {}
+ micromark-core-commonmark@2.0.3:
+ dependencies:
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ micromark-factory-destination: 2.0.1
+ micromark-factory-label: 2.0.1
+ micromark-factory-space: 2.0.1
+ micromark-factory-title: 2.0.1
+ micromark-factory-whitespace: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-html-tag-name: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-autolink-literal@2.1.0:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-footnote@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-strikethrough@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-table@2.1.2:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-tagfilter@2.0.0:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm-task-list-item@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-gfm@3.0.0:
+ dependencies:
+ micromark-extension-gfm-autolink-literal: 2.1.0
+ micromark-extension-gfm-footnote: 2.1.0
+ micromark-extension-gfm-strikethrough: 2.1.0
+ micromark-extension-gfm-table: 2.1.2
+ micromark-extension-gfm-tagfilter: 2.0.0
+ micromark-extension-gfm-task-list-item: 2.1.0
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-extension-math@3.1.0:
+ dependencies:
+ '@types/katex': 0.16.8
+ devlop: 1.1.0
+ katex: 0.16.47
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-destination@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-label@2.0.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-space@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-title@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-whitespace@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-character@2.1.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-chunked@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-classify-character@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-combine-extensions@2.0.1:
+ dependencies:
+ micromark-util-chunked: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-decode-string@2.0.1:
+ dependencies:
+ decode-named-character-reference: 1.3.0
+ micromark-util-character: 2.1.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-encode@2.0.1: {}
+
+ micromark-util-html-tag-name@2.0.1: {}
+
+ micromark-util-normalize-identifier@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-resolve-all@2.0.1:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-util-sanitize-uri@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-encode: 2.0.1
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-subtokenize@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-symbol@2.0.1: {}
+
+ micromark-util-types@2.0.2: {}
+
+ micromark@4.0.2:
+ dependencies:
+ '@types/debug': 4.1.13
+ debug: 4.4.3
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-encode: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
micromatch@4.0.8:
dependencies:
braces: 3.0.3
@@ -1389,6 +3027,8 @@ snapshots:
dependencies:
brace-expansion: 2.1.4
+ ms@2.1.3: {}
+
muggle-string@0.4.1: {}
mz@2.7.0:
@@ -1399,6 +3039,8 @@ snapshots:
nanoid@3.3.16: {}
+ nanoid@6.0.1: {}
+
node-releases@2.0.51: {}
normalize-path@3.0.0: {}
@@ -1407,6 +3049,8 @@ snapshots:
object-hash@3.0.0: {}
+ orderedmap@2.1.1: {}
+
package-manager-detector@1.8.0: {}
path-browserify@1.0.1: {}
@@ -1460,6 +3104,98 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ prosemirror-changeset@2.4.3:
+ dependencies:
+ prosemirror-transform: 1.12.1
+
+ prosemirror-commands@1.7.2:
+ dependencies:
+ prosemirror-model: 1.25.11
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.1
+
+ prosemirror-drop-indicator@0.1.4:
+ dependencies:
+ '@ocavue/utils': 1.8.0
+ prosemirror-model: 1.25.11
+ prosemirror-state: 1.4.4
+ prosemirror-view: 1.42.4
+
+ prosemirror-dropcursor@1.8.3:
+ dependencies:
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.1
+ prosemirror-view: 1.42.4
+
+ prosemirror-gapcursor@1.4.1:
+ dependencies:
+ prosemirror-keymap: 1.2.3
+ prosemirror-model: 1.25.11
+ prosemirror-state: 1.4.4
+ prosemirror-view: 1.42.4
+
+ prosemirror-history@1.5.0:
+ dependencies:
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.1
+ prosemirror-view: 1.42.4
+ rope-sequence: 1.3.4
+
+ prosemirror-inputrules@1.5.1:
+ dependencies:
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.1
+
+ prosemirror-keymap@1.2.3:
+ dependencies:
+ prosemirror-state: 1.4.4
+ w3c-keyname: 2.2.8
+
+ prosemirror-model@1.25.11:
+ dependencies:
+ orderedmap: 2.1.1
+
+ prosemirror-safari-ime-span@1.0.2:
+ dependencies:
+ prosemirror-state: 1.4.4
+ prosemirror-view: 1.42.4
+
+ prosemirror-schema-list@1.5.1:
+ dependencies:
+ prosemirror-model: 1.25.11
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.1
+
+ prosemirror-state@1.4.4:
+ dependencies:
+ prosemirror-model: 1.25.11
+ prosemirror-transform: 1.12.1
+ prosemirror-view: 1.42.4
+
+ prosemirror-tables@1.8.5:
+ dependencies:
+ prosemirror-keymap: 1.2.3
+ prosemirror-model: 1.25.11
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.1
+ prosemirror-view: 1.42.4
+
+ prosemirror-transform@1.12.1:
+ dependencies:
+ prosemirror-model: 1.25.11
+
+ prosemirror-view@1.42.4:
+ dependencies:
+ prosemirror-model: 1.25.11
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.1
+
+ prosemirror-virtual-cursor@0.4.2(prosemirror-model@1.25.11)(prosemirror-state@1.4.4)(prosemirror-view@1.42.4):
+ optionalDependencies:
+ prosemirror-model: 1.25.11
+ prosemirror-state: 1.4.4
+ prosemirror-view: 1.42.4
+
queue-microtask@1.2.3: {}
read-cache@1.0.0:
@@ -1470,6 +3206,56 @@ snapshots:
dependencies:
picomatch: 2.3.2
+ remark-gfm@4.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-gfm: 3.1.0
+ micromark-extension-gfm: 3.0.0
+ remark-parse: 11.0.0
+ remark-stringify: 11.0.0
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ remark-inline-links@7.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-definitions: 6.0.0
+ unist-util-visit: 5.1.0
+
+ remark-math@6.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-math: 3.0.0
+ micromark-extension-math: 3.1.0
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ remark-parse@11.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-from-markdown: 2.0.3
+ micromark-util-types: 2.0.2
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
+ remark-stringify@11.0.0:
+ dependencies:
+ '@types/mdast': 4.0.4
+ mdast-util-to-markdown: 2.1.2
+ unified: 11.0.5
+
+ remark@15.0.1:
+ dependencies:
+ '@types/mdast': 4.0.4
+ remark-parse: 11.0.0
+ remark-stringify: 11.0.0
+ unified: 11.0.5
+ transitivePeerDependencies:
+ - supports-color
+
resolve@1.22.12:
dependencies:
es-errors: 1.3.0
@@ -1510,12 +3296,16 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.62.3
fsevents: 2.3.3
+ rope-sequence@1.3.4: {}
+
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
source-map-js@1.2.1: {}
+ style-mod@4.1.4: {}
+
sucrase@3.35.1:
dependencies:
'@jridgewell/gen-mapping': 0.3.13
@@ -1575,10 +3365,46 @@ snapshots:
dependencies:
is-number: 7.0.0
+ trough@2.2.0: {}
+
ts-interface-checker@0.1.13: {}
typescript@5.9.3: {}
+ unified@11.0.5:
+ dependencies:
+ '@types/unist': 3.0.3
+ bail: 2.0.2
+ devlop: 1.1.0
+ extend: 3.0.2
+ is-plain-obj: 4.1.0
+ trough: 2.2.0
+ vfile: 6.0.3
+
+ unist-util-is@6.0.1:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-remove-position@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-visit: 5.1.0
+
+ unist-util-stringify-position@4.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-visit-parents@6.0.2:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+
+ unist-util-visit@5.1.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
update-browserslist-db@1.2.3(browserslist@4.28.7):
dependencies:
browserslist: 4.28.7
@@ -1587,6 +3413,16 @@ snapshots:
util-deprecate@1.0.2: {}
+ vfile-message@4.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-stringify-position: 4.0.0
+
+ vfile@6.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ vfile-message: 4.0.3
+
vite@5.4.21:
dependencies:
esbuild: 0.21.5
@@ -1617,3 +3453,7 @@ snapshots:
'@vue/shared': 3.5.40
optionalDependencies:
typescript: 5.9.3
+
+ w3c-keyname@2.2.8: {}
+
+ zwitch@2.0.4: {}
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index bb5c4cd..d663e72 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -1,8 +1,41 @@
-
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/admin/AdminLayout.vue b/frontend/src/admin/AdminLayout.vue
index 153032a..ab56572 100644
--- a/frontend/src/admin/AdminLayout.vue
+++ b/frontend/src/admin/AdminLayout.vue
@@ -1,12 +1,32 @@
-
-
+
+
-
-
-
+
+
+
+ {{ crumb }}
+
+
+
+ 搜索 / 命令 ⌘K
+
+
+
+
+
+
+
+
+
+
检查登录状态…
+
\ No newline at end of file
diff --git a/frontend/src/admin/CommandPalette.vue b/frontend/src/admin/CommandPalette.vue
new file mode 100644
index 0000000..572d628
--- /dev/null
+++ b/frontend/src/admin/CommandPalette.vue
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+ {{ sec.name }}
+
+
+ {{ it.label }}
+ {{ it.hint }}
+
+
+
没找到匹配的命令
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/admin/DashboardView.vue b/frontend/src/admin/DashboardView.vue
new file mode 100644
index 0000000..bc2aba8
--- /dev/null
+++ b/frontend/src/admin/DashboardView.vue
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
总文章
+
{{ d.total_posts }}
+
长文 {{ d.long_posts }} · 短文 {{ d.short_posts }}
+
+
+
已发布
+
{{ d.published_posts }}
+
前台可见
+
+
+
草稿
+
{{ d.draft_posts }}
+
还在写
+
+
+
标签
+
{{ d.total_tags }}
+
已使用
+
+
+
总字数
+
{{ d.total_words.toLocaleString() }}
+
Markdown 字符
+
+
+
+
+
+
+
最近 12 个月发布
+ 每月文章数
+
+
还没有发布数据
+
+
+ {{ b.month.slice(5) }}
+
+
+
+
+
+
热门标签
+ 按文章数
+
+
还没有标签
+
+
+
+ {{ t.name }}
+ {{ t.count }} 篇
+
+
+
+
+
+
+
+
+
最近发布
+ 查看全部 →
+
+
还没有发布
+
+
+
+
+
+
最近草稿
+ 写新的 →
+
+
没有草稿
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/admin/EditorView.vue b/frontend/src/admin/EditorView.vue
index e2b8b3a..c3bce07 100644
--- a/frontend/src/admin/EditorView.vue
+++ b/frontend/src/admin/EditorView.vue
@@ -1,8 +1,11 @@
-
-
-
{{ isEdit ? '编辑文章' : '写新的' }}
-
+