diff --git a/binding_transport.go b/binding_transport.go new file mode 100644 index 00000000..c6bd3865 --- /dev/null +++ b/binding_transport.go @@ -0,0 +1,346 @@ +package main + +import ( + "context" + "crypto/rand" + "crypto/subtle" + _ "embed" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "log/slog" + "net" + "net/http" + "strconv" + "strings" + "sync" + + "github.com/lxzan/gws" + "github.com/wailsapp/wails/v3/pkg/application" + + "voidraft/internal/services" +) + +//go:embed binding_transport.js +var bindingTransportClientTemplate string + +type bindingWebSocketTransport struct { + gws.BuiltinEventHandler + + logger *slog.Logger + messageProcessor *application.MessageProcessor + server *http.Server + jsClient []byte + upgrader *gws.Upgrader + authToken string + + clients sync.Map +} + +type bindingTransportClient struct { + conn *gws.Conn + ctx context.Context + cancel context.CancelFunc + closeOnce sync.Once +} + +type bindingTransportMessage struct { + ID string `json:"id,omitempty"` + Type string `json:"type"` + Request *application.RuntimeRequest `json:"request,omitempty"` + Response *bindingTransportResponse `json:"response,omitempty"` + Event *application.CustomEvent `json:"event,omitempty"` +} + +type bindingTransportResponse struct { + StatusCode int `json:"statusCode"` + Data any `json:"data,omitempty"` + Error string `json:"error,omitempty"` +} + +func newBindingTransport() *bindingWebSocketTransport { + return &bindingWebSocketTransport{ + logger: slog.Default(), + } +} + +func (t *bindingWebSocketTransport) Start(ctx context.Context, processor *application.MessageProcessor) error { + t.messageProcessor = processor + authToken, err := generateBindingTransportToken() + if err != nil { + return fmt.Errorf("generate websocket transport auth token: %w", err) + } + t.authToken = authToken + + t.upgrader = gws.NewUpgrader(t, &gws.ServerOption{ + Recovery: gws.Recovery, + Authorize: t.authorizeRequest, + SubProtocols: []string{authToken}, + }) + + mux := http.NewServeMux() + mux.HandleFunc("/wails/ws", t.handleWebSocket) + + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return fmt.Errorf("listen websocket transport: %w", err) + } + + wsURL := "ws://" + listener.Addr().String() + "/wails/ws" + t.jsClient = []byte(strings.NewReplacer( + "__WAILS_WS_URL__", strconv.Quote(wsURL), + "__WAILS_WS_TOKEN__", strconv.Quote(authToken), + ).Replace(bindingTransportClientTemplate)) + t.server = &http.Server{Handler: mux} + + go func() { + <-ctx.Done() + if err := t.Stop(); err != nil { + t.logger.Error("failed to stop binding websocket transport", "error", err) + } + }() + + go func() { + if err := t.server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) { + t.logger.Error("binding websocket transport server stopped unexpectedly", "error", err) + } + }() + + t.logger.Info("binding websocket transport listening", "addr", listener.Addr().String()) + return nil +} + +func (t *bindingWebSocketTransport) JSClient() []byte { + return t.jsClient +} + +func (t *bindingWebSocketTransport) Stop() error { + t.clients.Range(func(key, value any) bool { + client, ok := value.(*bindingTransportClient) + if ok { + client.close() + } + t.clients.Delete(key) + return true + }) + + if t.server == nil { + return nil + } + + return t.server.Shutdown(context.Background()) +} + +func (t *bindingWebSocketTransport) DispatchWailsEvent(event *application.CustomEvent) { + payload, err := json.Marshal(bindingTransportMessage{ + Type: "event", + Event: event, + }) + if err != nil { + t.logger.Warn("failed to encode websocket event", "event", event.Name, "error", err) + return + } + + broadcaster := gws.NewBroadcaster(gws.OpcodeText, payload) + defer func() { + _ = broadcaster.Close() + }() + + t.clients.Range(func(_ any, value any) bool { + client, ok := value.(*bindingTransportClient) + if !ok { + return true + } + + if err := broadcaster.Broadcast(client.conn); err != nil { + t.logger.Debug("failed to broadcast websocket event", "event", event.Name, "error", err) + } + + return true + }) +} + +func (t *bindingWebSocketTransport) handleWebSocket(rw http.ResponseWriter, req *http.Request) { + conn, err := t.upgrader.Upgrade(rw, req) + if err != nil { + t.logger.Error("failed to upgrade websocket connection", "error", err) + return + } + + go conn.ReadLoop() +} + +func (t *bindingWebSocketTransport) OnOpen(socket *gws.Conn) { + t.clients.Store(socket, newBindingTransportClient(socket)) +} + +func (t *bindingWebSocketTransport) OnClose(socket *gws.Conn, err error) { + if client, ok := t.loadClient(socket); ok { + client.close() + } + t.clients.Delete(socket) + + if err != nil && !errors.Is(err, net.ErrClosed) { + t.logger.Debug("binding websocket closed", "error", err) + } +} + +func (t *bindingWebSocketTransport) OnMessage(socket *gws.Conn, message *gws.Message) { + defer message.Close() + + body := append([]byte(nil), message.Bytes()...) + + var payload bindingTransportMessage + if err := json.Unmarshal(body, &payload); err != nil { + t.logger.Warn("failed to decode websocket request", "error", err) + return + } + + if payload.Type != "request" || payload.Request == nil { + return + } + + if payload.Request.Args == nil { + payload.Request.Args = &application.Args{} + } + + client, ok := t.loadClient(socket) + if !ok { + return + } + + go t.handleRequest(client, payload.ID, payload.Request) +} + +func (t *bindingWebSocketTransport) handleRequest(client *bindingTransportClient, id string, req *application.RuntimeRequest) { + if t.messageProcessor == nil { + t.sendResponse(client, id, nil, http.StatusServiceUnavailable, errors.New("binding transport not ready")) + return + } + + resp, err := t.messageProcessor.HandleRuntimeCallWithIDs(client.ctx, req) + statusCode := http.StatusOK + if err != nil { + statusCode = http.StatusUnprocessableEntity + if errors.Is(err, services.ErrDocumentRevisionConflict) { + statusCode = http.StatusConflict + } + } + + t.sendResponse(client, id, resp, statusCode, err) +} + +func (t *bindingWebSocketTransport) sendResponse(client *bindingTransportClient, id string, data any, statusCode int, err error) { + response := &bindingTransportResponse{ + StatusCode: statusCode, + Data: data, + } + if err != nil { + response.Error = err.Error() + } + + if err := client.writeJSON(bindingTransportMessage{ + ID: id, + Type: "response", + Response: response, + }); err != nil { + t.logger.Debug("failed to encode websocket response", "id", id, "error", err) + } +} + +func (t *bindingWebSocketTransport) loadClient(socket *gws.Conn) (*bindingTransportClient, bool) { + value, ok := t.clients.Load(socket) + if !ok { + return nil, false + } + + client, ok := value.(*bindingTransportClient) + return client, ok +} + +func (t *bindingWebSocketTransport) authorizeRequest(req *http.Request, _ gws.SessionStorage) bool { + if !isLoopbackRequest(req) { + t.logger.Warn("rejected non-loopback websocket connection", "remoteAddr", req.RemoteAddr) + return false + } + + protocols := parseWebSocketProtocols(req.Header.Get("Sec-WebSocket-Protocol")) + if len(protocols) == 1 && secureCompareString(protocols[0], t.authToken) { + return true + } + + t.logger.Debug("rejected websocket connection with invalid auth token", "remoteAddr", req.RemoteAddr) + return false +} + +func generateBindingTransportToken() (string, error) { + token := make([]byte, 32) + if _, err := rand.Read(token); err != nil { + return "", err + } + + return hex.EncodeToString(token), nil +} + +func isLoopbackRequest(req *http.Request) bool { + host, _, err := net.SplitHostPort(req.RemoteAddr) + if err != nil { + return false + } + + ip := net.ParseIP(host) + return ip != nil && ip.IsLoopback() +} + +func secureCompareString(actual string, expected string) bool { + if len(actual) == 0 || len(actual) != len(expected) { + return false + } + + return subtle.ConstantTimeCompare([]byte(actual), []byte(expected)) == 1 +} + +func parseWebSocketProtocols(header string) []string { + if header == "" { + return nil + } + + rawProtocols := strings.Split(header, ",") + protocols := make([]string, 0, len(rawProtocols)) + for _, protocol := range rawProtocols { + protocol = strings.TrimSpace(protocol) + if protocol == "" { + continue + } + + protocols = append(protocols, protocol) + } + + return protocols +} + +func newBindingTransportClient(conn *gws.Conn) *bindingTransportClient { + ctx, cancel := context.WithCancel(context.Background()) + return &bindingTransportClient{ + conn: conn, + ctx: ctx, + cancel: cancel, + } +} + +func (c *bindingTransportClient) close() { + c.closeOnce.Do(func() { + c.cancel() + _ = c.conn.WriteClose(1001, nil) + }) +} + +func (c *bindingTransportClient) writeJSON(message bindingTransportMessage) error { + payload, err := json.Marshal(message) + if err != nil { + return err + } + + return c.conn.WriteMessage(gws.OpcodeText, payload) +} diff --git a/binding_transport.js b/binding_transport.js new file mode 100644 index 00000000..9c71aeef --- /dev/null +++ b/binding_transport.js @@ -0,0 +1,333 @@ +const wsURL = __WAILS_WS_URL__; +const wsToken = __WAILS_WS_TOKEN__; +const reconnectDelay = 2000; +const connectAttemptTimeout = 3000; +const connectRetryWindow = 10000; +const requestTimeout = 30000; + +function generateID() { + if ( + typeof crypto !== "undefined" && + typeof crypto.randomUUID === "function" + ) { + return crypto.randomUUID(); + } + + return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`; +} + +function resolveWindowName(windowName) { + if (windowName) { + return windowName; + } + + return new URLSearchParams(window.location.search).get("windowName") || ""; +} + +function resolveClientID() { + return window._wails?.clientId || ""; +} + +function sleep(delay) { + return new Promise((resolve) => { + window.setTimeout(resolve, delay); + }); +} + +function toError(error, fallbackMessage) { + if (error instanceof Error) { + return error; + } + + return new Error(fallbackMessage); +} + +class WebSocketTransport { + constructor(url, token) { + this.url = url; + this.token = token; + this.ws = null; + this.connectPromise = null; + this.pendingRequests = new Map(); + this.reconnectTimer = null; + this.closed = false; + } + + async connect() { + if (this.closed) { + throw new Error("WebSocket transport is closed"); + } + + if (this.ws?.readyState === WebSocket.OPEN) { + return; + } + + if (this.connectPromise) { + return this.connectPromise; + } + + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + + const connectPromise = this.connectWithRetry(); + this.connectPromise = connectPromise; + + try { + await connectPromise; + } finally { + if (this.connectPromise === connectPromise) { + this.connectPromise = null; + } + } + } + + async connectWithRetry() { + const deadline = Date.now() + connectRetryWindow; + let lastError = new Error("WebSocket transport connection failed"); + + while (!this.closed) { + try { + await this.openOnce(); + return; + } catch (error) { + lastError = toError(error, "WebSocket transport connection failed"); + if (Date.now() >= deadline) { + break; + } + + await sleep(200); + } + } + + throw lastError; + } + + openOnce() { + return new Promise((resolve, reject) => { + const ws = new WebSocket(this.url, this.token); + let opened = false; + let settled = false; + + const finish = (callback) => { + if (settled) { + return; + } + + settled = true; + callback(); + }; + + const timeout = window.setTimeout(() => { + finish(() => { + try { + ws.close(); + } catch { + // Ignore close errors after connect timeout. + } + + reject( + new Error( + `WebSocket transport connection timed out after ${connectAttemptTimeout}ms`, + ), + ); + }); + }, connectAttemptTimeout); + + ws.onopen = () => { + finish(() => { + window.clearTimeout(timeout); + this.ws = ws; + opened = true; + resolve(); + }); + }; + + ws.onmessage = async (event) => { + const payload = + typeof event.data === "string" ? event.data : await event.data.text(); + this.handleMessage(payload); + }; + + ws.onerror = () => { + if (!opened) { + finish(() => { + window.clearTimeout(timeout); + reject(new Error("WebSocket transport connection failed")); + }); + } + }; + + ws.onclose = () => { + const isCurrentSocket = this.ws === ws; + if (isCurrentSocket) { + this.ws = null; + } + + if (!opened) { + finish(() => { + window.clearTimeout(timeout); + reject( + new Error("WebSocket transport connection closed before opening"), + ); + }); + return; + } + + if (!isCurrentSocket) { + return; + } + + this.rejectPendingRequests(new Error("WebSocket connection closed")); + this.scheduleReconnect(); + }; + }); + } + + async call(objectID, method, windowName, args) { + await this.connect(); + + const ws = this.ws; + if (!ws || ws.readyState !== WebSocket.OPEN) { + throw new Error("WebSocket transport is not connected"); + } + + return new Promise((resolve, reject) => { + const id = generateID(); + const timeout = window.setTimeout(() => { + if (!this.pendingRequests.has(id)) { + return; + } + + this.pendingRequests.delete(id); + reject(new Error(`Request timeout (${requestTimeout}ms)`)); + }, requestTimeout); + + this.pendingRequests.set(id, { resolve, reject, timeout }); + + try { + ws.send( + JSON.stringify({ + id, + type: "request", + request: { + object: objectID, + method, + args: args ?? undefined, + webviewWindowName: resolveWindowName(windowName) || undefined, + clientId: resolveClientID() || undefined, + }, + }), + ); + } catch (error) { + this.pendingRequests.delete(id); + window.clearTimeout(timeout); + reject(toError(error, "WebSocket send failed")); + } + }); + } + + handleMessage(raw) { + let message; + + try { + message = JSON.parse(raw); + } catch (error) { + console.error( + "[bindingTransport] Failed to parse websocket message", + error, + ); + return; + } + + if (message.type === "event") { + window._wails?.dispatchWailsEvent?.(message.event); + return; + } + + if (message.type !== "response" || !message.id) { + return; + } + + const pending = this.pendingRequests.get(message.id); + if (!pending) { + return; + } + + this.pendingRequests.delete(message.id); + window.clearTimeout(pending.timeout); + + if ( + message.response?.statusCode >= 200 && + message.response.statusCode < 300 + ) { + pending.resolve(message.response.data); + return; + } + + pending.reject( + new Error(message.response?.error || "WebSocket runtime call failed"), + ); + } + + rejectPendingRequests(error) { + this.pendingRequests.forEach((pending) => { + window.clearTimeout(pending.timeout); + pending.reject(error); + }); + this.pendingRequests.clear(); + } + + scheduleReconnect() { + if ( + this.closed || + this.reconnectTimer || + this.connectPromise || + this.ws?.readyState === WebSocket.OPEN + ) { + return; + } + + this.reconnectTimer = window.setTimeout(() => { + this.reconnectTimer = null; + void this.connect().catch(() => { + this.scheduleReconnect(); + }); + }, reconnectDelay); + } + + close() { + this.closed = true; + + if (this.reconnectTimer) { + window.clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + + const ws = this.ws; + this.ws = null; + + if (ws && ws.readyState < WebSocket.CLOSING) { + ws.close(); + } + + this.rejectPendingRequests(new Error("WebSocket transport closed")); + } +} + +const transport = new WebSocketTransport(wsURL, wsToken); + +window.addEventListener( + "beforeunload", + () => { + transport.close(); + }, + { once: true }, +); + +void transport.connect().catch(() => { + // The next runtime call will retry the connection. +}); + +export default transport; diff --git a/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts b/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts index 732e56fa..58ceb2fd 100644 --- a/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts +++ b/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts @@ -18,6 +18,14 @@ import * as application$0 from "../../application/models.js"; // @ts-ignore: Unused imports import * as $models from "./models.js"; +/** + * GetBadge returns the badge label on the application icon. + */ +export function GetBadge(): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(1150236961) as any; + return $resultPromise; +} + /** * HideAppIcon hides the app icon in the dock/taskbar. */ diff --git a/frontend/bindings/voidraft/internal/models/models.ts b/frontend/bindings/voidraft/internal/models/models.ts index 38354e33..b7146ad2 100644 --- a/frontend/bindings/voidraft/internal/models/models.ts +++ b/frontend/bindings/voidraft/internal/models/models.ts @@ -30,9 +30,9 @@ export class AppConfig { "updates": UpdatesConfig; /** - * Git备份设置 + * 同步设置 */ - "backup": GitBackupConfig; + "sync": SyncConfig; /** * 配置元数据 @@ -53,8 +53,8 @@ export class AppConfig { if (!("updates" in $$source)) { this["updates"] = (new UpdatesConfig()); } - if (!("backup" in $$source)) { - this["backup"] = (new GitBackupConfig()); + if (!("sync" in $$source)) { + this["sync"] = (new SyncConfig()); } if (!("metadata" in $$source)) { this["metadata"] = (new ConfigMetadata()); @@ -86,8 +86,8 @@ export class AppConfig { if ("updates" in $$parsedSource) { $$parsedSource["updates"] = $$createField3_0($$parsedSource["updates"]); } - if ("backup" in $$parsedSource) { - $$parsedSource["backup"] = $$createField4_0($$parsedSource["backup"]); + if ("sync" in $$parsedSource) { + $$parsedSource["sync"] = $$createField4_0($$parsedSource["sync"]); } if ("metadata" in $$parsedSource) { $$parsedSource["metadata"] = $$createField5_0($$parsedSource["metadata"]); @@ -140,7 +140,7 @@ export class AppearanceConfig { } /** - * Git备份相关类型定义 + * Git同步相关类型定义 * * AuthMethod 定义Git认证方式 */ @@ -403,10 +403,20 @@ export enum ExtensionName { */ HttpClient = "httpClient", + /** + * 编辑器内联图片 + */ + InlineImage = "inlineImage", + /** * 代码块导出图片 */ BlockImage = "blockImage", + + /** + * 代码块局部只读 + */ + BlockReadonly = "blockReadonly", }; /** @@ -516,11 +526,11 @@ export class GeneralConfig { } /** - * GitBackupConfig Git备份配置 + * GitSyncConfig 描述 Git 同步配置。 */ -export class GitBackupConfig { - "enabled": boolean; +export class GitSyncConfig { "repo_url": string; + "branch": string; "auth_method": AuthMethod; "username"?: string; "password"?: string; @@ -528,39 +538,27 @@ export class GitBackupConfig { "ssh_key_path"?: string; "ssh_key_passphrase"?: string; - /** - * 分钟 - */ - "backup_interval": number; - "auto_backup": boolean; - - /** Creates a new GitBackupConfig instance. */ - constructor($$source: Partial = {}) { - if (!("enabled" in $$source)) { - this["enabled"] = false; - } + /** Creates a new GitSyncConfig instance. */ + constructor($$source: Partial = {}) { if (!("repo_url" in $$source)) { this["repo_url"] = ""; } + if (!("branch" in $$source)) { + this["branch"] = ""; + } if (!("auth_method" in $$source)) { this["auth_method"] = ("" as AuthMethod); } - if (!("backup_interval" in $$source)) { - this["backup_interval"] = 0; - } - if (!("auto_backup" in $$source)) { - this["auto_backup"] = false; - } Object.assign(this, $$source); } /** - * Creates a new GitBackupConfig instance from a string or object. + * Creates a new GitSyncConfig instance from a string or object. */ - static createFrom($$source: any = {}): GitBackupConfig { + static createFrom($$source: any = {}): GitSyncConfig { let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - return new GitBackupConfig($$parsedSource as Partial); + return new GitSyncConfig($$parsedSource as Partial); } } @@ -1184,6 +1182,377 @@ export enum LanguageType { LangEnUS = "en-US", }; +/** + * LocalFSSyncConfig 描述本地文件系统同步配置。 + */ +export class LocalFSSyncConfig { + "root_path": string; + + /** Creates a new LocalFSSyncConfig instance. */ + constructor($$source: Partial = {}) { + if (!("root_path" in $$source)) { + this["root_path"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new LocalFSSyncConfig instance from a string or object. + */ + static createFrom($$source: any = {}): LocalFSSyncConfig { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new LocalFSSyncConfig($$parsedSource as Partial); + } +} + +/** + * SyncConfig 描述同步模块配置。 + */ +export class SyncConfig { + "target": SyncTarget; + "enabled": boolean; + "auto_sync": boolean; + + /** + * 分钟 + */ + "sync_interval": number; + "git": GitSyncConfig; + "localfs": LocalFSSyncConfig; + + /** Creates a new SyncConfig instance. */ + constructor($$source: Partial = {}) { + if (!("target" in $$source)) { + this["target"] = ("" as SyncTarget); + } + if (!("enabled" in $$source)) { + this["enabled"] = false; + } + if (!("auto_sync" in $$source)) { + this["auto_sync"] = false; + } + if (!("sync_interval" in $$source)) { + this["sync_interval"] = 0; + } + if (!("git" in $$source)) { + this["git"] = (new GitSyncConfig()); + } + if (!("localfs" in $$source)) { + this["localfs"] = (new LocalFSSyncConfig()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new SyncConfig instance from a string or object. + */ + static createFrom($$source: any = {}): SyncConfig { + const $$createField4_0 = $$createType9; + const $$createField5_0 = $$createType10; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("git" in $$parsedSource) { + $$parsedSource["git"] = $$createField4_0($$parsedSource["git"]); + } + if ("localfs" in $$parsedSource) { + $$parsedSource["localfs"] = $$createField5_0($$parsedSource["localfs"]); + } + return new SyncConfig($$parsedSource as Partial); + } +} + +/** + * SyncRunChanges 描述一次同步的合并变更统计。 + */ +export class SyncRunChanges { + "added": number; + "updated": number; + "deleted": number; + + /** Creates a new SyncRunChanges instance. */ + constructor($$source: Partial = {}) { + if (!("added" in $$source)) { + this["added"] = 0; + } + if (!("updated" in $$source)) { + this["updated"] = 0; + } + if (!("deleted" in $$source)) { + this["deleted"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new SyncRunChanges instance from a string or object. + */ + static createFrom($$source: any = {}): SyncRunChanges { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new SyncRunChanges($$parsedSource as Partial); + } +} + +/** + * SyncRunDetails 描述一次同步的详细信息。 + */ +export class SyncRunDetails { + "attempt": number; + "max_attempts": number; + "changes": SyncRunChanges; + "flow": SyncRunFlow; + "error"?: SyncRunErrorDetail | null; + "paths": SyncRunPaths; + + /** Creates a new SyncRunDetails instance. */ + constructor($$source: Partial = {}) { + if (!("attempt" in $$source)) { + this["attempt"] = 0; + } + if (!("max_attempts" in $$source)) { + this["max_attempts"] = 0; + } + if (!("changes" in $$source)) { + this["changes"] = (new SyncRunChanges()); + } + if (!("flow" in $$source)) { + this["flow"] = (new SyncRunFlow()); + } + if (!("paths" in $$source)) { + this["paths"] = (new SyncRunPaths()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new SyncRunDetails instance from a string or object. + */ + static createFrom($$source: any = {}): SyncRunDetails { + const $$createField2_0 = $$createType11; + const $$createField3_0 = $$createType12; + const $$createField4_0 = $$createType14; + const $$createField5_0 = $$createType15; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("changes" in $$parsedSource) { + $$parsedSource["changes"] = $$createField2_0($$parsedSource["changes"]); + } + if ("flow" in $$parsedSource) { + $$parsedSource["flow"] = $$createField3_0($$parsedSource["flow"]); + } + if ("error" in $$parsedSource) { + $$parsedSource["error"] = $$createField4_0($$parsedSource["error"]); + } + if ("paths" in $$parsedSource) { + $$parsedSource["paths"] = $$createField5_0($$parsedSource["paths"]); + } + return new SyncRunDetails($$parsedSource as Partial); + } +} + +/** + * SyncRunErrorDetail 描述失败详情。 + */ +export class SyncRunErrorDetail { + "stage": string; + "message": string; + "retryable": boolean; + + /** Creates a new SyncRunErrorDetail instance. */ + constructor($$source: Partial = {}) { + if (!("stage" in $$source)) { + this["stage"] = ""; + } + if (!("message" in $$source)) { + this["message"] = ""; + } + if (!("retryable" in $$source)) { + this["retryable"] = false; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new SyncRunErrorDetail instance from a string or object. + */ + static createFrom($$source: any = {}): SyncRunErrorDetail { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new SyncRunErrorDetail($$parsedSource as Partial); + } +} + +/** + * SyncRunFlow 描述一次同步的数据流向。 + */ +export class SyncRunFlow { + "pulled": boolean; + "pushed": boolean; + + /** Creates a new SyncRunFlow instance. */ + constructor($$source: Partial = {}) { + if (!("pulled" in $$source)) { + this["pulled"] = false; + } + if (!("pushed" in $$source)) { + this["pushed"] = false; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new SyncRunFlow instance from a string or object. + */ + static createFrom($$source: any = {}): SyncRunFlow { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new SyncRunFlow($$parsedSource as Partial); + } +} + +/** + * SyncRunPaths 描述排障相关路径信息。 + */ +export class SyncRunPaths { + "data_path"?: string; + "repo_path"?: string; + + /** Creates a new SyncRunPaths instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new SyncRunPaths instance from a string or object. + */ + static createFrom($$source: any = {}): SyncRunPaths { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new SyncRunPaths($$parsedSource as Partial); + } +} + +/** + * SyncRunRecord 描述前端展示所需的一条同步记录。 + */ +export class SyncRunRecord { + "id": number; + "target_type": SyncTarget; + "target_path": string; + "branch": string; + "trigger_type": SyncRunTriggerType; + "status": SyncRunStatus; + "started_at": string; + "finished_at": string; + "details": SyncRunDetails; + + /** Creates a new SyncRunRecord instance. */ + constructor($$source: Partial = {}) { + if (!("id" in $$source)) { + this["id"] = 0; + } + if (!("target_type" in $$source)) { + this["target_type"] = ("" as SyncTarget); + } + if (!("target_path" in $$source)) { + this["target_path"] = ""; + } + if (!("branch" in $$source)) { + this["branch"] = ""; + } + if (!("trigger_type" in $$source)) { + this["trigger_type"] = ("" as SyncRunTriggerType); + } + if (!("status" in $$source)) { + this["status"] = ("" as SyncRunStatus); + } + if (!("started_at" in $$source)) { + this["started_at"] = ""; + } + if (!("finished_at" in $$source)) { + this["finished_at"] = ""; + } + if (!("details" in $$source)) { + this["details"] = (new SyncRunDetails()); + } + + Object.assign(this, $$source); + } + + /** + * Creates a new SyncRunRecord instance from a string or object. + */ + static createFrom($$source: any = {}): SyncRunRecord { + const $$createField8_0 = $$createType16; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("details" in $$parsedSource) { + $$parsedSource["details"] = $$createField8_0($$parsedSource["details"]); + } + return new SyncRunRecord($$parsedSource as Partial); + } +} + +/** + * SyncRunStatus 描述同步执行结果。 + */ +export enum SyncRunStatus { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * SyncRunStatusSuccess 表示同步成功。 + */ + SyncRunStatusSuccess = "success", + + /** + * SyncRunStatusFailed 表示同步失败。 + */ + SyncRunStatusFailed = "failed", +}; + +/** + * SyncRunTriggerType 描述同步触发来源。 + */ +export enum SyncRunTriggerType { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * SyncRunTriggerManual 表示手动触发。 + */ + SyncRunTriggerManual = "manual", + + /** + * SyncRunTriggerAuto 表示自动同步触发。 + */ + SyncRunTriggerAuto = "auto", +}; + +/** + * SyncTarget 定义当前可选择的同步目标。 + */ +export enum SyncTarget { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * SyncTargetGit 表示 Git 同步。 + */ + SyncTargetGit = "git", + + /** + * SyncTargetLocalFS 表示本地文件系统同步。 + */ + SyncTargetLocalFS = "localfs", +}; + /** * SystemThemeType 系统主题类型定义 */ @@ -1283,7 +1652,7 @@ export class UpdatesConfig { * Creates a new UpdatesConfig instance from a string or object. */ static createFrom($$source: any = {}): UpdatesConfig { - const $$createField4_0 = $$createType9; + const $$createField4_0 = $$createType17; let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; if ("github" in $$parsedSource) { $$parsedSource["github"] = $$createField4_0($$parsedSource["github"]); @@ -1297,7 +1666,7 @@ const $$createType0 = GeneralConfig.createFrom; const $$createType1 = EditingConfig.createFrom; const $$createType2 = AppearanceConfig.createFrom; const $$createType3 = UpdatesConfig.createFrom; -const $$createType4 = GitBackupConfig.createFrom; +const $$createType4 = SyncConfig.createFrom; const $$createType5 = ConfigMetadata.createFrom; var $$createType6 = (function $$initCreateType6(...args): any { if ($$createType6 === $$initCreateType6) { @@ -1307,4 +1676,12 @@ var $$createType6 = (function $$initCreateType6(...args): any { }); const $$createType7 = $Create.Map($Create.Any, $Create.Any); const $$createType8 = HotkeyCombo.createFrom; -const $$createType9 = GithubConfig.createFrom; +const $$createType9 = GitSyncConfig.createFrom; +const $$createType10 = LocalFSSyncConfig.createFrom; +const $$createType11 = SyncRunChanges.createFrom; +const $$createType12 = SyncRunFlow.createFrom; +const $$createType13 = SyncRunErrorDetail.createFrom; +const $$createType14 = $Create.Nullable($$createType13); +const $$createType15 = SyncRunPaths.createFrom; +const $$createType16 = SyncRunDetails.createFrom; +const $$createType17 = GithubConfig.createFrom; diff --git a/frontend/bindings/voidraft/internal/services/backupservice.ts b/frontend/bindings/voidraft/internal/services/backupservice.ts deleted file mode 100644 index 5ff03b88..00000000 --- a/frontend/bindings/voidraft/internal/services/backupservice.ts +++ /dev/null @@ -1,79 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -/** - * BackupService 提供基于Git的备份同步功能 - * @module - */ - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import {Call as $Call, Create as $Create} from "@wailsio/runtime"; - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js"; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import * as models$0 from "../models/models.js"; - -/** - * HandleConfigChange 处理配置变更 - */ -export function HandleConfigChange(config: models$0.GitBackupConfig | null): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(395287784, config) as any; - return $resultPromise; -} - -/** - * Initialize 初始化备份服务 - */ -export function Initialize(): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(1052437974) as any; - return $resultPromise; -} - -/** - * Reinitialize 重新初始化 - */ -export function Reinitialize(): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(301562543) as any; - return $resultPromise; -} - -/** - * ServiceShutdown 服务关闭 - */ -export function ServiceShutdown(): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(422131801) as any; - return $resultPromise; -} - -export function ServiceStartup(options: application$0.ServiceOptions): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(2900331732, options) as any; - return $resultPromise; -} - -/** - * StartAutoBackup 启动自动备份 - */ -export function StartAutoBackup(): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(3035755449) as any; - return $resultPromise; -} - -/** - * StopAutoBackup 停止自动备份 - */ -export function StopAutoBackup(): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(2641894021) as any; - return $resultPromise; -} - -/** - * Sync 执行完整的同步流程:导出 -> commit -> pull -> 解决冲突 -> push -> 导入 - */ -export function Sync(): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(3420383211) as any; - return $resultPromise; -} diff --git a/frontend/bindings/voidraft/internal/services/documentservice.ts b/frontend/bindings/voidraft/internal/services/documentservice.ts index 83e96c27..f306ff4e 100644 --- a/frontend/bindings/voidraft/internal/services/documentservice.ts +++ b/frontend/bindings/voidraft/internal/services/documentservice.ts @@ -17,6 +17,10 @@ import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/applic // @ts-ignore: Unused imports import * as ent$0 from "../models/ent/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + /** * CreateDocument 创建文档 */ @@ -50,7 +54,7 @@ export function GetDocumentByID(id: number): Promise & { } /** - * ListAllDocumentsMeta 列出所有文档 + * ListAllDocumentsMeta lists document metadata. */ export function ListAllDocumentsMeta(): Promise<(ent$0.Document | null)[]> & { cancel(): void } { let $resultPromise = $Call.ByID(3073950297) as any; @@ -88,9 +92,13 @@ export function UnlockDocument(id: number): Promise & { cancel(): void } { /** * UpdateDocumentContent 更新文档内容 */ -export function UpdateDocumentContent(id: number, content: string): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(3251897116, id, content) as any; - return $resultPromise; +export function UpdateDocumentContent(id: number, content: string, baseUpdatedAt: string): Promise<$models.DocumentSaveResult | null> & { cancel(): void } { + let $resultPromise = $Call.ByID(3251897116, id, content, baseUpdatedAt) as any; + let $typingPromise = $resultPromise.then(($result: any) => { + return $$createType4($result); + }) as any; + $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); + return $typingPromise; } /** @@ -105,3 +113,5 @@ export function UpdateDocumentTitle(id: number, title: string): Promise & const $$createType0 = ent$0.Document.createFrom; const $$createType1 = $Create.Nullable($$createType0); const $$createType2 = $Create.Array($$createType1); +const $$createType3 = $models.DocumentSaveResult.createFrom; +const $$createType4 = $Create.Nullable($$createType3); diff --git a/frontend/bindings/voidraft/internal/services/index.ts b/frontend/bindings/voidraft/internal/services/index.ts index 4bc0bbe2..5de85d45 100644 --- a/frontend/bindings/voidraft/internal/services/index.ts +++ b/frontend/bindings/voidraft/internal/services/index.ts @@ -1,7 +1,6 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT -import * as BackupService from "./backupservice.js"; import * as ConfigService from "./configservice.js"; import * as DatabaseService from "./databaseservice.js"; import * as DialogService from "./dialogservice.js"; @@ -10,16 +9,18 @@ import * as ExtensionService from "./extensionservice.js"; import * as HotkeyService from "./hotkeyservice.js"; import * as HttpClientService from "./httpclientservice.js"; import * as KeyBindingService from "./keybindingservice.js"; +import * as MediaHTTPService from "./mediahttpservice.js"; +import * as MediaSyncService from "./mediasyncservice.js"; import * as MigrationService from "./migrationservice.js"; import * as SelfUpdateService from "./selfupdateservice.js"; import * as StartupService from "./startupservice.js"; +import * as SyncService from "./syncservice.js"; import * as SystemService from "./systemservice.js"; import * as TestService from "./testservice.js"; import * as ThemeService from "./themeservice.js"; import * as TranslationService from "./translationservice.js"; import * as WindowService from "./windowservice.js"; export { - BackupService, ConfigService, DatabaseService, DialogService, @@ -28,9 +29,12 @@ export { HotkeyService, HttpClientService, KeyBindingService, + MediaHTTPService, + MediaSyncService, MigrationService, SelfUpdateService, StartupService, + SyncService, SystemService, TestService, ThemeService, diff --git a/frontend/bindings/voidraft/internal/services/mediahttpservice.ts b/frontend/bindings/voidraft/internal/services/mediahttpservice.ts new file mode 100644 index 00000000..3a583789 --- /dev/null +++ b/frontend/bindings/voidraft/internal/services/mediahttpservice.ts @@ -0,0 +1,62 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * MediaHTTPService 负责媒体上传、删除和 HTTP 文件访问。 + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import {Call as $Call, Create as $Create} from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * DeleteImage permanently removes one indexed image asset and its local file when no active document still references it. + */ +export function DeleteImage(imageRef: string): Promise<$models.ImageDeleteResult | null> & { cancel(): void } { + let $resultPromise = $Call.ByID(143616668, imageRef) as any; + let $typingPromise = $resultPromise.then(($result: any) => { + return $$createType1($result); + }) as any; + $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); + return $typingPromise; +} + +/** + * ImportImage validates and stores an image in the indexed media store. + */ +export function ImportImage(request: $models.ImageImportRequest | null): Promise<$models.ImageAsset | null> & { cancel(): void } { + let $resultPromise = $Call.ByID(2860242998, request) as any; + let $typingPromise = $resultPromise.then(($result: any) => { + return $$createType3($result); + }) as any; + $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); + return $typingPromise; +} + +export function ServiceShutdown(): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(2519222661) as any; + return $resultPromise; +} + +/** + * ServiceStartup keeps the HTTP service lightweight. Root state is managed by MediaSyncService. + */ +export function ServiceStartup(options: application$0.ServiceOptions): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(2764039056, options) as any; + return $resultPromise; +} + +// Private type creation functions +const $$createType0 = $models.ImageDeleteResult.createFrom; +const $$createType1 = $Create.Nullable($$createType0); +const $$createType2 = $models.ImageAsset.createFrom; +const $$createType3 = $Create.Nullable($$createType2); diff --git a/frontend/bindings/voidraft/internal/services/mediasyncservice.ts b/frontend/bindings/voidraft/internal/services/mediasyncservice.ts new file mode 100644 index 00000000..b65a441e --- /dev/null +++ b/frontend/bindings/voidraft/internal/services/mediasyncservice.ts @@ -0,0 +1,25 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * MediaSyncService 管理媒体根目录状态、索引修复和孤儿资源清理。 + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import {Call as $Call, Create as $Create} from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js"; + +export function ServiceShutdown(): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(760095884) as any; + return $resultPromise; +} + +export function ServiceStartup(options: application$0.ServiceOptions): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(2265252271, options) as any; + return $resultPromise; +} diff --git a/frontend/bindings/voidraft/internal/services/models.ts b/frontend/bindings/voidraft/internal/services/models.ts index 85a31f07..ca50677f 100644 --- a/frontend/bindings/voidraft/internal/services/models.ts +++ b/frontend/bindings/voidraft/internal/services/models.ts @@ -12,6 +12,50 @@ import * as http$0 from "../../../net/http/models.js"; // @ts-ignore: Unused imports import * as time$0 from "../../../time/models.js"; +/** + * DocumentSaveResult describes the outcome of a document save request. + */ +export class DocumentSaveResult { + "document_id": number; + "updated_at": string; + "content_length": number; + "content_hash": string; + "saved_at": string; + "changed": boolean; + + /** Creates a new DocumentSaveResult instance. */ + constructor($$source: Partial = {}) { + if (!("document_id" in $$source)) { + this["document_id"] = 0; + } + if (!("updated_at" in $$source)) { + this["updated_at"] = ""; + } + if (!("content_length" in $$source)) { + this["content_length"] = 0; + } + if (!("content_hash" in $$source)) { + this["content_hash"] = ""; + } + if (!("saved_at" in $$source)) { + this["saved_at"] = ""; + } + if (!("changed" in $$source)) { + this["changed"] = false; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new DocumentSaveResult instance from a string or object. + */ + static createFrom($$source: any = {}): DocumentSaveResult { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new DocumentSaveResult($$parsedSource as Partial); + } +} + /** * HttpRequest HTTP请求结构 */ @@ -114,6 +158,123 @@ export class HttpResponse { } } +/** + * ImageAsset describes one imported image asset. + */ +export class ImageAsset { + "id": string; + "filename"?: string; + "path": string; + "url": string; + "mime_type": string; + "size": number; + "width": number; + "height": number; + "sha256": string; + "created_at": string; + "updated_at": string; + + /** Creates a new ImageAsset instance. */ + constructor($$source: Partial = {}) { + if (!("id" in $$source)) { + this["id"] = ""; + } + if (!("path" in $$source)) { + this["path"] = ""; + } + if (!("url" in $$source)) { + this["url"] = ""; + } + if (!("mime_type" in $$source)) { + this["mime_type"] = ""; + } + if (!("size" in $$source)) { + this["size"] = 0; + } + if (!("width" in $$source)) { + this["width"] = 0; + } + if (!("height" in $$source)) { + this["height"] = 0; + } + if (!("sha256" in $$source)) { + this["sha256"] = ""; + } + if (!("created_at" in $$source)) { + this["created_at"] = ""; + } + if (!("updated_at" in $$source)) { + this["updated_at"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new ImageAsset instance from a string or object. + */ + static createFrom($$source: any = {}): ImageAsset { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new ImageAsset($$parsedSource as Partial); + } +} + +/** + * ImageDeleteResult describes the outcome of a delete operation. + */ +export class ImageDeleteResult { + "path": string; + "deleted": boolean; + + /** Creates a new ImageDeleteResult instance. */ + constructor($$source: Partial = {}) { + if (!("path" in $$source)) { + this["path"] = ""; + } + if (!("deleted" in $$source)) { + this["deleted"] = false; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new ImageDeleteResult instance from a string or object. + */ + static createFrom($$source: any = {}): ImageDeleteResult { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new ImageDeleteResult($$parsedSource as Partial); + } +} + +/** + * ImageImportRequest describes an image import payload. + */ +export class ImageImportRequest { + "filename"?: string; + "mime_type"?: string; + "data"?: string; + "data_base64"?: string; + + /** Creates a new ImageImportRequest instance. */ + constructor($$source: Partial = {}) { + + Object.assign(this, $$source); + } + + /** + * Creates a new ImageImportRequest instance from a string or object. + */ + static createFrom($$source: any = {}): ImageImportRequest { + const $$createField2_0 = $Create.ByteSlice; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("data" in $$parsedSource) { + $$parsedSource["data"] = $$createField2_0($$parsedSource["data"]); + } + return new ImageImportRequest($$parsedSource as Partial); + } +} + /** * MemoryStats 内存统计信息 */ diff --git a/frontend/bindings/voidraft/internal/services/syncservice.ts b/frontend/bindings/voidraft/internal/services/syncservice.ts new file mode 100644 index 00000000..fdf7dc05 --- /dev/null +++ b/frontend/bindings/voidraft/internal/services/syncservice.ts @@ -0,0 +1,98 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * SyncService exposes app-layer sync operations. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import {Call as $Call, Create as $Create} from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as models$0 from "../models/models.js"; + +/** + * HandleConfigChange re-applies sync config changes. + */ +export function HandleConfigChange(config: models$0.SyncConfig | null): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(1326966557, config) as any; + return $resultPromise; +} + +/** + * Initialize reloads config and restarts auto-sync. + */ +export function Initialize(): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(1986210239) as any; + return $resultPromise; +} + +/** + * ListSyncRunLogs returns paginated sync execution records. + */ +export function ListSyncRunLogs(page: number, pageSize: number): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(990538328, page, pageSize) as any; + let $typingPromise = $resultPromise.then(($result: any) => { + return $$createType1($result); + }) as any; + $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); + return $typingPromise; +} + +/** + * Reinitialize is an alias used by config watchers. + */ +export function Reinitialize(): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(1904005378) as any; + return $resultPromise; +} + +/** + * ServiceShutdown stops observers and sync schedulers. + */ +export function ServiceShutdown(): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(4185231442) as any; + return $resultPromise; +} + +/** + * ServiceStartup initializes the sync service and config observers. + */ +export function ServiceStartup(options: application$0.ServiceOptions): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(2650385641, options) as any; + return $resultPromise; +} + +/** + * StartAutoSync starts auto-sync scheduling. + */ +export function StartAutoSync(): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(90694923) as any; + return $resultPromise; +} + +/** + * StopAutoSync stops auto-sync scheduling. + */ +export function StopAutoSync(): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(972414697) as any; + return $resultPromise; +} + +/** + * Sync runs one manual sync. + */ +export function Sync(): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(3361086502) as any; + return $resultPromise; +} + +// Private type creation functions +const $$createType0 = models$0.SyncRunRecord.createFrom; +const $$createType1 = $Create.Array($$createType0); diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 8b2a0b49..d6dc3e51 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -15,6 +15,9 @@ declare module 'vue' { AccordionItem: typeof import('./src/components/accordion/AccordionItem.vue')['default'] BlockLanguageSelector: typeof import('./src/components/toolbar/BlockLanguageSelector.vue')['default'] DocumentSelector: typeof import('./src/components/toolbar/DocumentSelector.vue')['default'] + DrawImageDialog: typeof import('./src/components/inlineImage/DrawImageDialog.vue')['default'] + DrawImageFooter: typeof import('./src/components/inlineImage/draw/DrawImageFooter.vue')['default'] + DrawImageToolbar: typeof import('./src/components/inlineImage/draw/DrawImageToolbar.vue')['default'] LinuxTitleBar: typeof import('./src/components/titlebar/LinuxTitleBar.vue')['default'] LoadingScreen: typeof import('./src/components/loading/LoadingScreen.vue')['default'] MacOSTitleBar: typeof import('./src/components/titlebar/MacOSTitleBar.vue')['default'] diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a61ba35f..4a1a87c8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,19 +8,19 @@ "name": "frontend", "version": "0.0.0", "dependencies": { - "@codemirror/autocomplete": "^6.20.0", - "@codemirror/commands": "^6.10.1", + "@codemirror/autocomplete": "^6.20.1", + "@codemirror/commands": "^6.10.3", "@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.11", "@codemirror/lang-java": "^6.0.2", - "@codemirror/lang-javascript": "^6.2.4", + "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lang-json": "^6.0.2", "@codemirror/lang-less": "^6.0.2", "@codemirror/lang-lezer": "^6.0.2", - "@codemirror/lang-liquid": "^6.3.1", + "@codemirror/lang-liquid": "^6.3.2", "@codemirror/lang-markdown": "^6.5.0", "@codemirror/lang-php": "^6.0.2", "@codemirror/lang-python": "^6.2.1", @@ -29,63 +29,63 @@ "@codemirror/lang-sql": "^6.10.0", "@codemirror/lang-vue": "^0.1.3", "@codemirror/lang-wast": "^6.0.2", - "@codemirror/lang-yaml": "^6.1.2", - "@codemirror/language": "^6.12.1", + "@codemirror/lang-yaml": "^6.1.3", + "@codemirror/language": "^6.12.3", "@codemirror/language-data": "^6.5.2", "@codemirror/legacy-modes": "^6.5.2", - "@codemirror/lint": "^6.9.2", - "@codemirror/search": "^6.5.11", - "@codemirror/state": "^6.5.4", - "@codemirror/view": "^6.39.11", + "@codemirror/lint": "^6.9.5", + "@codemirror/search": "^6.6.0", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.40.0", "@cospaia/prettier-plugin-clojure": "^0.0.2", "@lezer/highlight": "^1.2.3", - "@lezer/lr": "^1.4.7", + "@lezer/lr": "^1.4.8", "@prettier/plugin-xml": "^3.4.2", "@replit/codemirror-lang-svelte": "^6.0.0", "@toml-tools/lexer": "^1.0.1", "@toml-tools/parser": "^1.0.1", - "@types/katex": "^0.16.7", - "@zumer/snapdom": "^2.0.1", + "@types/katex": "^0.16.8", + "@zumer/snapdom": "^2.7.0", "codemirror": "^6.0.2", - "codemirror-lang-elixir": "^4.0.0", - "colors-named": "^1.0.4", - "colors-named-hex": "^1.0.3", + "codemirror-lang-elixir": "^4.0.1", + "colors-named": "^1.0.5", + "colors-named-hex": "^1.0.4", + "fabric": "^7.2.0", "groovy-beautify": "^0.0.17", "hsl-matcher": "^1.2.4", - "java-parser": "^3.0.1", - "katex": "^0.16.27", + "katex": "^0.16.44", "linguist-languages": "^9.3.1", - "marked": "^17.0.1", - "mermaid": "^11.12.2", - "php-parser": "^3.2.5", + "marked": "^17.0.5", + "mermaid": "^11.13.0", + "php-parser": "^3.5.0", "pinia": "^3.0.4", "pinia-plugin-persistedstate": "^4.7.1", "prettier": "^3.8.1", - "sass": "^1.97.3", - "vue": "^3.5.27", - "vue-i18n": "^11.2.8", + "sass": "^1.98.0", + "vue": "^3.5.31", + "vue-i18n": "^11.3.0", "vue-pick-colors": "^1.8.0", - "vue-router": "^4.6.4" + "vue-router": "^5.0.4" }, "devDependencies": { - "@eslint/js": "^9.39.2", + "@eslint/js": "^10.0.1", "@lezer/generator": "^1.8.0", - "@types/node": "^25.0.3", - "@vitejs/plugin-vue": "^6.0.3", - "@wailsio/runtime": "^3.0.0-alpha.77", + "@types/node": "^25.5.0", + "@vitejs/plugin-vue": "^6.0.5", + "@wailsio/runtime": "^3.0.0-alpha.79", "cross-env": "^10.1.0", - "eslint": "^9.39.2", - "eslint-plugin-vue": "^10.6.2", - "globals": "^16.5.0", + "eslint": "^10.1.0", + "eslint-plugin-vue": "^10.8.0", + "globals": "^17.4.0", "typescript": "^5.9.3", - "typescript-eslint": "^8.51.0", - "unplugin-vue-components": "^30.0.0", - "vite": "npm:rolldown-vite@latest", - "vite-plugin-node-polyfills": "^0.24.0", + "typescript-eslint": "^8.57.2", + "unplugin-vue-components": "^32.0.0", + "vite": "^7.3.1", + "vite-plugin-node-polyfills": "^0.25.0", "vitepress": "^2.0.0-alpha.12", - "vitest": "^4.0.16", - "vue-eslint-parser": "^10.2.0", - "vue-tsc": "^3.2.1" + "vitest": "^4.1.2", + "vue-eslint-parser": "^10.4.0", + "vue-tsc": "^3.2.6" } }, "node_modules/@antfu/install-pkg": { @@ -101,27 +101,39 @@ "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@antfu/install-pkg/node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", "license": "MIT", - "engines": { - "node": ">=18" + "optional": true, + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" } }, - "node_modules/@antfu/utils": { - "version": "9.3.0", - "resolved": "https://registry.npmmirror.com/@antfu/utils/-/utils-9.3.0.tgz", - "integrity": "sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA==", + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", "engines": { @@ -130,7 +142,7 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { @@ -139,7 +151,7 @@ }, "node_modules/@babel/parser": { "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.2.tgz", "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "license": "MIT", "dependencies": { @@ -154,7 +166,7 @@ }, "node_modules/@babel/types": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz", "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "license": "MIT", "dependencies": { @@ -166,54 +178,54 @@ } }, "node_modules/@braintree/sanitize-url": { - "version": "7.1.1", - "resolved": "https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz", - "integrity": "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==", + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", "license": "MIT" }, "node_modules/@chevrotain/cst-dts-gen": { - "version": "11.0.3", - "resolved": "https://registry.npmmirror.com/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", - "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "version": "11.2.0", + "resolved": "https://registry.npmmirror.com/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.2.0.tgz", + "integrity": "sha512-ssJFvn/UXhQQeICw3SR/fZPmYVj+JM2mP+Lx7bZ51cOeHaMWOKp3AUMuyM3QR82aFFXTfcAp67P5GpPjGmbZWQ==", "license": "Apache-2.0", "dependencies": { - "@chevrotain/gast": "11.0.3", - "@chevrotain/types": "11.0.3", - "lodash-es": "4.17.21" + "@chevrotain/gast": "11.2.0", + "@chevrotain/types": "11.2.0", + "lodash-es": "4.17.23" } }, "node_modules/@chevrotain/gast": { - "version": "11.0.3", - "resolved": "https://registry.npmmirror.com/@chevrotain/gast/-/gast-11.0.3.tgz", - "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "version": "11.2.0", + "resolved": "https://registry.npmmirror.com/@chevrotain/gast/-/gast-11.2.0.tgz", + "integrity": "sha512-c+KoD6eSI1xjAZZoNUW+V0l13UEn+a4ShmUrjIKs1BeEWCji0Kwhmqn5FSx1K4BhWL7IQKlV7wLR4r8lLArORQ==", "license": "Apache-2.0", "dependencies": { - "@chevrotain/types": "11.0.3", - "lodash-es": "4.17.21" + "@chevrotain/types": "11.2.0", + "lodash-es": "4.17.23" } }, "node_modules/@chevrotain/regexp-to-ast": { - "version": "11.0.3", - "resolved": "https://registry.npmmirror.com/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", - "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "version": "11.2.0", + "resolved": "https://registry.npmmirror.com/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.2.0.tgz", + "integrity": "sha512-lG73pBFqbXODTbXhdZwv0oyUaI+3Irm+uOv5/W79lI3g5hasYaJnVJOm3H2NkhA0Ef4XLBU4Scr7TJDJwgFkAw==", "license": "Apache-2.0" }, "node_modules/@chevrotain/types": { - "version": "11.0.3", - "resolved": "https://registry.npmmirror.com/@chevrotain/types/-/types-11.0.3.tgz", - "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "version": "11.2.0", + "resolved": "https://registry.npmmirror.com/@chevrotain/types/-/types-11.2.0.tgz", + "integrity": "sha512-vBMSj/lz/LqolbGQEHB0tlpW5BnljHVtp+kzjQfQU+5BtGMTuZCPVgaAjtKvQYXnHb/8i/02Kii00y0tsuwfsw==", "license": "Apache-2.0" }, "node_modules/@chevrotain/utils": { - "version": "11.0.3", - "resolved": "https://registry.npmmirror.com/@chevrotain/utils/-/utils-11.0.3.tgz", - "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "version": "11.2.0", + "resolved": "https://registry.npmmirror.com/@chevrotain/utils/-/utils-11.2.0.tgz", + "integrity": "sha512-+7whECg4yNWHottjvr2To2BRxL4XJVjIyyv5J4+bJ0iMOVU8j/8n1qPDLZS/90W/BObDR8VNL46lFbzY/Hosmw==", "license": "Apache-2.0" }, "node_modules/@codemirror/autocomplete": { - "version": "6.20.0", - "resolved": "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz", - "integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==", + "version": "6.20.1", + "resolved": "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.20.1.tgz", + "integrity": "sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A==", "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -223,13 +235,13 @@ } }, "node_modules/@codemirror/commands": { - "version": "6.10.1", - "resolved": "https://registry.npmmirror.com/@codemirror/commands/-/commands-6.10.1.tgz", - "integrity": "sha512-uWDWFypNdQmz2y1LaNJzK7fL7TYKLeUAU0npEC685OKTF3KcQ2Vu3klIM78D7I6wGhktme0lh3CuQLv0ZCrD9Q==", + "version": "6.10.3", + "resolved": "https://registry.npmmirror.com/@codemirror/commands/-/commands-6.10.3.tgz", + "integrity": "sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==", "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.4.0", + "@codemirror/state": "^6.6.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } @@ -312,9 +324,9 @@ } }, "node_modules/@codemirror/lang-javascript": { - "version": "6.2.4", - "resolved": "https://registry.npmmirror.com/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz", - "integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==", + "version": "6.2.5", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", + "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -375,9 +387,9 @@ } }, "node_modules/@codemirror/lang-liquid": { - "version": "6.3.1", - "resolved": "https://registry.npmmirror.com/@codemirror/lang-liquid/-/lang-liquid-6.3.1.tgz", - "integrity": "sha512-S/jE/D7iij2Pu70AC65ME6AYWxOOcX20cSJvaPgY5w7m2sfxsArAcUAuUgm/CZCVmqoi9KiOlS7gj/gyLipABw==", + "version": "6.3.2", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-liquid/-/lang-liquid-6.3.2.tgz", + "integrity": "sha512-6PDVU3ZnfeYyz1at1E/ttorErZvZFXXt1OPhtfe1EZJ2V2iDFa0CwPqPgG5F7NXN0yONGoBogKmFAafKTqlwIw==", "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -509,9 +521,9 @@ } }, "node_modules/@codemirror/lang-yaml": { - "version": "6.1.2", - "resolved": "https://registry.npmmirror.com/@codemirror/lang-yaml/-/lang-yaml-6.1.2.tgz", - "integrity": "sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==", + "version": "6.1.3", + "resolved": "https://registry.npmmirror.com/@codemirror/lang-yaml/-/lang-yaml-6.1.3.tgz", + "integrity": "sha512-AZ8DJBuXGVHybpBQhmZtgew5//4hv3tdkXnr3vDmOUMJRuB6vn/uuwtmTOTlqEaQFg3hQSVeA90NmvIQyUV6FQ==", "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -524,9 +536,9 @@ } }, "node_modules/@codemirror/language": { - "version": "6.12.1", - "resolved": "https://registry.npmmirror.com/@codemirror/language/-/language-6.12.1.tgz", - "integrity": "sha512-Fa6xkSiuGKc8XC8Cn96T+TQHYj4ZZ7RdFmXA3i9xe/3hLHfwPZdM+dqfX0Cp0zQklBKhVD8Yzc8LS45rkqcwpQ==", + "version": "6.12.3", + "resolved": "https://registry.npmmirror.com/@codemirror/language/-/language-6.12.3.tgz", + "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", @@ -578,9 +590,9 @@ } }, "node_modules/@codemirror/lint": { - "version": "6.9.2", - "resolved": "https://registry.npmmirror.com/@codemirror/lint/-/lint-6.9.2.tgz", - "integrity": "sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ==", + "version": "6.9.5", + "resolved": "https://registry.npmmirror.com/@codemirror/lint/-/lint-6.9.5.tgz", + "integrity": "sha512-GElsbU9G7QT9xXhpUg1zWGmftA/7jamh+7+ydKRuT0ORpWS3wOSP0yT1FOlIZa7mIJjpVPipErsyvVqB9cfTFA==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", @@ -589,32 +601,32 @@ } }, "node_modules/@codemirror/search": { - "version": "6.5.11", - "resolved": "https://registry.npmmirror.com/@codemirror/search/-/search-6.5.11.tgz", - "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", + "version": "6.6.0", + "resolved": "https://registry.npmmirror.com/@codemirror/search/-/search-6.6.0.tgz", + "integrity": "sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.37.0", "crelt": "^1.0.5" } }, "node_modules/@codemirror/state": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.4.tgz", - "integrity": "sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==", + "version": "6.6.0", + "resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.6.0.tgz", + "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", "license": "MIT", "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "node_modules/@codemirror/view": { - "version": "6.39.15", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.15.tgz", - "integrity": "sha512-aCWjgweIIXLBHh7bY6cACvXuyrZ0xGafjQ2VInjp4RM4gMfscK5uESiNdrH0pE+e1lZr2B4ONGsjchl2KsKZzg==", + "version": "6.40.0", + "resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.40.0.tgz", + "integrity": "sha512-WA0zdU7xfF10+5I3HhUUq3kqOx3KjqmtQ9lqZjfK7jtYk4G72YW9rezcSywpaUMCWOMlq+6E0pO1IWg1TNIhtg==", "license": "MIT", "dependencies": { - "@codemirror/state": "^6.5.0", + "@codemirror/state": "^6.6.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" @@ -626,17 +638,139 @@ "integrity": "sha512-5ZgNOdiiIHbcBLvJhonCGoHFfuLlfsA+CjohiZGVuyD2XMVi35YFr7vZ6eSHeWjFAUsKRFbcOqtoXsV1Wk7zXA==", "license": "MIT" }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@docsearch/css": { - "version": "4.2.0", - "resolved": "https://registry.npmmirror.com/@docsearch/css/-/css-4.2.0.tgz", - "integrity": "sha512-65KU9Fw5fGsPPPlgIghonMcndyx1bszzrDQYLfierN+Ha29yotMHzVS94bPkZS6On9LS8dE4qmW4P/fGjtCf/g==", + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/@docsearch/css/-/css-4.6.2.tgz", + "integrity": "sha512-fH/cn8BjEEdM2nJdjNMHIvOVYupG6AIDtFVDgIZrNzdCSj4KXr9kd+hsehqsNGYjpUjObeKYKvgy/IwCb1jZYQ==", "dev": true, "license": "MIT" }, "node_modules/@docsearch/js": { - "version": "4.2.0", - "resolved": "https://registry.npmmirror.com/@docsearch/js/-/js-4.2.0.tgz", - "integrity": "sha512-KBHVPO29QiGUFJYeAqxW0oXtGf/aghNmRrIRPT4/28JAefqoCkNn/ZM/jeQ7fHjl0KNM6C+KlLVYjwyz6lNZnA==", + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/@docsearch/js/-/js-4.6.2.tgz", + "integrity": "sha512-qj1yoxl3y4GKoK7+VM6fq/rQqPnvUmg3IKzJ9x0VzN14QVzdB/SG/J6VfV1BWT5RcPUFxIcVwoY1fwHM2fSRRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/sidepanel-js": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/@docsearch/sidepanel-js/-/sidepanel-js-4.6.2.tgz", + "integrity": "sha512-Pni85AP/GwRj7fFg8cBJp0U04tzbueBvWSd3gysgnOsVnQVSZwSYncfErUScLE1CAtR+qocPDFjmYR9AMRNJtQ==", "dev": true, "license": "MIT" }, @@ -648,9 +782,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", "cpu": [ "ppc64" ], @@ -665,9 +799,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", "cpu": [ "arm" ], @@ -682,9 +816,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", "cpu": [ "arm64" ], @@ -699,9 +833,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", "cpu": [ "x64" ], @@ -716,9 +850,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", "cpu": [ "arm64" ], @@ -733,9 +867,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", "cpu": [ "x64" ], @@ -750,9 +884,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", "cpu": [ "arm64" ], @@ -767,9 +901,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", "cpu": [ "x64" ], @@ -784,9 +918,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", "cpu": [ "arm" ], @@ -801,9 +935,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", "cpu": [ "arm64" ], @@ -818,9 +952,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", "cpu": [ "ia32" ], @@ -835,9 +969,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", "cpu": [ "loong64" ], @@ -852,9 +986,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", "cpu": [ "mips64el" ], @@ -869,9 +1003,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", "cpu": [ "ppc64" ], @@ -886,9 +1020,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", "cpu": [ "riscv64" ], @@ -903,9 +1037,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", "cpu": [ "s390x" ], @@ -920,9 +1054,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", "cpu": [ "x64" ], @@ -937,9 +1071,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", "cpu": [ "arm64" ], @@ -954,9 +1088,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", "cpu": [ "x64" ], @@ -971,9 +1105,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", "cpu": [ "arm64" ], @@ -988,9 +1122,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", "cpu": [ "x64" ], @@ -1004,10 +1138,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", "cpu": [ "x64" ], @@ -1022,9 +1173,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", "cpu": [ "arm64" ], @@ -1039,9 +1190,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", "cpu": [ "ia32" ], @@ -1056,9 +1207,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", "cpu": [ "x64" ], @@ -1074,7 +1225,7 @@ }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", @@ -1106,7 +1257,7 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", @@ -1115,118 +1266,89 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "version": "0.23.3", + "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.23.3.tgz", + "integrity": "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.7", + "@eslint/object-schema": "^3.0.3", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^10.2.4" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "version": "0.5.3", + "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.5.3.tgz", + "integrity": "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0" + "@eslint/core": "^1.1.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-1.1.1.tgz", + "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "version": "10.0.1", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-3.0.3.tgz", + "integrity": "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz", + "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0", + "@eslint/core": "^1.1.1", "levn": "^0.4.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@humanfs/core": { @@ -1240,33 +1362,19 @@ } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.7", + "resolved": "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1282,9 +1390,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1296,9 +1404,9 @@ } }, "node_modules/@iconify-json/simple-icons": { - "version": "1.2.56", - "resolved": "https://registry.npmmirror.com/@iconify-json/simple-icons/-/simple-icons-1.2.56.tgz", - "integrity": "sha512-oAvxOzgSjfvdj/Jsi3S7HDUCxO8/n2j8e1w1e/FktHUAXiWjNX00n3Tu3AP+n1ayKrypcUDXCzxn+0ENMl6ouw==", + "version": "1.2.75", + "resolved": "https://registry.npmmirror.com/@iconify-json/simple-icons/-/simple-icons-1.2.75.tgz", + "integrity": "sha512-KvcCUbvcBWb0sbqLIxHoY8z5/piXY08wcY9gfMhF+ph3AfzGMaSmZFkUY71HSXAljQngXkgs4bdKdekO0HQWvg==", "dev": true, "license": "CC0-1.0", "dependencies": { @@ -1312,41 +1420,41 @@ "license": "MIT" }, "node_modules/@iconify/utils": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-3.0.2.tgz", - "integrity": "sha512-EfJS0rLfVuRuJRn4psJHtK2A9TqVnkxPpHY6lYHiB9+8eSuudsxbwMiavocG45ujOo6FJ+CIRlRnlOGinzkaGQ==", + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-3.1.0.tgz", + "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", "license": "MIT", "dependencies": { "@antfu/install-pkg": "^1.1.0", - "@antfu/utils": "^9.2.0", "@iconify/types": "^2.0.0", - "debug": "^4.4.1", - "globals": "^15.15.0", - "kolorist": "^1.8.0", - "local-pkg": "^1.1.1", - "mlly": "^1.7.4" + "mlly": "^1.8.0" } }, - "node_modules/@iconify/utils/node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmmirror.com/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "node_modules/@intlify/core-base": { + "version": "11.3.0", + "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-11.3.0.tgz", + "integrity": "sha512-NNX5jIwF4TJBe7RtSKDMOA6JD9mp2mRcBHAwt2X+Q8PvnZub0yj5YYXlFu2AcESdgQpEv/5Yx2uOCV/yh7YkZg==", "license": "MIT", + "dependencies": { + "@intlify/devtools-types": "11.3.0", + "@intlify/message-compiler": "11.3.0", + "@intlify/shared": "11.3.0" + }, "engines": { - "node": ">=18" + "node": ">= 16" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/kazupon" } }, - "node_modules/@intlify/core-base": { - "version": "11.2.8", - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.2.8.tgz", - "integrity": "sha512-nBq6Y1tVkjIUsLsdOjDSJj4AsjvD0UG3zsg9Fyc+OivwlA/oMHSKooUy9tpKj0HqZ+NWFifweHavdljlBLTwdA==", + "node_modules/@intlify/devtools-types": { + "version": "11.3.0", + "resolved": "https://registry.npmmirror.com/@intlify/devtools-types/-/devtools-types-11.3.0.tgz", + "integrity": "sha512-G9CNL4WpANWVdUjubOIIS7/D2j/0j+1KJmhBJxHilWNKr9mmt3IjFV3Hq4JoBP23uOoC5ynxz/FHZ42M+YxfGw==", "license": "MIT", "dependencies": { - "@intlify/message-compiler": "11.2.8", - "@intlify/shared": "11.2.8" + "@intlify/core-base": "11.3.0", + "@intlify/shared": "11.3.0" }, "engines": { "node": ">= 16" @@ -1356,12 +1464,12 @@ } }, "node_modules/@intlify/message-compiler": { - "version": "11.2.8", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.2.8.tgz", - "integrity": "sha512-A5n33doOjmHsBtCN421386cG1tWp5rpOjOYPNsnpjIJbQ4POF0QY2ezhZR9kr0boKwaHjbOifvyQvHj2UTrDFQ==", + "version": "11.3.0", + "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-11.3.0.tgz", + "integrity": "sha512-RAJp3TMsqohg/Wa7bVF3cChRhecSYBLrTCQSj7j0UtWVFLP+6iEJoE2zb7GU5fp+fmG5kCbUdzhmlAUCWXiUJw==", "license": "MIT", "dependencies": { - "@intlify/shared": "11.2.8", + "@intlify/shared": "11.3.0", "source-map-js": "^1.0.2" }, "engines": { @@ -1372,9 +1480,9 @@ } }, "node_modules/@intlify/shared": { - "version": "11.2.8", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.2.8.tgz", - "integrity": "sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw==", + "version": "11.3.0", + "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-11.3.0.tgz", + "integrity": "sha512-LC6P/uay7rXL5zZ5+5iRJfLs/iUN8apu9tm8YqQVmW3Uq3X4A0dOFUIDuAmB7gAC29wTHOS3EiN/IosNSz0eNQ==", "license": "MIT", "engines": { "node": ">= 16" @@ -1387,7 +1495,6 @@ "version": "0.3.13", "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1398,7 +1505,6 @@ "version": "2.3.5", "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1409,7 +1515,6 @@ "version": "3.1.2", "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1425,7 +1530,6 @@ "version": "0.3.31", "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1433,15 +1537,15 @@ } }, "node_modules/@lezer/common": { - "version": "1.5.0", - "resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.5.0.tgz", - "integrity": "sha512-PNGcolp9hr4PJdXR4ix7XtixDrClScvtSCYW3rQG106oVMOOI+jFb+0+J3mbeL/53g1Zd6s0kJzaw6Ri68GmAA==", + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.5.1.tgz", + "integrity": "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==", "license": "MIT" }, "node_modules/@lezer/cpp": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/@lezer/cpp/-/cpp-1.1.3.tgz", - "integrity": "sha512-ykYvuFQKGsRi6IcE+/hCSGUhb/I4WPjd3ELhEblm2wS2cOznDFzO+ubK2c+ioysOnlZ3EduV+MVQFCPzAIoY3w==", + "version": "1.1.5", + "resolved": "https://registry.npmmirror.com/@lezer/cpp/-/cpp-1.1.5.tgz", + "integrity": "sha512-DIhSXmYtJKLehrjzDFN+2cPt547ySQ41nA8yqcDf/GxMc+YM736xqltFkvADL2M0VebU5I+3+4ks2Vv+Kyq3Aw==", "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", @@ -1450,14 +1554,14 @@ } }, "node_modules/@lezer/css": { - "version": "1.1.11", - "resolved": "https://registry.npmmirror.com/@lezer/css/-/css-1.1.11.tgz", - "integrity": "sha512-FuAnusbLBl1SEAtfN8NdShxYJiESKw9LAFysfea1T96jD3ydBn12oYjaSG1a04BQRIUd93/0D8e5CV1cUMkmQg==", + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/@lezer/css/-/css-1.3.3.tgz", + "integrity": "sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==", "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@lezer/lr": "^1.3.0" } }, "node_modules/@lezer/generator": { @@ -1475,14 +1579,14 @@ } }, "node_modules/@lezer/go": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/@lezer/go/-/go-1.0.0.tgz", - "integrity": "sha512-co9JfT3QqX1YkrMmourYw2Z8meGC50Ko4d54QEcQbEYpvdUvN4yb0NBZdn/9ertgvjsySxHsKzH3lbm3vqJ4Jw==", + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@lezer/go/-/go-1.0.1.tgz", + "integrity": "sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==", "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@lezer/lr": "^1.3.0" } }, "node_modules/@lezer/highlight": { @@ -1495,9 +1599,9 @@ } }, "node_modules/@lezer/html": { - "version": "1.3.12", - "resolved": "https://registry.npmmirror.com/@lezer/html/-/html-1.3.12.tgz", - "integrity": "sha512-RJ7eRWdaJe3bsiiLLHjCFT1JMk8m1YP9kaUbvu2rMLEoOnke9mcTVDyfOslsln0LtujdWespjJ39w6zo+RsQYw==", + "version": "1.3.13", + "resolved": "https://registry.npmmirror.com/@lezer/html/-/html-1.3.13.tgz", + "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", @@ -1517,9 +1621,9 @@ } }, "node_modules/@lezer/javascript": { - "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.5.1.tgz", - "integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==", + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", @@ -1550,7 +1654,7 @@ }, "node_modules/@lezer/lr": { "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.8.tgz", + "resolved": "https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.8.tgz", "integrity": "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==", "license": "MIT", "dependencies": { @@ -1558,19 +1662,19 @@ } }, "node_modules/@lezer/markdown": { - "version": "1.4.2", - "resolved": "https://registry.npmmirror.com/@lezer/markdown/-/markdown-1.4.2.tgz", - "integrity": "sha512-iYewCigG/517D0xJPQd7RGaCjZAFwROiH8T9h7OTtz0bRVtkxzFhGBFJ9JGKgBBs4uuo1cvxzyQ5iKhDLMcLUQ==", + "version": "1.6.3", + "resolved": "https://registry.npmmirror.com/@lezer/markdown/-/markdown-1.6.3.tgz", + "integrity": "sha512-jpGm5Ps+XErS+xA4urw7ogEGkeZOahVQF21Z6oECF0sj+2liwZopd2+I8uH5I/vZsRuuze3OxBREIANLf6KKUw==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.0.0", + "@lezer/common": "^1.5.0", "@lezer/highlight": "^1.0.0" } }, "node_modules/@lezer/php": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/@lezer/php/-/php-1.0.2.tgz", - "integrity": "sha512-GN7BnqtGRpFyeoKSEqxvGvhJQiI4zkgmYnDk/JIyc7H7Ifc1tkPnUn/R2R8meH3h/aBf5rzjvU8ZQoyiNDtDrA==", + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/@lezer/php/-/php-1.0.5.tgz", + "integrity": "sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA==", "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", @@ -1601,9 +1705,9 @@ } }, "node_modules/@lezer/sass": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/@lezer/sass/-/sass-1.0.7.tgz", - "integrity": "sha512-8HLlOkuX/SMHOggI2DAsXUw38TuURe+3eQ5hiuk9QmYOUyC55B1dYEIMkav5A4IELVaW4e1T4P9WRiI5ka4mdw==", + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/@lezer/sass/-/sass-1.1.0.tgz", + "integrity": "sha512-3mMGdCTUZ/84ArHOuXWQr37pnf7f+Nw9ycPUeKX+wu19b7pSMcZGLbaXwvD2APMBDOGxPmpK/O6S1v1EvLoqgQ==", "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", @@ -1623,9 +1727,9 @@ } }, "node_modules/@lezer/yaml": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/@lezer/yaml/-/yaml-1.0.3.tgz", - "integrity": "sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==", + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/@lezer/yaml/-/yaml-1.0.4.tgz", + "integrity": "sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==", "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", @@ -1640,26 +1744,26 @@ "license": "MIT" }, "node_modules/@mermaid-js/parser": { - "version": "0.6.3", - "resolved": "https://registry.npmmirror.com/@mermaid-js/parser/-/parser-0.6.3.tgz", - "integrity": "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==", + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@mermaid-js/parser/-/parser-1.0.1.tgz", + "integrity": "sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ==", "license": "MIT", "dependencies": { - "langium": "3.3.1" + "langium": "^4.0.0" } }, "node_modules/@parcel/watcher": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz", - "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", "hasInstallScript": true, "license": "MIT", "optional": true, "dependencies": { - "detect-libc": "^1.0.3", + "detect-libc": "^2.0.3", "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">= 10.0.0" @@ -1669,25 +1773,25 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.1", - "@parcel/watcher-darwin-arm64": "2.5.1", - "@parcel/watcher-darwin-x64": "2.5.1", - "@parcel/watcher-freebsd-x64": "2.5.1", - "@parcel/watcher-linux-arm-glibc": "2.5.1", - "@parcel/watcher-linux-arm-musl": "2.5.1", - "@parcel/watcher-linux-arm64-glibc": "2.5.1", - "@parcel/watcher-linux-arm64-musl": "2.5.1", - "@parcel/watcher-linux-x64-glibc": "2.5.1", - "@parcel/watcher-linux-x64-musl": "2.5.1", - "@parcel/watcher-win32-arm64": "2.5.1", - "@parcel/watcher-win32-ia32": "2.5.1", - "@parcel/watcher-win32-x64": "2.5.1" + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" } }, "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", - "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", "cpu": [ "arm64" ], @@ -1705,9 +1809,9 @@ } }, "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", - "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", "cpu": [ "arm64" ], @@ -1725,9 +1829,9 @@ } }, "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", - "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", "cpu": [ "x64" ], @@ -1745,9 +1849,9 @@ } }, "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", - "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", "cpu": [ "x64" ], @@ -1765,9 +1869,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", - "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", "cpu": [ "arm" ], @@ -1785,9 +1889,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", - "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", "cpu": [ "arm" ], @@ -1805,9 +1909,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", - "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", "cpu": [ "arm64" ], @@ -1825,9 +1929,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", - "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", "cpu": [ "arm64" ], @@ -1845,9 +1949,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", - "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", "cpu": [ "x64" ], @@ -1865,9 +1969,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", - "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", "cpu": [ "x64" ], @@ -1885,9 +1989,9 @@ } }, "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", - "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", "cpu": [ "arm64" ], @@ -1905,9 +2009,9 @@ } }, "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", - "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", "cpu": [ "ia32" ], @@ -1925,9 +2029,9 @@ } }, "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", - "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", "cpu": [ "x64" ], @@ -1986,9 +2090,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.53", - "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", - "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", + "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==", "dev": true, "license": "MIT" }, @@ -2038,22 +2142,9 @@ } } }, - "node_modules/@rollup/pluginutils/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", "cpu": [ "arm" @@ -2067,7 +2158,7 @@ }, "node_modules/@rollup/rollup-android-arm64": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", "cpu": [ "arm64" @@ -2081,7 +2172,7 @@ }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", "cpu": [ "arm64" @@ -2095,7 +2186,7 @@ }, "node_modules/@rollup/rollup-darwin-x64": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", "cpu": [ "x64" @@ -2109,7 +2200,7 @@ }, "node_modules/@rollup/rollup-freebsd-arm64": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", "cpu": [ "arm64" @@ -2123,7 +2214,7 @@ }, "node_modules/@rollup/rollup-freebsd-x64": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", "cpu": [ "x64" @@ -2137,7 +2228,7 @@ }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", "cpu": [ "arm" @@ -2151,7 +2242,7 @@ }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", "cpu": [ "arm" @@ -2165,7 +2256,7 @@ }, "node_modules/@rollup/rollup-linux-arm64-gnu": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", "cpu": [ "arm64" @@ -2179,7 +2270,7 @@ }, "node_modules/@rollup/rollup-linux-arm64-musl": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", "cpu": [ "arm64" @@ -2193,7 +2284,7 @@ }, "node_modules/@rollup/rollup-linux-loong64-gnu": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", "cpu": [ "loong64" @@ -2207,7 +2298,7 @@ }, "node_modules/@rollup/rollup-linux-loong64-musl": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", "cpu": [ "loong64" @@ -2221,7 +2312,7 @@ }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", "cpu": [ "ppc64" @@ -2235,7 +2326,7 @@ }, "node_modules/@rollup/rollup-linux-ppc64-musl": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", "cpu": [ "ppc64" @@ -2249,7 +2340,7 @@ }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", "cpu": [ "riscv64" @@ -2263,7 +2354,7 @@ }, "node_modules/@rollup/rollup-linux-riscv64-musl": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", "cpu": [ "riscv64" @@ -2277,7 +2368,7 @@ }, "node_modules/@rollup/rollup-linux-s390x-gnu": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", "cpu": [ "s390x" @@ -2291,7 +2382,7 @@ }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", "cpu": [ "x64" @@ -2305,7 +2396,7 @@ }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", "cpu": [ "x64" @@ -2319,7 +2410,7 @@ }, "node_modules/@rollup/rollup-openbsd-x64": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", "cpu": [ "x64" @@ -2333,7 +2424,7 @@ }, "node_modules/@rollup/rollup-openharmony-arm64": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", "cpu": [ "arm64" @@ -2347,7 +2438,7 @@ }, "node_modules/@rollup/rollup-win32-arm64-msvc": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", "cpu": [ "arm64" @@ -2361,7 +2452,7 @@ }, "node_modules/@rollup/rollup-win32-ia32-msvc": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", "cpu": [ "ia32" @@ -2375,7 +2466,7 @@ }, "node_modules/@rollup/rollup-win32-x64-gnu": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", "cpu": [ "x64" @@ -2389,7 +2480,7 @@ }, "node_modules/@rollup/rollup-win32-x64-msvc": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", "cpu": [ "x64" @@ -2402,120 +2493,76 @@ ] }, "node_modules/@shikijs/core": { - "version": "3.14.0", - "resolved": "https://registry.npmmirror.com/@shikijs/core/-/core-3.14.0.tgz", - "integrity": "sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw==", + "version": "3.23.0", + "resolved": "https://registry.npmmirror.com/@shikijs/core/-/core-3.23.0.tgz", + "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/types": "3.14.0", + "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "node_modules/@shikijs/engine-javascript": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@shikijs/engine-javascript/-/engine-javascript-3.15.0.tgz", - "integrity": "sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.15.0", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.3" - } - }, - "node_modules/@shikijs/engine-javascript/node_modules/@shikijs/types": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@shikijs/types/-/types-3.15.0.tgz", - "integrity": "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw==", + "version": "3.23.0", + "resolved": "https://registry.npmmirror.com/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", + "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", "dev": true, "license": "MIT", "dependencies": { + "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" + "oniguruma-to-es": "^4.3.4" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@shikijs/engine-oniguruma/-/engine-oniguruma-3.15.0.tgz", - "integrity": "sha512-HnqFsV11skAHvOArMZdLBZZApRSYS4LSztk2K3016Y9VCyZISnlYUYsL2hzlS7tPqKHvNqmI5JSUJZprXloMvA==", + "version": "3.23.0", + "resolved": "https://registry.npmmirror.com/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", + "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/types": "3.15.0", + "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, - "node_modules/@shikijs/engine-oniguruma/node_modules/@shikijs/types": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@shikijs/types/-/types-3.15.0.tgz", - "integrity": "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, "node_modules/@shikijs/langs": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@shikijs/langs/-/langs-3.15.0.tgz", - "integrity": "sha512-WpRvEFvkVvO65uKYW4Rzxs+IG0gToyM8SARQMtGGsH4GDMNZrr60qdggXrFOsdfOVssG/QQGEl3FnJ3EZ+8w8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.15.0" - } - }, - "node_modules/@shikijs/langs/node_modules/@shikijs/types": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@shikijs/types/-/types-3.15.0.tgz", - "integrity": "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw==", + "version": "3.23.0", + "resolved": "https://registry.npmmirror.com/@shikijs/langs/-/langs-3.23.0.tgz", + "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" + "@shikijs/types": "3.23.0" } }, "node_modules/@shikijs/themes": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@shikijs/themes/-/themes-3.15.0.tgz", - "integrity": "sha512-8ow2zWb1IDvCKjYb0KiLNrK4offFdkfNVPXb1OZykpLCzRU6j+efkY+Y7VQjNlNFXonSw+4AOdGYtmqykDbRiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.15.0" - } - }, - "node_modules/@shikijs/themes/node_modules/@shikijs/types": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@shikijs/types/-/types-3.15.0.tgz", - "integrity": "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw==", + "version": "3.23.0", + "resolved": "https://registry.npmmirror.com/@shikijs/themes/-/themes-3.23.0.tgz", + "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" + "@shikijs/types": "3.23.0" } }, "node_modules/@shikijs/transformers": { - "version": "3.14.0", - "resolved": "https://registry.npmmirror.com/@shikijs/transformers/-/transformers-3.14.0.tgz", - "integrity": "sha512-i67zQnY9wLMMnKasonVW1L9fKneSLZDj1ePsA4o0AZWU4uUobmJY9baRDa36z+a9/g0aG76/2tybQvm4hrwxIQ==", + "version": "3.23.0", + "resolved": "https://registry.npmmirror.com/@shikijs/transformers/-/transformers-3.23.0.tgz", + "integrity": "sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/core": "3.14.0", - "@shikijs/types": "3.14.0" + "@shikijs/core": "3.23.0", + "@shikijs/types": "3.23.0" } }, "node_modules/@shikijs/types": { - "version": "3.14.0", - "resolved": "https://registry.npmmirror.com/@shikijs/types/-/types-3.14.0.tgz", - "integrity": "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ==", + "version": "3.23.0", + "resolved": "https://registry.npmmirror.com/@shikijs/types/-/types-3.23.0.tgz", + "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2539,7 +2586,7 @@ }, "node_modules/@toml-tools/lexer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@toml-tools/lexer/-/lexer-1.0.1.tgz", + "resolved": "https://registry.npmmirror.com/@toml-tools/lexer/-/lexer-1.0.1.tgz", "integrity": "sha512-jn2fl8m/9QPcUD507Hbt2W3TVMKzF5HEY8xKIxqY2r2dTG2udeCKlo2ejJ5k/RSOJsWNIuw+Ir/nxW5PItUApA==", "license": "MIT", "dependencies": { @@ -2548,7 +2595,7 @@ }, "node_modules/@toml-tools/parser": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@toml-tools/parser/-/parser-1.0.1.tgz", + "resolved": "https://registry.npmmirror.com/@toml-tools/parser/-/parser-1.0.1.tgz", "integrity": "sha512-W+YdnB8KDgKjIqhoArEXjiTTPnKSXVvI/B+raHfou9+sip3rxhzVsELn46GG7dZyNHyu9pS+gYgYrdF9c5AQDg==", "license": "MIT", "dependencies": { @@ -2775,9 +2822,9 @@ "license": "MIT" }, "node_modules/@types/d3-shape": { - "version": "3.1.7", - "resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.7.tgz", - "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "version": "3.1.8", + "resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", "license": "MIT", "dependencies": { "@types/d3-path": "*" @@ -2827,6 +2874,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", @@ -2858,9 +2912,9 @@ "license": "MIT" }, "node_modules/@types/katex": { - "version": "0.16.7", - "resolved": "https://registry.npmmirror.com/@types/katex/-/katex-0.16.7.tgz", - "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", + "version": "0.16.8", + "resolved": "https://registry.npmmirror.com/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", "license": "MIT" }, "node_modules/@types/linkify-it": { @@ -2899,13 +2953,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.0.3", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.0.3.tgz", - "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", + "version": "25.5.0", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/trusted-types": { @@ -2930,17 +2984,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.52.0.tgz", - "integrity": "sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==", + "version": "8.57.2", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", + "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.52.0", - "@typescript-eslint/type-utils": "8.52.0", - "@typescript-eslint/utils": "8.52.0", - "@typescript-eslint/visitor-keys": "8.52.0", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/type-utils": "8.57.2", + "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -2953,14 +3007,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.52.0", - "eslint": "^8.57.0 || ^9.0.0", + "@typescript-eslint/parser": "^8.57.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", @@ -2969,16 +3023,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.52.0.tgz", - "integrity": "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==", + "version": "8.57.2", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.57.2.tgz", + "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.52.0", - "@typescript-eslint/types": "8.52.0", - "@typescript-eslint/typescript-estree": "8.52.0", - "@typescript-eslint/visitor-keys": "8.52.0", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3" }, "engines": { @@ -2989,19 +3043,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.52.0.tgz", - "integrity": "sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==", + "version": "8.57.2", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.52.0", - "@typescript-eslint/types": "^8.52.0", + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", "debug": "^4.4.3" }, "engines": { @@ -3016,14 +3070,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.52.0.tgz", - "integrity": "sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==", + "version": "8.57.2", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", + "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.52.0", - "@typescript-eslint/visitor-keys": "8.52.0" + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3034,9 +3088,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.52.0.tgz", - "integrity": "sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==", + "version": "8.57.2", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", "dev": true, "license": "MIT", "engines": { @@ -3051,15 +3105,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.52.0.tgz", - "integrity": "sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==", + "version": "8.57.2", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", + "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.52.0", - "@typescript-eslint/typescript-estree": "8.52.0", - "@typescript-eslint/utils": "8.52.0", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -3071,14 +3125,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.52.0.tgz", - "integrity": "sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==", + "version": "8.57.2", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.57.2.tgz", + "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", "dev": true, "license": "MIT", "engines": { @@ -3090,18 +3144,18 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.52.0.tgz", - "integrity": "sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==", + "version": "8.57.2", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.52.0", - "@typescript-eslint/tsconfig-utils": "8.52.0", - "@typescript-eslint/types": "8.52.0", - "@typescript-eslint/visitor-keys": "8.52.0", + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3", - "minimatch": "^9.0.5", + "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" @@ -3117,65 +3171,39 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/@typescript-eslint/utils": { + "version": "8.57.2", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.57.2.tgz", + "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.52.0.tgz", - "integrity": "sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.52.0", - "@typescript-eslint/types": "8.52.0", - "@typescript-eslint/typescript-estree": "8.52.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.52.0.tgz", - "integrity": "sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==", + "version": "8.57.2", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", + "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.52.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.57.2", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3192,49 +3220,59 @@ "dev": true, "license": "ISC" }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "license": "MIT", + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, "node_modules/@vitejs/plugin-vue": { - "version": "6.0.3", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.3.tgz", - "integrity": "sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==", + "version": "6.0.5", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz", + "integrity": "sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-beta.53" + "@rolldown/pluginutils": "1.0.0-rc.2" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "vue": "^3.2.25" } }, "node_modules/@vitest/expect": { - "version": "4.0.16", - "resolved": "https://registry.npmmirror.com/@vitest/expect/-/expect-4.0.16.tgz", - "integrity": "sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==", + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/@vitest/expect/-/expect-4.1.2.tgz", + "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.0.0", + "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.0.16", - "@vitest/utils": "4.0.16", - "chai": "^6.2.1", - "tinyrainbow": "^3.0.3" + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.0.16", - "resolved": "https://registry.npmmirror.com/@vitest/mocker/-/mocker-4.0.16.tgz", - "integrity": "sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==", + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/@vitest/mocker/-/mocker-4.1.2.tgz", + "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.0.16", + "@vitest/spy": "4.1.2", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -3243,7 +3281,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -3265,26 +3303,26 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.0.16", - "resolved": "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-4.0.16.tgz", - "integrity": "sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA==", + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-4.1.2.tgz", + "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.0.16", - "resolved": "https://registry.npmmirror.com/@vitest/runner/-/runner-4.0.16.tgz", - "integrity": "sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q==", + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/@vitest/runner/-/runner-4.1.2.tgz", + "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.0.16", + "@vitest/utils": "4.1.2", "pathe": "^2.0.3" }, "funding": { @@ -3292,13 +3330,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.0.16", - "resolved": "https://registry.npmmirror.com/@vitest/snapshot/-/snapshot-4.0.16.tgz", - "integrity": "sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA==", + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/@vitest/snapshot/-/snapshot-4.1.2.tgz", + "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.16", + "@vitest/pretty-format": "4.1.2", + "@vitest/utils": "4.1.2", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -3307,9 +3346,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.0.16", - "resolved": "https://registry.npmmirror.com/@vitest/spy/-/spy-4.0.16.tgz", - "integrity": "sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw==", + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/@vitest/spy/-/spy-4.1.2.tgz", + "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==", "dev": true, "license": "MIT", "funding": { @@ -3317,51 +3356,79 @@ } }, "node_modules/@vitest/utils": { - "version": "4.0.16", - "resolved": "https://registry.npmmirror.com/@vitest/utils/-/utils-4.0.16.tgz", - "integrity": "sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==", + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/@vitest/utils/-/utils-4.1.2.tgz", + "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.16", - "tinyrainbow": "^3.0.3" + "@vitest/pretty-format": "4.1.2", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@volar/language-core": { - "version": "2.4.27", - "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.27.tgz", - "integrity": "sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==", + "version": "2.4.28", + "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", "dev": true, "license": "MIT", "dependencies": { - "@volar/source-map": "2.4.27" + "@volar/source-map": "2.4.28" } }, "node_modules/@volar/source-map": { - "version": "2.4.27", - "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.27.tgz", - "integrity": "sha512-ynlcBReMgOZj2i6po+qVswtDUeeBRCTgDurjMGShbm8WYZgJ0PA4RmtebBJ0BCYol1qPv3GQF6jK7C9qoVc7lg==", + "version": "2.4.28", + "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", "dev": true, "license": "MIT" }, "node_modules/@volar/typescript": { - "version": "2.4.27", - "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.27.tgz", - "integrity": "sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg==", + "version": "2.4.28", + "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "2.4.27", + "@volar/language-core": "2.4.28", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, + "node_modules/@vue-macros/common": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@vue-macros/common/-/common-3.1.2.tgz", + "integrity": "sha512-h9t4ArDdniO9ekYHAD95t9AZcAbb19lEGK+26iAjUODOIJKmObDNBSe4+6ELQAA3vtYiFPPBtHh7+cQCKi3Dng==", + "license": "MIT", + "dependencies": { + "@vue/compiler-sfc": "^3.5.22", + "ast-kit": "^2.1.2", + "local-pkg": "^1.1.2", + "magic-string-ast": "^1.0.2", + "unplugin-utils": "^0.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/vue-macros" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.2.25" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, "node_modules/@vue/compiler-core": { "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.31.tgz", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.31.tgz", "integrity": "sha512-k/ueL14aNIEy5Onf0OVzR8kiqF/WThgLdFhxwa4e/KF/0qe38IwIdofoSWBTvvxQOesaz6riAFAUaYjoF9fLLQ==", "license": "MIT", "dependencies": { @@ -3374,7 +3441,7 @@ }, "node_modules/@vue/compiler-dom": { "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.31.tgz", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.31.tgz", "integrity": "sha512-BMY/ozS/xxjYqRFL+tKdRpATJYDTTgWSo0+AJvJNg4ig+Hgb0dOsHPXvloHQ5hmlivUqw1Yt2pPIqp4e0v1GUw==", "license": "MIT", "dependencies": { @@ -3384,7 +3451,7 @@ }, "node_modules/@vue/compiler-sfc": { "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.31.tgz", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.31.tgz", "integrity": "sha512-M8wpPgR9UJ8MiRGjppvx9uWJfLV7A/T+/rL8s/y3QG3u0c2/YZgff3d6SuimKRIhcYnWg5fTfDMlz2E6seUW8Q==", "license": "MIT", "dependencies": { @@ -3399,37 +3466,9 @@ "source-map-js": "^1.2.1" } }, - "node_modules/@vue/compiler-sfc/node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/@vue/compiler-ssr": { "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.31.tgz", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.31.tgz", "integrity": "sha512-h0xIMxrt/LHOvJKMri+vdYT92BrK3HFLtDqq9Pr/lVVfE4IyKZKvWf0vJFW10Yr6nX02OR4MkJwI0c1HDa1hog==", "license": "MIT", "dependencies": { @@ -3438,21 +3477,21 @@ } }, "node_modules/@vue/devtools-api": { - "version": "7.7.8", - "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.8.tgz", - "integrity": "sha512-BtFcAmDbtXGwurWUFf8ogIbgZyR+rcVES1TSNEI8Em80fD8Anu+qTRN1Fc3J6vdRHlVM3fzPV1qIo+B4AiqGzw==", + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", "license": "MIT", "dependencies": { - "@vue/devtools-kit": "^7.7.8" + "@vue/devtools-kit": "^7.7.9" } }, "node_modules/@vue/devtools-kit": { - "version": "7.7.8", - "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.8.tgz", - "integrity": "sha512-4Y8op+AoxOJhB9fpcEF6d5vcJXWKgHxC3B0ytUB8zz15KbP9g9WgVzral05xluxi2fOeAy6t140rdQ943GcLRQ==", + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", "license": "MIT", "dependencies": { - "@vue/devtools-shared": "^7.7.8", + "@vue/devtools-shared": "^7.7.9", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", @@ -3462,22 +3501,22 @@ } }, "node_modules/@vue/devtools-shared": { - "version": "7.7.8", - "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.8.tgz", - "integrity": "sha512-XHpO3jC5nOgYr40M9p8Z4mmKfTvUxKyRcUnpBAYg11pE78eaRFBKb0kG5yKLroMuJeeNH9LWmKp2zMU5LUc7CA==", + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", "license": "MIT", "dependencies": { "rfdc": "^1.4.1" } }, "node_modules/@vue/language-core": { - "version": "3.2.1", - "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-3.2.1.tgz", - "integrity": "sha512-g6oSenpnGMtpxHGAwKuu7HJJkNZpemK/zg3vZzZbJ6cnnXq1ssxuNrXSsAHYM3NvH8p4IkTw+NLmuxyeYz4r8A==", + "version": "3.2.6", + "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-3.2.6.tgz", + "integrity": "sha512-xYYYX3/aVup576tP/23sEUpgiEnujrENaoNRbaozC1/MA9I6EGFQRJb4xrt/MmUCAGlxTKL2RmT8JLTPqagCkg==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "2.4.27", + "@volar/language-core": "2.4.28", "@vue/compiler-dom": "^3.5.0", "@vue/shared": "^3.5.0", "alien-signals": "^3.0.0", @@ -3486,22 +3525,9 @@ "picomatch": "^4.0.2" } }, - "node_modules/@vue/language-core/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@vue/reactivity": { "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.31.tgz", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.31.tgz", "integrity": "sha512-DtKXxk9E/KuVvt8VxWu+6Luc9I9ETNcqR1T1oW1gf02nXaZ1kuAx58oVu7uX9XxJR0iJCro6fqBLw9oSBELo5g==", "license": "MIT", "dependencies": { @@ -3510,7 +3536,7 @@ }, "node_modules/@vue/runtime-core": { "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.31.tgz", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.31.tgz", "integrity": "sha512-AZPmIHXEAyhpkmN7aWlqjSfYynmkWlluDNPHMCZKFHH+lLtxP/30UJmoVhXmbDoP1Ng0jG0fyY2zCj1PnSSA6Q==", "license": "MIT", "dependencies": { @@ -3520,7 +3546,7 @@ }, "node_modules/@vue/runtime-dom": { "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.31.tgz", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.31.tgz", "integrity": "sha512-xQJsNRmGPeDCJq/u813tyonNgWBFjzfVkBwDREdEWndBnGdHLHgkwNBQxLtg4zDrzKTEcnikUy1UUNecb3lJ6g==", "license": "MIT", "dependencies": { @@ -3532,7 +3558,7 @@ }, "node_modules/@vue/server-renderer": { "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.31.tgz", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.31.tgz", "integrity": "sha512-GJuwRvMcdZX/CriUnyIIOGkx3rMV3H6sOu0JhdKbduaeCji6zb60iOGMY7tFoN24NfsUYoFBhshZtGxGpxO4iA==", "license": "MIT", "dependencies": { @@ -3545,20 +3571,20 @@ }, "node_modules/@vue/shared": { "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.31.tgz", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.31.tgz", "integrity": "sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw==", "license": "MIT" }, "node_modules/@vueuse/core": { - "version": "13.9.0", - "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-13.9.0.tgz", - "integrity": "sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==", + "version": "14.2.1", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-14.2.1.tgz", + "integrity": "sha512-3vwDzV+GDUNpdegRY6kzpLm4Igptq+GA0QkJ3W61Iv27YWwW/ufSlOfgQIpN6FZRMG0mkaz4gglJRtq5SeJyIQ==", "dev": true, "license": "MIT", "dependencies": { "@types/web-bluetooth": "^0.0.21", - "@vueuse/metadata": "13.9.0", - "@vueuse/shared": "13.9.0" + "@vueuse/metadata": "14.2.1", + "@vueuse/shared": "14.2.1" }, "funding": { "url": "https://github.com/sponsors/antfu" @@ -3568,14 +3594,14 @@ } }, "node_modules/@vueuse/integrations": { - "version": "13.9.0", - "resolved": "https://registry.npmmirror.com/@vueuse/integrations/-/integrations-13.9.0.tgz", - "integrity": "sha512-SDobKBbPIOe0cVL7QxMzGkuUGHvWTdihi9zOrrWaWUgFKe15cwEcwfWmgrcNzjT6kHnNmWuTajPHoIzUjYNYYQ==", + "version": "14.2.1", + "resolved": "https://registry.npmmirror.com/@vueuse/integrations/-/integrations-14.2.1.tgz", + "integrity": "sha512-2LIUpBi/67PoXJGqSDQUF0pgQWpNHh7beiA+KG2AbybcNm+pTGWT6oPGlBgUoDWmYwfeQqM/uzOHqcILpKL7nA==", "dev": true, "license": "MIT", "dependencies": { - "@vueuse/core": "13.9.0", - "@vueuse/shared": "13.9.0" + "@vueuse/core": "14.2.1", + "@vueuse/shared": "14.2.1" }, "funding": { "url": "https://github.com/sponsors/antfu" @@ -3585,7 +3611,7 @@ "axios": "^1", "change-case": "^5", "drauu": "^0.4", - "focus-trap": "^7", + "focus-trap": "^7 || ^8", "fuse.js": "^7", "idb-keyval": "^6", "jwt-decode": "^4", @@ -3635,9 +3661,9 @@ } }, "node_modules/@vueuse/metadata": { - "version": "13.9.0", - "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-13.9.0.tgz", - "integrity": "sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==", + "version": "14.2.1", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-14.2.1.tgz", + "integrity": "sha512-1ButlVtj5Sb/HDtIy1HFr1VqCP4G6Ypqt5MAo0lCgjokrk2mvQKsK2uuy0vqu/Ks+sHfuHo0B9Y9jn9xKdjZsw==", "dev": true, "license": "MIT", "funding": { @@ -3645,9 +3671,9 @@ } }, "node_modules/@vueuse/shared": { - "version": "13.9.0", - "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-13.9.0.tgz", - "integrity": "sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==", + "version": "14.2.1", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-14.2.1.tgz", + "integrity": "sha512-shTJncjV9JTI4oVNyF1FQonetYAiTBd+Qj7cY89SWbXSkx7gyhrgtEdF2ZAVWS1S3SHlaROO6F2IesJxQEkZBw==", "dev": true, "license": "MIT", "funding": { @@ -3658,9 +3684,9 @@ } }, "node_modules/@wailsio/runtime": { - "version": "3.0.0-alpha.77", - "resolved": "https://registry.npmmirror.com/@wailsio/runtime/-/runtime-3.0.0-alpha.77.tgz", - "integrity": "sha512-DMWjT8VFCk8O818mnw2dbrgZilOf1TzmGGp5lemZyGej7g+SSqAhMFOHp9eCiGQ32EbxmGOdTO4aNZVA00j9Nw==", + "version": "3.0.0-alpha.79", + "resolved": "https://registry.npmmirror.com/@wailsio/runtime/-/runtime-3.0.0-alpha.79.tgz", + "integrity": "sha512-NITzxKmJsMEruc39L166lbPJVECxzcbdqpHVqOOF7Cu/7Zqk/e3B/gNpkUjhNyo5rVb3V1wpS8oEgLUmpu1cwA==", "dev": true, "license": "MIT" }, @@ -3683,15 +3709,18 @@ } }, "node_modules/@zumer/snapdom": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/@zumer/snapdom/-/snapdom-2.0.1.tgz", - "integrity": "sha512-78/qbYl2FTv4H6qaXcNfAujfIOSzdvs83NW63VbyC9QA3sqNPfPvhn4xYMO6Gy11hXwJUEhd0z65yKiNzDwy9w==", - "license": "MIT" + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/@zumer/snapdom/-/snapdom-2.7.0.tgz", + "integrity": "sha512-ZiELKzDszeFOazPQ/ExXzgtdoW9jADVjDjInr5XDAlVdCx0RbNsFiG7RLyM48XnA7EyCA9yTvmXSc3ElDrTRqA==", + "license": "MIT", + "workspaces": [ + "packages/*" + ] }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3710,10 +3739,20 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -3728,35 +3767,12 @@ } }, "node_modules/alien-signals": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-3.1.1.tgz", - "integrity": "sha512-ogkIWbVrLwKtHY6oOAXaYkAxP+cTH7V5FZ5+Tm4NZFd8VDZ6uNMDrfzqctTZ42eTMCSR3ne3otpcxmqSnFfPYA==", + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-3.1.2.tgz", + "integrity": "sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==", "dev": true, "license": "MIT" }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, "node_modules/asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmmirror.com/asn1.js/-/asn1.js-4.10.1.tgz", @@ -3770,9 +3786,9 @@ } }, "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "dev": true, "license": "MIT" }, @@ -3800,6 +3816,38 @@ "node": ">=12" } }, + "node_modules/ast-kit": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/ast-kit/-/ast-kit-2.2.0.tgz", + "integrity": "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-walker-scope": { + "version": "0.8.3", + "resolved": "https://registry.npmmirror.com/ast-walker-scope/-/ast-walker-scope-0.8.3.tgz", + "integrity": "sha512-cbdCP0PGOBq0ASG+sjnKIoYkWMKhhz+F/h9pRexUdX2Hd38+WOlBkRKlqkGOSm0YQpcFMQBJeK4WspUAkwsEdg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.4", + "ast-kit": "^2.1.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -3817,17 +3865,20 @@ } }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -3845,18 +3896,30 @@ "license": "MIT" }, "node_modules/birpc": { - "version": "2.6.1", - "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.6.1.tgz", - "integrity": "sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==", + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/bn.js": { - "version": "5.2.2", - "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-5.2.2.tgz", - "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "version": "5.2.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-5.2.3.tgz", + "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==", "dev": true, "license": "MIT" }, @@ -3868,27 +3931,16 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "5.0.5", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "optional": true, - "dependencies": { - "fill-range": "^7.1.1" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=8" + "node": "18 || 20 || >=22" } }, "node_modules/brorand": { @@ -3964,25 +4016,24 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/browserify-sign/-/browserify-sign-4.2.3.tgz", - "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "version": "4.2.5", + "resolved": "https://registry.npmmirror.com/browserify-sign/-/browserify-sign-4.2.5.tgz", + "integrity": "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==", "dev": true, "license": "ISC", "dependencies": { - "bn.js": "^5.2.1", - "browserify-rsa": "^4.1.0", + "bn.js": "^5.2.2", + "browserify-rsa": "^4.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.5", - "hash-base": "~3.0", + "elliptic": "^6.6.1", "inherits": "^2.0.4", - "parse-asn1": "^5.1.7", + "parse-asn1": "^5.1.9", "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1" }, "engines": { - "node": ">= 0.12" + "node": ">= 0.10" } }, "node_modules/browserify-sign/node_modules/isarray": { @@ -4015,23 +4066,6 @@ "dev": true, "license": "MIT" }, - "node_modules/browserify-sign/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, "node_modules/browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmmirror.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz", @@ -4046,7 +4080,7 @@ "version": "5.7.1", "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -4131,14 +4165,19 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, + "node_modules/canvas": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/canvas/-/canvas-3.2.3.tgz", + "integrity": "sha512-PzE5nJZPz72YUAfo8oTp0u3fqqY7IzlTubneAihqDYAUcBk7ryeCmBbdJBEdaH0bptSOe2VT2Zwcb3UaFyaSWw==", + "hasInstallScript": true, "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.3" + }, "engines": { - "node": ">=6" + "node": "^18.12.0 || >= 20.9.0" } }, "node_modules/ccount": { @@ -4153,32 +4192,15 @@ } }, "node_modules/chai": { - "version": "6.2.1", - "resolved": "https://registry.npmmirror.com/chai/-/chai-6.2.1.tgz", - "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/character-entities-html4": { "version": "2.1.0", "resolved": "https://registry.npmmirror.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz", @@ -4202,17 +4224,17 @@ } }, "node_modules/chevrotain": { - "version": "11.0.3", - "resolved": "https://registry.npmmirror.com/chevrotain/-/chevrotain-11.0.3.tgz", - "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "version": "11.2.0", + "resolved": "https://registry.npmmirror.com/chevrotain/-/chevrotain-11.2.0.tgz", + "integrity": "sha512-mHCHTxM51nCklUw9RzRVc0DLjAh/SAUPM4k/zMInlTIo25ldWXOZoPt7XEIk/LwoT4lFVmJcu9g5MHtx371x3A==", "license": "Apache-2.0", "dependencies": { - "@chevrotain/cst-dts-gen": "11.0.3", - "@chevrotain/gast": "11.0.3", - "@chevrotain/regexp-to-ast": "11.0.3", - "@chevrotain/types": "11.0.3", - "@chevrotain/utils": "11.0.3", - "lodash-es": "4.17.21" + "@chevrotain/cst-dts-gen": "11.2.0", + "@chevrotain/gast": "11.2.0", + "@chevrotain/regexp-to-ast": "11.2.0", + "@chevrotain/types": "11.2.0", + "@chevrotain/utils": "11.2.0", + "lodash-es": "4.17.23" } }, "node_modules/chevrotain-allstar": { @@ -4242,15 +4264,23 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC", + "optional": true + }, "node_modules/cipher-base": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/cipher-base/-/cipher-base-1.0.6.tgz", - "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/cipher-base/-/cipher-base-1.0.7.tgz", + "integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==", "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.4", - "safe-buffer": "^5.2.1" + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.2" }, "engines": { "node": ">= 0.10" @@ -4272,38 +4302,19 @@ } }, "node_modules/codemirror-lang-elixir": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/codemirror-lang-elixir/-/codemirror-lang-elixir-4.0.0.tgz", - "integrity": "sha512-mzFesxo/t6KOxwnkqVd34R/q7yk+sMtHh6vUKGAvjwHmpL7bERHB+vQAsmU/nqrndkwVeJEHWGw/z/ybfdiudA==", + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/codemirror-lang-elixir/-/codemirror-lang-elixir-4.0.1.tgz", + "integrity": "sha512-z6W/XB4b7TZrp9EZYBGVq93vQfvKbff+1iM8YZaVErL0dguBAeLmVRlEv1NuDZHOP1qjJ3NwyibkUkNWn7q9VQ==", + "license": "Apache-2.0", "dependencies": { "@codemirror/language": "^6.0.0", "lezer-elixir": "^1.0.0" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/colors-named": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/colors-named/-/colors-named-1.0.4.tgz", - "integrity": "sha512-R254qrKSxFJNa7QmM7vrRaz5Hygr7MIaNbXcIx7WfmlYJ9OjZQ+aczGlnKS8lLtNT0GM9aGZ4EcmNXrh5ttv6g==", + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/colors-named/-/colors-named-1.0.5.tgz", + "integrity": "sha512-xaspf9oddAOqP2LYNOgp8E3BwAzugrdO9J1kDNS5ySrzTgV9hrXGBt5w87ioLEr2pM4Ukt+GKedvzaLRxpv8pA==", "license": "MIT", "engines": { "node": ">=14.16" @@ -4313,9 +4324,9 @@ } }, "node_modules/colors-named-hex": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/colors-named-hex/-/colors-named-hex-1.0.3.tgz", - "integrity": "sha512-vhUoMdCdOKgD9Ni3p6uV3ET1JJCHzlcK6lN3/yl+6TUHinDE6HUFlmnvkh/NDZ2M9049Ipn3mX85qu6akRiC1g==", + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/colors-named-hex/-/colors-named-hex-1.0.4.tgz", + "integrity": "sha512-X+Enw/2fFAgDRhUac69cRO/RJvHnWDBBrP8J1sJuEU16Buiiu8PPpJP4abSo0V+fJbkfwmQITE6zKx/SBJERGw==", "license": "MIT", "engines": { "node": ">=14.16" @@ -4344,17 +4355,10 @@ "node": ">= 12" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, "node_modules/confbox": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz", - "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", "license": "MIT" }, "node_modules/console-browserify": { @@ -4370,16 +4374,23 @@ "dev": true, "license": "MIT" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/copy-anything": { - "version": "3.0.5", - "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz", - "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", "license": "MIT", "dependencies": { - "is-what": "^4.1.8" + "is-what": "^5.2.0" }, "engines": { - "node": ">=12.13" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/mesqueeb" @@ -4413,9 +4424,9 @@ } }, "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "dev": true, "license": "MIT" }, @@ -4534,9 +4545,23 @@ "node": ">=4" } }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/csstype": { "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, @@ -4803,9 +4828,9 @@ } }, "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", "license": "ISC", "engines": { "node": ">=12" @@ -4914,12 +4939,6 @@ "d3-path": "1" } }, - "node_modules/d3-sankey/node_modules/internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", - "license": "ISC" - }, "node_modules/d3-scale": { "version": "4.0.2", "resolved": "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz", @@ -5039,25 +5058,40 @@ } }, "node_modules/dagre-d3-es": { - "version": "7.0.13", - "resolved": "https://registry.npmmirror.com/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz", - "integrity": "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==", + "version": "7.0.14", + "resolved": "https://registry.npmmirror.com/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", "license": "MIT", "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "license": "MIT", + "optional": true, + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/dayjs": { - "version": "1.11.19", - "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz", - "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "version": "1.11.20", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "devOptional": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -5071,9 +5105,42 @@ } } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT", + "optional": true + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" @@ -5121,9 +5188,9 @@ "license": "MIT" }, "node_modules/delaunator": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/delaunator/-/delaunator-5.0.1.tgz", - "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", "license": "ISC", "dependencies": { "robust-predicates": "^3.0.2" @@ -5151,16 +5218,13 @@ } }, "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", "optional": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, "engines": { - "node": ">=0.10" + "node": ">=8" } }, "node_modules/devlop": { @@ -5190,9 +5254,9 @@ } }, "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "dev": true, "license": "MIT" }, @@ -5211,7 +5275,7 @@ }, "node_modules/dompurify": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", + "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.3.3.tgz", "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { @@ -5250,15 +5314,25 @@ } }, "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "dev": true, "license": "MIT" }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/entities": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "license": "BSD-2-Clause", "engines": { @@ -5289,9 +5363,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, "license": "MIT" }, @@ -5309,9 +5383,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5322,31 +5396,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" } }, "node_modules/escape-string-regexp": { @@ -5363,33 +5438,30 @@ } }, "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-10.1.0.tgz", + "integrity": "sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", - "@eslint/plugin-kit": "^0.4.1", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.3", + "@eslint/config-helpers": "^0.5.3", + "@eslint/core": "^1.1.1", + "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", + "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", @@ -5399,8 +5471,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -5408,7 +5479,7 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" @@ -5423,9 +5494,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "10.6.2", - "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-10.6.2.tgz", - "integrity": "sha512-nA5yUs/B1KmKzvC42fyD0+l9Yd+LtEpVhWRbXuDj0e+ZURcTtyRbMDWUeJmTAh2wC6jC83raS63anNM2YT3NPw==", + "version": "10.8.0", + "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-10.8.0.tgz", + "integrity": "sha512-f1J/tcbnrpgC8suPN5AtdJ5MQjuXbSU9pGRSSYAuF3SHoiYCOdEX6O22pLaRyLHXvDcOe+O5ENgc1owQ587agA==", "dev": true, "license": "MIT", "dependencies": { @@ -5442,7 +5513,7 @@ "peerDependencies": { "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "vue-eslint-parser": "^10.0.0" }, "peerDependenciesMeta": { @@ -5455,57 +5526,59 @@ } }, "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "version": "9.1.2", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "version": "11.2.0", + "resolved": "https://registry.npmmirror.com/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5575,10 +5648,20 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/expect-type": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/expect-type/-/expect-type-1.2.2.tgz", - "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5586,11 +5669,24 @@ } }, "node_modules/exsolve": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.7.tgz", - "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", "license": "MIT" }, + "node_modules/fabric": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/fabric/-/fabric-7.2.0.tgz", + "integrity": "sha512-XSYmSqSMrlbCg+/j7/uU/PFeZuA5hHRDp7sGbDlMvz/T6BHt2MQSOYtz/AIdr+kmReA1s5jTzHJ8AjHwYUcmfQ==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "optionalDependencies": { + "canvas": "^3.2.0", + "jsdom": "^26.1.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5612,6 +5708,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5625,19 +5738,6 @@ "node": ">=16.0.0" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "optional": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", @@ -5671,19 +5771,19 @@ }, "node_modules/flatted": { "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.4.2.tgz", "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, "node_modules/focus-trap": { - "version": "7.6.6", - "resolved": "https://registry.npmmirror.com/focus-trap/-/focus-trap-7.6.6.tgz", - "integrity": "sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==", + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/focus-trap/-/focus-trap-8.0.1.tgz", + "integrity": "sha512-9ptSG6z51YQOstI/oN4XuVGP/03u2nh0g//qz7L6zX0i6PZiPnkcf3GenXq7N2hZnASXaMxTPpbKwdI+PFvxlw==", "dev": true, "license": "MIT", "dependencies": { - "tabbable": "^6.3.0" + "tabbable": "^6.4.0" } }, "node_modules/for-each": { @@ -5702,6 +5802,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT", + "optional": true + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", @@ -5727,6 +5834,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -5766,6 +5883,13 @@ "node": ">= 0.4" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT", + "optional": true + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", @@ -5780,9 +5904,9 @@ } }, "node_modules/globals": { - "version": "16.5.0", - "resolved": "https://registry.npmmirror.com/globals/-/globals-16.5.0.tgz", - "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "version": "17.4.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", "dev": true, "license": "MIT", "engines": { @@ -5817,16 +5941,6 @@ "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", "license": "MIT" }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -5975,6 +6089,19 @@ "url": "https://jaywcjlove.github.io/#/sponsor" } }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-void-elements": { "version": "3.0.0", "resolved": "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz", @@ -5986,6 +6113,20 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/https-browserify/-/https-browserify-1.0.0.tgz", @@ -5993,6 +6134,20 @@ "dev": true, "license": "MIT" }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -6009,7 +6164,7 @@ "version": "1.2.1", "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -6038,27 +6193,10 @@ }, "node_modules/immutable": { "version": "5.1.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", + "resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.5.tgz", "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", "license": "MIT" }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6073,17 +6211,21 @@ "version": "2.0.4", "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC", - "engines": { - "node": ">=12" - } + "optional": true + }, + "node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" }, "node_modules/is-arguments": { "version": "1.2.0", @@ -6142,14 +6284,15 @@ } }, "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" }, @@ -6190,15 +6333,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.12.0" - } + "optional": true }, "node_modules/is-regex": { "version": "1.2.1", @@ -6236,12 +6376,12 @@ } }, "node_modules/is-what": { - "version": "4.1.16", - "resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz", - "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", "license": "MIT", "engines": { - "node": ">=12.13" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/mesqueeb" @@ -6271,28 +6411,66 @@ "node": ">=10" } }, - "node_modules/java-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/java-parser/-/java-parser-3.0.1.tgz", - "integrity": "sha512-sDIR7u9b7O2JViNUxiZRhnRz7URII/eE7g2B+BmGxDeS6Ex3OYAcCyz5oh0H4LQ+hL/BS8OJTz8apMy9xtGmrQ==", - "license": "Apache-2.0", + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmmirror.com/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "license": "MIT", + "optional": true, "dependencies": { - "chevrotain": "11.0.3", - "chevrotain-allstar": "0.3.1", - "lodash": "4.17.21" + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, + "node_modules/jsdom/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, "bin": { - "js-yaml": "bin/js-yaml.js" + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" } }, "node_modules/json-buffer": { @@ -6316,10 +6494,22 @@ "dev": true, "license": "MIT" }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/katex": { - "version": "0.16.27", - "resolved": "https://registry.npmmirror.com/katex/-/katex-0.16.27.tgz", - "integrity": "sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==", + "version": "0.16.44", + "resolved": "https://registry.npmmirror.com/katex/-/katex-0.16.44.tgz", + "integrity": "sha512-EkxoDTk8ufHqHlf9QxGwcxeLkWRR3iOuYfRpfORgYfqc8s13bgb+YtRY59NK5ZpRaCwq1kqA6a5lpX8C/eLphQ==", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" @@ -6347,33 +6537,75 @@ "resolved": "https://registry.npmmirror.com/khroma/-/khroma-2.1.0.tgz", "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" }, - "node_modules/kolorist": { - "version": "1.8.0", - "resolved": "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz", - "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", - "license": "MIT" - }, "node_modules/langium": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/langium/-/langium-3.3.1.tgz", - "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/langium/-/langium-4.2.1.tgz", + "integrity": "sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ==", "license": "MIT", "dependencies": { - "chevrotain": "~11.0.3", - "chevrotain-allstar": "~0.3.0", + "chevrotain": "~11.1.1", + "chevrotain-allstar": "~0.3.1", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", - "vscode-uri": "~3.0.8" + "vscode-uri": "~3.1.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=20.10.0", + "npm": ">=10.2.3" } }, - "node_modules/langium/node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", - "license": "MIT" + "node_modules/langium/node_modules/@chevrotain/cst-dts-gen": { + "version": "11.1.2", + "resolved": "https://registry.npmmirror.com/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.1.2.tgz", + "integrity": "sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "11.1.2", + "@chevrotain/types": "11.1.2", + "lodash-es": "4.17.23" + } + }, + "node_modules/langium/node_modules/@chevrotain/gast": { + "version": "11.1.2", + "resolved": "https://registry.npmmirror.com/@chevrotain/gast/-/gast-11.1.2.tgz", + "integrity": "sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "11.1.2", + "lodash-es": "4.17.23" + } + }, + "node_modules/langium/node_modules/@chevrotain/regexp-to-ast": { + "version": "11.1.2", + "resolved": "https://registry.npmmirror.com/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.1.2.tgz", + "integrity": "sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw==", + "license": "Apache-2.0" + }, + "node_modules/langium/node_modules/@chevrotain/types": { + "version": "11.1.2", + "resolved": "https://registry.npmmirror.com/@chevrotain/types/-/types-11.1.2.tgz", + "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", + "license": "Apache-2.0" + }, + "node_modules/langium/node_modules/@chevrotain/utils": { + "version": "11.1.2", + "resolved": "https://registry.npmmirror.com/@chevrotain/utils/-/utils-11.1.2.tgz", + "integrity": "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==", + "license": "Apache-2.0" + }, + "node_modules/langium/node_modules/chevrotain": { + "version": "11.1.2", + "resolved": "https://registry.npmmirror.com/chevrotain/-/chevrotain-11.1.2.tgz", + "integrity": "sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.1.2", + "@chevrotain/gast": "11.1.2", + "@chevrotain/regexp-to-ast": "11.1.2", + "@chevrotain/types": "11.1.2", + "@chevrotain/utils": "11.1.2", + "lodash-es": "4.17.23" + } }, "node_modules/layout-base": { "version": "1.0.2", @@ -6396,9 +6628,10 @@ } }, "node_modules/lezer-elixir": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/lezer-elixir/-/lezer-elixir-1.1.2.tgz", - "integrity": "sha512-K3yPMJcNhqCL6ugr5NkgOC1g37rcOM38XZezO9lBXy0LwWFd8zdWXfmRbY829vZVk0OGCQoI02yDWp9FF2OWZA==", + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/lezer-elixir/-/lezer-elixir-1.1.3.tgz", + "integrity": "sha512-Ymc58/WhxdZS9yEOlnKbF3rdeBdFcPm4OEm26KMqA1Za9vztXi7I5qwGw1KxYmm3Nv0iDHq//EQyBwSEzKG9Mg==", + "license": "Apache-2.0", "dependencies": { "@lezer/highlight": "^1.2.0", "@lezer/lr": "^1.3.0" @@ -6406,7 +6639,7 @@ }, "node_modules/linguist-languages": { "version": "9.3.1", - "resolved": "https://registry.npmjs.org/linguist-languages/-/linguist-languages-9.3.1.tgz", + "resolved": "https://registry.npmmirror.com/linguist-languages/-/linguist-languages-9.3.1.tgz", "integrity": "sha512-Mum2sqg3MyhgKfpulFhKZMAK/1VnV6m9vCV8YQCSqWs+pbKouKn9EqRshZjVWUaJjl6NTTDcYJk/1+C02siXEQ==", "license": "MIT" }, @@ -6427,6 +6660,23 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/local-pkg/node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "license": "MIT" + }, + "node_modules/local-pkg/node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", @@ -6443,24 +6693,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "version": "4.17.23", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", "license": "MIT" }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC", + "optional": true }, "node_modules/magic-string": { "version": "0.30.21", @@ -6471,6 +6715,21 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magic-string-ast": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/magic-string-ast/-/magic-string-ast-1.0.3.tgz", + "integrity": "sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==", + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.19" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, "node_modules/mark.js": { "version": "8.11.1", "resolved": "https://registry.npmmirror.com/mark.js/-/mark.js-8.11.1.tgz", @@ -6479,9 +6738,9 @@ "license": "MIT" }, "node_modules/marked": { - "version": "17.0.1", - "resolved": "https://registry.npmmirror.com/marked/-/marked-17.0.1.tgz", - "integrity": "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==", + "version": "17.0.5", + "resolved": "https://registry.npmmirror.com/marked/-/marked-17.0.5.tgz", + "integrity": "sha512-6hLvc0/JEbRjRgzI6wnT2P1XuM1/RrrDEX0kPt0N7jGm1133g6X7DlxFasUIx+72aKAr904GTxhSLDrd5DIlZg==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -6514,7 +6773,7 @@ }, "node_modules/mdast-util-to-hast": { "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", "dev": true, "license": "MIT", @@ -6535,27 +6794,28 @@ } }, "node_modules/mermaid": { - "version": "11.12.2", - "resolved": "https://registry.npmmirror.com/mermaid/-/mermaid-11.12.2.tgz", - "integrity": "sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==", + "version": "11.13.0", + "resolved": "https://registry.npmmirror.com/mermaid/-/mermaid-11.13.0.tgz", + "integrity": "sha512-fEnci+Immw6lKMFI8sqzjlATTyjLkRa6axrEgLV2yHTfv8r+h1wjFbV6xeRtd4rUV1cS4EpR9rwp3Rci7TRWDw==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.1.1", - "@iconify/utils": "^3.0.1", - "@mermaid-js/parser": "^0.6.3", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^1.0.1", "@types/d3": "^7.4.3", - "cytoscape": "^3.29.3", + "@upsetjs/venn.js": "^2.0.0", + "cytoscape": "^3.33.1", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", - "dagre-d3-es": "7.0.13", - "dayjs": "^1.11.18", - "dompurify": "^3.2.5", - "katex": "^0.16.22", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.19", + "dompurify": "^3.3.1", + "katex": "^0.16.25", "khroma": "^2.1.0", - "lodash-es": "^4.17.21", - "marked": "^16.2.1", + "lodash-es": "^4.17.23", + "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", @@ -6668,20 +6928,6 @@ ], "license": "MIT" }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "optional": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmmirror.com/miller-rabin/-/miller-rabin-4.0.1.tgz", @@ -6697,12 +6943,25 @@ } }, "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "dev": true, "license": "MIT" }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -6718,16 +6977,29 @@ "license": "MIT" }, "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "version": "10.2.4", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.2" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minisearch": { @@ -6743,46 +7015,36 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "license": "MIT" }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT", + "optional": true + }, "node_modules/mlly": { - "version": "1.8.0", - "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.0.tgz", - "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "version": "1.8.2", + "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", "license": "MIT", "dependencies": { - "acorn": "^8.15.0", + "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", - "ufo": "^1.6.1" - } - }, - "node_modules/mlly/node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "license": "MIT" - }, - "node_modules/mlly/node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" + "ufo": "^1.6.3" } }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "devOptional": true, "license": "MIT" }, "node_modules/muggle-string": { "version": "0.4.1", "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz", "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -6803,6 +7065,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT", + "optional": true + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", @@ -6810,6 +7079,19 @@ "dev": true, "license": "MIT" }, + "node_modules/node-abi": { + "version": "3.89.0", + "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.89.0.tgz", + "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==", + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz", @@ -6876,6 +7158,13 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nwsapi": { + "version": "2.2.23", + "resolved": "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", + "license": "MIT", + "optional": true + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", @@ -6948,6 +7237,16 @@ ], "license": "MIT" }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "optional": true, + "dependencies": { + "wrappy": "1" + } + }, "node_modules/oniguruma-parser": { "version": "0.12.1", "resolved": "https://registry.npmmirror.com/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", @@ -6956,14 +7255,14 @@ "license": "MIT" }, "node_modules/oniguruma-to-es": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz", - "integrity": "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==", + "version": "4.3.5", + "resolved": "https://registry.npmmirror.com/oniguruma-to-es/-/oniguruma-to-es-4.3.5.tgz", + "integrity": "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==", "dev": true, "license": "MIT", "dependencies": { "oniguruma-parser": "^0.12.1", - "regex": "^6.0.1", + "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, @@ -7025,9 +7324,9 @@ } }, "node_modules/package-manager-detector": { - "version": "1.5.0", - "resolved": "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.5.0.tgz", - "integrity": "sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==", + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", "license": "MIT" }, "node_modules/pako": { @@ -7037,37 +7336,49 @@ "dev": true, "license": "(MIT AND Zlib)" }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/parse-asn1": { - "version": "5.1.7", - "resolved": "https://registry.npmmirror.com/parse-asn1/-/parse-asn1-5.1.7.tgz", - "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "version": "5.1.9", + "resolved": "https://registry.npmmirror.com/parse-asn1/-/parse-asn1-5.1.9.tgz", + "integrity": "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==", "dev": true, "license": "ISC", "dependencies": { "asn1.js": "^4.10.1", "browserify-aes": "^1.2.0", "evp_bytestokey": "^1.0.3", - "hash-base": "~3.0", - "pbkdf2": "^3.1.2", + "pbkdf2": "^3.1.5", "safe-buffer": "^5.2.1" }, "engines": { "node": ">= 0.10" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "optional": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", @@ -7115,55 +7426,21 @@ "license": "MIT" }, "node_modules/pbkdf2": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/pbkdf2/-/pbkdf2-3.1.3.tgz", - "integrity": "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==", + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/pbkdf2/-/pbkdf2-3.1.5.tgz", + "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==", "dev": true, "license": "MIT", "dependencies": { - "create-hash": "~1.1.3", + "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "ripemd160": "=2.0.1", + "ripemd160": "^2.0.3", "safe-buffer": "^5.2.1", - "sha.js": "^2.4.11", - "to-buffer": "^1.2.0" + "sha.js": "^2.4.12", + "to-buffer": "^1.2.1" }, "engines": { - "node": ">=0.12" - } - }, - "node_modules/pbkdf2/node_modules/create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "sha.js": "^2.4.0" - } - }, - "node_modules/pbkdf2/node_modules/hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1" - } - }, - "node_modules/pbkdf2/node_modules/ripemd160": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hash-base": "^2.0.0", - "inherits": "^2.0.1" + "node": ">= 0.10" } }, "node_modules/perfect-debounce": { @@ -7173,9 +7450,9 @@ "license": "MIT" }, "node_modules/php-parser": { - "version": "3.2.5", - "resolved": "https://registry.npmmirror.com/php-parser/-/php-parser-3.2.5.tgz", - "integrity": "sha512-M1ZYlALFFnESbSdmRtTQrBFUHSriHgPhgqtTF/LCbZM4h7swR5PHtUceB2Kzby5CfqcsYwBn7OXTJ0+8Sajwkw==", + "version": "3.5.0", + "resolved": "https://registry.npmmirror.com/php-parser/-/php-parser-3.5.0.tgz", + "integrity": "sha512-EHdzSckQNP86jQRCEsMYhs+YzS4BfvfxnyhvzHVhVRoRUGEMFi8f3xKfuS9xdChBazZSyvb10SZbqhYQLGBcQg==", "license": "BSD-3-Clause" }, "node_modules/picocolors": { @@ -7185,13 +7462,12 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", - "optional": true, "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -7257,14 +7533,14 @@ } }, "node_modules/pkg-types": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz", - "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", "license": "MIT", "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" } }, "node_modules/points-on-curve": { @@ -7294,10 +7570,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, + "version": "8.5.8", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", "funding": [ { "type": "opencollective", @@ -7323,9 +7598,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", "dependencies": { @@ -7336,6 +7611,34 @@ "node": ">=4" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7348,7 +7651,7 @@ }, "node_modules/prettier": { "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.8.1.tgz", "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "license": "MIT", "bin": { @@ -7405,26 +7708,37 @@ } }, "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "dev": true, "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.0", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -7483,11 +7797,27 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -7512,9 +7842,9 @@ } }, "node_modules/regex": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/regex/-/regex-6.0.1.tgz", - "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", "dev": true, "license": "MIT", "dependencies": { @@ -7545,13 +7875,13 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.11", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -7565,16 +7895,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", @@ -7582,25 +7902,74 @@ "license": "MIT" }, "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/ripemd160/-/ripemd160-2.0.3.tgz", + "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==", "dev": true, "license": "MIT", "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "hash-base": "^3.1.2", + "inherits": "^2.0.4" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ripemd160/node_modules/hash-base": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/hash-base/-/hash-base-3.1.2.tgz", + "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ripemd160/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ripemd160/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, + "node_modules/ripemd160/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, "node_modules/robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", "license": "Unlicense" }, "node_modules/rollup": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.60.0.tgz", "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", "dev": true, "license": "MIT", @@ -7655,6 +8024,13 @@ "points-on-path": "^0.2.1" } }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmmirror.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "license": "MIT", + "optional": true + }, "node_modules/rw": { "version": "1.3.3", "resolved": "https://registry.npmmirror.com/rw/-/rw-1.3.3.tgz", @@ -7665,7 +8041,7 @@ "version": "5.2.1", "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -7707,13 +8083,13 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.97.3", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", - "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", + "version": "1.98.0", + "resolved": "https://registry.npmmirror.com/sass/-/sass-1.98.0.tgz", + "integrity": "sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A==", "license": "MIT", "dependencies": { "chokidar": "^4.0.0", - "immutable": "^5.0.2", + "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -7726,14 +8102,33 @@ "@parcel/watcher": "^2.4.1" } }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "optional": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "devOptional": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { "node": ">=10" @@ -7809,42 +8204,18 @@ } }, "node_modules/shiki": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/shiki/-/shiki-3.15.0.tgz", - "integrity": "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.15.0", - "@shikijs/engine-javascript": "3.15.0", - "@shikijs/engine-oniguruma": "3.15.0", - "@shikijs/langs": "3.15.0", - "@shikijs/themes": "3.15.0", - "@shikijs/types": "3.15.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/shiki/node_modules/@shikijs/core": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@shikijs/core/-/core-3.15.0.tgz", - "integrity": "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.15.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.5" - } - }, - "node_modules/shiki/node_modules/@shikijs/types": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@shikijs/types/-/types-3.15.0.tgz", - "integrity": "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw==", + "version": "3.23.0", + "resolved": "https://registry.npmmirror.com/shiki/-/shiki-3.23.0.tgz", + "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", "dev": true, "license": "MIT", "dependencies": { + "@shikijs/core": "3.23.0", + "@shikijs/engine-javascript": "3.23.0", + "@shikijs/engine-oniguruma": "3.23.0", + "@shikijs/langs": "3.23.0", + "@shikijs/themes": "3.23.0", + "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } @@ -7932,6 +8303,53 @@ "dev": true, "license": "ISC" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", @@ -7969,9 +8387,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/std-env/-/std-env-4.0.0.tgz", + "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", "dev": true, "license": "MIT" }, @@ -8000,15 +8418,22 @@ } }, "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "devOptional": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "safe-buffer": "~5.1.0" } }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "devOptional": true, + "license": "MIT" + }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmmirror.com/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -8025,22 +8450,19 @@ } }, "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "license": "MIT", + "optional": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/style-mod": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/style-mod/-/style-mod-4.1.2.tgz", - "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", "license": "MIT" }, "node_modules/stylis": { @@ -8050,30 +8472,17 @@ "license": "MIT" }, "node_modules/superjson": { - "version": "2.2.2", - "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz", - "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "version": "2.2.6", + "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", "license": "MIT", "dependencies": { - "copy-anything": "^3.0.2" + "copy-anything": "^4" }, "engines": { "node": ">=16" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -8087,13 +8496,50 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "license": "MIT", + "optional": true + }, "node_modules/tabbable": { - "version": "6.3.0", - "resolved": "https://registry.npmmirror.com/tabbable/-/tabbable-6.3.0.tgz", - "integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==", + "version": "6.4.0", + "resolved": "https://registry.npmmirror.com/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", "dev": true, "license": "MIT" }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmmirror.com/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -8114,11 +8560,19 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -8131,51 +8585,40 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "node": ">=14.0.0" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmmirror.com/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", "license": "MIT", - "engines": { - "node": ">=12" + "optional": true, + "dependencies": { + "tldts-core": "^6.1.86" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "bin": { + "tldts": "bin/cli.js" } }, - "node_modules/tinyrainbow": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-3.0.3.tgz", - "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", - "dev": true, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmmirror.com/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", "license": "MIT", - "engines": { - "node": ">=14.0.0" - } + "optional": true }, "node_modules/to-buffer": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/to-buffer/-/to-buffer-1.2.1.tgz", - "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", "dev": true, "license": "MIT", "dependencies": { @@ -8187,17 +8630,30 @@ "node": ">= 0.4" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "license": "MIT", "optional": true, "dependencies": { - "is-number": "^7.0.0" + "punycode": "^2.3.1" }, "engines": { - "node": ">=8.0" + "node": ">=18" } }, "node_modules/trim-lines": { @@ -8212,9 +8668,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -8240,6 +8696,19 @@ "dev": true, "license": "MIT" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", @@ -8283,16 +8752,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.52.0.tgz", - "integrity": "sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA==", + "version": "8.57.2", + "resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.57.2.tgz", + "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.52.0", - "@typescript-eslint/parser": "8.52.0", - "@typescript-eslint/typescript-estree": "8.52.0", - "@typescript-eslint/utils": "8.52.0" + "@typescript-eslint/eslint-plugin": "8.57.2", + "@typescript-eslint/parser": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8302,20 +8771,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/ufo": { - "version": "1.6.1", - "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.1.tgz", - "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "version": "1.6.3", + "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", "license": "MIT" }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.18.2", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, @@ -8362,9 +8831,9 @@ } }, "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", "dev": true, "license": "MIT", "dependencies": { @@ -8393,98 +8862,96 @@ } }, "node_modules/unplugin": { - "version": "2.3.10", - "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.10.tgz", - "integrity": "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==", - "dev": true, + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-3.0.0.tgz", + "integrity": "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" }, "engines": { - "node": ">=18.12.0" + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.3.1.tgz", + "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==", + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" } }, "node_modules/unplugin-vue-components": { - "version": "30.0.0", - "resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-30.0.0.tgz", - "integrity": "sha512-4qVE/lwCgmdPTp6h0qsRN2u642tt4boBQtcpn4wQcWZAsr8TQwq+SPT3NDu/6kBFxzo/sSEK4ioXhOOBrXc3iw==", + "version": "32.0.0", + "resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-32.0.0.tgz", + "integrity": "sha512-uLdccgS7mf3pv1bCCP20y/hm+u1eOjAmygVkh+Oa70MPkzgl1eQv1L0CwdHNM3gscO8/GDMGIET98Ja47CBbZg==", "dev": true, "license": "MIT", "dependencies": { - "chokidar": "^4.0.3", - "debug": "^4.4.3", + "chokidar": "^5.0.0", "local-pkg": "^1.1.2", - "magic-string": "^0.30.19", - "mlly": "^1.8.0", + "magic-string": "^0.30.21", + "mlly": "^1.8.2", + "obug": "^2.1.1", + "picomatch": "^4.0.3", "tinyglobby": "^0.2.15", - "unplugin": "^2.3.10", + "unplugin": "^3.0.0", "unplugin-utils": "^0.3.1" }, "engines": { - "node": ">=14" + "node": ">=20.19.0" }, "funding": { "url": "https://github.com/sponsors/antfu" }, "peerDependencies": { - "@babel/parser": "^7.15.8", "@nuxt/kit": "^3.2.2 || ^4.0.0", - "vue": "2 || 3" + "vue": "^3.0.0" }, "peerDependenciesMeta": { - "@babel/parser": { - "optional": true - }, "@nuxt/kit": { "optional": true } } }, - "node_modules/unplugin-vue-components/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/unplugin-vue-components/node_modules/unplugin-utils": { - "version": "0.3.1", - "resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.3.1.tgz", - "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==", + "node_modules/unplugin-vue-components/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", "dependencies": { - "pathe": "^2.0.3", - "picomatch": "^4.0.3" + "readdirp": "^5.0.0" }, "engines": { - "node": ">=20.19.0" + "node": ">= 20.19.0" }, "funding": { - "url": "https://github.com/sponsors/sxzz" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/unplugin/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "node_modules/unplugin-vue-components/node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 20.19.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/uri-js": { @@ -8536,7 +9003,7 @@ "version": "1.0.2", "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/uuid": { @@ -8583,13 +9050,13 @@ } }, "node_modules/vite": { - "version": "7.2.2", - "resolved": "https://registry.npmmirror.com/vite/-/vite-7.2.2.tgz", - "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", + "version": "7.3.1", + "resolved": "https://registry.npmmirror.com/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.25.0", + "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", @@ -8658,14 +9125,14 @@ } }, "node_modules/vite-plugin-node-polyfills": { - "version": "0.24.0", - "resolved": "https://registry.npmmirror.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.24.0.tgz", - "integrity": "sha512-GA9QKLH+vIM8NPaGA+o2t8PDfFUl32J8rUp1zQfMKVJQiNkOX4unE51tR6ppl6iKw5yOrDAdSH7r/UIFLCVhLw==", + "version": "0.25.0", + "resolved": "https://registry.npmmirror.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.25.0.tgz", + "integrity": "sha512-rHZ324W3LhfGPxWwQb2N048TThB6nVvnipsqBUJEzh3R9xeK9KI3si+GMQxCuAcpPJBVf0LpDtJ+beYzB3/chg==", "dev": true, "license": "MIT", "dependencies": { "@rollup/plugin-inject": "^5.0.5", - "node-stdlib-browser": "^1.2.0" + "node-stdlib-browser": "^1.3.1" }, "funding": { "url": "https://github.com/sponsors/davidmyersdev" @@ -8674,69 +9141,39 @@ "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, - "node_modules/vite/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/vitepress": { - "version": "2.0.0-alpha.12", - "resolved": "https://registry.npmmirror.com/vitepress/-/vitepress-2.0.0-alpha.12.tgz", - "integrity": "sha512-yZwCwRRepcpN5QeAhwSnEJxS3I6zJcVixqL1dnm6km4cnriLpQyy2sXQDsE5Ti3pxGPbhU51nTMwI+XC1KNnJg==", + "version": "2.0.0-alpha.17", + "resolved": "https://registry.npmmirror.com/vitepress/-/vitepress-2.0.0-alpha.17.tgz", + "integrity": "sha512-Z3VPUpwk/bHYqt1uMVOOK1/4xFiWQov1GNc2FvMdz6kvje4JRXEOngVI9C+bi5jeedMSHiA4dwKkff1NCvbZ9Q==", "dev": true, "license": "MIT", "dependencies": { - "@docsearch/css": "^4.0.0-beta.7", - "@docsearch/js": "^4.0.0-beta.7", - "@iconify-json/simple-icons": "^1.2.47", - "@shikijs/core": "^3.9.2", - "@shikijs/transformers": "^3.9.2", - "@shikijs/types": "^3.9.2", + "@docsearch/css": "^4.5.3", + "@docsearch/js": "^4.5.3", + "@docsearch/sidepanel-js": "^4.5.3", + "@iconify-json/simple-icons": "^1.2.69", + "@shikijs/core": "^3.22.0", + "@shikijs/transformers": "^3.22.0", + "@shikijs/types": "^3.22.0", "@types/markdown-it": "^14.1.2", - "@vitejs/plugin-vue": "^6.0.1", - "@vue/devtools-api": "^8.0.0", - "@vue/shared": "^3.5.18", - "@vueuse/core": "^13.6.0", - "@vueuse/integrations": "^13.6.0", - "focus-trap": "^7.6.5", + "@vitejs/plugin-vue": "^6.0.4", + "@vue/devtools-api": "^8.0.5", + "@vue/shared": "^3.5.27", + "@vueuse/core": "^14.2.0", + "@vueuse/integrations": "^14.2.0", + "focus-trap": "^8.0.0", "mark.js": "8.11.1", - "minisearch": "^7.1.2", - "shiki": "^3.9.2", - "vite": "^7.1.2", - "vue": "^3.5.18" + "minisearch": "^7.2.0", + "shiki": "^3.22.0", + "vite": "^7.3.1", + "vue": "^3.5.27" }, "bin": { "vitepress": "bin/vitepress.js" }, "peerDependencies": { "markdown-it-mathjax3": "^4", - "oxc-minify": "^0.82.1", + "oxc-minify": "*", "postcss": "^8" }, "peerDependenciesMeta": { @@ -8752,74 +9189,68 @@ } }, "node_modules/vitepress/node_modules/@vue/devtools-api": { - "version": "8.0.3", - "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-8.0.3.tgz", - "integrity": "sha512-YxZE7xNvvfq5XmjJh1ml+CzVNrRjuZYCuT5Xjj0u9RlXU7za/MRuZDUXcKfp0j7IvYkDut49vlKqbiQ1xhXP2w==", + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-8.1.1.tgz", + "integrity": "sha512-bsDMJ07b3GN1puVwJb/fyFnj/U2imyswK5UQVLZwVl7O05jDrt6BHxeG5XffmOOdasOj/bOmIjxJvGPxU7pcqw==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-kit": "^8.0.3" + "@vue/devtools-kit": "^8.1.1" } }, "node_modules/vitepress/node_modules/@vue/devtools-kit": { - "version": "8.0.3", - "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-8.0.3.tgz", - "integrity": "sha512-UF4YUOVGdfzXLCv5pMg2DxocB8dvXz278fpgEE+nJ/DRALQGAva7sj9ton0VWZ9hmXw+SV8yKMrxP2MpMhq9Wg==", + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-8.1.1.tgz", + "integrity": "sha512-gVBaBv++i+adg4JpH71k9ppl4soyR7Y2McEqO5YNgv0BI1kMZ7BDX5gnwkZ5COYgiCyhejZG+yGNrBAjj6Coqg==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-shared": "^8.0.3", + "@vue/devtools-shared": "^8.1.1", "birpc": "^2.6.1", "hookable": "^5.5.3", - "mitt": "^3.0.1", - "perfect-debounce": "^2.0.0", - "speakingurl": "^14.0.1", - "superjson": "^2.2.2" + "perfect-debounce": "^2.0.0" } }, "node_modules/vitepress/node_modules/@vue/devtools-shared": { - "version": "8.0.3", - "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-8.0.3.tgz", - "integrity": "sha512-s/QNll7TlpbADFZrPVsaUNPCOF8NvQgtgmmB7Tip6pLf/HcOvBTly0lfLQ0Eylu9FQ4OqBhFpLyBgwykiSf8zw==", + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-8.1.1.tgz", + "integrity": "sha512-+h4ttmJYl/txpxHKaoZcaKpC+pvckgLzIDiSQlaQ7kKthKh8KuwoLW2D8hPJEnqKzXOvu15UHEoGyngAXCz0EQ==", "dev": true, - "license": "MIT", - "dependencies": { - "rfdc": "^1.4.1" - } + "license": "MIT" }, "node_modules/vitepress/node_modules/perfect-debounce": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-2.0.0.tgz", - "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", "dev": true, "license": "MIT" }, "node_modules/vitest": { - "version": "4.0.16", - "resolved": "https://registry.npmmirror.com/vitest/-/vitest-4.0.16.tgz", - "integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==", + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/vitest/-/vitest-4.1.2.tgz", + "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.0.16", - "@vitest/mocker": "4.0.16", - "@vitest/pretty-format": "4.0.16", - "@vitest/runner": "4.0.16", - "@vitest/snapshot": "4.0.16", - "@vitest/spy": "4.0.16", - "@vitest/utils": "4.0.16", - "es-module-lexer": "^1.7.0", - "expect-type": "^1.2.2", + "@vitest/expect": "4.1.2", + "@vitest/mocker": "4.1.2", + "@vitest/pretty-format": "4.1.2", + "@vitest/runner": "4.1.2", + "@vitest/snapshot": "4.1.2", + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", - "std-env": "^3.10.0", + "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -8835,12 +9266,13 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.0.16", - "@vitest/browser-preview": "4.0.16", - "@vitest/browser-webdriverio": "4.0.16", - "@vitest/ui": "4.0.16", + "@vitest/browser-playwright": "4.1.2", + "@vitest/browser-preview": "4.1.2", + "@vitest/browser-webdriverio": "4.1.2", + "@vitest/ui": "4.1.2", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -8869,32 +9301,12 @@ }, "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, - "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/vitest/node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -8949,12 +9361,11 @@ "version": "3.1.0", "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz", "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", - "dev": true, "license": "MIT" }, "node_modules/vue": { "version": "3.5.31", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.31.tgz", "integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==", "license": "MIT", "dependencies": { @@ -8974,16 +9385,16 @@ } }, "node_modules/vue-eslint-parser": { - "version": "10.2.0", - "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz", - "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==", + "version": "10.4.0", + "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-10.4.0.tgz", + "integrity": "sha512-Vxi9pJdbN3ZnVGLODVtZ7y4Y2kzAAE2Cm0CZ3ZDRvydVYxZ6VrnBhLikBsRS+dpwj4Jv4UCv21PTEwF5rQ9WXg==", "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.2.0 || ^9.0.0", + "eslint-visitor-keys": "^4.2.0 || ^5.0.0", + "espree": "^10.3.0 || ^11.0.0", "esquery": "^1.6.0", "semver": "^7.6.3" }, @@ -8994,17 +9405,18 @@ "url": "https://github.com/sponsors/mysticatea" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0" } }, "node_modules/vue-i18n": { - "version": "11.2.8", - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.2.8.tgz", - "integrity": "sha512-vJ123v/PXCZntd6Qj5Jumy7UBmIuE92VrtdX+AXr+1WzdBHojiBxnAxdfctUFL+/JIN+VQH4BhsfTtiGsvVObg==", + "version": "11.3.0", + "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-11.3.0.tgz", + "integrity": "sha512-1J+xDfDJTLhDxElkd3+XUhT7FYSZd2b8pa7IRKGxhWH/8yt6PTvi3xmWhGwhYT5EaXdatui11pF2R6tL73/zPA==", "license": "MIT", "dependencies": { - "@intlify/core-base": "11.2.8", - "@intlify/shared": "11.2.8", + "@intlify/core-base": "11.3.0", + "@intlify/devtools-types": "11.3.0", + "@intlify/shared": "11.3.0", "@vue/devtools-api": "^6.5.0" }, "engines": { @@ -9037,35 +9449,120 @@ } }, "node_modules/vue-router": { - "version": "4.6.4", - "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz", - "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "version": "5.0.4", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-5.0.4.tgz", + "integrity": "sha512-lCqDLCI2+fKVRl2OzXuzdSWmxXFLQRxQbmHugnRpTMyYiT+hNaycV0faqG5FBHDXoYrZ6MQcX87BvbY8mQ20Bg==", "license": "MIT", "dependencies": { - "@vue/devtools-api": "^6.6.4" + "@babel/generator": "^7.28.6", + "@vue-macros/common": "^3.1.1", + "@vue/devtools-api": "^8.0.6", + "ast-walker-scope": "^0.8.3", + "chokidar": "^5.0.0", + "json5": "^2.2.3", + "local-pkg": "^1.1.2", + "magic-string": "^0.30.21", + "mlly": "^1.8.0", + "muggle-string": "^0.4.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "scule": "^1.3.0", + "tinyglobby": "^0.2.15", + "unplugin": "^3.0.0", + "unplugin-utils": "^0.3.1", + "yaml": "^2.8.2" }, "funding": { "url": "https://github.com/sponsors/posva" }, "peerDependencies": { + "@pinia/colada": ">=0.21.2", + "@vue/compiler-sfc": "^3.5.17", + "pinia": "^3.0.4", "vue": "^3.5.0" + }, + "peerDependenciesMeta": { + "@pinia/colada": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "pinia": { + "optional": true + } } }, "node_modules/vue-router/node_modules/@vue/devtools-api": { - "version": "6.6.4", - "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", - "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-8.1.1.tgz", + "integrity": "sha512-bsDMJ07b3GN1puVwJb/fyFnj/U2imyswK5UQVLZwVl7O05jDrt6BHxeG5XffmOOdasOj/bOmIjxJvGPxU7pcqw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.1.1" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-kit": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-8.1.1.tgz", + "integrity": "sha512-gVBaBv++i+adg4JpH71k9ppl4soyR7Y2McEqO5YNgv0BI1kMZ7BDX5gnwkZ5COYgiCyhejZG+yGNrBAjj6Coqg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.1.1", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "perfect-debounce": "^2.0.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-shared": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-8.1.1.tgz", + "integrity": "sha512-+h4ttmJYl/txpxHKaoZcaKpC+pvckgLzIDiSQlaQ7kKthKh8KuwoLW2D8hPJEnqKzXOvu15UHEoGyngAXCz0EQ==", + "license": "MIT" + }, + "node_modules/vue-router/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/vue-router/node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", "license": "MIT" }, + "node_modules/vue-router/node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/vue-tsc": { - "version": "3.2.1", - "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-3.2.1.tgz", - "integrity": "sha512-I23Rk8dkQfmcSbxDO0dmg9ioMLjKA1pjlU3Lz6Jfk2pMGu3Uryu9810XkcZH24IzPbhzPCnkKo2rEMRX0skSrw==", + "version": "3.2.6", + "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-3.2.6.tgz", + "integrity": "sha512-gYW/kWI0XrwGzd0PKc7tVB/qpdeAkIZLNZb10/InizkQjHjnT8weZ/vBarZoj4kHKbUTZT/bAVgoOr8x4NsQ/Q==", "dev": true, "license": "MIT", "dependencies": { - "@volar/typescript": "2.4.27", - "@vue/language-core": "3.2.1" + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.2.6" }, "bin": { "vue-tsc": "bin/vue-tsc.js" @@ -9080,13 +9577,83 @@ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", "license": "MIT" }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "license": "MIT", + "optional": true, + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">=12" + } + }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", - "dev": true, "license": "MIT" }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", @@ -9104,9 +9671,9 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "version": "1.1.20", + "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -9152,6 +9719,35 @@ "node": ">=0.10.0" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC", + "optional": true + }, + "node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmmirror.com/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xml-name-validator": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz", @@ -9162,6 +9758,13 @@ "node": ">=12" } }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT", + "optional": true + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz", @@ -9172,6 +9775,21 @@ "node": ">=0.4" } }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -9195,50 +9813,6 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } - }, - "src/common/prettier/plugins/scala/prettier-plugin-scala": { - "name": "@simochee/prettier-plugin-scala", - "version": "0.1.0", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@simochee/scala-parser": "file:../scala-parser" - }, - "devDependencies": { - "@tsconfig/node-ts": "^23.6.1", - "@tsconfig/node24": "^24.0.1", - "@types/node": "^22.10.2", - "prettier": "^3.4.2", - "tsup": "^8.5.0", - "typescript": "^5.7.2", - "vitest": "^2.1.9" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "prettier": "^3.0.0" - } - }, - "src/common/prettier/plugins/scala/scala-parser": { - "name": "@simochee/scala-parser", - "version": "0.1.0", - "extraneous": true, - "license": "MIT", - "dependencies": { - "chevrotain": "^11.0.3" - }, - "devDependencies": { - "@tsconfig/node-ts": "^23.6.1", - "@tsconfig/node24": "^24.0.1", - "@types/node": "^22.10.2", - "tsup": "^8.5.0", - "typescript": "^5.7.2", - "vitest": "^2.1.9" - }, - "engines": { - "node": ">=20.0.0" - } } } } diff --git a/frontend/package.json b/frontend/package.json index 923672b3..3215d1a6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,19 +22,19 @@ "app:generate": "cd .. && wails3 generate bindings -ts" }, "dependencies": { - "@codemirror/autocomplete": "^6.20.0", - "@codemirror/commands": "^6.10.1", + "@codemirror/autocomplete": "^6.20.1", + "@codemirror/commands": "^6.10.3", "@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.11", "@codemirror/lang-java": "^6.0.2", - "@codemirror/lang-javascript": "^6.2.4", + "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lang-json": "^6.0.2", "@codemirror/lang-less": "^6.0.2", "@codemirror/lang-lezer": "^6.0.2", - "@codemirror/lang-liquid": "^6.3.1", + "@codemirror/lang-liquid": "^6.3.2", "@codemirror/lang-markdown": "^6.5.0", "@codemirror/lang-php": "^6.0.2", "@codemirror/lang-python": "^6.2.1", @@ -43,65 +43,62 @@ "@codemirror/lang-sql": "^6.10.0", "@codemirror/lang-vue": "^0.1.3", "@codemirror/lang-wast": "^6.0.2", - "@codemirror/lang-yaml": "^6.1.2", - "@codemirror/language": "^6.12.1", + "@codemirror/lang-yaml": "^6.1.3", + "@codemirror/language": "^6.12.3", "@codemirror/language-data": "^6.5.2", "@codemirror/legacy-modes": "^6.5.2", - "@codemirror/lint": "^6.9.2", - "@codemirror/search": "^6.5.11", - "@codemirror/state": "^6.5.4", - "@codemirror/view": "^6.39.11", + "@codemirror/lint": "^6.9.5", + "@codemirror/search": "^6.6.0", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.40.0", "@cospaia/prettier-plugin-clojure": "^0.0.2", "@lezer/highlight": "^1.2.3", - "@lezer/lr": "^1.4.7", + "@lezer/lr": "^1.4.8", "@prettier/plugin-xml": "^3.4.2", "@replit/codemirror-lang-svelte": "^6.0.0", "@toml-tools/lexer": "^1.0.1", "@toml-tools/parser": "^1.0.1", - "@types/katex": "^0.16.7", - "@zumer/snapdom": "^2.0.1", + "@types/katex": "^0.16.8", + "@zumer/snapdom": "^2.7.0", "codemirror": "^6.0.2", - "codemirror-lang-elixir": "^4.0.0", - "colors-named": "^1.0.4", - "colors-named-hex": "^1.0.3", + "codemirror-lang-elixir": "^4.0.1", + "colors-named": "^1.0.5", + "colors-named-hex": "^1.0.4", + "fabric": "^7.2.0", "groovy-beautify": "^0.0.17", "hsl-matcher": "^1.2.4", - "java-parser": "^3.0.1", - "katex": "^0.16.27", + "katex": "^0.16.44", "linguist-languages": "^9.3.1", - "marked": "^17.0.1", - "mermaid": "^11.12.3", - "php-parser": "^3.2.5", + "marked": "^17.0.5", + "mermaid": "^11.13.0", + "php-parser": "^3.5.0", "pinia": "^3.0.4", "pinia-plugin-persistedstate": "^4.7.1", "prettier": "^3.8.1", - "sass": "^1.97.3", - "vue": "^3.5.27", - "vue-i18n": "^11.2.8", + "sass": "^1.98.0", + "vue": "^3.5.31", + "vue-i18n": "^11.3.0", "vue-pick-colors": "^1.8.0", - "vue-router": "^4.6.4" + "vue-router": "^5.0.4" }, "devDependencies": { - "@eslint/js": "^9.39.2", + "@eslint/js": "^10.0.1", "@lezer/generator": "^1.8.0", - "@types/node": "^25.0.3", - "@vitejs/plugin-vue": "^6.0.3", - "@wailsio/runtime": "^3.0.0-alpha.77", + "@types/node": "^25.5.0", + "@vitejs/plugin-vue": "^6.0.5", + "@wailsio/runtime": "^3.0.0-alpha.79", "cross-env": "^10.1.0", - "eslint": "^9.39.2", - "eslint-plugin-vue": "^10.6.2", - "globals": "^16.5.0", + "eslint": "^10.1.0", + "eslint-plugin-vue": "^10.8.0", + "globals": "^17.4.0", "typescript": "^5.9.3", - "typescript-eslint": "^8.51.0", - "unplugin-vue-components": "^30.0.0", - "vite": "npm:rolldown-vite@latest", - "vite-plugin-node-polyfills": "^0.24.0", + "typescript-eslint": "^8.57.2", + "unplugin-vue-components": "^32.0.0", + "vite": "^7.3.1", + "vite-plugin-node-polyfills": "^0.25.0", "vitepress": "^2.0.0-alpha.12", - "vitest": "^4.0.16", - "vue-eslint-parser": "^10.2.0", - "vue-tsc": "^3.2.1" - }, - "overrides": { - "vite": "npm:rolldown-vite@latest" + "vitest": "^4.1.2", + "vue-eslint-parser": "^10.4.0", + "vue-tsc": "^3.2.6" } } diff --git a/frontend/public/images/blockReadonly.svg b/frontend/public/images/blockReadonly.svg new file mode 100644 index 00000000..1d3ec031 --- /dev/null +++ b/frontend/public/images/blockReadonly.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/images/inlineImage.svg b/frontend/public/images/inlineImage.svg new file mode 100644 index 00000000..a071a659 --- /dev/null +++ b/frontend/public/images/inlineImage.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/copy-dark.svg b/frontend/src/assets/icons/copy-dark.svg new file mode 100644 index 00000000..9d63294e --- /dev/null +++ b/frontend/src/assets/icons/copy-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/icons/pencil-white.svg b/frontend/src/assets/icons/pencil-white.svg new file mode 100644 index 00000000..0cc758ec --- /dev/null +++ b/frontend/src/assets/icons/pencil-white.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/icons/resize-handle-se-dark.png b/frontend/src/assets/icons/resize-handle-se-dark.png new file mode 100644 index 00000000..ecff343a Binary files /dev/null and b/frontend/src/assets/icons/resize-handle-se-dark.png differ diff --git a/frontend/src/assets/icons/resize-handle-se-light.png b/frontend/src/assets/icons/resize-handle-se-light.png new file mode 100644 index 00000000..19246810 Binary files /dev/null and b/frontend/src/assets/icons/resize-handle-se-light.png differ diff --git a/frontend/src/assets/icons/trash-white.svg b/frontend/src/assets/icons/trash-white.svg new file mode 100644 index 00000000..864fb2d0 --- /dev/null +++ b/frontend/src/assets/icons/trash-white.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/common/constant/config.ts b/frontend/src/common/constant/config.ts index c3ad2ba0..839d7afd 100644 --- a/frontend/src/common/constant/config.ts +++ b/frontend/src/common/constant/config.ts @@ -1,6 +1,7 @@ import { AppConfig, AuthMethod, + SyncTarget, KeyBindingType, LanguageType, SystemThemeType, @@ -8,12 +9,8 @@ import { } from '@/../bindings/voidraft/internal/models/models'; import {FONT_OPTIONS} from './fonts'; -export type NumberConfigKey = 'fontSize' | 'tabSize' | 'lineHeight'; -export type ConfigSection = 'general' | 'editing' | 'appearance' | 'updates' | 'backup'; - // 统一配置键映射(平级展开) export const CONFIG_KEY_MAP = { - // general alwaysOnTop: 'general.alwaysOnTop', dataPath: 'general.dataPath', enableSystemTray: 'general.enableSystemTray', @@ -24,7 +21,7 @@ export const CONFIG_KEY_MAP = { enableLoadingAnimation: 'general.enableLoadingAnimation', enableTabs: 'general.enableTabs', enableMemoryMonitor: 'general.enableMemoryMonitor', - // editing + fontSize: 'editing.fontSize', fontFamily: 'editing.fontFamily', fontWeight: 'editing.fontWeight', @@ -34,33 +31,33 @@ export const CONFIG_KEY_MAP = { tabType: 'editing.tabType', keymapMode: 'editing.keymapMode', autoSaveDelay: 'editing.autoSaveDelay', - // appearance + language: 'appearance.language', systemTheme: 'appearance.systemTheme', currentTheme: 'appearance.currentTheme', - // updates - version: 'updates.version', + autoUpdate: 'updates.autoUpdate', - primarySource: 'updates.primarySource', - backupSource: 'updates.backupSource', backupBeforeUpdate: 'updates.backupBeforeUpdate', updateTimeout: 'updates.updateTimeout', github: 'updates.github', - gitea: 'updates.gitea', - // backup - enabled: 'backup.enabled', - repo_url: 'backup.repo_url', - auth_method: 'backup.auth_method', - username: 'backup.username', - password: 'backup.password', - token: 'backup.token', - ssh_key_path: 'backup.ssh_key_path', - ssh_key_passphrase: 'backup.ssh_key_passphrase', - backup_interval: 'backup.backup_interval', - auto_backup: 'backup.auto_backup', + + sync_target: 'sync.target', + sync_enabled: 'sync.enabled', + sync_auto_sync: 'sync.auto_sync', + sync_sync_interval: 'sync.sync_interval', + git_repo_url: 'sync.git.repo_url', + git_branch: 'sync.git.branch', + git_auth_method: 'sync.git.auth_method', + git_username: 'sync.git.username', + git_password: 'sync.git.password', + git_token: 'sync.git.token', + git_ssh_key_path: 'sync.git.ssh_key_path', + git_ssh_key_passphrase: 'sync.git.ssh_key_passphrase', + localfs_root_path: 'sync.localfs.root_path', } as const; export type ConfigKey = keyof typeof CONFIG_KEY_MAP; +export type NumberConfigKey = 'fontSize' | 'tabSize' | 'lineHeight'; // 配置限制 export const CONFIG_LIMITS = { @@ -116,20 +113,27 @@ export const DEFAULT_CONFIG: AppConfig = { repo: "voidraft", }, }, - backup: { + sync: { + target: SyncTarget.SyncTargetGit, enabled: false, - repo_url: "", - auth_method: AuthMethod.UserPass, - username: "", - password: "", - token: "", - ssh_key_path: "", - ssh_key_passphrase: "", - backup_interval: 60, - auto_backup: true, + auto_sync: false, + sync_interval: 60, + git: { + repo_url: '', + branch: 'main', + auth_method: AuthMethod.UserPass, + username: '', + password: '', + token: '', + ssh_key_path: '', + ssh_key_passphrase: '', + }, + localfs: { + root_path: '', + }, }, metadata: { version: '1.0.0', lastUpdated: new Date().toString(), } -}; \ No newline at end of file +}; diff --git a/frontend/src/common/prettier/plugins/clang/index.ts b/frontend/src/common/prettier/plugins/clang/index.ts index c9e8e97f..98e3353a 100644 --- a/frontend/src/common/prettier/plugins/clang/index.ts +++ b/frontend/src/common/prettier/plugins/clang/index.ts @@ -23,7 +23,7 @@ const languages = [ { name: 'C', aliases: ['c'], - parsers: ['c'], + parsers: [parserName], extensions: ['.c', '.h'], filenames: ['*.c', '*.h'], aceMode: 'c_cpp', @@ -34,7 +34,7 @@ const languages = [ { name: 'C++', aliases: ['cpp', 'cxx', 'cc'], - parsers: ['cpp'], + parsers: [parserName], extensions: ['.cpp', '.cxx', '.cc', '.hpp', '.hxx', '.hh', '.C', '.H'], filenames: ['*.cpp', '*.cxx', '*.cc', '*.hpp', '*.hxx', '*.hh', '*.C', '*.H'], aceMode: 'c_cpp', @@ -45,7 +45,7 @@ const languages = [ { name: 'Objective-C', aliases: ['objc', 'objectivec'], - parsers: ['objective-c'], + parsers: [parserName], extensions: ['.m'], filenames: ['*.m'], aceMode: 'objectivec', @@ -56,7 +56,7 @@ const languages = [ { name: 'Objective-C++', aliases: ['objcpp', 'objectivecpp'], - parsers: ['objective-cpp'], + parsers: [parserName], extensions: ['.mm'], filenames: ['*.mm'], aceMode: 'objectivec', @@ -67,7 +67,7 @@ const languages = [ { name: 'C#', aliases: ['csharp', 'cs'], - parsers: ['cs'], + parsers: [parserName], extensions: ['.cs'], filenames: ['*.cs'], aceMode: 'csharp', @@ -78,7 +78,7 @@ const languages = [ { name: 'Java', aliases: ['java'], - parsers: ['java'], + parsers: [parserName], extensions: ['.java'], filenames: ['*.java'], aceMode: 'java', @@ -89,7 +89,7 @@ const languages = [ { name: 'Protocol Buffer', aliases: ['protobuf', 'proto'], - parsers: ['proto'], + parsers: [parserName], extensions: ['.proto'], filenames: ['*.proto'], aceMode: 'protobuf', @@ -158,17 +158,7 @@ const clangPrinter: Printer = { // Helper function to determine clang-format style function getClangStyle(options: any): string { - // You can extend this to support more options - const style = options.clangStyle || 'LLVM'; - - // Support common styles - const validStyles = ['LLVM', 'Google', 'Chromium', 'Mozilla', 'WebKit', 'Microsoft', 'GNU']; - if (validStyles.includes(style)) { - return style; - } - - // Default to LLVM style - return 'LLVM'; + return options.clangStyle || 'LLVM'; } // Plugin options diff --git a/frontend/src/common/prettier/plugins/java/comments.d.ts b/frontend/src/common/prettier/plugins/java/comments.d.ts deleted file mode 100644 index 844c4ea6..00000000 --- a/frontend/src/common/prettier/plugins/java/comments.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { IToken } from "java-parser"; -import { type AstPath } from "prettier"; -import { type JavaNode, type JavaNonTerminal, type JavaParserOptions } from "./printers/helpers.js"; -export declare function determineFormatterOffOnRanges(cst: JavaNonTerminal): void; -export declare function isFullyBetweenFormatterOffOn(path: AstPath): boolean; -export declare function canAttachComment(node: JavaNode): boolean; -export declare function handleLineComment(commentNode: JavaComment, _: string, options: JavaParserOptions): boolean; -export declare function handleRemainingComment(commentNode: JavaComment): boolean; -export type JavaComment = IToken & { - value: string; - leading: boolean; - trailing: boolean; - printed: boolean; - enclosingNode?: JavaNonTerminal; - precedingNode?: JavaNonTerminal; - followingNode?: JavaNonTerminal; -}; diff --git a/frontend/src/common/prettier/plugins/java/comments.js b/frontend/src/common/prettier/plugins/java/comments.js deleted file mode 100644 index 0e15efe4..00000000 --- a/frontend/src/common/prettier/plugins/java/comments.js +++ /dev/null @@ -1,199 +0,0 @@ -import { util } from "prettier"; -import parser from "./parser.js"; -import { isEmptyStatement, isNonTerminal, isTerminal } from "./printers/helpers.js"; -const formatterOffOnRangesByCst = new WeakMap(); -export function determineFormatterOffOnRanges(cst) { - const { comments } = cst; - if (!comments) { - return; - } - const ranges = comments - .filter(({ image }) => /^(\/\/\s*@formatter:(off|on)\s*|\/\*\s*@formatter:(off|on)\s*\*\/)$/.test(image)) - .reduce((ranges, { image, startOffset }) => { - const previous = ranges.at(-1); - if (image.endsWith("off")) { - if ((previous === null || previous === void 0 ? void 0 : previous.on) !== Infinity) { - ranges.push({ off: startOffset, on: Infinity }); - } - } - else if ((previous === null || previous === void 0 ? void 0 : previous.on) === Infinity) { - previous.on = startOffset; - } - return ranges; - }, new Array()); - formatterOffOnRangesByCst.set(cst, ranges); -} -export function isFullyBetweenFormatterOffOn(path) { - var _a; - const { node, root } = path; - const start = parser.locStart(node); - const end = parser.locEnd(node); - return (((_a = formatterOffOnRangesByCst - .get(root)) === null || _a === void 0 ? void 0 : _a.some(range => range.off < start && end < range.on)) === true); -} -export function canAttachComment(node) { - var _a, _b, _c; - if (isTerminal(node)) { - const { name, CATEGORIES } = node.tokenType; - return (name === "Identifier" || - (CATEGORIES === null || CATEGORIES === void 0 ? void 0 : CATEGORIES.find(({ name }) => name === "BinaryOperator")) !== undefined); - } - const { children, name } = node; - switch (name) { - case "argumentList": - case "blockStatements": - case "emptyStatement": - case "enumBodyDeclarations": - return false; - case "annotationInterfaceMemberDeclaration": - case "classMemberDeclaration": - case "interfaceMemberDeclaration": - case "methodBody": - return !children.Semicolon; - case "blockStatement": - return !children.statement || !isEmptyStatement(children.statement[0]); - case "classBodyDeclaration": - return !((_a = children.classMemberDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.Semicolon); - case "recordBodyDeclaration": - return !((_c = (_b = children.classBodyDeclaration) === null || _b === void 0 ? void 0 : _b[0].children.classMemberDeclaration) === null || _c === void 0 ? void 0 : _c[0].children.Semicolon); - case "statement": - return !isEmptyStatement(node); - case "statementWithoutTrailingSubstatement": - return !children.emptyStatement; - default: - return true; - } -} -export function handleLineComment(commentNode, _, options) { - return [ - handleBinaryExpressionComments, - handleFqnOrRefTypeComments, - handleIfStatementComments, - handleJumpStatementComments, - handleLabeledStatementComments, - handleNameComments - ].some(fn => fn(commentNode, options)); -} -export function handleRemainingComment(commentNode) { - return [ - handleFqnOrRefTypeComments, - handleMethodDeclaratorComments, - handleNameComments, - handleJumpStatementComments - ].some(fn => fn(commentNode)); -} -function handleBinaryExpressionComments(commentNode, options) { - const { enclosingNode, precedingNode, followingNode } = commentNode; - if (enclosingNode && - isNonTerminal(enclosingNode) && - enclosingNode.name === "binaryExpression") { - if (isBinaryOperator(followingNode)) { - if (options.experimentalOperatorPosition === "start") { - util.addLeadingComment(followingNode, commentNode); - } - else { - util.addTrailingComment(followingNode, commentNode); - } - return true; - } - else if (options.experimentalOperatorPosition === "start" && - isBinaryOperator(precedingNode)) { - util.addLeadingComment(precedingNode, commentNode); - return true; - } - } - return false; -} -function handleFqnOrRefTypeComments(commentNode) { - const { enclosingNode, followingNode } = commentNode; - if (enclosingNode && - isNonTerminal(enclosingNode) && - enclosingNode.name === "fqnOrRefType" && - followingNode) { - util.addLeadingComment(followingNode, commentNode); - return true; - } - return false; -} -function handleIfStatementComments(commentNode) { - const { enclosingNode, precedingNode } = commentNode; - if (enclosingNode && - isNonTerminal(enclosingNode) && - enclosingNode.name === "ifStatement" && - precedingNode && - isNonTerminal(precedingNode) && - precedingNode.name === "statement") { - util.addDanglingComment(enclosingNode, commentNode, undefined); - return true; - } - return false; -} -function handleJumpStatementComments(commentNode) { - const { enclosingNode, precedingNode, followingNode } = commentNode; - if (enclosingNode && - !precedingNode && - !followingNode && - isNonTerminal(enclosingNode) && - ["breakStatement", "continueStatement", "returnStatement"].includes(enclosingNode.name)) { - util.addTrailingComment(enclosingNode, commentNode); - return true; - } - return false; -} -function handleLabeledStatementComments(commentNode) { - const { enclosingNode, precedingNode } = commentNode; - if (enclosingNode && - precedingNode && - isNonTerminal(enclosingNode) && - enclosingNode.name === "labeledStatement" && - isTerminal(precedingNode) && - precedingNode.tokenType.name === "Identifier") { - util.addLeadingComment(precedingNode, commentNode); - return true; - } - return false; -} -function handleMethodDeclaratorComments(commentNode) { - const { enclosingNode } = commentNode; - if (enclosingNode && - isNonTerminal(enclosingNode) && - enclosingNode.name === "methodDeclarator" && - !enclosingNode.children.receiverParameter && - !enclosingNode.children.formalParameterList && - enclosingNode.children.LBrace[0].startOffset < commentNode.startOffset && - commentNode.startOffset < enclosingNode.children.RBrace[0].startOffset) { - util.addDanglingComment(enclosingNode, commentNode, undefined); - return true; - } - return false; -} -function handleNameComments(commentNode) { - const { enclosingNode, precedingNode } = commentNode; - if (enclosingNode && - precedingNode && - isNonTerminal(enclosingNode) && - isTerminal(precedingNode) && - precedingNode.tokenType.name === "Identifier" && - [ - "ambiguousName", - "classOrInterfaceTypeToInstantiate", - "expressionName", - "moduleDeclaration", - "moduleName", - "packageDeclaration", - "packageName", - "packageOrTypeName", - "typeName" - ].includes(enclosingNode.name)) { - util.addTrailingComment(precedingNode, commentNode); - return true; - } - return false; -} -function isBinaryOperator(node) { - var _a; - return (node !== undefined && - (isNonTerminal(node) - ? node.name === "shiftOperator" - : (_a = node.tokenType.CATEGORIES) === null || _a === void 0 ? void 0 : _a.some(({ name }) => name === "BinaryOperator"))); -} diff --git a/frontend/src/common/prettier/plugins/java/index.d.ts b/frontend/src/common/prettier/plugins/java/index.d.ts deleted file mode 100644 index 1ae54b62..00000000 --- a/frontend/src/common/prettier/plugins/java/index.d.ts +++ /dev/null @@ -1,563 +0,0 @@ -import type { JavaNode } from "./printers/helpers.js"; -declare const _default: { - languages: { - name: string; - parsers: "java"[]; - group: string; - tmScope: string; - aceMode: string; - codemirrorMode: string; - codemirrorMimeType: string; - extensions: string[]; - linguistLanguageId: number; - vscodeLanguageIds: string[]; - }[]; - parsers: { - java: { - parse(text: string, options: import("./printers/helpers.js").JavaParserOptions): import("./printers/helpers.js").JavaNonTerminal; - astFormat: string; - hasPragma(text: string): boolean; - locStart(node: JavaNode): number; - locEnd(node: JavaNode): number; - }; - }; - printers: { - java: { - print(path: import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath | import("prettier").AstPath, options: import("prettier").ParserOptions, print: (path: import("prettier").AstPath) => import("prettier").Doc, args: unknown): import("prettier/doc.js").builders.Doc; - hasPrettierIgnore(path: import("prettier").AstPath): boolean; - canAttachComment: typeof import("./comments.js").canAttachComment; - isBlockComment(node: JavaNode): boolean; - printComment(commentPath: import("prettier").AstPath): string | import("prettier/doc.js").builders.Doc[]; - getCommentChildNodes(node: JavaNode): any[]; - handleComments: { - ownLine: typeof import("./comments.js").handleLineComment; - endOfLine: typeof import("./comments.js").handleLineComment; - remaining: typeof import("./comments.js").handleRemainingComment; - }; - }; - }; - options: { - entrypoint: { - type: "choice"; - category: string; - default: string; - choices: { - value: string; - description: string; - }[]; - description: string; - }; - arrowParens: { - type: "choice"; - category: string; - default: string; - choices: { - value: string; - description: string; - }[]; - description: string; - }; - trailingComma: { - type: "choice"; - category: string; - default: string; - choices: { - value: string; - description: string; - }[]; - description: string; - }; - experimentalOperatorPosition: { - type: "choice"; - category: string; - default: string; - choices: { - value: string; - description: string; - }[]; - description: string; - }; - }; - defaultOptions: { - arrowParens: "avoid"; - }; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/index.js b/frontend/src/common/prettier/plugins/java/index.js deleted file mode 100644 index b83a4434..00000000 --- a/frontend/src/common/prettier/plugins/java/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import options from "./options.js"; -import parser from "./parser.js"; -import printer from "./printer.js"; -export default { - languages: [ - { - name: "Java", - parsers: ["java"], - group: "Java", - tmScope: "source.java", - aceMode: "java", - codemirrorMode: "clike", - codemirrorMimeType: "text/x-java", - extensions: [".java"], - linguistLanguageId: 181, - vscodeLanguageIds: ["java"] - } - ], - parsers: { - java: parser - }, - printers: { - java: printer - }, - options, - defaultOptions: { - arrowParens: "avoid" - } -}; diff --git a/frontend/src/common/prettier/plugins/java/options.d.ts b/frontend/src/common/prettier/plugins/java/options.d.ts deleted file mode 100644 index c67b27f1..00000000 --- a/frontend/src/common/prettier/plugins/java/options.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -declare const _default: { - entrypoint: { - type: "choice"; - category: string; - default: string; - choices: { - value: string; - description: string; - }[]; - description: string; - }; - arrowParens: { - type: "choice"; - category: string; - default: string; - choices: { - value: string; - description: string; - }[]; - description: string; - }; - trailingComma: { - type: "choice"; - category: string; - default: string; - choices: { - value: string; - description: string; - }[]; - description: string; - }; - experimentalOperatorPosition: { - type: "choice"; - category: string; - default: string; - choices: { - value: string; - description: string; - }[]; - description: string; - }; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/options.js b/frontend/src/common/prettier/plugins/java/options.js deleted file mode 100644 index 2651cb75..00000000 --- a/frontend/src/common/prettier/plugins/java/options.js +++ /dev/null @@ -1,284 +0,0 @@ -export default { - entrypoint: { - type: "choice", - category: "Global", - default: "compilationUnit", - // sed -nr 's/.*\.RULE\(([^,]+),.*/\1/p' $(ls path/to/java-parser/rules/folder/*) - choices: [ - { value: "arrayInitializer", description: "" }, - { value: "variableInitializerList", description: "" }, - { value: "block", description: "" }, - { value: "blockStatements", description: "" }, - { value: "blockStatement", description: "" }, - { value: "localVariableDeclarationStatement", description: "" }, - { value: "localVariableDeclaration", description: "" }, - { value: "localVariableType", description: "" }, - { value: "statement", description: "" }, - { value: "statementWithoutTrailingSubstatement", description: "" }, - { value: "emptyStatement", description: "" }, - { value: "labeledStatement", description: "" }, - { value: "expressionStatement", description: "" }, - { value: "statementExpression", description: "" }, - { value: "ifStatement", description: "" }, - { value: "assertStatement", description: "" }, - { value: "switchStatement", description: "" }, - { value: "switchBlock", description: "" }, - { value: "switchBlockStatementGroup", description: "" }, - { value: "switchLabel", description: "" }, - { value: "switchRule", description: "" }, - { value: "caseConstant", description: "" }, - { value: "casePattern", description: "" }, - { value: "whileStatement", description: "" }, - { value: "doStatement", description: "" }, - { value: "forStatement", description: "" }, - { value: "basicForStatement", description: "" }, - { value: "forInit", description: "" }, - { value: "forUpdate", description: "" }, - { value: "statementExpressionList", description: "" }, - { value: "enhancedForStatement", description: "" }, - { value: "breakStatement", description: "" }, - { value: "continueStatement", description: "" }, - { value: "returnStatement", description: "" }, - { value: "throwStatement", description: "" }, - { value: "synchronizedStatement", description: "" }, - { value: "tryStatement", description: "" }, - { value: "catches", description: "" }, - { value: "catchClause", description: "" }, - { value: "catchFormalParameter", description: "" }, - { value: "catchType", description: "" }, - { value: "finally", description: "" }, - { value: "tryWithResourcesStatement", description: "" }, - { value: "resourceSpecification", description: "" }, - { value: "resourceList", description: "" }, - { value: "resource", description: "" }, - { value: "yieldStatement", description: "" }, - { value: "variableAccess", description: "" }, - { value: "classDeclaration", description: "" }, - { value: "normalClassDeclaration", description: "" }, - { value: "classModifier", description: "" }, - { value: "typeParameters", description: "" }, - { value: "typeParameterList", description: "" }, - { value: "classExtends", description: "" }, - { value: "classImplements", description: "" }, - { value: "interfaceTypeList", description: "" }, - { value: "classPermits", description: "" }, - { value: "classBody", description: "" }, - { value: "classBodyDeclaration", description: "" }, - { value: "classMemberDeclaration", description: "" }, - { value: "fieldDeclaration", description: "" }, - { value: "fieldModifier", description: "" }, - { value: "variableDeclaratorList", description: "" }, - { value: "variableDeclarator", description: "" }, - { value: "variableDeclaratorId", description: "" }, - { value: "variableInitializer", description: "" }, - { value: "unannType", description: "" }, - { value: "unannPrimitiveTypeWithOptionalDimsSuffix", description: "" }, - { value: "unannPrimitiveType", description: "" }, - { value: "unannReferenceType", description: "" }, - { value: "unannClassOrInterfaceType", description: "" }, - { value: "unannClassType", description: "" }, - { value: "unannInterfaceType", description: "" }, - { value: "unannTypeVariable", description: "" }, - { value: "methodDeclaration", description: "" }, - { value: "methodModifier", description: "" }, - { value: "methodHeader", description: "" }, - { value: "result", description: "" }, - { value: "methodDeclarator", description: "" }, - { value: "receiverParameter", description: "" }, - { value: "formalParameterList", description: "" }, - { value: "formalParameter", description: "" }, - { value: "variableParaRegularParameter", description: "" }, - { value: "variableArityParameter", description: "" }, - { value: "variableModifier", description: "" }, - { value: "throws", description: "" }, - { value: "exceptionTypeList", description: "" }, - { value: "exceptionType", description: "" }, - { value: "methodBody", description: "" }, - { value: "instanceInitializer", description: "" }, - { value: "staticInitializer", description: "" }, - { value: "constructorDeclaration", description: "" }, - { value: "constructorModifier", description: "" }, - { value: "constructorDeclarator", description: "" }, - { value: "simpleTypeName", description: "" }, - { value: "constructorBody", description: "" }, - { value: "explicitConstructorInvocation", description: "" }, - { value: "unqualifiedExplicitConstructorInvocation", description: "" }, - { value: "qualifiedExplicitConstructorInvocation", description: "" }, - { value: "enumDeclaration", description: "" }, - { value: "enumBody", description: "" }, - { value: "enumConstantList", description: "" }, - { value: "enumConstant", description: "" }, - { value: "enumConstantModifier", description: "" }, - { value: "enumBodyDeclarations", description: "" }, - { value: "recordDeclaration", description: "" }, - { value: "recordHeader", description: "" }, - { value: "recordComponentList", description: "" }, - { value: "recordComponent", description: "" }, - { value: "variableArityRecordComponent", description: "" }, - { value: "recordComponentModifier", description: "" }, - { value: "recordBody", description: "" }, - { value: "recordBodyDeclaration", description: "" }, - { value: "compactConstructorDeclaration", description: "" }, - { value: "isDims", description: "" }, - { value: "expression", description: "" }, - { value: "lambdaExpression", description: "" }, - { value: "lambdaParameters", description: "" }, - { value: "lambdaParametersWithBraces", description: "" }, - { value: "lambdaParameterList", description: "" }, - { value: "conciseLambdaParameterList", description: "" }, - { value: "normalLambdaParameterList", description: "" }, - { value: "normalLambdaParameter", description: "" }, - { value: "regularLambdaParameter", description: "" }, - { value: "lambdaParameterType", description: "" }, - { value: "conciseLambdaParameter", description: "" }, - { value: "lambdaBody", description: "" }, - { value: "conditionalExpression", description: "" }, - { value: "binaryExpression", description: "" }, - { value: "unaryExpression", description: "" }, - { value: "unaryExpressionNotPlusMinus", description: "" }, - { value: "primary", description: "" }, - { value: "primaryPrefix", description: "" }, - { value: "primarySuffix", description: "" }, - { value: "fqnOrRefType", description: "" }, - { value: "fqnOrRefTypePartRest", description: "" }, - { value: "fqnOrRefTypePartCommon", description: "" }, - { value: "fqnOrRefTypePartFirst", description: "" }, - { value: "parenthesisExpression", description: "" }, - { value: "castExpression", description: "" }, - { value: "primitiveCastExpression", description: "" }, - { value: "referenceTypeCastExpression", description: "" }, - { value: "newExpression", description: "" }, - { value: "unqualifiedClassInstanceCreationExpression", description: "" }, - { value: "classOrInterfaceTypeToInstantiate", description: "" }, - { value: "typeArgumentsOrDiamond", description: "" }, - { value: "diamond", description: "" }, - { value: "methodInvocationSuffix", description: "" }, - { value: "argumentList", description: "" }, - { value: "arrayCreationExpression", description: "" }, - { - value: "arrayCreationExpressionWithoutInitializerSuffix", - description: "" - }, - { value: "arrayCreationWithInitializerSuffix", description: "" }, - { value: "dimExprs", description: "" }, - { value: "dimExpr", description: "" }, - { value: "classLiteralSuffix", description: "" }, - { value: "arrayAccessSuffix", description: "" }, - { value: "methodReferenceSuffix", description: "" }, - { value: "templateArgument", description: "" }, - { value: "template", description: "" }, - { value: "stringTemplate", description: "" }, - { value: "textBlockTemplate", description: "" }, - { value: "embeddedExpression", description: "" }, - { value: "pattern", description: "" }, - { value: "typePattern", description: "" }, - { value: "recordPattern", description: "" }, - { value: "componentPatternList", description: "" }, - { value: "componentPattern", description: "" }, - { value: "matchAllPattern", description: "" }, - { value: "guard", description: "" }, - { value: "isRefTypeInMethodRef", description: "" }, - { value: "interfaceDeclaration", description: "" }, - { value: "normalInterfaceDeclaration", description: "" }, - { value: "interfaceModifier", description: "" }, - { value: "interfaceExtends", description: "" }, - { value: "interfacePermits", description: "" }, - { value: "interfaceBody", description: "" }, - { value: "interfaceMemberDeclaration", description: "" }, - { value: "constantDeclaration", description: "" }, - { value: "constantModifier", description: "" }, - { value: "interfaceMethodDeclaration", description: "" }, - { value: "interfaceMethodModifier", description: "" }, - { value: "annotationInterfaceDeclaration", description: "" }, - { value: "annotationInterfaceBody", description: "" }, - { value: "annotationInterfaceMemberDeclaration", description: "" }, - { value: "annotationInterfaceElementDeclaration", description: "" }, - { value: "annotationInterfaceElementModifier", description: "" }, - { value: "defaultValue", description: "" }, - { value: "annotation", description: "" }, - { value: "elementValuePairList", description: "" }, - { value: "elementValuePair", description: "" }, - { value: "elementValue", description: "" }, - { value: "elementValueArrayInitializer", description: "" }, - { value: "elementValueList", description: "" }, - { value: "literal", description: "" }, - { value: "integerLiteral", description: "" }, - { value: "floatingPointLiteral", description: "" }, - { value: "booleanLiteral", description: "" }, - { value: "shiftOperator", description: "" }, - { value: "moduleName", description: "" }, - { value: "packageName", description: "" }, - { value: "typeName", description: "" }, - { value: "expressionName", description: "" }, - { value: "methodName", description: "" }, - { value: "packageOrTypeName", description: "" }, - { value: "ambiguousName", description: "" }, - { value: "compilationUnit", description: "" }, - { value: "ordinaryCompilationUnit", description: "" }, - { value: "modularCompilationUnit", description: "" }, - { value: "packageDeclaration", description: "" }, - { value: "packageModifier", description: "" }, - { value: "importDeclaration", description: "" }, - { value: "typeDeclaration", description: "" }, - { value: "moduleDeclaration", description: "" }, - { value: "moduleDirective", description: "" }, - { value: "requiresModuleDirective", description: "" }, - { value: "exportsModuleDirective", description: "" }, - { value: "opensModuleDirective", description: "" }, - { value: "usesModuleDirective", description: "" }, - { value: "providesModuleDirective", description: "" }, - { value: "requiresModifier", description: "" }, - { value: "primitiveType", description: "" }, - { value: "numericType", description: "" }, - { value: "integralType", description: "" }, - { value: "floatingPointType", description: "" }, - { value: "referenceType", description: "" }, - { value: "classOrInterfaceType", description: "" }, - { value: "classType", description: "" }, - { value: "interfaceType", description: "" }, - { value: "typeVariable", description: "" }, - { value: "dims", description: "" }, - { value: "typeParameter", description: "" }, - { value: "typeParameterModifier", description: "" }, - { value: "typeBound", description: "" }, - { value: "additionalBound", description: "" }, - { value: "typeArguments", description: "" }, - { value: "typeArgumentList", description: "" }, - { value: "typeArgument", description: "" }, - { value: "wildcard", description: "" }, - { value: "wildcardBounds", description: "" } - ], - description: "Prettify from the entrypoint, allowing to use prettier on snippet." - }, - arrowParens: { - type: "choice", - category: "Java", - default: "always", - choices: [ - { value: "always", description: "" }, - { value: "avoid", description: "" } - ], - description: "Include parentheses around a sole arrow function parameter." - }, - trailingComma: { - type: "choice", - category: "Java", - default: "all", - choices: [ - { value: "all", description: "" }, - { value: "es5", description: "" }, - { value: "none", description: "" } - ], - description: "Print trailing commas wherever possible when multi-line." - }, - experimentalOperatorPosition: { - type: "choice", - category: "Java", - default: "end", - choices: [ - { value: "start", description: "" }, - { value: "end", description: "" } - ], - description: "Where to print operators when binary expressions wrap lines." - } -}; diff --git a/frontend/src/common/prettier/plugins/java/parser.d.ts b/frontend/src/common/prettier/plugins/java/parser.d.ts deleted file mode 100644 index 4ed8b4d6..00000000 --- a/frontend/src/common/prettier/plugins/java/parser.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { type JavaNode, type JavaNonTerminal, type JavaParserOptions } from "./printers/helpers.js"; -declare const _default: { - parse(text: string, options: JavaParserOptions): JavaNonTerminal; - astFormat: string; - hasPragma(text: string): boolean; - locStart(node: JavaNode): number; - locEnd(node: JavaNode): number; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/parser.js b/frontend/src/common/prettier/plugins/java/parser.js deleted file mode 100644 index f5befcf9..00000000 --- a/frontend/src/common/prettier/plugins/java/parser.js +++ /dev/null @@ -1,24 +0,0 @@ -import { parse } from "java-parser"; -import { determineFormatterOffOnRanges } from "./comments.js"; -import { isTerminal } from "./printers/helpers.js"; -export default { - parse(text, options) { - var _a; - const cst = parse(text, options.entrypoint); - (_a = cst.comments) === null || _a === void 0 ? void 0 : _a.forEach(comment => { - comment.value = comment.image; - }); - determineFormatterOffOnRanges(cst); - return cst; - }, - astFormat: "java", - hasPragma(text) { - return /^\/\*\*\n\s+\*\s@(format|prettier)\n\s+\*\//.test(text); - }, - locStart(node) { - return isTerminal(node) ? node.startOffset : node.location.startOffset; - }, - locEnd(node) { - return (isTerminal(node) ? node.endOffset : node.location.endOffset) + 1; - } -}; diff --git a/frontend/src/common/prettier/plugins/java/printer.d.ts b/frontend/src/common/prettier/plugins/java/printer.d.ts deleted file mode 100644 index 565fc6eb..00000000 --- a/frontend/src/common/prettier/plugins/java/printer.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { AstPath } from "prettier"; -import { canAttachComment, handleLineComment, handleRemainingComment } from "./comments.js"; -import { type JavaNode } from "./printers/helpers.js"; -declare const _default: { - print(path: DistributedAstPath, options: import("prettier").ParserOptions, print: (path: AstPath) => import("prettier").Doc, args: unknown): import("prettier/doc.js").builders.Doc; - hasPrettierIgnore(path: AstPath): boolean; - canAttachComment: typeof canAttachComment; - isBlockComment(node: JavaNode): boolean; - printComment(commentPath: AstPath): string | import("prettier/doc.js").builders.Doc[]; - getCommentChildNodes(node: JavaNode): any[]; - handleComments: { - ownLine: typeof handleLineComment; - endOfLine: typeof handleLineComment; - remaining: typeof handleRemainingComment; - }; -}; -export default _default; -type DistributedAstPath = T extends any ? AstPath : never; diff --git a/frontend/src/common/prettier/plugins/java/printer.js b/frontend/src/common/prettier/plugins/java/printer.js deleted file mode 100644 index 88b01648..00000000 --- a/frontend/src/common/prettier/plugins/java/printer.js +++ /dev/null @@ -1,40 +0,0 @@ -import { canAttachComment, handleLineComment, handleRemainingComment, isFullyBetweenFormatterOffOn } from "./comments.js"; -import { isNonTerminal, isTerminal, printComment } from "./printers/helpers.js"; -import { printerForNodeType } from "./printers/index.js"; -export default { - print(path, options, print, args) { - return hasTerminal(path) - ? path.node.image - : printerForNodeType(path.node.name)(path, print, options, args); - }, - hasPrettierIgnore(path) { - var _a; - const { node } = path; - return (((_a = node.comments) === null || _a === void 0 ? void 0 : _a.some(({ image }) => /^(\/\/\s*prettier-ignore|\/\*\s*prettier-ignore\s*\*\/)$/.test(image))) === true || - (canAttachComment(node) && isFullyBetweenFormatterOffOn(path))); - }, - canAttachComment, - isBlockComment(node) { - return isTerminal(node) && node.tokenType.name === "TraditionalComment"; - }, - printComment(commentPath) { - const { node } = commentPath; - if (isNonTerminal(node) || node.tokenType.GROUP !== "comments") { - throw new Error(`Not a comment: ${JSON.stringify(node)}`); - } - return printComment(node); - }, - getCommentChildNodes(node) { - return isNonTerminal(node) - ? Object.values(node.children).flatMap(child => child) - : []; - }, - handleComments: { - ownLine: handleLineComment, - endOfLine: handleLineComment, - remaining: handleRemainingComment - } -}; -function hasTerminal(path) { - return isTerminal(path.node); -} diff --git a/frontend/src/common/prettier/plugins/java/printers/arrays.d.ts b/frontend/src/common/prettier/plugins/java/printers/arrays.d.ts deleted file mode 100644 index ab22bfc9..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/arrays.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -declare const _default: { - arrayInitializer(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn, options: import("./helpers.js").JavaParserOptions): import("prettier/doc.js").builders.Group | "{}"; - variableInitializerList(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): import("prettier/doc.js").builders.Doc[]; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/printers/arrays.js b/frontend/src/common/prettier/plugins/java/printers/arrays.js deleted file mode 100644 index 704bc6c0..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/arrays.js +++ /dev/null @@ -1,9 +0,0 @@ -import { printArrayInitializer, printList } from "./helpers.js"; -export default { - arrayInitializer(path, print, options) { - return printArrayInitializer(path, print, options, "variableInitializerList"); - }, - variableInitializerList(path, print) { - return printList(path, print, "variableInitializer"); - } -}; diff --git a/frontend/src/common/prettier/plugins/java/printers/blocks-and-statements.d.ts b/frontend/src/common/prettier/plugins/java/printers/blocks-and-statements.d.ts deleted file mode 100644 index a5af49ee..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/blocks-and-statements.d.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { builders } from "prettier/doc"; -import { printSingle } from "./helpers.js"; -declare const _default: { - block(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[]; - blockStatements(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - blockStatement: typeof printSingle; - localVariableDeclarationStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - localVariableDeclaration(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - localVariableType: typeof printSingle; - statement: typeof printSingle; - statementWithoutTrailingSubstatement: typeof printSingle; - emptyStatement(): string; - labeledStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - expressionStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - statementExpression: typeof printSingle; - ifStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - assertStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - switchStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - switchBlock(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[]; - switchBlockStatementGroup(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - switchLabel(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): "default" | builders.Group | builders.Doc[]; - switchRule(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - caseConstant: typeof printSingle; - casePattern: typeof printSingle; - whileStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - doStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): (string | builders.Group | builders.Doc[])[]; - forStatement: typeof printSingle; - basicForStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - forInit: typeof printSingle; - forUpdate: typeof printSingle; - statementExpressionList(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Group; - enhancedForStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Group; - breakStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[] | "break;"; - continueStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[] | "continue;"; - returnStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - throwStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - synchronizedStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - tryStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc; - catches(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - catchClause(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - catchFormalParameter(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - catchType(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - finally(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - tryWithResourcesStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - resourceSpecification(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Group | "()"; - resourceList(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - resource: typeof printSingle; - yieldStatement(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - variableAccess: typeof printSingle; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/printers/blocks-and-statements.js b/frontend/src/common/prettier/plugins/java/printers/blocks-and-statements.js deleted file mode 100644 index 224f1565..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/blocks-and-statements.js +++ /dev/null @@ -1,337 +0,0 @@ -import { builders } from "prettier/doc"; -import { call, definedKeys, indentInParentheses, isBinaryExpression, isEmptyStatement, lineEndWithComments, lineStartWithComments, map, onlyDefinedKey, printBlock, printDanglingComments, printSingle, printWithModifiers } from "./helpers.js"; -const { group, hardline, ifBreak, indent, join, line, softline } = builders; -export default { - block(path, print) { - const statements = path.node.children.blockStatements - ? call(path, print, "blockStatements") - : []; - return printBlock(path, statements.length ? [statements] : []); - }, - blockStatements(path, print) { - return join(hardline, map(path, statementPath => { - const { node, previous } = statementPath; - const statement = print(statementPath); - return previous && - lineStartWithComments(node) > lineEndWithComments(previous) + 1 - ? [hardline, statement] - : statement; - }, "blockStatement").filter(doc => doc !== "")); - }, - blockStatement: printSingle, - localVariableDeclarationStatement(path, print) { - return [call(path, print, "localVariableDeclaration"), ";"]; - }, - localVariableDeclaration(path, print) { - const declaration = join(" ", [ - call(path, print, "localVariableType"), - call(path, print, "variableDeclaratorList") - ]); - return printWithModifiers(path, print, "variableModifier", declaration); - }, - localVariableType: printSingle, - statement: printSingle, - statementWithoutTrailingSubstatement: printSingle, - emptyStatement() { - return ""; - }, - labeledStatement(path, print) { - return [ - call(path, print, "Identifier"), - ": ", - call(path, print, "statement") - ]; - }, - expressionStatement(path, print) { - return [call(path, print, "statementExpression"), ";"]; - }, - statementExpression: printSingle, - ifStatement(path, print) { - var _a; - const { children } = path.node; - const hasEmptyStatement = isEmptyStatement(children.statement[0]); - const statements = map(path, print, "statement"); - const statement = [ - "if ", - indentInParentheses(call(path, print, "expression")), - hasEmptyStatement ? ";" : [" ", statements[0]] - ]; - if (children.Else) { - const danglingComments = printDanglingComments(path); - if (danglingComments.length) { - statement.push(hardline, ...danglingComments, hardline); - } - else { - const elseHasBlock = ((_a = children.statement[0].children - .statementWithoutTrailingSubstatement) === null || _a === void 0 ? void 0 : _a[0].children.block) !== - undefined; - statement.push(elseHasBlock ? " " : hardline); - } - const elseHasEmptyStatement = isEmptyStatement(children.statement[1]); - statement.push("else", elseHasEmptyStatement ? ";" : [" ", statements[1]]); - } - return statement; - }, - assertStatement(path, print) { - return ["assert ", ...join([" : "], map(path, print, "expression")), ";"]; - }, - switchStatement(path, print) { - return join(" ", [ - "switch", - indentInParentheses(call(path, print, "expression")), - call(path, print, "switchBlock") - ]); - }, - switchBlock(path, print) { - const { children } = path.node; - const caseKeys = definedKeys(children, [ - "switchBlockStatementGroup", - "switchRule" - ]); - const cases = caseKeys.length === 1 ? map(path, print, caseKeys[0]) : []; - return printBlock(path, cases); - }, - switchBlockStatementGroup(path, print) { - var _a, _b; - const { children } = path.node; - const switchLabel = call(path, print, "switchLabel"); - if (!children.blockStatements) { - return [switchLabel, ":"]; - } - const blockStatements = call(path, print, "blockStatements"); - const statements = children.blockStatements[0].children.blockStatement; - const onlyStatementIsBlock = statements.length === 1 && - ((_b = (_a = statements[0].children.statement) === null || _a === void 0 ? void 0 : _a[0].children.statementWithoutTrailingSubstatement) === null || _b === void 0 ? void 0 : _b[0].children.block) !== undefined; - return [ - switchLabel, - ":", - onlyStatementIsBlock - ? [" ", blockStatements] - : indent([hardline, blockStatements]) - ]; - }, - switchLabel(path, print) { - var _a, _b; - const { children } = path.node; - if (!((_b = (_a = children.caseConstant) !== null && _a !== void 0 ? _a : children.casePattern) !== null && _b !== void 0 ? _b : children.Null)) { - return "default"; - } - const values = []; - if (children.Null) { - values.push("null"); - if (children.Default) { - values.push("default"); - } - } - else { - const valuesKey = onlyDefinedKey(children, [ - "caseConstant", - "casePattern" - ]); - values.push(...map(path, print, valuesKey)); - } - const hasMultipleValues = values.length > 1; - const label = hasMultipleValues - ? ["case", indent([line, ...join([",", line], values)])] - : ["case ", values[0]]; - return children.guard - ? [ - group([...label, hasMultipleValues ? line : " "]), - call(path, print, "guard") - ] - : group(label); - }, - switchRule(path, print) { - const { children } = path.node; - const bodyKey = onlyDefinedKey(children, [ - "block", - "expression", - "throwStatement" - ]); - const parts = [ - call(path, print, "switchLabel"), - " -> ", - call(path, print, bodyKey) - ]; - if (children.Semicolon) { - parts.push(";"); - } - return parts; - }, - caseConstant: printSingle, - casePattern: printSingle, - whileStatement(path, print) { - const statement = call(path, print, "statement"); - const hasEmptyStatement = isEmptyStatement(path.node.children.statement[0]); - return [ - "while ", - indentInParentheses(call(path, print, "expression")), - ...[hasEmptyStatement ? ";" : " ", statement] - ]; - }, - doStatement(path, print) { - const hasEmptyStatement = isEmptyStatement(path.node.children.statement[0]); - return [ - "do", - hasEmptyStatement ? ";" : [" ", call(path, print, "statement")], - " while ", - indentInParentheses(call(path, print, "expression")), - ";" - ]; - }, - forStatement: printSingle, - basicForStatement(path, print) { - const { children } = path.node; - const danglingComments = printDanglingComments(path); - if (danglingComments.length) { - danglingComments.push(hardline); - } - const expressions = ["forInit", "expression", "forUpdate"].map(expressionKey => expressionKey in children ? call(path, print, expressionKey) : ""); - const hasEmptyStatement = isEmptyStatement(children.statement[0]); - return [ - ...danglingComments, - "for ", - expressions.some(expression => expression !== "") - ? indentInParentheses(join([";", line], expressions)) - : "(;;)", - hasEmptyStatement ? ";" : [" ", call(path, print, "statement")] - ]; - }, - forInit: printSingle, - forUpdate: printSingle, - statementExpressionList(path, print) { - return group(map(path, print, "statementExpression").map((expression, index) => index === 0 ? expression : [",", indent([line, expression])])); - }, - enhancedForStatement(path, print) { - var _a; - const statementNode = path.node.children.statement[0]; - const forStatement = [ - printDanglingComments(path), - "for ", - "(", - call(path, print, "localVariableDeclaration"), - " : ", - call(path, print, "expression"), - ")" - ]; - if (isEmptyStatement(statementNode)) { - forStatement.push(";"); - } - else { - const hasStatementBlock = ((_a = statementNode.children.statementWithoutTrailingSubstatement) === null || _a === void 0 ? void 0 : _a[0].children.block) !== undefined; - const statement = call(path, print, "statement"); - forStatement.push(hasStatementBlock ? [" ", statement] : indent([line, statement])); - } - return group(forStatement); - }, - breakStatement(path, print) { - return path.node.children.Identifier - ? ["break ", call(path, print, "Identifier"), ";"] - : "break;"; - }, - continueStatement(path, print) { - return path.node.children.Identifier - ? ["continue ", call(path, print, "Identifier"), ";"] - : "continue;"; - }, - returnStatement(path, print) { - const { children } = path.node; - const statement = ["return"]; - if (children.expression) { - statement.push(" "); - const expression = call(path, print, "expression"); - if (isBinaryExpression(children.expression[0])) { - statement.push(group([ - ifBreak("("), - indent([softline, expression]), - softline, - ifBreak(")") - ])); - } - else { - statement.push(expression); - } - } - statement.push(";"); - return statement; - }, - throwStatement(path, print) { - return ["throw ", call(path, print, "expression"), ";"]; - }, - synchronizedStatement(path, print) { - return [ - "synchronized ", - indentInParentheses(call(path, print, "expression")), - " ", - call(path, print, "block") - ]; - }, - tryStatement(path, print) { - const { children } = path.node; - if (children.tryWithResourcesStatement) { - return call(path, print, "tryWithResourcesStatement"); - } - const blocks = ["try", call(path, print, "block")]; - if (children.catches) { - blocks.push(call(path, print, "catches")); - } - if (children.finally) { - blocks.push(call(path, print, "finally")); - } - return join(" ", blocks); - }, - catches(path, print) { - return join(" ", map(path, print, "catchClause")); - }, - catchClause(path, print) { - return [ - "catch ", - indentInParentheses(call(path, print, "catchFormalParameter")), - " ", - call(path, print, "block") - ]; - }, - catchFormalParameter(path, print) { - return join(" ", [ - ...map(path, print, "variableModifier"), - call(path, print, "catchType"), - call(path, print, "variableDeclaratorId") - ]); - }, - catchType(path, print) { - return join([line, "| "], [call(path, print, "unannClassType"), ...map(path, print, "classType")]); - }, - finally(path, print) { - return ["finally ", call(path, print, "block")]; - }, - tryWithResourcesStatement(path, print) { - const { children } = path.node; - const blocks = [ - "try", - call(path, print, "resourceSpecification"), - call(path, print, "block") - ]; - if (children.catches) { - blocks.push(call(path, print, "catches")); - } - if (children.finally) { - blocks.push(call(path, print, "finally")); - } - return join(" ", blocks); - }, - resourceSpecification(path, print) { - const resources = [call(path, print, "resourceList")]; - if (path.node.children.Semicolon) { - resources.push(ifBreak(";")); - } - return indentInParentheses(resources); - }, - resourceList(path, print) { - return join([";", line], map(path, print, "resource")); - }, - resource: printSingle, - yieldStatement(path, print) { - return ["yield ", call(path, print, "expression"), ";"]; - }, - variableAccess: printSingle -}; diff --git a/frontend/src/common/prettier/plugins/java/printers/classes.d.ts b/frontend/src/common/prettier/plugins/java/printers/classes.d.ts deleted file mode 100644 index dae34f5e..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/classes.d.ts +++ /dev/null @@ -1,157 +0,0 @@ -import type { ClassBodyCstNode, EnumBodyDeclarationsCstNode } from "java-parser"; -import type { AstPath } from "prettier"; -import { builders } from "prettier/doc"; -import { printClassPermits, printClassType, printSingle, type JavaPrintFn } from "./helpers.js"; -declare const _default: { - classDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc[]; - normalClassDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc[]; - classModifier: typeof printSingle; - typeParameters(path: AstPath, print: JavaPrintFn): builders.Group; - typeParameterList(path: AstPath, print: JavaPrintFn): builders.Doc[]; - classExtends(path: AstPath, print: JavaPrintFn): builders.Doc[]; - classImplements(path: AstPath, print: JavaPrintFn): builders.Group; - classPermits: typeof printClassPermits; - interfaceTypeList(path: AstPath, print: JavaPrintFn): builders.Group; - classBody(path: AstPath, print: JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[]; - classBodyDeclaration: typeof printSingle; - classMemberDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc; - fieldDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc[]; - fieldModifier: typeof printSingle; - variableDeclaratorList(path: AstPath, print: JavaPrintFn): builders.Group | builders.Doc[]; - variableDeclarator(path: AstPath, print: JavaPrintFn): builders.Doc; - variableDeclaratorId(path: AstPath, print: JavaPrintFn): builders.Doc; - variableInitializer: typeof printSingle; - unannType: typeof printSingle; - unannPrimitiveTypeWithOptionalDimsSuffix(path: AstPath, print: JavaPrintFn): builders.Doc; - unannPrimitiveType: typeof printSingle; - unannReferenceType(path: AstPath, print: JavaPrintFn): builders.Doc; - unannClassOrInterfaceType: typeof printSingle; - unannClassType: typeof printClassType; - unannInterfaceType: typeof printSingle; - unannTypeVariable: typeof printSingle; - methodDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc[]; - methodModifier: typeof printSingle; - methodHeader(path: AstPath, print: JavaPrintFn): builders.Group; - result: typeof printSingle; - methodDeclarator(path: AstPath, print: JavaPrintFn): builders.Doc[]; - receiverParameter(path: AstPath, print: JavaPrintFn): builders.Doc[]; - formalParameterList(path: AstPath, print: JavaPrintFn): builders.Doc[]; - formalParameter: typeof printSingle; - variableParaRegularParameter(path: AstPath, print: JavaPrintFn): builders.Doc[]; - variableArityParameter(path: AstPath, print: JavaPrintFn): builders.Doc[]; - variableModifier: typeof printSingle; - throws(path: AstPath, print: JavaPrintFn): builders.Doc[]; - exceptionTypeList(path: AstPath, print: JavaPrintFn): builders.Doc[]; - exceptionType: typeof printSingle; - methodBody: typeof printSingle; - instanceInitializer: typeof printSingle; - staticInitializer(path: AstPath, print: JavaPrintFn): builders.Doc[]; - constructorDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc[]; - constructorModifier: typeof printSingle; - constructorDeclarator(path: AstPath, print: JavaPrintFn): builders.Doc[]; - simpleTypeName: typeof printSingle; - constructorBody(path: AstPath, print: JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[]; - explicitConstructorInvocation: typeof printSingle; - unqualifiedExplicitConstructorInvocation(path: AstPath, print: JavaPrintFn): builders.Doc[]; - qualifiedExplicitConstructorInvocation(path: AstPath, print: JavaPrintFn): builders.Doc[]; - enumDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc[]; - enumBody(path: AstPath, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[]; - enumConstantList(path: AstPath, print: JavaPrintFn): builders.Doc[]; - enumConstant(path: AstPath, print: JavaPrintFn): builders.Doc[]; - enumConstantModifier: typeof printSingle; - enumBodyDeclarations(path: AstPath, print: JavaPrintFn): builders.Doc[]; - recordDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc[]; - recordHeader(path: AstPath, print: JavaPrintFn): builders.Group | "()"; - recordComponentList(path: AstPath, print: JavaPrintFn): builders.Doc[]; - recordComponent(path: AstPath, print: JavaPrintFn): builders.Group; - variableArityRecordComponent(path: AstPath, print: JavaPrintFn): builders.Doc[]; - recordComponentModifier: typeof printSingle; - recordBody(path: AstPath, print: JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[]; - recordBodyDeclaration: typeof printSingle; - compactConstructorDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc[]; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/printers/classes.js b/frontend/src/common/prettier/plugins/java/printers/classes.js deleted file mode 100644 index b69b51d2..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/classes.js +++ /dev/null @@ -1,446 +0,0 @@ -import { builders } from "prettier/doc"; -import { call, each, hasDeclarationAnnotations, hasLeadingComments, indentInParentheses, isBinaryExpression, lineEndWithComments, lineStartWithComments, map, onlyDefinedKey, printBlock, printClassPermits, printClassType, printDanglingComments, printList, printSingle, printWithModifiers } from "./helpers.js"; -const { group, hardline, indent, indentIfBreak, join, line, softline } = builders; -export default { - classDeclaration(path, print) { - const declarationKey = onlyDefinedKey(path.node.children, [ - "enumDeclaration", - "normalClassDeclaration", - "recordDeclaration" - ]); - const declaration = call(path, print, declarationKey); - return printWithModifiers(path, print, "classModifier", declaration, true); - }, - normalClassDeclaration(path, print) { - const { classExtends, classImplements, classPermits, typeParameters } = path.node.children; - const header = ["class ", call(path, print, "typeIdentifier")]; - if (typeParameters) { - header.push(call(path, print, "typeParameters")); - } - if (classExtends) { - header.push(indent([line, call(path, print, "classExtends")])); - } - if (classImplements) { - header.push(indent([line, call(path, print, "classImplements")])); - } - if (classPermits) { - header.push(indent([line, call(path, print, "classPermits")])); - } - return [group(header), " ", call(path, print, "classBody")]; - }, - classModifier: printSingle, - typeParameters(path, print) { - return group([ - "<", - indent([softline, call(path, print, "typeParameterList")]), - softline, - ">" - ]); - }, - typeParameterList(path, print) { - return printList(path, print, "typeParameter"); - }, - classExtends(path, print) { - return ["extends ", call(path, print, "classType")]; - }, - classImplements(path, print) { - return group([ - "implements", - indent([line, call(path, print, "interfaceTypeList")]) - ]); - }, - classPermits: printClassPermits, - interfaceTypeList(path, print) { - return group(printList(path, print, "interfaceType")); - }, - classBody(path, print) { - return printBlock(path, printClassBodyDeclarations(path, print)); - }, - classBodyDeclaration: printSingle, - classMemberDeclaration(path, print) { - const { children } = path.node; - return children.Semicolon - ? "" - : call(path, print, onlyDefinedKey(children)); - }, - fieldDeclaration(path, print) { - const declaration = [ - call(path, print, "unannType"), - " ", - call(path, print, "variableDeclaratorList"), - ";" - ]; - return printWithModifiers(path, print, "fieldModifier", declaration); - }, - fieldModifier: printSingle, - variableDeclaratorList(path, print) { - var _a; - const declarators = map(path, print, "variableDeclarator"); - return declarators.length > 1 && - path.node.children.variableDeclarator.some(({ children }) => children.Equals) - ? group(indent(join([",", line], declarators)), { - shouldBreak: ((_a = path.getNode(4)) === null || _a === void 0 ? void 0 : _a.name) !== "forInit" - }) - : join(", ", declarators); - }, - variableDeclarator(path, print) { - var _a, _b; - const { children } = path.node; - const variableInitializer = (_a = children.variableInitializer) === null || _a === void 0 ? void 0 : _a[0]; - const declaratorId = call(path, print, "variableDeclaratorId"); - if (!variableInitializer) { - return declaratorId; - } - const expression = (_b = variableInitializer.children.expression) === null || _b === void 0 ? void 0 : _b[0]; - const declarator = [declaratorId, " ", call(path, print, "Equals")]; - const initializer = call(path, print, "variableInitializer"); - if (hasLeadingComments(variableInitializer) || - (expression && isBinaryExpression(expression))) { - declarator.push(group(indent([line, initializer]))); - } - else { - const groupId = Symbol("assignment"); - declarator.push(group(indent(line), { id: groupId }), indentIfBreak(initializer, { groupId })); - } - return group(declarator); - }, - variableDeclaratorId(path, print) { - const { dims, Underscore } = path.node.children; - if (Underscore) { - return "_"; - } - const identifier = call(path, print, "Identifier"); - return dims ? [identifier, call(path, print, "dims")] : identifier; - }, - variableInitializer: printSingle, - unannType: printSingle, - unannPrimitiveTypeWithOptionalDimsSuffix(path, print) { - const type = call(path, print, "unannPrimitiveType"); - return path.node.children.dims ? [type, call(path, print, "dims")] : type; - }, - unannPrimitiveType: printSingle, - unannReferenceType(path, print) { - const type = call(path, print, "unannClassOrInterfaceType"); - return path.node.children.dims ? [type, call(path, print, "dims")] : type; - }, - unannClassOrInterfaceType: printSingle, - unannClassType: printClassType, - unannInterfaceType: printSingle, - unannTypeVariable: printSingle, - methodDeclaration(path, print) { - const declaration = [ - call(path, print, "methodHeader"), - path.node.children.methodBody[0].children.Semicolon ? "" : " ", - call(path, print, "methodBody") - ]; - return printWithModifiers(path, print, "methodModifier", declaration); - }, - methodModifier: printSingle, - methodHeader(path, print) { - const { typeParameters, annotation, throws } = path.node.children; - const header = []; - if (typeParameters) { - header.push(call(path, print, "typeParameters")); - } - if (annotation) { - header.push(join(line, map(path, print, "annotation"))); - } - header.push(call(path, print, "result"), call(path, print, "methodDeclarator")); - return throws - ? group([ - ...join(" ", header), - group(indent([line, call(path, print, "throws")])) - ]) - : group(join(" ", header)); - }, - result: printSingle, - methodDeclarator(path, print) { - const { dims, formalParameterList, receiverParameter } = path.node.children; - const declarator = [call(path, print, "Identifier")]; - const parameters = []; - if (receiverParameter) { - parameters.push(call(path, print, "receiverParameter")); - } - if (formalParameterList) { - parameters.push(call(path, print, "formalParameterList")); - } - const items = parameters.length - ? join([",", line], parameters) - : printDanglingComments(path); - declarator.push(items.length ? indentInParentheses(items) : "()"); - if (dims) { - declarator.push(call(path, print, "dims")); - } - return declarator; - }, - receiverParameter(path, print) { - return join(" ", [ - ...map(path, print, "annotation"), - call(path, print, "unannType"), - path.node.children.Identifier - ? [call(path, print, "Identifier"), ".this"] - : "this" - ]); - }, - formalParameterList(path, print) { - return printList(path, print, "formalParameter"); - }, - formalParameter: printSingle, - variableParaRegularParameter(path, print) { - return join(" ", [ - ...map(path, print, "variableModifier"), - call(path, print, "unannType"), - call(path, print, "variableDeclaratorId") - ]); - }, - variableArityParameter(path, print) { - const type = join(" ", [ - ...map(path, print, "variableModifier"), - call(path, print, "unannType"), - ...map(path, print, "annotation") - ]); - return [type, "... ", call(path, print, "Identifier")]; - }, - variableModifier: printSingle, - throws(path, print) { - return ["throws ", call(path, print, "exceptionTypeList")]; - }, - exceptionTypeList(path, print) { - return join(", ", map(path, print, "exceptionType")); - }, - exceptionType: printSingle, - methodBody: printSingle, - instanceInitializer: printSingle, - staticInitializer(path, print) { - return ["static ", call(path, print, "block")]; - }, - constructorDeclaration(path, print) { - const declaration = [call(path, print, "constructorDeclarator")]; - if (path.node.children.throws) { - declaration.push(group(indent([line, call(path, print, "throws")]))); - } - declaration.push(" ", call(path, print, "constructorBody")); - return printWithModifiers(path, print, "constructorModifier", declaration, true); - }, - constructorModifier: printSingle, - constructorDeclarator(path, print) { - const { children } = path.node; - const parameters = []; - if (children.receiverParameter) { - parameters.push(call(path, print, "receiverParameter")); - } - if (children.formalParameterList) { - parameters.push(call(path, print, "formalParameterList")); - } - const header = [call(path, print, "simpleTypeName")]; - header.push(parameters.length - ? indentInParentheses(join([",", line], parameters)) - : "()"); - return children.typeParameters - ? [call(path, print, "typeParameters"), " ", ...header] - : header; - }, - simpleTypeName: printSingle, - constructorBody(path, print) { - const { children } = path.node; - const statements = []; - if (children.explicitConstructorInvocation) { - statements.push(call(path, print, "explicitConstructorInvocation")); - } - if (children.blockStatements) { - statements.push(call(path, print, "blockStatements")); - } - return printBlock(path, statements); - }, - explicitConstructorInvocation: printSingle, - unqualifiedExplicitConstructorInvocation(path, print) { - const { children } = path.node; - const invocation = []; - if (children.typeArguments) { - invocation.push(call(path, print, "typeArguments")); - } - invocation.push(children.Super ? "super" : "this"); - if (children.argumentList) { - invocation.push(group(["(", call(path, print, "argumentList"), ")"])); - } - else { - invocation.push(indentInParentheses(printDanglingComments(path), { shouldBreak: true })); - } - invocation.push(";"); - return invocation; - }, - qualifiedExplicitConstructorInvocation(path, print) { - const { children } = path.node; - const invocation = [call(path, print, "expressionName"), "."]; - if (children.typeArguments) { - invocation.push(call(path, print, "typeArguments")); - } - invocation.push("super"); - if (children.argumentList) { - invocation.push(group(["(", call(path, print, "argumentList"), ")"])); - } - else { - invocation.push(indentInParentheses(printDanglingComments(path), { shouldBreak: true })); - } - invocation.push(";"); - return invocation; - }, - enumDeclaration(path, print) { - const header = ["enum", call(path, print, "typeIdentifier")]; - if (path.node.children.classImplements) { - header.push(call(path, print, "classImplements")); - } - return join(" ", [...header, call(path, print, "enumBody")]); - }, - enumBody(path, print, options) { - var _a; - const { children } = path.node; - const contents = []; - const hasNonEmptyDeclaration = ((_a = children.enumBodyDeclarations) !== null && _a !== void 0 ? _a : []) - .flatMap(({ children }) => { var _a; return (_a = children.classBodyDeclaration) !== null && _a !== void 0 ? _a : []; }) - .some(({ children }) => { var _a; return !((_a = children.classMemberDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.Semicolon); }); - if (children.enumConstantList) { - contents.push(call(path, print, "enumConstantList")); - if (!hasNonEmptyDeclaration && options.trailingComma !== "none") { - contents.push(","); - } - } - if (hasNonEmptyDeclaration) { - contents.push(";", hardline, call(path, print, "enumBodyDeclarations")); - } - return printBlock(path, contents.length ? [contents] : []); - }, - enumConstantList(path, print) { - return join([",", hardline], map(path, constantPath => { - const constant = print(constantPath); - const { node, previous } = constantPath; - return !previous || - lineStartWithComments(node) <= lineEndWithComments(previous) + 1 - ? constant - : [hardline, constant]; - }, "enumConstant")); - }, - enumConstant(path, print) { - const { argumentList, classBody } = path.node.children; - const initializer = [call(path, print, "Identifier")]; - if (argumentList) { - initializer.push(group(["(", call(path, print, "argumentList"), ")"])); - } - if (classBody) { - initializer.push(" ", call(path, print, "classBody")); - } - return printWithModifiers(path, print, "enumConstantModifier", initializer); - }, - enumConstantModifier: printSingle, - enumBodyDeclarations(path, print) { - return join(hardline, printClassBodyDeclarations(path, print)); - }, - recordDeclaration(path, print) { - const { children } = path.node; - const header = ["record ", call(path, print, "typeIdentifier")]; - if (children.typeParameters) { - header.push(call(path, print, "typeParameters")); - } - header.push(call(path, print, "recordHeader")); - if (children.classImplements) { - header.push(" ", call(path, print, "classImplements")); - } - return [group(header), " ", call(path, print, "recordBody")]; - }, - recordHeader(path, print) { - return path.node.children.recordComponentList - ? indentInParentheses(call(path, print, "recordComponentList")) - : indentInParentheses(printDanglingComments(path), { shouldBreak: true }); - }, - recordComponentList(path, print) { - return join([",", line], map(path, componentPath => { - const { node, previous } = componentPath; - const blankLine = previous && - lineStartWithComments(node) > lineEndWithComments(previous) + 1; - const component = print(componentPath); - return blankLine ? [softline, component] : component; - }, "recordComponent")); - }, - recordComponent(path, print) { - const { children } = path.node; - const component = [call(path, print, "unannType")]; - if (children.Identifier || - children.variableArityRecordComponent[0].children.annotation) { - component.push(" "); - } - const suffixKey = onlyDefinedKey(children, [ - "Identifier", - "variableArityRecordComponent" - ]); - component.push(call(path, print, suffixKey)); - return group(join(line, [...map(path, print, "recordComponentModifier"), component])); - }, - variableArityRecordComponent(path, print) { - return [ - ...join(" ", map(path, print, "annotation")), - "... ", - call(path, print, "Identifier") - ]; - }, - recordComponentModifier: printSingle, - recordBody(path, print) { - const declarations = []; - let previousRequiresPadding = false; - each(path, declarationPath => { - var _a, _b, _c, _d; - const declaration = print(declarationPath); - if (declaration === "") { - return; - } - const { node, previous } = declarationPath; - const fieldDeclaration = (_c = (_b = (_a = node.children.classBodyDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.classMemberDeclaration) === null || _b === void 0 ? void 0 : _b[0].children.fieldDeclaration) === null || _c === void 0 ? void 0 : _c[0].children; - const currentRequiresPadding = !fieldDeclaration || - hasDeclarationAnnotations((_d = fieldDeclaration.fieldModifier) !== null && _d !== void 0 ? _d : []); - const blankLine = declarations.length > 0 && - (previousRequiresPadding || - currentRequiresPadding || - lineStartWithComments(node) > lineEndWithComments(previous) + 1); - declarations.push(blankLine ? [hardline, declaration] : declaration); - previousRequiresPadding = currentRequiresPadding; - }, "recordBodyDeclaration"); - return printBlock(path, declarations); - }, - recordBodyDeclaration: printSingle, - compactConstructorDeclaration(path, print) { - const declaration = [ - call(path, print, "simpleTypeName"), - " ", - call(path, print, "constructorBody") - ]; - return printWithModifiers(path, print, "constructorModifier", declaration, true); - } -}; -function printClassBodyDeclarations(path, print) { - var _a; - if (!path.node.children.classBodyDeclaration) { - return []; - } - const declarations = []; - let previousRequiresPadding = path.node.name === "enumBodyDeclarations" || - ((_a = path.grandparent) === null || _a === void 0 ? void 0 : _a.name) === - "normalClassDeclaration"; - each(path, declarationPath => { - var _a, _b, _c; - const declaration = print(declarationPath); - if (declaration === "") { - return; - } - const { node, previous } = declarationPath; - const fieldDeclaration = (_b = (_a = node.children.classMemberDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.fieldDeclaration) === null || _b === void 0 ? void 0 : _b[0].children; - const currentRequiresPadding = fieldDeclaration - ? hasDeclarationAnnotations((_c = fieldDeclaration.fieldModifier) !== null && _c !== void 0 ? _c : []) - : true; - const blankLine = previousRequiresPadding || - (declarations.length > 0 && - (currentRequiresPadding || - lineStartWithComments(node) > lineEndWithComments(previous) + 1)); - declarations.push(blankLine ? [hardline, declaration] : declaration); - previousRequiresPadding = currentRequiresPadding; - }, "classBodyDeclaration"); - return declarations; -} diff --git a/frontend/src/common/prettier/plugins/java/printers/expressions.d.ts b/frontend/src/common/prettier/plugins/java/printers/expressions.d.ts deleted file mode 100644 index 29eff550..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/expressions.d.ts +++ /dev/null @@ -1,134 +0,0 @@ -import type { StringTemplateCstNode, TextBlockTemplateCstNode } from "java-parser"; -import type { AstPath } from "prettier"; -import { builders } from "prettier/doc"; -import type { JavaComment } from "../comments.js"; -import { printSingle, type JavaPrintFn } from "./helpers.js"; -declare const _default: { - expression: typeof printSingle; - lambdaExpression(path: AstPath, print: JavaPrintFn, _: import("./helpers.js").JavaParserOptions, args?: unknown): builders.Doc[]; - lambdaParameters(path: AstPath, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Doc; - lambdaParametersWithBraces(path: AstPath, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Doc; - lambdaParameterList: typeof printSingle; - conciseLambdaParameterList(path: AstPath, print: JavaPrintFn): builders.Doc[]; - normalLambdaParameterList(path: AstPath, print: JavaPrintFn): builders.Doc[]; - normalLambdaParameter: typeof printSingle; - regularLambdaParameter(path: AstPath, print: JavaPrintFn): builders.Doc[]; - lambdaParameterType: typeof printSingle; - conciseLambdaParameter: typeof printSingle; - lambdaBody: typeof printSingle; - conditionalExpression(path: AstPath, print: JavaPrintFn): builders.Doc; - binaryExpression(path: AstPath, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Doc; - unaryExpression(path: AstPath, print: JavaPrintFn): builders.Doc[]; - unaryExpressionNotPlusMinus(path: AstPath, print: JavaPrintFn): builders.Doc[]; - primary(path: AstPath, print: JavaPrintFn): builders.Doc; - primaryPrefix: typeof printSingle; - primarySuffix(path: AstPath, print: JavaPrintFn): builders.Doc; - fqnOrRefType(path: AstPath, print: JavaPrintFn, _: import("./helpers.js").JavaParserOptions, args: unknown): builders.Doc[]; - fqnOrRefTypePartFirst(path: AstPath, print: JavaPrintFn): builders.Doc[]; - fqnOrRefTypePartRest(path: AstPath, print: JavaPrintFn): builders.Doc[]; - fqnOrRefTypePartCommon(path: AstPath, print: JavaPrintFn): builders.Doc; - parenthesisExpression(path: AstPath, print: JavaPrintFn): builders.Group | "()" | (string | builders.Indent)[]; - castExpression: typeof printSingle; - primitiveCastExpression(path: AstPath, print: JavaPrintFn): builders.Doc[]; - referenceTypeCastExpression(path: AstPath, print: JavaPrintFn): builders.Doc[]; - newExpression: typeof printSingle; - unqualifiedClassInstanceCreationExpression(path: AstPath, print: JavaPrintFn): builders.Doc[]; - classOrInterfaceTypeToInstantiate(path: AstPath, print: JavaPrintFn): builders.Doc[]; - typeArgumentsOrDiamond: typeof printSingle; - diamond(): string; - methodInvocationSuffix(path: AstPath, print: JavaPrintFn): builders.Group | "()"; - argumentList(path: AstPath, print: JavaPrintFn): builders.Group | (builders.Indent | builders.Softline)[] | (builders.BreakParent | builders.Group)[]; - arrayCreationExpression(path: AstPath, print: JavaPrintFn): builders.Doc[]; - arrayCreationExpressionWithoutInitializerSuffix(path: AstPath, print: JavaPrintFn): builders.Doc; - arrayCreationWithInitializerSuffix(path: AstPath, print: JavaPrintFn): builders.Doc[]; - dimExprs(path: AstPath, print: JavaPrintFn): builders.Doc[]; - dimExpr(path: AstPath, print: JavaPrintFn): builders.Doc[]; - classLiteralSuffix(path: AstPath, print: JavaPrintFn): builders.Doc[]; - arrayAccessSuffix(path: AstPath, print: JavaPrintFn): builders.Doc[]; - methodReferenceSuffix(path: AstPath, print: JavaPrintFn): builders.Doc[]; - templateArgument: typeof printSingle; - template: typeof printSingle; - stringTemplate(path: AstPath, print: JavaPrintFn): builders.Indent; - textBlockTemplate(path: AstPath, print: JavaPrintFn): builders.Indent; - embeddedExpression: typeof printSingle; - pattern: typeof printSingle; - typePattern: typeof printSingle; - recordPattern(path: AstPath, print: JavaPrintFn): builders.Doc[]; - componentPatternList(path: AstPath, print: JavaPrintFn): builders.Doc[]; - componentPattern: typeof printSingle; - matchAllPattern: typeof printSingle; - guard(path: AstPath, print: JavaPrintFn): builders.Doc[]; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/printers/expressions.js b/frontend/src/common/prettier/plugins/java/printers/expressions.js deleted file mode 100644 index 9f3fa458..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/expressions.js +++ /dev/null @@ -1,598 +0,0 @@ -import { builders, utils } from "prettier/doc"; -import { call, definedKeys, each, findBaseIndent, flatMap, hasLeadingComments, indentInParentheses, isBinaryExpression, isNonTerminal, isTerminal, map, onlyDefinedKey, printDanglingComments, printList, printName, printSingle } from "./helpers.js"; -const { breakParent, conditionalGroup, group, hardline, ifBreak, indent, indentIfBreak, join, line, lineSuffixBoundary, softline } = builders; -const { removeLines, willBreak } = utils; -export default { - expression: printSingle, - lambdaExpression(path, print, _, args = {}) { - var _a; - const hug = (_a = args.hug) !== null && _a !== void 0 ? _a : false; - const parameters = call(path, print, "lambdaParameters"); - const expression = [hug ? removeLines(parameters) : parameters, " ->"]; - const lambdaExpression = path.node.children.lambdaBody[0].children.expression; - const body = call(path, print, "lambdaBody"); - if (lambdaExpression) { - const suffix = indent([line, body]); - expression.push(group(hug ? [suffix, softline] : suffix)); - } - else { - expression.push(" ", body); - } - return expression; - }, - lambdaParameters(path, print, options) { - const parameters = printSingle(path, print); - return !path.node.children.lambdaParametersWithBraces && - options.arrowParens === "always" - ? ["(", parameters, ")"] - : parameters; - }, - lambdaParametersWithBraces(path, print, options) { - var _a; - const { lambdaParameterList } = path.node.children; - if (!lambdaParameterList) { - return "()"; - } - const { conciseLambdaParameterList, normalLambdaParameterList } = lambdaParameterList[0].children; - const parameterCount = ((_a = conciseLambdaParameterList === null || conciseLambdaParameterList === void 0 ? void 0 : conciseLambdaParameterList[0].children.conciseLambdaParameter) !== null && _a !== void 0 ? _a : normalLambdaParameterList === null || normalLambdaParameterList === void 0 ? void 0 : normalLambdaParameterList[0].children.normalLambdaParameter).length; - const parameters = call(path, print, "lambdaParameterList"); - if (parameterCount > 1) { - return indentInParentheses(parameters); - } - return conciseLambdaParameterList && options.arrowParens === "avoid" - ? parameters - : ["(", parameters, ")"]; - }, - lambdaParameterList: printSingle, - conciseLambdaParameterList(path, print) { - return printList(path, print, "conciseLambdaParameter"); - }, - normalLambdaParameterList(path, print) { - return printList(path, print, "normalLambdaParameter"); - }, - normalLambdaParameter: printSingle, - regularLambdaParameter(path, print) { - return join(" ", [ - ...map(path, print, "variableModifier"), - call(path, print, "lambdaParameterType"), - call(path, print, "variableDeclaratorId") - ]); - }, - lambdaParameterType: printSingle, - conciseLambdaParameter: printSingle, - lambdaBody: printSingle, - conditionalExpression(path, print) { - var _a; - const binaryExpression = call(path, print, "binaryExpression"); - if (!path.node.children.QuestionMark) { - return binaryExpression; - } - const expressions = map(path, print, "expression"); - const contents = indent(join(line, [ - binaryExpression, - ["? ", expressions[0]], - [": ", expressions[1]] - ])); - const isNestedTernary = ((_a = path.getNode(4)) === null || _a === void 0 ? void 0 : _a.name) === - "conditionalExpression"; - return isNestedTernary ? contents : group(contents); - }, - binaryExpression(path, print, options) { - var _a, _b; - const { children } = path.node; - const operands = flatMap(path, print, definedKeys(children, [ - "expression", - "pattern", - "referenceType", - "unaryExpression" - ])); - const operators = flatMap(path, operatorPath => { - const { node } = operatorPath; - let image; - if (isTerminal(node)) { - image = node.image; - } - else if (node.children.Less) { - image = "<<"; - } - else { - image = node.children.Greater.length === 2 ? ">>" : ">>>"; - } - return { image, doc: print(operatorPath) }; - }, definedKeys(children, [ - "AssignmentOperator", - "BinaryOperator", - "Instanceof", - "shiftOperator" - ])); - const hasNonAssignmentOperators = (operators.length > 0 && !children.AssignmentOperator) || - (children.expression !== undefined && - isBinaryExpression(children.expression[0])); - const isInList = ((_a = path.getNode(4)) === null || _a === void 0 ? void 0 : _a.name) === "elementValue" || - ((_b = path.getNode(6)) === null || _b === void 0 ? void 0 : _b.name) === "argumentList"; - return binary(operands, operators, { - hasNonAssignmentOperators, - isInList, - isRoot: true, - operatorPosition: options.experimentalOperatorPosition - }); - }, - unaryExpression(path, print) { - return [ - ...map(path, print, "UnaryPrefixOperator"), - call(path, print, "primary"), - ...map(path, print, "UnarySuffixOperator") - ]; - }, - unaryExpressionNotPlusMinus(path, print) { - const { children } = path.node; - const expression = []; - if (children.UnaryPrefixOperatorNotPlusMinus) { - expression.push(...map(path, print, "UnaryPrefixOperatorNotPlusMinus")); - } - expression.push(call(path, print, "primary")); - if (children.UnarySuffixOperator) { - expression.push(...map(path, print, "UnarySuffixOperator")); - } - return join(" ", expression); - }, - primary(path, print) { - var _a, _b; - const { children } = path.node; - if (!children.primarySuffix) { - return call(path, print, "primaryPrefix"); - } - const methodInvocations = children.primarySuffix - .filter(({ children }) => children.methodInvocationSuffix) - .map(({ children }) => children.methodInvocationSuffix[0].children); - const hasLambdaMethodParameter = methodInvocations.some(({ argumentList }) => argumentList === null || argumentList === void 0 ? void 0 : argumentList[0].children.expression.some(({ children }) => children.lambdaExpression)); - const prefixIsCallExpression = children.primaryPrefix[0].children.newExpression; - const callExpressionCount = methodInvocations.length + - (prefixIsCallExpression ? 1 : 0) + - children.primarySuffix.filter(({ children }) => children.unqualifiedClassInstanceCreationExpression).length; - const fqnOrRefType = (_a = children.primaryPrefix[0].children.fqnOrRefType) === null || _a === void 0 ? void 0 : _a[0].children; - const prefixIsMethodInvocation = (fqnOrRefType === null || fqnOrRefType === void 0 ? void 0 : fqnOrRefType.fqnOrRefTypePartRest) !== undefined && - ((_b = children.primarySuffix) === null || _b === void 0 ? void 0 : _b[0].children.methodInvocationSuffix) !== undefined; - const prefixIsStaticMethodInvocation = prefixIsMethodInvocation && isCapitalizedIdentifier(fqnOrRefType); - const prefixIsInstanceMethodInvocation = prefixIsMethodInvocation && !prefixIsStaticMethodInvocation; - const mustBreakForCallExpressions = methodInvocations.length > 2 && hasLambdaMethodParameter; - const separator = mustBreakForCallExpressions ? hardline : softline; - const prefix = [ - call(path, prefixPath => print(prefixPath, { - lastSeparator: prefixIsStaticMethodInvocation || - (prefixIsInstanceMethodInvocation && callExpressionCount === 1) - ? "" - : separator - }), "primaryPrefix") - ]; - const canBreakForCallExpressions = callExpressionCount > 2 || - (callExpressionCount === 2 && prefixIsInstanceMethodInvocation) || - willBreak(prefix); - const suffixes = []; - each(path, suffixPath => { - const { node, previous } = suffixPath; - const suffix = print(suffixPath); - if (node.children.Dot) { - if ((canBreakForCallExpressions && - ((!previous && prefixIsCallExpression) || - (previous === null || previous === void 0 ? void 0 : previous.children.methodInvocationSuffix) || - (previous === null || previous === void 0 ? void 0 : previous.children.unqualifiedClassInstanceCreationExpression))) || - (!node.children.templateArgument && willBreak(suffix))) { - suffixes.push(separator); - } - suffixes.push(suffix); - } - else if (previous) { - suffixes.push(suffix); - } - else { - prefix.push(prefixIsInstanceMethodInvocation && callExpressionCount >= 2 - ? indent(suffix) - : suffix); - } - }, "primarySuffix"); - const hasSuffixComments = children.primarySuffix.some(suffix => hasLeadingComments(suffix)); - return group(canBreakForCallExpressions || hasSuffixComments - ? [prefix, indent(suffixes)] - : [prefix, ...suffixes]); - }, - primaryPrefix: printSingle, - primarySuffix(path, print) { - const { children } = path.node; - if (!children.Dot) { - return printSingle(path, print); - } - const suffix = ["."]; - if (children.This) { - suffix.push("this"); - } - else if (children.Identifier) { - if (children.typeArguments) { - suffix.push(call(path, print, "typeArguments")); - } - suffix.push(call(path, print, "Identifier")); - } - else { - const suffixKey = onlyDefinedKey(children, [ - "templateArgument", - "unqualifiedClassInstanceCreationExpression" - ]); - suffix.push(call(path, print, suffixKey)); - } - return suffix; - }, - fqnOrRefType(path, print, _, args) { - var _a; - const lastSeparator = (_a = args.lastSeparator) !== null && _a !== void 0 ? _a : ""; - const fqnOrRefType = [ - call(path, print, "fqnOrRefTypePartFirst"), - ...map(path, partPath => { - const part = print(partPath); - return partPath.isLast - ? [willBreak(part) ? hardline : lastSeparator, part] - : part; - }, "fqnOrRefTypePartRest") - ]; - fqnOrRefType.push(indent(fqnOrRefType.pop())); - return path.node.children.dims - ? [fqnOrRefType, call(path, print, "dims")] - : fqnOrRefType; - }, - fqnOrRefTypePartFirst(path, print) { - return join(" ", [ - ...map(path, print, "annotation"), - call(path, print, "fqnOrRefTypePartCommon") - ]); - }, - fqnOrRefTypePartRest(path, print) { - const common = call(path, print, "fqnOrRefTypePartCommon"); - const type = path.node.children.typeArguments - ? [call(path, print, "typeArguments"), common] - : common; - return [".", ...join(" ", [...map(path, print, "annotation"), type])]; - }, - fqnOrRefTypePartCommon(path, print) { - const { children } = path.node; - const keywordKey = onlyDefinedKey(children, ["Identifier", "Super"]); - const keyword = call(path, print, keywordKey); - return children.typeArguments - ? [keyword, call(path, print, "typeArguments")] - : keyword; - }, - parenthesisExpression(path, print) { - var _a; - const expression = call(path, print, "expression"); - const ancestorName = (_a = path.getNode(14)) === null || _a === void 0 ? void 0 : _a.name; - const binaryExpression = path.getNode(8); - return ancestorName && - ["guard", "returnStatement"].includes(ancestorName) && - binaryExpression && - binaryExpression.name === "binaryExpression" && - Object.keys(binaryExpression.children).length === 1 - ? indentInParentheses(expression) - : ["(", indent(expression), ")"]; - }, - castExpression: printSingle, - primitiveCastExpression(path, print) { - return [ - "(", - call(path, print, "primitiveType"), - ") ", - call(path, print, "unaryExpression") - ]; - }, - referenceTypeCastExpression(path, print) { - const { children } = path.node; - const type = call(path, print, "referenceType"); - const cast = children.additionalBound - ? indentInParentheses(join(line, [type, ...map(path, print, "additionalBound")])) - : ["(", type, ")"]; - const expressionKey = onlyDefinedKey(children, [ - "lambdaExpression", - "unaryExpressionNotPlusMinus" - ]); - return [cast, " ", call(path, print, expressionKey)]; - }, - newExpression: printSingle, - unqualifiedClassInstanceCreationExpression(path, print) { - const { children } = path.node; - const expression = ["new "]; - if (children.typeArguments) { - expression.push(call(path, print, "typeArguments")); - } - expression.push(call(path, print, "classOrInterfaceTypeToInstantiate"), children.argumentList - ? group(["(", call(path, print, "argumentList"), ")"]) - : "()"); - if (children.classBody) { - expression.push(" ", call(path, print, "classBody")); - } - return expression; - }, - classOrInterfaceTypeToInstantiate(path, print) { - const { children } = path.node; - const type = children.annotation - ? flatMap(path, childPath => [ - print(childPath), - isNonTerminal(childPath.node) ? " " : "." - ], ["annotation", "Identifier"]) - : printName(path, print); - if (children.typeArgumentsOrDiamond) { - type.push(call(path, print, "typeArgumentsOrDiamond")); - } - return type; - }, - typeArgumentsOrDiamond: printSingle, - diamond() { - return "<>"; - }, - methodInvocationSuffix(path, print) { - return path.node.children.argumentList - ? group(["(", call(path, print, "argumentList"), ")"]) - : indentInParentheses(printDanglingComments(path), { shouldBreak: true }); - }, - argumentList(path, print) { - var _a, _b, _c, _d; - const expressions = path.node.children.expression; - const lastExpression = expressions.at(-1); - const lastExpressionLambdaBodyExpression = (_b = (_a = lastExpression.children.lambdaExpression) === null || _a === void 0 ? void 0 : _a[0].children.lambdaBody[0].children.expression) === null || _b === void 0 ? void 0 : _b[0].children; - const lastExpressionLambdaBodyTernaryExpression = (_c = lastExpressionLambdaBodyExpression === null || lastExpressionLambdaBodyExpression === void 0 ? void 0 : lastExpressionLambdaBodyExpression.conditionalExpression) === null || _c === void 0 ? void 0 : _c[0].children; - const isHuggable = !lastExpression.comments && - (!lastExpressionLambdaBodyExpression || - (lastExpressionLambdaBodyTernaryExpression === null || lastExpressionLambdaBodyTernaryExpression === void 0 ? void 0 : lastExpressionLambdaBodyTernaryExpression.QuestionMark) !== undefined || - ((_d = lastExpressionLambdaBodyTernaryExpression === null || lastExpressionLambdaBodyTernaryExpression === void 0 ? void 0 : lastExpressionLambdaBodyTernaryExpression.binaryExpression) === null || _d === void 0 ? void 0 : _d[0].children.unaryExpression.length) === 1) && - expressions.findIndex(({ children }) => children.lambdaExpression) === - expressions.length - 1; - const args = map(path, print, "expression"); - const allArgsExpandable = [ - indent([softline, ...join([",", line], args)]), - softline - ]; - if (!isHuggable || willBreak(args.at(-1)[0])) { - return allArgsExpandable; - } - const headArgs = args.slice(0, -1); - const huggedLastArg = path.call(argPath => print(argPath, { hug: true }), "children", "expression", args.length - 1); - const lastArgExpanded = join(", ", [ - ...headArgs, - group(huggedLastArg, { shouldBreak: true }) - ]); - if (willBreak(huggedLastArg)) { - return [ - breakParent, - conditionalGroup([lastArgExpanded, allArgsExpandable]) - ]; - } - return conditionalGroup([ - join(", ", [...headArgs, huggedLastArg]), - lastArgExpanded, - allArgsExpandable - ]); - }, - arrayCreationExpression(path, print) { - const { children } = path.node; - const typeKey = onlyDefinedKey(children, [ - "classOrInterfaceType", - "primitiveType" - ]); - const suffixKey = onlyDefinedKey(children, [ - "arrayCreationExpressionWithoutInitializerSuffix", - "arrayCreationWithInitializerSuffix" - ]); - return ["new ", call(path, print, typeKey), call(path, print, suffixKey)]; - }, - arrayCreationExpressionWithoutInitializerSuffix(path, print) { - const expressions = call(path, print, "dimExprs"); - return path.node.children.dims - ? [expressions, call(path, print, "dims")] - : expressions; - }, - arrayCreationWithInitializerSuffix(path, print) { - return [ - call(path, print, "dims"), - " ", - call(path, print, "arrayInitializer") - ]; - }, - dimExprs(path, print) { - return map(path, print, "dimExpr"); - }, - dimExpr(path, print) { - return join(" ", [ - ...map(path, print, "annotation"), - ["[", call(path, print, "expression"), "]"] - ]); - }, - classLiteralSuffix(path, print) { - const lSquares = map(path, print, "LSquare"); - const rSquares = map(path, print, "RSquare"); - return [ - ...lSquares.flatMap((lSquare, index) => [lSquare, rSquares[index]]), - ".class" - ]; - }, - arrayAccessSuffix(path, print) { - return ["[", call(path, print, "expression"), "]"]; - }, - methodReferenceSuffix(path, print) { - const { children } = path.node; - const reference = ["::"]; - if (children.typeArguments) { - reference.push(call(path, print, "typeArguments")); - } - reference.push(call(path, print, onlyDefinedKey(children, ["Identifier", "New"]))); - return reference; - }, - templateArgument: printSingle, - template: printSingle, - stringTemplate(path, print) { - return printTemplate(path, print, "StringTemplateBegin", "StringTemplateMid", "StringTemplateEnd"); - }, - textBlockTemplate(path, print) { - return printTemplate(path, print, "TextBlockTemplateBegin", "TextBlockTemplateMid", "TextBlockTemplateEnd"); - }, - embeddedExpression: printSingle, - pattern: printSingle, - typePattern: printSingle, - recordPattern(path, print) { - const patterns = path.node.children.componentPatternList - ? indentInParentheses(call(path, print, "componentPatternList")) - : "()"; - return [call(path, print, "referenceType"), patterns]; - }, - componentPatternList(path, print) { - return printList(path, print, "componentPattern"); - }, - componentPattern: printSingle, - matchAllPattern: printSingle, - guard(path, print) { - var _a; - const expression = call(path, print, "expression"); - const hasParentheses = ((_a = path.node.children.expression[0].children.conditionalExpression) === null || _a === void 0 ? void 0 : _a[0].children.binaryExpression[0].children.unaryExpression[0].children.primary[0].children.primaryPrefix[0].children.parenthesisExpression) !== - undefined; - return [ - "when ", - hasParentheses - ? expression - : group([ - ifBreak("("), - indent([softline, expression]), - softline, - ifBreak(")") - ]) - ]; - } -}; -function binary(operands, operators, { hasNonAssignmentOperators = false, isInList = false, isRoot = false, operatorPosition }) { - let levelOperator; - let levelPrecedence; - let level = []; - while (operators.length) { - const nextOperator = operators[0].image; - const nextPrecedence = getOperatorPrecedence(nextOperator); - if (levelPrecedence === undefined || nextPrecedence === levelPrecedence) { - const { image: operator, doc: operatorDoc } = operators.shift(); - level.push(operands.shift()); - if (levelOperator !== undefined && - needsParentheses(levelOperator, operator)) { - level = [["(", group(indent(level)), ")"]]; - } - const parts = [" ", operatorDoc, line]; - if (operatorPosition === "start" && !isAssignmentOperator(operator)) { - parts.reverse(); - } - level.push(parts); - levelOperator = operator; - levelPrecedence = nextPrecedence; - } - else if (nextPrecedence < levelPrecedence) { - if (!isRoot) { - break; - } - level.push(operands.shift()); - const content = group(indent(level)); - operands.unshift(levelOperator !== undefined && - needsParentheses(levelOperator, nextOperator) - ? ["(", content, ")"] - : content); - level = []; - levelOperator = undefined; - levelPrecedence = undefined; - } - else { - const content = binary(operands, operators, { operatorPosition }); - operands.unshift(levelOperator !== undefined && - needsParentheses(nextOperator, levelOperator) - ? ["(", indent(content), ")"] - : content); - } - } - level.push(operands.shift()); - if (!levelOperator || - (!isInList && - !isAssignmentOperator(levelOperator) && - levelOperator !== "instanceof")) { - return group(level); - } - if (!isRoot || hasNonAssignmentOperators) { - return group(indent(level)); - } - const groupId = Symbol("assignment"); - return group([ - level[0], - group(indent(level[1]), { id: groupId }), - indentIfBreak(level[2], { groupId }) - ]); -} -const precedencesByOperator = new Map([ - ["||"], - ["&&"], - ["|"], - ["^"], - ["&"], - ["==", "!="], - ["<", ">", "<=", ">=", "instanceof"], - ["<<", ">>", ">>>"], - ["+", "-"], - ["*", "/", "%"] -].flatMap((operators, index) => operators.map(operator => [operator, index]))); -function getOperatorPrecedence(operator) { - var _a; - return (_a = precedencesByOperator.get(operator)) !== null && _a !== void 0 ? _a : -1; -} -function needsParentheses(operator, parentOperator) { - return ((operator === "&&" && parentOperator === "||") || - (["|", "^", "&", "<<", ">>", ">>>"].includes(parentOperator) && - getOperatorPrecedence(operator) > - getOperatorPrecedence(parentOperator)) || - [operator, parentOperator].every(o => ["==", "!="].includes(o)) || - [operator, parentOperator].every(o => ["<<", ">>", ">>>"].includes(o)) || - (operator === "*" && parentOperator === "/") || - (operator === "/" && parentOperator === "*") || - (operator === "%" && ["+", "-", "*", "/"].includes(parentOperator)) || - (["*", "/"].includes(operator) && parentOperator === "%")); -} -const assignmentOperators = new Set([ - "=", - "*=", - "/=", - "%=", - "+=", - "-=", - "<<=", - ">>=", - ">>>=", - "&=", - "^=", - "|=" -]); -function isAssignmentOperator(operator) { - return assignmentOperators.has(operator); -} -function isCapitalizedIdentifier(fqnOrRefType) { - var _a, _b, _c; - const nextToLastIdentifier = (_c = (_b = [ - fqnOrRefType.fqnOrRefTypePartFirst[0], - ...((_a = fqnOrRefType.fqnOrRefTypePartRest) !== null && _a !== void 0 ? _a : []) - ].at(-2)) === null || _b === void 0 ? void 0 : _b.children.fqnOrRefTypePartCommon[0].children.Identifier) === null || _c === void 0 ? void 0 : _c[0].image; - return /^\p{Uppercase_Letter}/u.test(nextToLastIdentifier !== null && nextToLastIdentifier !== void 0 ? nextToLastIdentifier : ""); -} -function printTemplate(path, print, beginKey, midKey, endKey) { - const begin = call(path, ({ node }) => node.image, beginKey); - const mids = map(path, ({ node }) => node.image, midKey); - const end = call(path, ({ node }) => node.image, endKey); - const lines = [begin, ...mids, end].join("").split("\n").slice(1); - const baseIndent = findBaseIndent(lines); - const prefix = "\n" + " ".repeat(baseIndent); - const parts = [begin, ...mids, end].map(image => join(hardline, image.split(prefix))); - return indent([ - parts[0], - ...map(path, (expressionPath, index) => { - const expression = group([ - indent([softline, print(expressionPath), lineSuffixBoundary]), - softline - ]); - return index === 0 ? expression : [parts[index], expression]; - }, "embeddedExpression"), - parts.at(-1) - ]); -} diff --git a/frontend/src/common/prettier/plugins/java/printers/helpers.d.ts b/frontend/src/common/prettier/plugins/java/printers/helpers.d.ts deleted file mode 100644 index a270d5eb..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/helpers.d.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { AnnotationCstNode, ClassPermitsCstNode, ClassTypeCtx, CstElement, CstNode, ExpressionCstNode, InterfacePermitsCstNode, IToken, StatementCstNode } from "java-parser"; -import type { AstPath, Doc, ParserOptions } from "prettier"; -import { builders } from "prettier/doc"; -import type { JavaComment } from "../comments.js"; -export declare function onlyDefinedKey, K extends Key & string>(obj: T, options?: K[]): K; -export declare function definedKeys, K extends Key & string>(obj: T, options?: K[]): K[]; -export declare function printWithModifiers>(path: AstPath, print: JavaPrintFn, modifierChild: P, contents: Doc, noTypeAnnotations?: boolean): builders.Doc[]; -export declare function hasDeclarationAnnotations(modifiers: ModifierNode[]): boolean; -export declare function call>(path: AstPath, callback: MapCallback, P>, U>, child: P): U; -export declare function each>(path: AstPath, callback: MapCallback, P>, void>, child: P): void; -export declare function map>(path: AstPath, callback: MapCallback, P>, U>, child: P): U[]; -export declare function flatMap>(path: AstPath, callback: MapCallback, P>, U>, children: P[]): U[]; -export declare function printSingle(path: AstPath, print: JavaPrintFn, _?: JavaParserOptions, args?: unknown): builders.Doc; -export declare function lineStartWithComments(node: JavaNonTerminal): number; -export declare function lineEndWithComments(node: JavaNonTerminal): number; -export declare function printDanglingComments(path: AstPath): builders.Doc[]; -export declare function printComment(node: JavaTerminal): string | builders.Doc[]; -export declare function hasLeadingComments(node: JavaNode): boolean | undefined; -export declare function indentInParentheses(contents: Doc, opts?: { - shouldBreak?: boolean; -}): builders.Group | "()"; -export declare function printArrayInitializer>(path: AstPath, print: JavaPrintFn, options: JavaParserOptions, child: P): builders.Group | "{}"; -export declare function printBlock(path: AstPath, contents: Doc[]): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[]; -export declare function printName(path: AstPath, print: JavaPrintFn): builders.Doc[]; -export declare function printList>(path: AstPath, print: JavaPrintFn, child: P): builders.Doc[]; -export declare function printClassPermits(path: AstPath, print: JavaPrintFn): builders.Group; -export declare function printClassType(path: AstPath, print: JavaPrintFn): builders.Doc[]; -export declare function isBinaryExpression(expression: ExpressionCstNode): boolean; -export declare function findBaseIndent(lines: string[]): number; -export declare function isEmptyStatement(statement: StatementCstNode): boolean; -export declare function isNonTerminal(node: CstElement): node is JavaNonTerminal; -export declare function isTerminal(node: CstElement): node is IToken; -export type JavaNode = CstElement & { - comments?: JavaComment[]; -}; -export type JavaNonTerminal = Exclude; -export type JavaTerminal = Exclude; -export type JavaNodePrinters = { - [T in JavaNonTerminal["name"]]: JavaNodePrinter; -}; -export type JavaNodePrinter = (path: AstPath>, print: JavaPrintFn, options: JavaParserOptions, args?: unknown) => Doc; -export type JavaPrintFn = (path: AstPath, args?: unknown) => Doc; -export type JavaParserOptions = ParserOptions & { - entrypoint?: string; -}; -export type IterProperties = T extends any[] ? IndexProperties : ArrayProperties; -type Key = T extends T ? keyof T : never; -type ModifierNode = JavaNonTerminal & { - children: { - annotation?: AnnotationCstNode[]; - }; -}; -type IsTuple = T extends [] ? true : T extends [infer _First, ...infer Remain] ? IsTuple : false; -type IndexProperties = IsTuple extends true ? Exclude["length"], T["length"]> : number; -type ArrayProperties = { - [K in keyof T]: NonNullable extends readonly any[] ? K : never; -}[keyof T]; -type ArrayElement = T extends Array ? E : never; -type MapCallback = (path: AstPath>, index: number, value: any) => U; -type IndexValue = T extends any[] ? P extends number ? T[P] : never : P extends keyof T ? T[P] : never; -export {}; diff --git a/frontend/src/common/prettier/plugins/java/printers/helpers.js b/frontend/src/common/prettier/plugins/java/printers/helpers.js deleted file mode 100644 index e0072c80..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/helpers.js +++ /dev/null @@ -1,239 +0,0 @@ -import { builders } from "prettier/doc"; -import parser from "../parser.js"; -const { group, hardline, ifBreak, indent, join, line, softline } = builders; -export function onlyDefinedKey(obj, options) { - const keys = definedKeys(obj, options); - if (keys.length === 1) { - return keys[0]; - } - throw new Error(keys.length > 1 - ? `More than one defined key found: ${keys}` - : "No defined keys found"); -} -export function definedKeys(obj, options) { - return (options !== null && options !== void 0 ? options : Object.keys(obj)).filter(key => obj[key] !== undefined); -} -const indexByModifier = [ - "public", - "protected", - "private", - "abstract", - "default", - "static", - "final", - "transient", - "volatile", - "synchronized", - "native", - "sealed", - "non-sealed", - "strictfp" -].reduce((map, name, index) => map.set(name, index), new Map()); -export function printWithModifiers(path, print, modifierChild, contents, noTypeAnnotations = false) { - const declarationAnnotations = []; - const otherModifiers = []; - const typeAnnotations = []; - each(path, modifierPath => { - const { children } = modifierPath.node; - const modifier = print(modifierPath); - if (children.annotation) { - (otherModifiers.length ? typeAnnotations : declarationAnnotations).push(modifier); - } - else { - otherModifiers.push(modifier); - declarationAnnotations.push(...typeAnnotations); - typeAnnotations.length = 0; - } - }, modifierChild); - if (noTypeAnnotations) { - declarationAnnotations.push(...typeAnnotations); - typeAnnotations.length = 0; - } - otherModifiers.sort((a, b) => indexByModifier.get(a) - indexByModifier.get(b)); - return join(hardline, [ - ...declarationAnnotations, - join(" ", [...otherModifiers, ...typeAnnotations, contents]) - ]); -} -export function hasDeclarationAnnotations(modifiers) { - let hasAnnotation = false; - let hasNonAnnotation = false; - for (const modifier of modifiers) { - if (modifier.children.annotation) { - hasAnnotation = true; - } - else if (hasAnnotation) { - return true; - } - else { - hasNonAnnotation = true; - } - } - return hasAnnotation && !hasNonAnnotation; -} -export function call(path, callback, child) { - return path.map(callback, "children", child)[0]; -} -export function each(path, callback, child) { - if (path.node.children[child]) { - path.each(callback, "children", child); - } -} -export function map(path, callback, child) { - return path.node.children[child] ? path.map(callback, "children", child) : []; -} -export function flatMap(path, callback, children) { - return children - .flatMap(child => map(path, callback, child).map((doc, index) => { - const node = path.node.children[child][index]; - return { - doc, - startOffset: parser.locStart(node) - }; - })) - .sort((a, b) => a.startOffset - b.startOffset) - .map(({ doc }) => doc); -} -export function printSingle(path, print, _, args) { - return call(path, childPath => print(childPath, args), onlyDefinedKey(path.node.children)); -} -export function lineStartWithComments(node) { - const { comments, location } = node; - return comments - ? Math.min(location.startLine, comments[0].startLine) - : location.startLine; -} -export function lineEndWithComments(node) { - const { comments, location } = node; - return comments - ? Math.max(location.endLine, comments.at(-1).endLine) - : location.endLine; -} -export function printDanglingComments(path) { - if (!path.node.comments) { - return []; - } - const comments = []; - path.each(commentPath => { - const comment = commentPath.node; - if (comment.leading || comment.trailing) { - return; - } - comment.printed = true; - comments.push(printComment(comment)); - }, "comments"); - return join(hardline, comments); -} -export function printComment(node) { - const { image } = node; - const lines = image.split("\n").map(line => line.trim()); - return lines.length > 1 && - lines[0].startsWith("/*") && - lines.slice(1).every(line => line.startsWith("*")) && - lines.at(-1).endsWith("*/") - ? join(hardline, lines.map((line, index) => (index === 0 ? line : ` ${line}`))) - : image; -} -export function hasLeadingComments(node) { - var _a; - return (_a = node.comments) === null || _a === void 0 ? void 0 : _a.some(({ leading }) => leading); -} -export function indentInParentheses(contents, opts) { - return !Array.isArray(contents) || contents.length - ? group(["(", indent([softline, contents]), softline, ")"], opts) - : "()"; -} -export function printArrayInitializer(path, print, options, child) { - const list = []; - if (child && child in path.node.children) { - list.push(call(path, print, child)); - if (options.trailingComma !== "none") { - list.push(ifBreak(",")); - } - } - list.push(...printDanglingComments(path)); - return list.length ? group(["{", indent([line, ...list]), line, "}"]) : "{}"; -} -export function printBlock(path, contents) { - if (!contents.length) { - const danglingComments = printDanglingComments(path); - return danglingComments.length - ? ["{", indent([hardline, ...danglingComments]), hardline, "}"] - : "{}"; - } - return group([ - "{", - indent([hardline, ...join(hardline, contents)]), - hardline, - "}" - ]); -} -export function printName(path, print) { - return join(".", map(path, print, "Identifier")); -} -export function printList(path, print, child) { - return join([",", line], map(path, print, child)); -} -export function printClassPermits(path, print) { - return group([ - "permits", - indent([line, group(printList(path, print, "typeName"))]) - ]); -} -export function printClassType(path, print) { - const { children } = path.node; - return definedKeys(children, ["annotation", "Identifier", "typeArguments"]) - .flatMap(child => children[child].map((node, index) => ({ - child, - index, - startOffset: parser.locStart(node) - }))) - .sort((a, b) => a.startOffset - b.startOffset) - .flatMap(({ child, index: childIndex }, index, array) => { - const node = children[child][childIndex]; - const next = array.at(index + 1); - const nextNode = next && children[next.child][next.index]; - const docs = [path.call(print, "children", child, childIndex)]; - if (nextNode) { - if (isNonTerminal(node)) { - docs.push(node.name === "annotation" ? " " : "."); - } - else if (isTerminal(nextNode) || nextNode.name === "annotation") { - docs.push("."); - } - } - return docs; - }); -} -export function isBinaryExpression(expression) { - var _a; - const conditionalExpression = (_a = expression.children.conditionalExpression) === null || _a === void 0 ? void 0 : _a[0].children; - if (!conditionalExpression) { - return false; - } - const isTernary = conditionalExpression.QuestionMark !== undefined; - if (isTernary) { - return false; - } - const hasNonAssignmentOperators = Object.values(conditionalExpression.binaryExpression[0].children).some(child => { - var _a; - return isTerminal(child[0]) && - !((_a = child[0].tokenType.CATEGORIES) === null || _a === void 0 ? void 0 : _a.some(category => category.name === "AssignmentOperator")); - }); - return hasNonAssignmentOperators; -} -export function findBaseIndent(lines) { - return lines.length - ? Math.min(...lines.map(line => line.search(/\S/)).filter(indent => indent >= 0)) - : 0; -} -export function isEmptyStatement(statement) { - var _a; - return (((_a = statement.children.statementWithoutTrailingSubstatement) === null || _a === void 0 ? void 0 : _a[0].children.emptyStatement) !== undefined); -} -export function isNonTerminal(node) { - return !isTerminal(node); -} -export function isTerminal(node) { - return "tokenType" in node; -} diff --git a/frontend/src/common/prettier/plugins/java/printers/index.d.ts b/frontend/src/common/prettier/plugins/java/printers/index.d.ts deleted file mode 100644 index 569b54ae..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import type { JavaNodePrinter, JavaNodePrinters } from "./helpers.js"; -export declare function printerForNodeType(type: T): JavaNodePrinter; diff --git a/frontend/src/common/prettier/plugins/java/printers/index.js b/frontend/src/common/prettier/plugins/java/printers/index.js deleted file mode 100644 index e629424e..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import arrays from "./arrays.js"; -import blocksAndStatements from "./blocks-and-statements.js"; -import classes from "./classes.js"; -import expressions from "./expressions.js"; -import interfaces from "./interfaces.js"; -import lexicalStructure from "./lexical-structure.js"; -import names from "./names.js"; -import packagesAndModules from "./packages-and-modules.js"; -import typesValuesAndVariables from "./types-values-and-variables.js"; -const printersByNodeType = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, arrays), blocksAndStatements), classes), expressions), interfaces), lexicalStructure), names), packagesAndModules), typesValuesAndVariables); -export function printerForNodeType(type) { - return printersByNodeType[type]; -} diff --git a/frontend/src/common/prettier/plugins/java/printers/interfaces.d.ts b/frontend/src/common/prettier/plugins/java/printers/interfaces.d.ts deleted file mode 100644 index cb9025ef..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/interfaces.d.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { builders } from "prettier/doc"; -import { printClassPermits, printSingle } from "./helpers.js"; -declare const _default: { - interfaceDeclaration(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - normalInterfaceDeclaration(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - interfaceModifier: typeof printSingle; - interfaceExtends(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Group; - interfacePermits: typeof printClassPermits; - interfaceBody(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[]; - interfaceMemberDeclaration(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc; - constantDeclaration(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - constantModifier: typeof printSingle; - interfaceMethodDeclaration(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - interfaceMethodModifier: typeof printSingle; - annotationInterfaceDeclaration(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - annotationInterfaceBody(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[]; - annotationInterfaceMemberDeclaration(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc; - annotationInterfaceElementDeclaration(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - annotationInterfaceElementModifier: typeof printSingle; - defaultValue(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - annotation(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - elementValuePairList(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - elementValuePair(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - elementValue: typeof printSingle; - elementValueArrayInitializer(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Group | "{}"; - elementValueList(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Group; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/printers/interfaces.js b/frontend/src/common/prettier/plugins/java/printers/interfaces.js deleted file mode 100644 index 93a5109f..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/interfaces.js +++ /dev/null @@ -1,157 +0,0 @@ -import { builders } from "prettier/doc"; -import { call, each, hasDeclarationAnnotations, indentInParentheses, lineEndWithComments, lineStartWithComments, onlyDefinedKey, printArrayInitializer, printBlock, printClassPermits, printList, printSingle, printWithModifiers } from "./helpers.js"; -const { group, hardline, indent, join, line } = builders; -export default { - interfaceDeclaration(path, print) { - const declarationKey = onlyDefinedKey(path.node.children, [ - "annotationInterfaceDeclaration", - "normalInterfaceDeclaration" - ]); - return printWithModifiers(path, print, "interfaceModifier", call(path, print, declarationKey), true); - }, - normalInterfaceDeclaration(path, print) { - const { interfaceExtends, interfacePermits, typeParameters } = path.node.children; - const header = ["interface ", call(path, print, "typeIdentifier")]; - if (typeParameters) { - header.push(call(path, print, "typeParameters")); - } - if (interfaceExtends) { - header.push(indent([line, call(path, print, "interfaceExtends")])); - } - if (interfacePermits) { - header.push(indent([line, call(path, print, "interfacePermits")])); - } - return [group(header), " ", call(path, print, "interfaceBody")]; - }, - interfaceModifier: printSingle, - interfaceExtends(path, print) { - return group([ - "extends", - indent([line, call(path, print, "interfaceTypeList")]) - ]); - }, - interfacePermits: printClassPermits, - interfaceBody(path, print) { - const declarations = []; - let previousRequiresPadding = false; - each(path, declarationPath => { - var _a, _b, _c, _d; - const declaration = print(declarationPath); - if (declaration === "") { - return; - } - const { node, previous } = declarationPath; - const constantDeclaration = (_a = node.children.constantDeclaration) === null || _a === void 0 ? void 0 : _a[0].children; - const methodDeclaration = (_b = node.children.interfaceMethodDeclaration) === null || _b === void 0 ? void 0 : _b[0].children; - const currentRequiresPadding = (!constantDeclaration && !methodDeclaration) || - (methodDeclaration === null || methodDeclaration === void 0 ? void 0 : methodDeclaration.methodBody[0].children.block) !== undefined || - hasDeclarationAnnotations((_d = (_c = constantDeclaration === null || constantDeclaration === void 0 ? void 0 : constantDeclaration.constantModifier) !== null && _c !== void 0 ? _c : methodDeclaration === null || methodDeclaration === void 0 ? void 0 : methodDeclaration.interfaceMethodModifier) !== null && _d !== void 0 ? _d : []); - const blankLine = declarations.length > 0 && - (previousRequiresPadding || - currentRequiresPadding || - lineStartWithComments(node) > lineEndWithComments(previous) + 1); - declarations.push(blankLine ? [hardline, declaration] : declaration); - previousRequiresPadding = currentRequiresPadding; - }, "interfaceMemberDeclaration"); - return printBlock(path, declarations); - }, - interfaceMemberDeclaration(path, print) { - const { children } = path.node; - return children.Semicolon - ? "" - : call(path, print, onlyDefinedKey(children)); - }, - constantDeclaration(path, print) { - const declaration = [ - call(path, print, "unannType"), - " ", - call(path, print, "variableDeclaratorList"), - ";" - ]; - return printWithModifiers(path, print, "constantModifier", declaration); - }, - constantModifier: printSingle, - interfaceMethodDeclaration(path, print) { - const declaration = [ - call(path, print, "methodHeader"), - path.node.children.methodBody[0].children.Semicolon ? "" : " ", - call(path, print, "methodBody") - ]; - return printWithModifiers(path, print, "interfaceMethodModifier", declaration); - }, - interfaceMethodModifier: printSingle, - annotationInterfaceDeclaration(path, print) { - return join(" ", [ - "@interface", - call(path, print, "typeIdentifier"), - call(path, print, "annotationInterfaceBody") - ]); - }, - annotationInterfaceBody(path, print) { - const declarations = []; - each(path, declarationPath => { - const declaration = print(declarationPath); - if (declaration === "") { - return; - } - declarations.push(declarationPath.isFirst ? declaration : [hardline, declaration]); - }, "annotationInterfaceMemberDeclaration"); - return printBlock(path, declarations); - }, - annotationInterfaceMemberDeclaration(path, print) { - const { children } = path.node; - return children.Semicolon - ? "" - : call(path, print, onlyDefinedKey(children)); - }, - annotationInterfaceElementDeclaration(path, print) { - const { dims, defaultValue } = path.node.children; - const declaration = [ - call(path, print, "unannType"), - " ", - call(path, print, "Identifier"), - "()" - ]; - if (dims) { - declaration.push(call(path, print, "dims")); - } - if (defaultValue) { - declaration.push(" ", call(path, print, "defaultValue")); - } - declaration.push(";"); - return printWithModifiers(path, print, "annotationInterfaceElementModifier", declaration); - }, - annotationInterfaceElementModifier: printSingle, - defaultValue(path, print) { - return ["default ", call(path, print, "elementValue")]; - }, - annotation(path, print) { - const { children } = path.node; - const annotation = ["@", call(path, print, "typeName")]; - if (children.elementValue || children.elementValuePairList) { - const valuesKey = onlyDefinedKey(children, [ - "elementValue", - "elementValuePairList" - ]); - annotation.push(indentInParentheses(call(path, print, valuesKey))); - } - return annotation; - }, - elementValuePairList(path, print) { - return printList(path, print, "elementValuePair"); - }, - elementValuePair(path, print) { - return join(" ", [ - call(path, print, "Identifier"), - call(path, print, "Equals"), - call(path, print, "elementValue") - ]); - }, - elementValue: printSingle, - elementValueArrayInitializer(path, print, options) { - return printArrayInitializer(path, print, options, "elementValueList"); - }, - elementValueList(path, print) { - return group(printList(path, print, "elementValue")); - } -}; diff --git a/frontend/src/common/prettier/plugins/java/printers/lexical-structure.d.ts b/frontend/src/common/prettier/plugins/java/printers/lexical-structure.d.ts deleted file mode 100644 index a63a9e27..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/lexical-structure.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { builders } from "prettier/doc"; -import { printSingle } from "./helpers.js"; -declare const _default: { - literal(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc; - integerLiteral: typeof printSingle; - floatingPointLiteral: typeof printSingle; - booleanLiteral: typeof printSingle; - shiftOperator(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/printers/lexical-structure.js b/frontend/src/common/prettier/plugins/java/printers/lexical-structure.js deleted file mode 100644 index d94cfd59..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/lexical-structure.js +++ /dev/null @@ -1,29 +0,0 @@ -import { builders } from "prettier/doc"; -import { findBaseIndent, map, onlyDefinedKey, printSingle } from "./helpers.js"; -const { hardline, indent, join } = builders; -export default { - literal(path, print) { - const { TextBlock } = path.node.children; - if (!TextBlock) { - return printSingle(path, print); - } - const [open, ...lines] = TextBlock[0].image.split("\n"); - const baseIndent = findBaseIndent(lines); - const textBlock = join(hardline, [ - open, - ...lines.map(line => line.slice(baseIndent)) - ]); - const ancestor = path.getNode(14); - return (ancestor === null || ancestor === void 0 ? void 0 : ancestor.name) === "variableInitializer" || - ((ancestor === null || ancestor === void 0 ? void 0 : ancestor.name) === "binaryExpression" && - ancestor.children.AssignmentOperator) - ? indent(textBlock) - : textBlock; - }, - integerLiteral: printSingle, - floatingPointLiteral: printSingle, - booleanLiteral: printSingle, - shiftOperator(path, print) { - return map(path, print, onlyDefinedKey(path.node.children)); - } -}; diff --git a/frontend/src/common/prettier/plugins/java/printers/names.d.ts b/frontend/src/common/prettier/plugins/java/printers/names.d.ts deleted file mode 100644 index 18ad1e81..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/names.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { printName, printSingle } from "./helpers.js"; -declare const _default: { - typeIdentifier: typeof printSingle; - moduleName: typeof printName; - packageName: typeof printName; - typeName: typeof printName; - expressionName: typeof printName; - methodName: typeof printSingle; - packageOrTypeName: typeof printName; - ambiguousName: typeof printName; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/printers/names.js b/frontend/src/common/prettier/plugins/java/printers/names.js deleted file mode 100644 index 3dfec2fd..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/names.js +++ /dev/null @@ -1,11 +0,0 @@ -import { printName, printSingle } from "./helpers.js"; -export default { - typeIdentifier: printSingle, - moduleName: printName, - packageName: printName, - typeName: printName, - expressionName: printName, - methodName: printSingle, - packageOrTypeName: printName, - ambiguousName: printName -}; diff --git a/frontend/src/common/prettier/plugins/java/printers/packages-and-modules.d.ts b/frontend/src/common/prettier/plugins/java/printers/packages-and-modules.d.ts deleted file mode 100644 index c35219ed..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/packages-and-modules.d.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { ExportsModuleDirectiveCstNode, ImportDeclarationCstNode, OpensModuleDirectiveCstNode } from "java-parser"; -import type { AstPath } from "prettier"; -import { builders } from "prettier/doc"; -import { printSingle, type JavaPrintFn } from "./helpers.js"; -declare const _default: { - compilationUnit(path: AstPath, print: JavaPrintFn): builders.Doc[]; - ordinaryCompilationUnit(path: AstPath, print: JavaPrintFn): builders.Doc[]; - modularCompilationUnit(path: AstPath, print: JavaPrintFn): builders.Doc[]; - packageDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc[]; - packageModifier: typeof printSingle; - importDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc; - typeDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc; - moduleDeclaration(path: AstPath, print: JavaPrintFn): builders.Doc[]; - moduleDirective: typeof printSingle; - requiresModuleDirective(path: AstPath, print: JavaPrintFn): builders.Doc[]; - exportsModuleDirective(path: AstPath, print: JavaPrintFn): builders.Doc[]; - opensModuleDirective(path: AstPath, print: JavaPrintFn): builders.Doc[]; - usesModuleDirective(path: AstPath, print: JavaPrintFn): builders.Doc[]; - providesModuleDirective(path: AstPath, print: JavaPrintFn): builders.Doc[]; - requiresModifier: typeof printSingle; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/printers/packages-and-modules.js b/frontend/src/common/prettier/plugins/java/printers/packages-and-modules.js deleted file mode 100644 index e1a98c02..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/packages-and-modules.js +++ /dev/null @@ -1,169 +0,0 @@ -import { builders } from "prettier/doc"; -import { call, lineEndWithComments, lineStartWithComments, map, printBlock, printDanglingComments, printName, printSingle } from "./helpers.js"; -const { group, hardline, indent, join, line } = builders; -export default { - compilationUnit(path, print) { - return [...printDanglingComments(path), printSingle(path, print), hardline]; - }, - ordinaryCompilationUnit(path, print) { - const { children } = path.node; - const declarations = []; - if (children.packageDeclaration) { - declarations.push(call(path, print, "packageDeclaration")); - } - if (children.importDeclaration) { - const staticCount = sortImports(children.importDeclaration); - const importDeclarations = map(path, print, "importDeclaration").filter(doc => doc !== ""); - const staticDeclarations = importDeclarations.slice(0, staticCount); - const nonStaticDeclarations = importDeclarations.slice(staticCount); - declarations.push(...[staticDeclarations, nonStaticDeclarations] - .filter(({ length }) => length) - .map(declarations => join(hardline, declarations))); - } - if (children.typeDeclaration) { - declarations.push(...map(path, print, "typeDeclaration").filter(declaration => declaration !== "")); - } - return join([hardline, hardline], declarations); - }, - modularCompilationUnit(path, print) { - const { children } = path.node; - const declarations = []; - if (children.importDeclaration) { - const staticCount = sortImports(children.importDeclaration); - const importDeclarations = map(path, print, "importDeclaration").filter(doc => doc !== ""); - const staticDeclarations = importDeclarations.slice(0, staticCount); - const nonStaticDeclarations = importDeclarations.slice(staticCount); - declarations.push(...[staticDeclarations, nonStaticDeclarations] - .filter(({ length }) => length) - .map(declarations => join(hardline, declarations))); - } - declarations.push(call(path, print, "moduleDeclaration")); - return join([hardline, hardline], declarations); - }, - packageDeclaration(path, print) { - return join(hardline, [ - ...map(path, print, "packageModifier"), - ["package ", printName(path, print), ";"] - ]); - }, - packageModifier: printSingle, - importDeclaration(path, print) { - const { children } = path.node; - if (children.emptyStatement) { - return call(path, print, "emptyStatement"); - } - const declaration = ["import "]; - if (children.Static) { - declaration.push("static "); - } - declaration.push(call(path, print, "packageOrTypeName")); - if (children.Star) { - declaration.push(".*"); - } - declaration.push(";"); - return declaration; - }, - typeDeclaration(path, print) { - return path.node.children.Semicolon ? "" : printSingle(path, print); - }, - moduleDeclaration(path, print) { - const { annotation, Open } = path.node.children; - const prefix = []; - if (annotation) { - prefix.push(...map(path, print, "annotation")); - } - if (Open) { - prefix.push("open"); - } - const declarations = map(path, declarationPath => { - const declaration = print(declarationPath); - const { node, previous } = declarationPath; - return !previous || - lineStartWithComments(node) <= lineEndWithComments(previous) + 1 - ? declaration - : [hardline, declaration]; - }, "moduleDirective"); - return join(" ", [ - ...prefix, - "module", - printName(path, print), - printBlock(path, declarations) - ]); - }, - moduleDirective: printSingle, - requiresModuleDirective(path, print) { - return join(" ", [ - "requires", - ...map(path, print, "requiresModifier"), - [call(path, print, "moduleName"), ";"] - ]); - }, - exportsModuleDirective(path, print) { - return printToModuleNamesDirective(path, print, "exports"); - }, - opensModuleDirective(path, print) { - return printToModuleNamesDirective(path, print, "opens"); - }, - usesModuleDirective(path, print) { - return ["uses ", call(path, print, "typeName"), ";"]; - }, - providesModuleDirective(path, print) { - const [firstTypeName, ...restTypeNames] = map(path, print, "typeName"); - return [ - "provides ", - firstTypeName, - group(indent([ - line, - group(indent(["with", line, ...join([",", line], restTypeNames)])) - ])), - ";" - ]; - }, - requiresModifier: printSingle -}; -function sortImports(importDeclarations) { - importDeclarations.sort(({ children: a }, { children: b }) => { - if (a.Static && !b.Static) { - return -1; - } - else if (b.Static && !a.Static) { - return 1; - } - if (!b.packageOrTypeName) { - if (a.packageOrTypeName) { - return -1; - } - return 0; - } - else if (!a.packageOrTypeName) { - return 1; - } - return compareFqn(a.packageOrTypeName[0], b.packageOrTypeName[0]); - }); - return importDeclarations.reduce((staticCount, importDeclaration) => importDeclaration.children.Static ? staticCount + 1 : staticCount, 0); -} -function compareFqn(a, b) { - const identifiersA = a.children.Identifier; - const identifiersB = b.children.Identifier; - const minParts = Math.min(identifiersA.length, identifiersB.length); - for (let i = 0; i < minParts; i++) { - const imageA = identifiersA[i].image; - const imageB = identifiersB[i].image; - if (imageA < imageB) { - return -1; - } - else if (imageA > imageB) { - return 1; - } - } - return identifiersA.length - identifiersB.length; -} -function printToModuleNamesDirective(path, print, prefix) { - const directive = [prefix, " ", call(path, print, "packageName")]; - if (path.node.children.moduleName) { - const moduleNames = join([",", line], map(path, print, "moduleName")); - directive.push(group(indent([line, group(indent(["to", line, ...moduleNames]))]))); - } - directive.push(";"); - return directive; -} diff --git a/frontend/src/common/prettier/plugins/java/printers/types-values-and-variables.d.ts b/frontend/src/common/prettier/plugins/java/printers/types-values-and-variables.d.ts deleted file mode 100644 index 776c47ba..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/types-values-and-variables.d.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { builders } from "prettier/doc"; -import { printClassType, printSingle } from "./helpers.js"; -declare const _default: { - primitiveType(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - numericType: typeof printSingle; - integralType: typeof printSingle; - floatingPointType: typeof printSingle; - referenceType(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - classOrInterfaceType: typeof printSingle; - classType: typeof printClassType; - interfaceType: typeof printSingle; - typeVariable(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - dims(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - typeParameter(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - typeParameterModifier: typeof printSingle; - typeBound(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - additionalBound(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - typeArguments(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Group; - typeArgumentList(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - typeArgument: typeof printSingle; - wildcard(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; - wildcardBounds(path: import("prettier").AstPath, print: import("./helpers.js").JavaPrintFn): builders.Doc[]; -}; -export default _default; diff --git a/frontend/src/common/prettier/plugins/java/printers/types-values-and-variables.js b/frontend/src/common/prettier/plugins/java/printers/types-values-and-variables.js deleted file mode 100644 index 3738ac51..00000000 --- a/frontend/src/common/prettier/plugins/java/printers/types-values-and-variables.js +++ /dev/null @@ -1,90 +0,0 @@ -import { builders } from "prettier/doc"; -import { call, definedKeys, flatMap, isNonTerminal, map, onlyDefinedKey, printClassType, printList, printSingle } from "./helpers.js"; -const { group, indent, join, line, softline } = builders; -export default { - primitiveType(path, print) { - const { children } = path.node; - const typeKey = onlyDefinedKey(children, ["Boolean", "numericType"]); - return join(" ", [ - ...map(path, print, "annotation"), - call(path, print, typeKey) - ]); - }, - numericType: printSingle, - integralType: printSingle, - floatingPointType: printSingle, - referenceType(path, print) { - const { children } = path.node; - const typeKey = onlyDefinedKey(children, [ - "primitiveType", - "classOrInterfaceType" - ]); - const type = call(path, print, typeKey); - return join(" ", [ - ...map(path, print, "annotation"), - children.dims ? [type, call(path, print, "dims")] : type - ]); - }, - classOrInterfaceType: printSingle, - classType: printClassType, - interfaceType: printSingle, - typeVariable(path, print) { - return join(" ", [ - ...map(path, print, "annotation"), - call(path, print, "Identifier") - ]); - }, - dims(path, print) { - return flatMap(path, childPath => { - const child = print(childPath); - return isNonTerminal(childPath.node) ? [child, " "] : child; - }, definedKeys(path.node.children, ["annotation", "LSquare", "RSquare"])); - }, - typeParameter(path, print) { - const parameter = [ - ...map(path, print, "typeParameterModifier"), - call(path, print, "typeIdentifier") - ]; - if (path.node.children.typeBound) { - parameter.push(call(path, print, "typeBound")); - } - return join(" ", parameter); - }, - typeParameterModifier: printSingle, - typeBound(path, print) { - const bound = ["extends ", call(path, print, "classOrInterfaceType")]; - if (path.node.children.additionalBound) { - bound.push(group(indent([line, ...join(line, map(path, print, "additionalBound"))]))); - } - return bound; - }, - additionalBound(path, print) { - return ["& ", call(path, print, "interfaceType")]; - }, - typeArguments(path, print) { - return group([ - "<", - indent([softline, call(path, print, "typeArgumentList")]), - softline, - ">" - ]); - }, - typeArgumentList(path, print) { - return printList(path, print, "typeArgument"); - }, - typeArgument: printSingle, - wildcard(path, print) { - const wildcard = [...map(path, print, "annotation"), "?"]; - if (path.node.children.wildcardBounds) { - wildcard.push(call(path, print, "wildcardBounds")); - } - return join(" ", wildcard); - }, - wildcardBounds(path, print) { - return [ - path.node.children.Extends ? "extends" : "super", - " ", - call(path, print, "referenceType") - ]; - } -}; diff --git a/frontend/src/common/utils/latestTaskQueue.ts b/frontend/src/common/utils/latestTaskQueue.ts new file mode 100644 index 00000000..1dcb2983 --- /dev/null +++ b/frontend/src/common/utils/latestTaskQueue.ts @@ -0,0 +1,171 @@ +/** + * 按 key 串行执行任务,并只保留最后一个待执行任务。 + */ + +interface Waiter { + resolve: (result: Result) => void; + reject: (error: unknown) => void; +} + +interface Task { + payload: Input; + waiters: Waiter[]; +} + +interface State { + inFlight: Task | null; + pending: Task | null; + meta: Meta; +} + +export interface LatestTaskQueue { + enqueue(key: Key, payload: Input): Promise; + isIdle(key: Key): boolean; + getMeta(key: Key): Meta | undefined; + updateMeta(key: Key, updater: (meta: Meta) => void): void; + remove(key: Key): void; +} + +export interface LatestTaskQueueOptions { + createMeta(): Meta; + run(key: Key, payload: Input, meta: Meta): Promise; + same?(current: Input, incoming: Input, meta: Meta): boolean; + merge?(current: Input, incoming: Input, meta: Meta): Input; + next?(pending: Input, result: Result, meta: Meta): Input; + onSuccess?(key: Key, payload: Input, result: Result, meta: Meta): void; + onError?(key: Key, payload: Input, error: unknown, meta: Meta): void; + clearPendingOnError?: boolean; +} + +export const createLatestTaskQueue = ( + options: LatestTaskQueueOptions +): LatestTaskQueue => { + const states = new Map>(); + const same = options.same || (() => false); + const merge = options.merge || ((_current: Input, incoming: Input) => incoming); + const clearPendingOnError = options.clearPendingOnError ?? true; + + const ensureState = (key: Key): State => { + let state = states.get(key); + if (!state) { + state = { + inFlight: null, + pending: null, + meta: options.createMeta() + }; + states.set(key, state); + } + return state; + }; + + const enqueue = (key: Key, payload: Input): Promise => { + const state = ensureState(key); + + return new Promise((resolve, reject) => { + const incoming: Task = { + payload, + waiters: [{resolve, reject}] + }; + + if (state.inFlight && same(state.inFlight.payload, payload, state.meta)) { + state.inFlight.waiters.push(incoming.waiters[0]); + return; + } + + if (state.pending && same(state.pending.payload, payload, state.meta)) { + state.pending.waiters.push(incoming.waiters[0]); + return; + } + + if (!state.inFlight) { + state.inFlight = incoming; + void run(key, state, incoming).catch(() => undefined); + return; + } + + if (!state.pending) { + state.pending = incoming; + return; + } + + state.pending.payload = merge(state.pending.payload, payload, state.meta); + state.pending.waiters.push(incoming.waiters[0]); + }); + }; + + const isIdle = (key: Key): boolean => { + const state = states.get(key); + return !state || (!state.inFlight && !state.pending); + }; + + const getMeta = (key: Key): Meta | undefined => { + return states.get(key)?.meta; + }; + + const updateMeta = (key: Key, updater: (meta: Meta) => void) => { + updater(ensureState(key).meta); + }; + + const remove = (key: Key) => { + states.delete(key); + }; + + async function run( + key: Key, + state: State, + task: Task + ): Promise { + let resultBox: { value: Result } | undefined; + + try { + const result = await options.run(key, task.payload, state.meta); + resultBox = {value: result}; + options.onSuccess?.(key, task.payload, result, state.meta); + resolve(task, result); + } catch (error) { + options.onError?.(key, task.payload, error, state.meta); + reject(task, error); + + if (clearPendingOnError && state.pending) { + reject(state.pending, error); + state.pending = null; + } + + throw error; + } finally { + if (state.inFlight === task) { + state.inFlight = null; + } + } + + if (!state.pending) { + return; + } + + const nextTask = state.pending; + state.pending = null; + + if (resultBox && options.next) { + nextTask.payload = options.next(nextTask.payload, resultBox.value, state.meta); + } + + state.inFlight = nextTask; + await run(key, state, nextTask); + } + + function resolve(task: Task, result: Result) { + task.waiters.forEach(({resolve}) => resolve(result)); + } + + function reject(task: Task, error: unknown) { + task.waiters.forEach(({reject}) => reject(error)); + } + + return { + enqueue, + isIdle, + getMeta, + updateMeta, + remove + }; +}; diff --git a/frontend/src/components/inlineImage/DrawImageDialog.vue b/frontend/src/components/inlineImage/DrawImageDialog.vue new file mode 100644 index 00000000..31d5d667 --- /dev/null +++ b/frontend/src/components/inlineImage/DrawImageDialog.vue @@ -0,0 +1,293 @@ + + + + + diff --git a/frontend/src/components/inlineImage/draw/DrawImageFooter.vue b/frontend/src/components/inlineImage/draw/DrawImageFooter.vue new file mode 100644 index 00000000..246fdf9a --- /dev/null +++ b/frontend/src/components/inlineImage/draw/DrawImageFooter.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/frontend/src/components/inlineImage/draw/DrawImageToolbar.vue b/frontend/src/components/inlineImage/draw/DrawImageToolbar.vue new file mode 100644 index 00000000..ce2b936d --- /dev/null +++ b/frontend/src/components/inlineImage/draw/DrawImageToolbar.vue @@ -0,0 +1,342 @@ + + + + + diff --git a/frontend/src/components/inlineImage/draw/types.ts b/frontend/src/components/inlineImage/draw/types.ts new file mode 100644 index 00000000..87a17861 --- /dev/null +++ b/frontend/src/components/inlineImage/draw/types.ts @@ -0,0 +1 @@ +export type ToolMode = 'select' | 'pan' | 'pen'; diff --git a/frontend/src/components/inlineImage/draw/useInlineImageDraw.ts b/frontend/src/components/inlineImage/draw/useInlineImageDraw.ts new file mode 100644 index 00000000..50577b41 --- /dev/null +++ b/frontend/src/components/inlineImage/draw/useInlineImageDraw.ts @@ -0,0 +1,933 @@ +import {Canvas, FabricImage, PencilBrush, type FabricObject} from 'fabric'; +import {computed, nextTick, onUnmounted, ref, shallowRef, watch} from 'vue'; +import {useI18n} from 'vue-i18n'; +import {useConfigStore} from '@/stores/configStore'; +import { + buildVersionedInlineImageUrl, + canvasToPngBlob, + deleteImageAsset, + importImageBlob, +} from '@/views/editor/extensions/inlineImage/clipboard'; +import {inlineImageDrawManager} from '@/views/editor/extensions/inlineImage/manager'; +import {updateInlineImageData} from '@/views/editor/extensions/inlineImage/inlineImageParsing'; +import type {ToolMode} from './types'; + +type CanvasSnapshot = string | Record; +type InlineCanvasObject = FabricObject & {name?: string}; + +const INLINE_IMAGE_BACKGROUND_NAME = 'inline-image-background'; +const MIN_DIALOG_WIDTH = 640; +const MIN_DIALOG_HEIGHT = 440; + +export function useInlineImageDraw() { + const state = inlineImageDrawManager.useState(); + const configStore = useConfigStore(); + const {t} = useI18n(); + + const dialogRef = ref(null); + const headerRef = ref(null); + const contentRef = ref(null); + const footerRef = ref(null); + const stageRef = ref(null); + const canvasRef = ref(null); + + const fabricCanvas = shallowRef(null); + const isLoading = ref(false); + const isSaving = ref(false); + const errorMessage = ref(''); + const toolMode = ref('pen'); + const brushColor = ref('#f42525'); + const brushWidth = ref(3); + const brushWidthOptions = [1, 3, 5, 7, 10, 15, 24]; + const zoom = ref(1); + const baseScale = ref(1); + const imageWidth = ref(1); + const imageHeight = ref(1); + const scaledWidth = ref(1); + const scaledHeight = ref(1); + const stagePaddingX = ref(0); + const stagePaddingY = ref(0); + const stageOverflow = ref<'hidden' | 'auto'>('hidden'); + const canvasOffsetX = ref(0); + const canvasOffsetY = ref(0); + const isStagePanning = ref(false); + const history = ref([]); + const historyIndex = ref(-1); + const isRestoring = ref(false); + const viewportObserver = shallowRef(null); + const dialogWidth = ref(980); + const dialogHeight = ref(720); + const chromeWidth = ref(0); + const chromeHeight = ref(0); + let viewportResizeFrame: number | null = null; + + const stagePanState = { + startX: 0, + startY: 0, + scrollLeft: 0, + scrollTop: 0, + }; + + const isVisible = computed(() => state.value.visible); + const currentTagId = computed(() => state.value.tagId); + const currentAssetRef = computed(() => state.value.assetRef); + const currentImageUrl = computed(() => state.value.imageUrl); + const currentView = computed(() => state.value.view); + const zoomLabel = computed(() => `${Math.round(baseScale.value * zoom.value * 100)}%`); + const canUndo = computed(() => historyIndex.value > 0); + const canRedo = computed(() => historyIndex.value >= 0 && historyIndex.value < history.value.length - 1); + const stageCursor = computed(() => { + if (isStagePanning.value) { + return 'grabbing'; + } + if (toolMode.value === 'pan') { + return 'grab'; + } + if (toolMode.value === 'select') { + return 'default'; + } + return 'crosshair'; + }); + + const dialogStyle = computed(() => ({ + width: `${dialogWidth.value}px`, + height: `${dialogHeight.value}px`, + '--inline-image-draw-font-family': configStore.config.editing.fontFamily || 'var(--voidraft-font-mono, system-ui, -apple-system, sans-serif)', + '--inline-image-draw-font-weight': configStore.config.editing.fontWeight || '400', + '--inline-image-draw-line-height': String(configStore.config.editing.lineHeight || 1.5), + })); + + watch(isVisible, async visible => { + if (visible) { + window.addEventListener('keydown', handleKeyDown); + await nextTick(); + startViewportObserver(); + await initCanvas(); + return; + } + + window.removeEventListener('keydown', handleKeyDown); + stopViewportObserver(); + disposeCanvas(); + errorMessage.value = ''; + }); + + watch(currentImageUrl, async imageUrl => { + if (isVisible.value && imageUrl) { + await initCanvas(); + } + }); + + watch([brushColor, brushWidth], () => { + configureBrush(); + }); + + onUnmounted(() => { + window.removeEventListener('keydown', handleKeyDown); + stopViewportObserver(); + stopStagePan(); + disposeCanvas(); + }); + + function asCanvasObject(object: unknown): InlineCanvasObject | null { + if (!object || typeof object !== 'object' || !('set' in object)) { + return null; + } + + return object as InlineCanvasObject; + } + + function isBackgroundObject(object: unknown): boolean { + return asCanvasObject(object)?.name === INLINE_IMAGE_BACKGROUND_NAME; + } + + function lockCanvasObject(object: InlineCanvasObject): void { + object.selectable = false; + object.evented = false; + object.hasControls = false; + object.lockMovementX = true; + object.lockMovementY = true; + object.lockScalingFlip = true; + } + + function normalizeBrushWidth(value: number): number { + return brushWidthOptions.reduce((closest, option) => ( + Math.abs(option - value) < Math.abs(closest - value) ? option : closest + ), brushWidthOptions[0]); + } + + function createBrush(): PencilBrush | null { + if (!fabricCanvas.value) { + return null; + } + + const brush = new PencilBrush(fabricCanvas.value); + brush.color = brushColor.value; + brush.width = brushWidth.value; + brush.decimate = 0; + brush.strokeLineCap = 'round'; + brush.strokeLineJoin = 'round'; + return brush; + } + + function configureBrush(): void { + if (!fabricCanvas.value) { + return; + } + + if (!fabricCanvas.value.freeDrawingBrush) { + fabricCanvas.value.freeDrawingBrush = createBrush() ?? undefined; + } + + const brush = fabricCanvas.value.freeDrawingBrush; + if (brush) { + brush.color = brushColor.value; + brush.width = brushWidth.value; + brush.strokeLineCap = 'round'; + brush.strokeLineJoin = 'round'; + + if (brush instanceof PencilBrush) { + brush.decimate = 0; + } + } + } + + function currentAccentColor(): string { + return getComputedStyle(document.documentElement) + .getPropertyValue('--search-focus-border') + .trim() || '#4a9eff'; + } + + function applyControlStyles(target?: unknown): void { + const canvas = fabricCanvas.value; + if (!canvas) { + return; + } + + const accent = currentAccentColor(); + const applyTo = (object: unknown) => { + const canvasObject = asCanvasObject(object); + if (!canvasObject || isBackgroundObject(canvasObject)) { + return; + } + + canvasObject.cornerColor = accent; + canvasObject.borderColor = accent; + canvasObject.cornerStrokeColor = accent; + canvasObject.cornerDashArray = [4, 4]; + canvasObject.borderDashArray = [4, 4]; + canvasObject.borderScaleFactor = 2; + canvasObject.transparentCorners = false; + canvasObject.cornerStyle = 'rect'; + }; + + if (target) { + applyTo(target); + return; + } + + canvas.getObjects().forEach(applyTo); + } + + function setTool(nextTool: ToolMode): void { + toolMode.value = nextTool; + + const canvas = fabricCanvas.value; + if (!canvas) { + return; + } + + canvas.discardActiveObject(); + canvas.isDrawingMode = nextTool === 'pen'; + canvas.selection = nextTool === 'select'; + canvas.defaultCursor = nextTool === 'pan' + ? 'grab' + : nextTool === 'select' + ? 'default' + : 'crosshair'; + + canvas.getObjects().forEach(object => { + const canvasObject = asCanvasObject(object); + if (!canvasObject) { + return; + } + + if (isBackgroundObject(canvasObject)) { + lockCanvasObject(canvasObject); + canvasObject.hasBorders = false; + return; + } + + const selectable = nextTool === 'select'; + canvasObject.selectable = selectable; + canvasObject.evented = selectable; + applyControlStyles(canvasObject); + }); + + configureBrush(); + canvas.requestRenderAll(); + } + + function resetHistory(): void { + history.value = []; + historyIndex.value = -1; + } + + function captureHistory(force = false): void { + const canvas = fabricCanvas.value; + if (!canvas) { + return; + } + if (!force && isRestoring.value) { + return; + } + + const snapshot: CanvasSnapshot = canvas.toJSON(); + const nextHistory = historyIndex.value < history.value.length - 1 + ? history.value.slice(0, historyIndex.value + 1) + : history.value.slice(); + + nextHistory.push(snapshot); + history.value = nextHistory; + historyIndex.value = nextHistory.length - 1; + } + + async function restoreHistory(index: number): Promise { + const canvas = fabricCanvas.value; + if (!canvas || index < 0 || index >= history.value.length) { + return; + } + + isRestoring.value = true; + const snapshot = history.value[index]; + + await canvas.loadFromJSON(snapshot, (_serialized, object) => { + const canvasObject = asCanvasObject(object); + if (canvasObject && (isBackgroundObject(canvasObject) || canvasObject.type === 'image')) { + lockCanvasObject(canvasObject); + canvasObject.hasBorders = false; + } + applyControlStyles(canvasObject); + }); + + setTool(toolMode.value); + configureBrush(); + canvas.requestRenderAll(); + historyIndex.value = index; + isRestoring.value = false; + } + + function undo(): void { + if (canUndo.value) { + void restoreHistory(historyIndex.value - 1); + } + } + + function redo(): void { + if (canRedo.value) { + void restoreHistory(historyIndex.value + 1); + } + } + + function deleteSelection(): boolean { + const canvas = fabricCanvas.value; + if (!canvas || isLoading.value) { + return false; + } + + const activeObjects = (canvas.getActiveObjects?.() || []).filter(object => !isBackgroundObject(object)); + if (activeObjects.length === 0) { + return false; + } + + activeObjects.forEach(object => { + canvas.remove(object); + }); + + canvas.discardActiveObject(); + canvas.requestRenderAll(); + captureHistory(); + return true; + } + + function onPathCreated(): void { + if (!isRestoring.value) { + captureHistory(); + } + } + + function onObjectModified(): void { + if (!isRestoring.value) { + captureHistory(); + } + } + + function onObjectAdded(event: {target?: unknown}): void { + if (!isRestoring.value && event?.target) { + applyControlStyles(event.target); + } + } + + function measureChrome(): void { + const headerHeight = headerRef.value?.offsetHeight || 0; + const footerHeight = footerRef.value?.offsetHeight || 0; + const contentHorizontalPadding = contentRef.value + ? contentRef.value.offsetWidth - contentRef.value.clientWidth + : 0; + const contentVerticalPadding = contentRef.value + ? contentRef.value.offsetHeight - contentRef.value.clientHeight + : 0; + + chromeWidth.value = contentHorizontalPadding; + chromeHeight.value = headerHeight + footerHeight + contentVerticalPadding; + } + + function getOverlayViewport(): {width: number; height: number} { + const overlay = dialogRef.value?.parentElement; + if (!overlay) { + return { + width: Math.max(320, window.innerWidth - 56), + height: Math.max(260, window.innerHeight - 56), + }; + } + + const style = getComputedStyle(overlay); + const horizontalPadding = (Number.parseFloat(style.paddingLeft) || 0) + (Number.parseFloat(style.paddingRight) || 0); + const verticalPadding = (Number.parseFloat(style.paddingTop) || 0) + (Number.parseFloat(style.paddingBottom) || 0); + + return { + width: Math.max(320, overlay.clientWidth - horizontalPadding), + height: Math.max(260, overlay.clientHeight - verticalPadding), + }; + } + + function updateDialogSize(): void { + const dpr = window.devicePixelRatio || 1; + const logicalWidth = imageWidth.value / dpr; + const logicalHeight = imageHeight.value / dpr; + const viewport = getOverlayViewport(); + + measureChrome(); + + const maxWidth = Math.max(Math.min(MIN_DIALOG_WIDTH, viewport.width), viewport.width); + const maxHeight = Math.max(Math.min(MIN_DIALOG_HEIGHT, viewport.height), viewport.height); + const proportionalWidth = Math.round(viewport.width * 0.84); + const proportionalHeight = Math.round(viewport.height * 0.84); + const desiredWidth = Math.max( + Math.min(MIN_DIALOG_WIDTH, viewport.width), + proportionalWidth, + Math.round(logicalWidth + chromeWidth.value + 32), + ); + const desiredHeight = Math.max( + Math.min(MIN_DIALOG_HEIGHT, viewport.height), + proportionalHeight, + Math.round(logicalHeight + chromeHeight.value + 32), + ); + + dialogWidth.value = Math.min(desiredWidth, maxWidth); + dialogHeight.value = Math.min(desiredHeight, maxHeight); + } + + function queueViewportResize(): void { + if (!isVisible.value) { + return; + } + + if (viewportResizeFrame !== null) { + cancelAnimationFrame(viewportResizeFrame); + } + + viewportResizeFrame = requestAnimationFrame(() => { + viewportResizeFrame = null; + void nextTick(() => { + updateDialogSize(); + updateCanvasScale(); + }); + }); + } + + function startViewportObserver(): void { + stopViewportObserver(); + + const overlay = dialogRef.value?.parentElement; + if (!overlay || typeof ResizeObserver === 'undefined') { + return; + } + + const observer = new ResizeObserver(() => { + queueViewportResize(); + }); + observer.observe(overlay); + viewportObserver.value = observer; + } + + function stopViewportObserver(): void { + viewportObserver.value?.disconnect(); + viewportObserver.value = null; + if (viewportResizeFrame !== null) { + cancelAnimationFrame(viewportResizeFrame); + viewportResizeFrame = null; + } + } + + function syncCanvasViewport(): void { + const canvas = fabricCanvas.value; + const wrapper = canvas?.wrapperEl; + if (!canvas || !wrapper) { + return; + } + + wrapper.style.transform = canvasOffsetX.value || canvasOffsetY.value + ? `translate(${canvasOffsetX.value}px, ${canvasOffsetY.value}px)` + : 'translate(0px, 0px)'; + wrapper.style.transformOrigin = 'top left'; + wrapper.style.willChange = isStagePanning.value ? 'transform' : ''; + canvas.calcOffset(); + } + + function setCanvasOffset(offsetX: number, offsetY: number, sync = true): void { + canvasOffsetX.value = offsetX; + canvasOffsetY.value = offsetY; + if (sync) { + syncCanvasViewport(); + } + } + + function clampCanvasOffset(offsetX: number, offsetY: number): {x: number; y: number} { + const stage = stageRef.value; + if (!stage || stageOverflow.value === 'auto') { + return {x: 0, y: 0}; + } + + const stageWidth = stage.clientWidth || scaledWidth.value; + const stageHeight = stage.clientHeight || scaledHeight.value; + const minVisibleX = Math.min(140, Math.max(48, scaledWidth.value * 0.24)); + const minVisibleY = Math.min(120, Math.max(48, scaledHeight.value * 0.24)); + const centeredLeft = stagePaddingX.value; + const centeredTop = stagePaddingY.value; + + const minOffsetX = minVisibleX - scaledWidth.value - centeredLeft; + const maxOffsetX = stageWidth - minVisibleX - centeredLeft; + const minOffsetY = minVisibleY - scaledHeight.value - centeredTop; + const maxOffsetY = stageHeight - minVisibleY - centeredTop; + + return { + x: Math.min(maxOffsetX, Math.max(minOffsetX, offsetX)), + y: Math.min(maxOffsetY, Math.max(minOffsetY, offsetY)), + }; + } + + function updateCanvasScale(anchor?: {x: number; y: number}): void { + const canvas = fabricCanvas.value; + if (!canvas) { + return; + } + + const stage = stageRef.value; + const stageWidth = stage?.clientWidth || imageWidth.value; + const stageHeight = stage?.clientHeight || imageHeight.value; + const previousScrollLeft = stage?.scrollLeft || 0; + const previousScrollTop = stage?.scrollTop || 0; + const previousScaledWidth = scaledWidth.value || stageWidth; + const previousScaledHeight = scaledHeight.value || stageHeight; + const previousPaddingX = stagePaddingX.value; + const previousPaddingY = stagePaddingY.value; + const previousAnchor = anchor || {x: stageWidth / 2, y: stageHeight / 2}; + const previousViewportAnchor = { + x: previousScrollLeft + previousAnchor.x - previousPaddingX, + y: previousScrollTop + previousAnchor.y - previousPaddingY, + }; + + const dpr = window.devicePixelRatio || 1; + const logicalWidth = imageWidth.value / dpr; + const logicalHeight = imageHeight.value / dpr; + + baseScale.value = Math.min(stageWidth / logicalWidth, stageHeight / logicalHeight, 1); + const scale = baseScale.value * zoom.value; + const nextScaledWidth = Math.round(logicalWidth * scale); + const nextScaledHeight = Math.round(logicalHeight * scale); + + canvas.setDimensions({width: `${nextScaledWidth}px`, height: `${nextScaledHeight}px`}, {cssOnly: true}); + canvas.setViewportTransform([1, 0, 0, 1, 0, 0]); + + stageOverflow.value = nextScaledWidth > stageWidth || nextScaledHeight > stageHeight ? 'auto' : 'hidden'; + scaledWidth.value = nextScaledWidth; + scaledHeight.value = nextScaledHeight; + stagePaddingX.value = nextScaledWidth < stageWidth ? Math.floor((stageWidth - nextScaledWidth) / 2) : 0; + stagePaddingY.value = nextScaledHeight < stageHeight ? Math.floor((stageHeight - nextScaledHeight) / 2) : 0; + + if (stageOverflow.value === 'auto') { + setCanvasOffset(0, 0, false); + } else { + const clamped = clampCanvasOffset(canvasOffsetX.value, canvasOffsetY.value); + setCanvasOffset(clamped.x, clamped.y, false); + } + + if (stage) { + const anchorRatioX = previousViewportAnchor.x / Math.max(previousScaledWidth, 1); + const anchorRatioY = previousViewportAnchor.y / Math.max(previousScaledHeight, 1); + const nextAnchor = { + x: anchorRatioX * nextScaledWidth, + y: anchorRatioY * nextScaledHeight, + }; + + void nextTick(() => { + const maxScrollLeft = Math.max(0, nextScaledWidth + stagePaddingX.value * 2 - stageWidth); + const maxScrollTop = Math.max(0, nextScaledHeight + stagePaddingY.value * 2 - stageHeight); + stage.scrollLeft = Math.min(maxScrollLeft, Math.max(0, nextAnchor.x + stagePaddingX.value - previousAnchor.x)); + stage.scrollTop = Math.min(maxScrollTop, Math.max(0, nextAnchor.y + stagePaddingY.value - previousAnchor.y)); + fabricCanvas.value?.calcOffset(); + }); + } + + syncCanvasViewport(); + } + + function setZoom(nextZoom: number, anchor?: {x: number; y: number}): void { + zoom.value = Math.min(Math.max(nextZoom, 0.2), 4); + updateCanvasScale(anchor); + } + + function zoomIn(): void { + if (!isLoading.value) { + setZoom(zoom.value + 0.2); + } + } + + function zoomOut(): void { + if (!isLoading.value) { + setZoom(zoom.value - 0.2); + } + } + + function resetZoom(): void { + zoom.value = 1; + updateCanvasScale(); + } + + function handleWheel(event: WheelEvent): void { + if (isLoading.value) { + return; + } + + event.preventDefault(); + + const stage = stageRef.value; + if (!stage) { + return; + } + + const rect = stage.getBoundingClientRect(); + setZoom( + zoom.value * (event.deltaY < 0 ? 1.05 : 0.95), + { + x: event.clientX - rect.left, + y: event.clientY - rect.top, + }, + ); + } + + function handleStagePan(event: MouseEvent): void { + if (!isStagePanning.value || !stageRef.value) { + return; + } + + if (stageOverflow.value === 'auto') { + stageRef.value.scrollLeft = stagePanState.scrollLeft - (event.clientX - stagePanState.startX); + stageRef.value.scrollTop = stagePanState.scrollTop - (event.clientY - stagePanState.startY); + fabricCanvas.value?.calcOffset(); + return; + } + + const clamped = clampCanvasOffset( + stagePanState.scrollLeft + (event.clientX - stagePanState.startX), + stagePanState.scrollTop + (event.clientY - stagePanState.startY), + ); + setCanvasOffset(clamped.x, clamped.y); + } + + function shouldStartPan(event: MouseEvent): boolean { + if (!stageRef.value) { + return false; + } + + if (event.button === 1) { + return true; + } + + return event.button === 0 && toolMode.value === 'pan'; + } + + function stopStagePan(): void { + isStagePanning.value = false; + window.removeEventListener('mousemove', handleStagePan); + window.removeEventListener('mouseup', stopStagePan); + syncCanvasViewport(); + } + + function onStageMouseDown(event: MouseEvent): void { + if (isStagePanning.value || !stageRef.value || !shouldStartPan(event)) { + return; + } + + event.preventDefault(); + isStagePanning.value = true; + stagePanState.startX = event.clientX; + stagePanState.startY = event.clientY; + stagePanState.scrollLeft = stageOverflow.value === 'auto' ? stageRef.value.scrollLeft : canvasOffsetX.value; + stagePanState.scrollTop = stageOverflow.value === 'auto' ? stageRef.value.scrollTop : canvasOffsetY.value; + + window.addEventListener('mousemove', handleStagePan); + window.addEventListener('mouseup', stopStagePan); + } + + function onStageScroll(): void { + fabricCanvas.value?.calcOffset(); + } + + function bindCanvasPan(canvas: Canvas): void { + canvas.upperCanvasEl?.addEventListener('mousedown', onStageMouseDown); + canvas.lowerCanvasEl?.addEventListener('mousedown', onStageMouseDown); + } + + function unbindCanvasPan(canvas: Canvas): void { + canvas.upperCanvasEl?.removeEventListener('mousedown', onStageMouseDown); + canvas.lowerCanvasEl?.removeEventListener('mousedown', onStageMouseDown); + } + + async function loadImage(): Promise { + const canvas = fabricCanvas.value; + if (!canvas || !currentImageUrl.value) { + return; + } + + isLoading.value = true; + errorMessage.value = ''; + + try { + const image = await FabricImage.fromURL(currentImageUrl.value); + image.set({ + left: 0, + top: 0, + originX: 'left', + originY: 'top', + selectable: false, + evented: false, + hasControls: false, + hasBorders: false, + lockMovementX: true, + lockMovementY: true, + name: INLINE_IMAGE_BACKGROUND_NAME, + hoverCursor: 'default', + }); + + canvas.clear(); + imageWidth.value = image.width || 1; + imageHeight.value = image.height || 1; + canvas.setDimensions({width: imageWidth.value, height: imageHeight.value}); + zoom.value = 1; + setCanvasOffset(0, 0); + updateDialogSize(); + await nextTick(); + updateCanvasScale(); + canvas.add(image); + canvas.sendObjectToBack(image); + canvas.requestRenderAll(); + resetHistory(); + captureHistory(true); + + const suggestedWidth = Math.round(Math.max(3, Math.min(15, imageWidth.value / 300))); + brushWidth.value = normalizeBrushWidth(suggestedWidth); + setTool(toolMode.value); + } catch (error) { + console.error('[inlineImage] Failed to load draw image:', error); + errorMessage.value = t('inlineImage.drawDialog.loadFailed'); + } finally { + isLoading.value = false; + } + } + + async function initCanvas(): Promise { + disposeCanvas(); + errorMessage.value = ''; + + if (!canvasRef.value) { + return; + } + + const canvas = new Canvas(canvasRef.value, { + selection: false, + preserveObjectStacking: true, + }); + + fabricCanvas.value = canvas; + bindCanvasPan(canvas); + canvas.on('path:created', onPathCreated); + canvas.on('object:modified', onObjectModified); + canvas.on('object:added', onObjectAdded); + canvas.freeDrawingBrush = createBrush() ?? undefined; + if (canvas.upperCanvasEl) { + canvas.upperCanvasEl.style.touchAction = 'none'; + } + if (canvas.lowerCanvasEl) { + canvas.lowerCanvasEl.style.touchAction = 'none'; + } + + updateDialogSize(); + await loadImage(); + } + + function disposeCanvas(): void { + if (!fabricCanvas.value) { + return; + } + + unbindCanvasPan(fabricCanvas.value); + fabricCanvas.value.off('path:created', onPathCreated); + fabricCanvas.value.off('object:modified', onObjectModified); + fabricCanvas.value.off('object:added', onObjectAdded); + fabricCanvas.value.dispose(); + fabricCanvas.value = null; + } + + async function saveImage(): Promise { + if (!currentView.value || !currentTagId.value || !currentAssetRef.value || !canvasRef.value || isLoading.value || isSaving.value) { + return; + } + + isSaving.value = true; + errorMessage.value = ''; + + try { + const previousAssetRef = currentAssetRef.value; + const previousImageUrl = currentImageUrl.value; + const previousWidth = imageWidth.value; + const previousHeight = imageHeight.value; + const blob = await canvasToPngBlob(canvasRef.value); + const updatedAsset = await importImageBlob(blob, 'inline-image-drawn.png'); + const updatedImageUrl = buildVersionedInlineImageUrl(updatedAsset); + + try { + updateInlineImageData(currentView.value, currentTagId.value, { + assetRef: updatedAsset.id, + file: updatedImageUrl, + width: updatedAsset.width, + height: updatedAsset.height, + }); + await deleteImageAsset(previousAssetRef); + } catch (error) { + updateInlineImageData(currentView.value, currentTagId.value, { + assetRef: previousAssetRef, + file: previousImageUrl, + width: previousWidth, + height: previousHeight, + }); + try { + await deleteImageAsset(updatedAsset.id); + } catch (cleanupError) { + console.error('[inlineImage] Failed to cleanup imported draw image:', cleanupError); + } + throw error; + } + + inlineImageDrawManager.hide(); + } catch (error) { + console.error('[inlineImage] Failed to save draw image:', error); + errorMessage.value = t('inlineImage.drawDialog.saveFailed'); + } finally { + isSaving.value = false; + } + } + + function closeDialog(): void { + inlineImageDrawManager.hide(); + } + + function handleKeyDown(event: KeyboardEvent): void { + if (!isVisible.value) { + return; + } + + if (event.key === 'Escape') { + event.preventDefault(); + closeDialog(); + return; + } + + const target = event.target as HTMLElement | null; + const tagName = target?.tagName?.toLowerCase(); + if (tagName === 'input' || tagName === 'textarea' || tagName === 'select' || target?.isContentEditable) { + return; + } + + const isMod = event.metaKey || event.ctrlKey; + if (isMod && event.key === 'Enter') { + event.preventDefault(); + void saveImage(); + return; + } + + if (event.key === 'Backspace' || event.key === 'Delete') { + if (deleteSelection()) { + event.preventDefault(); + } + return; + } + + if (!isMod) { + return; + } + + const lowerKey = event.key.toLowerCase(); + if (lowerKey === 'z' && !event.shiftKey) { + event.preventDefault(); + undo(); + return; + } + if ((lowerKey === 'z' && event.shiftKey) || lowerKey === 'y') { + event.preventDefault(); + redo(); + } + } + + return { + dialogRef, + headerRef, + contentRef, + footerRef, + stageRef, + canvasRef, + dialogWidth, + dialogHeight, + dialogStyle, + isVisible, + isLoading, + isSaving, + errorMessage, + toolMode, + brushColor, + brushWidth, + brushWidthOptions, + zoomLabel, + imageWidth, + imageHeight, + canUndo, + canRedo, + stagePaddingX, + stagePaddingY, + stageOverflow, + stageCursor, + setTool, + undo, + redo, + zoomOut, + zoomIn, + resetZoom, + handleWheel, + onStageMouseDown, + onStageScroll, + saveImage, + closeDialog, + isStagePanning, + }; +} diff --git a/frontend/src/components/tabs/TabContainer.vue b/frontend/src/components/tabs/TabContainer.vue index 59730297..58e79703 100644 --- a/frontend/src/components/tabs/TabContainer.vue +++ b/frontend/src/components/tabs/TabContainer.vue @@ -68,9 +68,7 @@ const switchToTab = async (documentId: number) => { // 如果旧文档有未保存修改,保存它 if (oldDocId && editorStore.hasUnsavedChanges(oldDocId)) { try { - const content = editorStore.getCurrentContent(); - await documentStore.saveDocument(oldDocId, content); - editorStore.syncAfterSave(oldDocId); + await editorStore.saveDirtyEditor(oldDocId); } catch (error) { console.error('save document error:', error); } @@ -273,4 +271,4 @@ watch(() => tabStore.tabs.length, () => { } } } - \ No newline at end of file + diff --git a/frontend/src/components/titlebar/LinuxTitleBar.vue b/frontend/src/components/titlebar/LinuxTitleBar.vue index dc6174bb..ffdc654c 100644 --- a/frontend/src/components/titlebar/LinuxTitleBar.vue +++ b/frontend/src/components/titlebar/LinuxTitleBar.vue @@ -2,7 +2,7 @@
- voidraft + voidraft
{{ titleText }} @@ -61,6 +61,7 @@ import {useDocumentStore} from '@/stores/documentStore'; import {useTabStore} from '@/stores/tabStore'; import TabContainer from '@/components/tabs/TabContainer.vue'; import { + APP_ICON_URL, minimizeWindow, toggleMaximize, closeWindow, diff --git a/frontend/src/components/titlebar/WindowsTitleBar.vue b/frontend/src/components/titlebar/WindowsTitleBar.vue index 55cbd757..3ee6fc4b 100644 --- a/frontend/src/components/titlebar/WindowsTitleBar.vue +++ b/frontend/src/components/titlebar/WindowsTitleBar.vue @@ -51,6 +51,7 @@ import {useDocumentStore} from '@/stores/documentStore'; import {useTabStore} from '@/stores/tabStore'; import TabContainer from '@/components/tabs/TabContainer.vue'; import { + APP_ICON_URL, minimizeWindow, toggleMaximize, closeWindow, diff --git a/frontend/src/components/titlebar/index.ts b/frontend/src/components/titlebar/index.ts index 790fa67d..6dabd925 100644 --- a/frontend/src/components/titlebar/index.ts +++ b/frontend/src/components/titlebar/index.ts @@ -4,6 +4,8 @@ import * as runtime from '@wailsio/runtime'; * Titlebar utility functions */ +export const APP_ICON_URL = `${import.meta.env.BASE_URL}appicon.png`; + // Window control functions export const minimizeWindow = async () => { try { diff --git a/frontend/src/components/toolbar/BlockLanguageSelector.vue b/frontend/src/components/toolbar/BlockLanguageSelector.vue index 071fdc39..2ad3bb2e 100644 --- a/frontend/src/components/toolbar/BlockLanguageSelector.vue +++ b/frontend/src/components/toolbar/BlockLanguageSelector.vue @@ -183,7 +183,7 @@ const selectLanguage = (languageId: SupportedLanguage) => { try { const view = editorStore.currentEditor; const state = view.state; - const dispatch = view.dispatch; + const dispatch = view.dispatch.bind(view); const [targetLanguage, autoDetect] = languageId === 'auto' ? ['text', true] @@ -510,4 +510,4 @@ const scrollToCurrentLanguage = () => { background-color: var(--text-muted); } } - \ No newline at end of file + diff --git a/frontend/src/components/toolbar/DocumentSelector.vue b/frontend/src/components/toolbar/DocumentSelector.vue index 22c95951..2e5dfada 100644 --- a/frontend/src/components/toolbar/DocumentSelector.vue +++ b/frontend/src/components/toolbar/DocumentSelector.vue @@ -119,11 +119,11 @@ const selectDoc = async (doc: DocumentItem) => { // 如果旧文档有未保存修改,保存它 if (oldDocId && editorStore.hasUnsavedChanges(oldDocId)) { - - const content = editorStore.getCurrentContent(); - await documentStore.saveDocument(oldDocId, content); - editorStore.syncAfterSave(oldDocId); - + try { + await editorStore.saveDirtyEditor(oldDocId); + } catch (error) { + console.error('save document error:', error); + } } // 打开新文档 diff --git a/frontend/src/i18n/locales/en-US.ts b/frontend/src/i18n/locales/en-US.ts index 5607cc18..c2385f23 100644 --- a/frontend/src/i18n/locales/en-US.ts +++ b/frontend/src/i18n/locales/en-US.ts @@ -182,7 +182,7 @@ export default { general: 'General', editing: 'Editor', appearance: 'Appearance', - backupPage: 'Backup', + syncPage: 'Sync', keyBindings: 'Key Bindings', updates: 'Updates', reset: 'Reset', @@ -257,11 +257,16 @@ export default { restartNow: 'Restart Now', hotkeyPreview: 'Preview:', none: 'None', - backup: { - basicSettings: 'Basic Settings', - enableBackup: 'Enable Git Backup', - autoBackup: 'Auto Backup', - backupInterval: 'Backup Interval', + sync: { + basicSettings: 'Basic Settings', + enableSync: 'Enable Sync', + targetType: 'Sync Type', + targetTypes: { + git: 'Git', + localfs: 'Local File System' + }, + autoSync: 'Auto Sync', + syncInterval: 'Sync Interval', intervals: { '5min': '5 minutes', '10min': '10 minutes', @@ -270,8 +275,13 @@ export default { '1hour': '1 hour' }, repositoryConfig: 'Repository Configuration', - repoUrl: 'Repository URL', - repoUrlPlaceholder: 'Enter Git repository URL', + storageConfig: 'Storage Configuration', + repoUrl: 'Repository URL', + repoUrlPlaceholder: 'Enter Git repository URL', + branch: 'Branch', + branchPlaceholder: 'Enter branch name, default is main', + localfsRootPath: 'Local Storage Directory', + localfsRootPathPlaceholder: 'Select local sync directory', authConfig: 'Authentication Configuration', authMethod: 'Authentication Method', authMethods: { @@ -289,9 +299,47 @@ export default { sshKeyPathPlaceholder: 'Select SSH key file', sshKeyPassphrase: 'SSH Key Passphrase', sshKeyPassphrasePlaceholder: 'Enter SSH key passphrase', - backupOperations: 'Backup Operations', - syncToRemote: 'Sync to Remote', + syncOperations: 'Sync Operations', + syncHistory: 'Sync History', + syncToTarget: 'Sync to Target', syncing: 'Syncing...', + syncSuccess: 'Sync completed', + historyLoading: 'Loading sync history...', + historyEmpty: 'No sync history yet', + historyDetails: 'View details', + historyStatus: { + success: 'Success', + failed: 'Failed' + }, + historyTrigger: { + manual: 'Manual', + auto: 'Auto' + }, + historyFlow: { + pulled: 'Pulled', + pushed: 'Pushed', + pulledAndPushed: 'Pulled and pushed', + noChanges: 'No changes' + }, + historyBoolean: { + yes: 'Yes', + no: 'No' + }, + historyFields: { + attempt: 'Attempts', + pulled: 'Pulled to local', + pushed: 'Pushed to remote', + changes: 'Changes', + errorStage: 'Failure stage', + errorMessage: 'Error message', + dataPath: 'Data path', + repoPath: 'Local sync repo path' + }, + pagination: { + prev: 'Previous', + next: 'Next', + page: 'Page {page}' + }, actions: { sync: 'Sync', } @@ -354,14 +402,50 @@ export default { name: 'HTTP Client', description: 'Send HTTP requests directly in the editor and view responses' }, + inlineImage: { + name: 'Inline Images', + description: 'Paste clipboard images into the editor and render them as inline image widgets' + }, blockImage: { name: 'Block Image Export', description: 'Render the current code block to an image and copy it to the clipboard', copyMenu: 'Copy block as image' + }, + blockReadonly: { + name: 'Block Readonly', + description: 'Protect readonly code block ranges and allow toggling block access from the context menu', + markReadonly: 'Set block readonly', + markWritable: 'Set block writable' } }, monitor: { memory: 'Memory', clickToClean: 'Click to clean memory' + }, + inlineImage: { + copy: 'Copy', + copied: 'Copied!', + draw: 'Draw', + delete: 'Delete', + drawDialog: { + title: 'Image Annotation', + select: 'Select', + pan: 'Pan', + pen: 'Brush', + color: 'Color', + strokeWidth: 'Stroke Width', + undo: 'Undo', + redo: 'Redo', + zoomIn: 'Zoom In', + zoomOut: 'Zoom Out', + resetZoom: 'Reset Zoom', + loading: 'Loading image...', + zoom: 'Zoom', + cancel: 'Cancel', + save: 'Save', + saving: 'Saving...', + loadFailed: 'Failed to load image', + saveFailed: 'Failed to save image' + } } }; diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts index b8fe43fe..d5810ebe 100644 --- a/frontend/src/i18n/locales/zh-CN.ts +++ b/frontend/src/i18n/locales/zh-CN.ts @@ -1,369 +1,453 @@ export default { - locale: 'zh-CN', - common: { - ok: '确定', - cancel: '取消', - edit: '编辑', - delete: '删除', - confirm: '确认', - save: '保存', - reset: '重置' - }, - titlebar: { - minimize: '最小化', - maximize: '最大化', - restore: '向下还原', - close: '关闭' - }, - toolbar: { - editor: { - lines: 'Ln', - characters: 'Ch', - selected: 'Sel' + locale: 'zh-CN', + common: { + ok: '确定', + cancel: '取消', + edit: '编辑', + delete: '删除', + confirm: '确认', + save: '保存', + reset: '重置' }, - fontSizeTooltip: '字体大小 (Ctrl+滚轮调整)', - settings: '设置', - alwaysOnTop: '窗口置顶', - blockLanguage: '块语言', - searchLanguage: '搜索语言...', - noLanguageFound: '未找到匹配的语言', - formatHint: '点击格式化区块(Ctrl+Shift+F)', - previewMarkdown: '预览 Markdown', - closePreview: '关闭预览', - // 文档选择器 - selectDocument: '选择文档', - searchOrCreateDocument: '搜索或输入新文档名...', - createDocument: '创建', - noDocumentFound: '没有找到文档', - loading: '加载中...', - rename: '重命名', - delete: '删除', - confirm: '确认', - confirmDelete: '再次点击确认删除', - openInNewWindow: '在新窗口中打开', - alreadyOpenInNewWindow: '已在新窗口中打开', - }, - languages: { - 'zh-CN': '简体中文', - 'en-US': 'English' - }, - systemTheme: { - dark: '深色', - light: '浅色', - auto: '跟随系统' - }, - keybindings: { - keymapMode: '快捷键模式', - modes: { - standard: '标准模式', - emacs: 'Emacs 模式' + titlebar: { + minimize: '最小化', + maximize: '最大化', + restore: '向下还原', + close: '关闭' }, - headers: { - shortcut: '快捷键', - extension: '扩展', - description: '描述' + toolbar: { + editor: { + lines: 'Ln', + characters: 'Ch', + selected: 'Sel' + }, + fontSizeTooltip: '字体大小 (Ctrl+滚轮调整)', + settings: '设置', + alwaysOnTop: '窗口置顶', + blockLanguage: '块语言', + searchLanguage: '搜索语言...', + noLanguageFound: '未找到匹配的语言', + formatHint: '点击格式化区块(Ctrl+Shift+F)', + previewMarkdown: '预览 Markdown', + closePreview: '关闭预览', + // 文档选择器 + selectDocument: '选择文档', + searchOrCreateDocument: '搜索或输入新文档名...', + createDocument: '创建', + noDocumentFound: '没有找到文档', + loading: '加载中...', + rename: '重命名', + delete: '删除', + confirm: '确认', + confirmDelete: '再次点击确认删除', + openInNewWindow: '在新窗口中打开', + alreadyOpenInNewWindow: '已在新窗口中打开', }, - resetToDefault: '重置为默认', - confirmReset: '确认重置?', - noKeybinding: '未设置', - waitingForKey: '等待输入...', - clickToSet: '点击设置快捷键', - editKeybinding: '编辑快捷键', - config: { - enabled: '启用', - preventDefault: '阻止默认', - keybinding: '快捷键' + languages: { + 'zh-CN': '简体中文', + 'en-US': 'English' }, - keyPlaceholder: '输入键名, 回车添加', - invalidFormat: '格式错误', - conflict: '冲突: {command}', - maxKeysReached: '最多只能添加4个键', - commands: { - showSearch: '显示搜索面板', - hideSearch: '隐藏搜索面板', - searchToggleCase: '切换大小写敏感匹配', - searchToggleWord: '切换整词匹配', - searchToggleRegex: '切换正则表达式匹配', - searchShowReplace: '显示替换功能', - searchReplaceAll: '替换全部匹配项', - blockSelectAll: '块内选择全部', - blockAddAfterCurrent: '在当前块后添加新块', - blockAddAfterLast: '在最后添加新块', - blockAddBeforeCurrent: '在当前块前添加新块', - blockGotoPrevious: '跳转到上一个块', - blockGotoNext: '跳转到下一个块', - blockSelectPrevious: '选择上一个块', - blockSelectNext: '选择下一个块', - blockDelete: '删除当前块', - blockMoveUp: '向上移动当前块', - blockMoveDown: '向下移动当前块', - blockDeleteLine: '删除行', - blockMoveLineUp: '向上移动行', - blockMoveLineDown: '向下移动行', - blockTransposeChars: '字符转置', - blockFormat: '格式化代码块', - blockCopy: '复制', - blockCut: '剪切', - blockPaste: '粘贴', - copyBlockImage: '复制块图片', - historyUndo: '撤销', - historyRedo: '重做', - historyUndoSelection: '撤销选择', - historyRedoSelection: '重做选择', - foldCode: '折叠代码', - unfoldCode: '展开代码', - foldAll: '折叠全部', - unfoldAll: '展开全部', - cursorSyntaxLeft: '光标按语法左移', - cursorSyntaxRight: '光标按语法右移', - selectSyntaxLeft: '按语法选择左侧', - selectSyntaxRight: '按语法选择右侧', - copyLineUp: '向上复制行', - copyLineDown: '向下复制行', - insertBlankLine: '插入空行', - selectLine: '选择行', - selectParentSyntax: '选择父级语法', - simplifySelection: '简化选择', - addCursorAbove: '在上方添加光标', - addCursorBelow: '在下方添加光标', - cursorGroupLeft: '光标按单词左移', - cursorGroupRight: '光标按单词右移', - selectGroupLeft: '按单词选择左侧', - selectGroupRight: '按单词选择右侧', - deleteToLineEnd: '删除到行尾', - deleteToLineStart: '删除到行首', - cursorLineStart: '移动到行首', - cursorLineEnd: '移动到行尾', - selectLineStart: '选择到行首', - selectLineEnd: '选择到行尾', - cursorDocStart: '跳转到文档开头', - cursorDocEnd: '跳转到文档结尾', - selectDocStart: '选择到文档开头', - selectDocEnd: '选择到文档结尾', - selectMatchingBracket: '选择到匹配括号', - splitLine: '分割行', - indentLess: '减少缩进', - indentMore: '增加缩进', - indentSelection: '缩进选择', - cursorMatchingBracket: '光标到匹配括号', - toggleComment: '切换注释', - toggleBlockComment: '切换块注释', - insertNewlineAndIndent: '插入新行并缩进', - deleteCharBackward: '向后删除字符', - deleteCharForward: '向前删除字符', - deleteGroupBackward: '向后删除组', - deleteGroupForward: '向前删除组', - - // Emacs 模式额外的基础导航命令 - cursorCharLeft: '光标左移一个字符', - cursorCharRight: '光标右移一个字符', - cursorLineUp: '光标上移一行', - cursorLineDown: '光标下移一行', - cursorPageUp: '向上翻页', - cursorPageDown: '向下翻页', - selectCharLeft: '选择左移一个字符', - selectCharRight: '选择右移一个字符', - selectLineUp: '选择上移一行', - selectLineDown: '选择下移一行', - } - }, - tabs: { - contextMenu: { - closeTab: '关闭标签', - closeOthers: '关闭其他', - closeLeft: '关闭左侧', - closeRight: '关闭右侧' - } - }, - settings: { - title: '设置', - backToEditor: '返回编辑器', - systemInfo: '系统信息', - general: '常规', - editing: '编辑器', - appearance: '外观', - backupPage: '备份', - extensions: '扩展', - keyBindings: '快捷键', - updates: '更新', - reset: '重置', - apply: '应用', - cancel: '取消', - dangerZone: '危险操作', - resetAllSettings: '重置所有设置', - confirmReset: '确认重置?', - globalHotkey: '全局键盘快捷键', - enableGlobalHotkey: '启用全局热键', - window: '窗口/应用程序', - showInSystemTray: '在系统托盘中显示', - enableSystemTray: '启用系统托盘', - alwaysOnTop: '窗口始终置顶', - enableWindowSnap: '启用窗口吸附', - enableLoadingAnimation: '启用加载动画', - enableTabs: '启用标签页', - enableMemoryMonitor: '启用内存监视器', - startup: '启动设置', - startAtLogin: '开机自启动', - dataStorage: '数据存储', - dataPath: '数据存储路径', - clickToSelectPath: '点击选择路径', - resetDefault: '恢复默认', - resetToDefaultPath: '恢复为默认路径', - fontSize: '字体大小', - fontSettings: '字体设置', - fontFamily: '字体', - fontWeight: '字体粗细', - lineHeight: '行高', - tabSettings: 'Tab 设置', - tabSize: 'Tab 大小', - tabType: 'Tab 类型', - spaces: '空格', - tabs: '制表符', - enableTabIndent: '启用 Tab 缩进', - language: '界面语言', - systemTheme: '系统主题', - presetTheme: '预设主题', - saveOptions: '保存选项', - autoSaveDelay: '自动保存延迟(毫秒)', - updateSettings: '更新设置', - autoCheckUpdates: '自动检查更新', - autoCheckUpdatesDescription: '应用启动时自动检查更新', - manualCheck: '手动更新', - currentVersion: '当前版本', - checkForUpdates: '检查更新', - checking: '正在检查...', - checkFailed: '检查失败', - newVersionAvailable: '发现新版本', - upToDate: '已是最新版本', - viewUpdate: '查看更新', - releaseNotes: '更新日志', - networkError: '网络连接错误,请检查网络设置', - updateNow: '立即更新', - updating: '正在更新...', - updateSuccess: '更新成功', - updateSuccessRestartRequired: '更新已成功应用,请重启应用以生效', - updateSuccessNoRestart: '更新已完成,无需重启', - restartNow: '立即重启', - extensionsPage: { - loading: '加载中', - categoryEditing: '编辑增强', - categoryUI: '界面增强', - categoryTools: '工具扩展', - enabled: '启用', - configuration: '配置', - resetToDefault: '重置为默认配置', + systemTheme: { + dark: '深色', + light: '浅色', + auto: '跟随系统' }, + keybindings: { + keymapMode: '快捷键模式', + modes: { + standard: '标准模式', + emacs: 'Emacs 模式' + }, + headers: { + shortcut: '快捷键', + extension: '扩展', + description: '描述' + }, + resetToDefault: '重置为默认', + confirmReset: '确认重置?', + noKeybinding: '未设置', + waitingForKey: '等待输入...', + clickToSet: '点击设置快捷键', + editKeybinding: '编辑快捷键', + config: { + enabled: '启用', + preventDefault: '阻止默认', + keybinding: '快捷键' + }, + keyPlaceholder: '输入键名, 回车添加', + invalidFormat: '格式错误', + conflict: '冲突: {command}', + maxKeysReached: '最多只能添加4个键', + commands: { + showSearch: '显示搜索面板', + hideSearch: '隐藏搜索面板', + searchToggleCase: '切换大小写敏感匹配', + searchToggleWord: '切换整词匹配', + searchToggleRegex: '切换正则表达式匹配', + searchShowReplace: '显示替换功能', + searchReplaceAll: '替换全部匹配项', + blockSelectAll: '块内选择全部', + blockAddAfterCurrent: '在当前块后添加新块', + blockAddAfterLast: '在最后添加新块', + blockAddBeforeCurrent: '在当前块前添加新块', + blockGotoPrevious: '跳转到上一个块', + blockGotoNext: '跳转到下一个块', + blockSelectPrevious: '选择上一个块', + blockSelectNext: '选择下一个块', + blockDelete: '删除当前块', + blockMoveUp: '向上移动当前块', + blockMoveDown: '向下移动当前块', + blockDeleteLine: '删除行', + blockMoveLineUp: '向上移动行', + blockMoveLineDown: '向下移动行', + blockTransposeChars: '字符转置', + blockFormat: '格式化代码块', + blockCopy: '复制', + blockCut: '剪切', + blockPaste: '粘贴', + copyBlockImage: '复制块图片', + historyUndo: '撤销', + historyRedo: '重做', + historyUndoSelection: '撤销选择', + historyRedoSelection: '重做选择', + foldCode: '折叠代码', + unfoldCode: '展开代码', + foldAll: '折叠全部', + unfoldAll: '展开全部', + cursorSyntaxLeft: '光标按语法左移', + cursorSyntaxRight: '光标按语法右移', + selectSyntaxLeft: '按语法选择左侧', + selectSyntaxRight: '按语法选择右侧', + copyLineUp: '向上复制行', + copyLineDown: '向下复制行', + insertBlankLine: '插入空行', + selectLine: '选择行', + selectParentSyntax: '选择父级语法', + simplifySelection: '简化选择', + addCursorAbove: '在上方添加光标', + addCursorBelow: '在下方添加光标', + cursorGroupLeft: '光标按单词左移', + cursorGroupRight: '光标按单词右移', + selectGroupLeft: '按单词选择左侧', + selectGroupRight: '按单词选择右侧', + deleteToLineEnd: '删除到行尾', + deleteToLineStart: '删除到行首', + cursorLineStart: '移动到行首', + cursorLineEnd: '移动到行尾', + selectLineStart: '选择到行首', + selectLineEnd: '选择到行尾', + cursorDocStart: '跳转到文档开头', + cursorDocEnd: '跳转到文档结尾', + selectDocStart: '选择到文档开头', + selectDocEnd: '选择到文档结尾', + selectMatchingBracket: '选择到匹配括号', + splitLine: '分割行', + indentLess: '减少缩进', + indentMore: '增加缩进', + indentSelection: '缩进选择', + cursorMatchingBracket: '光标到匹配括号', + toggleComment: '切换注释', + toggleBlockComment: '切换块注释', + insertNewlineAndIndent: '插入新行并缩进', + deleteCharBackward: '向后删除字符', + deleteCharForward: '向前删除字符', + deleteGroupBackward: '向后删除组', + deleteGroupForward: '向前删除组', - customThemeColors: '自定义主题颜色', - resetToDefault: '重置为默认', - colorValue: '颜色值', - hotkeyPreview: '预览:', - none: '无', - backup: { - basicSettings: '基本设置', - enableBackup: '启用备份', - autoBackup: '自动备份', - backupInterval: '备份间隔', - intervals: { - '5min': '5分钟', - '10min': '10分钟', - '15min': '15分钟', - '30min': '30分钟', - '1hour': '1小时' - }, - repositoryConfig: '仓库配置', - repoUrl: '仓库地址', - repoUrlPlaceholder: '请输入Git仓库地址', - authConfig: '认证配置', - authMethod: '认证方式', - authMethods: { - token: '访问令牌', - sshKey: 'SSH密钥', - userPass: '用户名密码' - }, - username: '用户名', - usernamePlaceholder: '请输入用户名', - password: '密码', - passwordPlaceholder: '请输入密码', - token: '访问令牌', - tokenPlaceholder: '请输入访问令牌', - sshKeyPath: 'SSH密钥路径', - sshKeyPathPlaceholder: '请选择SSH密钥文件', - sshKeyPassphrase: 'SSH密钥密码', - sshKeyPassphrasePlaceholder: '请输入SSH密钥密码', - backupOperations: '备份操作', - syncToRemote: '同步到远程', - syncing: '同步中...', - actions: { - sync: '同步', - } - }, - }, - extensions: { - rainbowBrackets: { - name: '彩虹括号', - description: '用不同颜色显示嵌套括号' - }, - hyperlink: { - name: '超链接', - description: '识别并可点击超链接' - }, - colorSelector: { - name: '颜色选择器', - description: 'CSS代码块颜色值的可视化和选择' - }, - translator: { - name: '划词翻译', - description: '选择文本后显示翻译按钮,支持多种翻译服务' + // Emacs 模式额外的基础导航命令 + cursorCharLeft: '光标左移一个字符', + cursorCharRight: '光标右移一个字符', + cursorLineUp: '光标上移一行', + cursorLineDown: '光标下移一行', + cursorPageUp: '向上翻页', + cursorPageDown: '向下翻页', + selectCharLeft: '选择左移一个字符', + selectCharRight: '选择右移一个字符', + selectLineUp: '选择上移一行', + selectLineDown: '选择下移一行', + } }, - minimap: { - name: '小地图', - description: '显示小地图视图' + tabs: { + contextMenu: { + closeTab: '关闭标签', + closeOthers: '关闭其他', + closeLeft: '关闭左侧', + closeRight: '关闭右侧' + } }, - search: { - name: '搜索功能', - description: '文本搜索和替换功能' - }, - fold: { - name: '代码折叠', - description: '折叠和展开代码段以提高代码可读性' - }, - markdown: { - name: 'Markdown 渲染', - description: '渲染 Markdown 元素,“所见即所得”' - }, - codeblock: { - name: '代码块', - description: '代码块相关功能' - }, - lineNumbers: { - name: '行号显示', - description: '在编辑器左侧显示行号,并高亮当前行' - }, - contextMenu: { - name: '上下文菜单', - description: '在编辑器中右键点击时显示上下文菜单' - }, - highlightWhitespace: { - name: '显示空白字符', - description: '在编辑器中显示空格和制表符等空白字符' + settings: { + title: '设置', + backToEditor: '返回编辑器', + systemInfo: '系统信息', + general: '常规', + editing: '编辑器', + appearance: '外观', + syncPage: '同步', + extensions: '扩展', + keyBindings: '快捷键', + updates: '更新', + reset: '重置', + apply: '应用', + cancel: '取消', + dangerZone: '危险操作', + resetAllSettings: '重置所有设置', + confirmReset: '确认重置?', + globalHotkey: '全局键盘快捷键', + enableGlobalHotkey: '启用全局热键', + window: '窗口/应用程序', + showInSystemTray: '在系统托盘中显示', + enableSystemTray: '启用系统托盘', + alwaysOnTop: '窗口始终置顶', + enableWindowSnap: '启用窗口吸附', + enableLoadingAnimation: '启用加载动画', + enableTabs: '启用标签页', + enableMemoryMonitor: '启用内存监视器', + startup: '启动设置', + startAtLogin: '开机自启动', + dataStorage: '数据存储', + dataPath: '数据存储路径', + clickToSelectPath: '点击选择路径', + resetDefault: '恢复默认', + resetToDefaultPath: '恢复为默认路径', + fontSize: '字体大小', + fontSettings: '字体设置', + fontFamily: '字体', + fontWeight: '字体粗细', + lineHeight: '行高', + tabSettings: 'Tab 设置', + tabSize: 'Tab 大小', + tabType: 'Tab 类型', + spaces: '空格', + tabs: '制表符', + enableTabIndent: '启用 Tab 缩进', + language: '界面语言', + systemTheme: '系统主题', + presetTheme: '预设主题', + saveOptions: '保存选项', + autoSaveDelay: '自动保存延迟(毫秒)', + updateSettings: '更新设置', + autoCheckUpdates: '自动检查更新', + autoCheckUpdatesDescription: '应用启动时自动检查更新', + manualCheck: '手动更新', + currentVersion: '当前版本', + checkForUpdates: '检查更新', + checking: '正在检查...', + checkFailed: '检查失败', + newVersionAvailable: '发现新版本', + upToDate: '已是最新版本', + viewUpdate: '查看更新', + releaseNotes: '更新日志', + networkError: '网络连接错误,请检查网络设置', + updateNow: '立即更新', + updating: '正在更新...', + updateSuccess: '更新成功', + updateSuccessRestartRequired: '更新已成功应用,请重启应用以生效', + updateSuccessNoRestart: '更新已完成,无需重启', + restartNow: '立即重启', + extensionsPage: { + loading: '加载中', + categoryEditing: '编辑增强', + categoryUI: '界面增强', + categoryTools: '工具扩展', + enabled: '启用', + configuration: '配置', + resetToDefault: '重置为默认配置', + }, + + customThemeColors: '自定义主题颜色', + resetToDefault: '重置为默认', + colorValue: '颜色值', + hotkeyPreview: '预览:', + none: '无', + sync: { + basicSettings: '基本设置', + enableSync: '启用同步', + targetType: '同步方式', + targetTypes: { + git: 'Git', + localfs: '本地文件系统' + }, + autoSync: '自动同步', + syncInterval: '同步间隔', + intervals: { + '5min': '5分钟', + '10min': '10分钟', + '15min': '15分钟', + '30min': '30分钟', + '1hour': '1小时' + }, + repositoryConfig: '仓库配置', + storageConfig: '存储配置', + repoUrl: '仓库地址', + repoUrlPlaceholder: '请输入Git仓库地址', + branch: '分支', + branchPlaceholder: '请输入分支名,默认 main', + localfsRootPath: '本地存储目录', + localfsRootPathPlaceholder: '请选择本地同步目录', + authConfig: '认证配置', + authMethod: '认证方式', + authMethods: { + token: '访问令牌', + sshKey: 'SSH密钥', + userPass: '用户名密码' + }, + username: '用户名', + usernamePlaceholder: '请输入用户名', + password: '密码', + passwordPlaceholder: '请输入密码', + token: '访问令牌', + tokenPlaceholder: '请输入访问令牌', + sshKeyPath: 'SSH密钥路径', + sshKeyPathPlaceholder: '请选择SSH密钥文件', + sshKeyPassphrase: 'SSH密钥密码', + sshKeyPassphrasePlaceholder: '请输入SSH密钥密码', + syncOperations: '同步操作', + syncToTarget: '同步到目标', + syncHistory: '同步记录', + syncing: '同步中...', + syncSuccess: '同步成功', + historyLoading: '正在加载同步记录...', + historyEmpty: '暂无同步记录', + historyDetails: '查看详细信息', + historyStatus: { + success: '成功', + failed: '失败' + }, + historyTrigger: { + manual: '手动', + auto: '自动' + }, + historyFlow: { + pulled: '已拉取', + pushed: '已推送', + pulledAndPushed: '已拉取并推送', + noChanges: '无变更' + }, + historyBoolean: { + yes: '是', + no: '否' + }, + historyFields: { + attempt: '尝试次数', + pulled: '拉取到本地', + pushed: '推送到远端', + changes: '变更统计', + errorStage: '失败阶段', + errorMessage: '错误信息', + dataPath: '数据目录', + repoPath: '本地同步仓库路径' + }, + pagination: { + prev: '上一页', + next: '下一页', + page: '第 {page} 页' + }, + actions: { + sync: '同步', + } + }, }, - highlightTrailingWhitespace: { - name: '高亮行尾空白', - description: '高亮显示行尾的多余空白字符' + extensions: { + rainbowBrackets: { + name: '彩虹括号', + description: '用不同颜色显示嵌套括号' + }, + hyperlink: { + name: '超链接', + description: '识别并可点击超链接' + }, + colorSelector: { + name: '颜色选择器', + description: 'CSS代码块颜色值的可视化和选择' + }, + translator: { + name: '划词翻译', + description: '选择文本后显示翻译按钮,支持多种翻译服务' + }, + minimap: { + name: '小地图', + description: '显示小地图视图' + }, + search: { + name: '搜索功能', + description: '文本搜索和替换功能' + }, + fold: { + name: '代码折叠', + description: '折叠和展开代码段以提高代码可读性' + }, + markdown: { + name: 'Markdown 渲染', + description: '渲染 Markdown 元素,“所见即所得”' + }, + codeblock: { + name: '代码块', + description: '代码块相关功能' + }, + lineNumbers: { + name: '行号显示', + description: '在编辑器左侧显示行号,并高亮当前行' + }, + contextMenu: { + name: '上下文菜单', + description: '在编辑器中右键点击时显示上下文菜单' + }, + highlightWhitespace: { + name: '显示空白字符', + description: '在编辑器中显示空格和制表符等空白字符' + }, + highlightTrailingWhitespace: { + name: '高亮行尾空白', + description: '高亮显示行尾的多余空白字符' + }, + httpClient: { + name: 'HTTP 客户端', + description: '在编辑器中直接发送 HTTP 请求并查看响应' + }, + inlineImage: { + name: '内联图片', + description: '支持将剪贴板图片粘贴进编辑器,并以内联图片组件渲染' + }, + blockImage: { + name: '代码块导出图片', + description: '将当前代码块渲染为图片并复制到剪贴板', + copyMenu: '复制块为图片' + }, + blockReadonly: { + name: '代码块局部只读', + description: '为只读代码块范围提供写保护,并支持在右键菜单中切换块状态', + markReadonly: '设为只读块', + markWritable: '设为可写块' + }, }, - httpClient: { - name: 'HTTP 客户端', - description: '在编辑器中直接发送 HTTP 请求并查看响应' + monitor: { + memory: '内存', + clickToClean: '点击清理内存' }, - blockImage: { - name: '代码块导出图片', - description: '将当前代码块渲染为图片并复制到剪贴板', - copyMenu: '复制块为图片' + inlineImage: { + copy: '复制', + copied: '已复制', + draw: '绘制', + delete: '删除', + drawDialog: { + title: '图片标注', + select: '选择', + pan: '拖动', + pen: '画笔', + color: '颜色', + strokeWidth: '线宽', + undo: '撤销', + redo: '重做', + zoomIn: '放大', + zoomOut: '缩小', + resetZoom: '重置缩放', + loading: '正在加载图片...', + zoom: '缩放', + cancel: '取消', + save: '保存', + saving: '保存中...', + loadFailed: '图片加载失败', + saveFailed: '图片保存失败' + } } - }, - monitor: { - memory: '内存', - clickToClean: '点击清理内存' - } }; diff --git a/frontend/src/main.ts b/frontend/src/main.ts index f4d59248..863064b2 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,21 +1,39 @@ -import { createApp } from 'vue'; -import App from './App.vue'; -import '@/assets/styles/index.css'; -import { createPinia } from 'pinia'; -import i18n from './i18n'; -import router from './router'; -import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'; -import { registerDirectives } from './directives'; -import {EditorView} from "@codemirror/view"; +import { createApp } from "vue"; +import App from "./App.vue"; +import "@/assets/styles/index.css"; +import { createPinia } from "pinia"; +import i18n from "./i18n"; +import router from "./router"; +import piniaPluginPersistedstate from "pinia-plugin-persistedstate"; +import { registerDirectives } from "./directives"; +import { EditorView } from "@codemirror/view"; +import { setTransport } from "@wailsio/runtime"; + +const WAILS_TRANSPORT_MODULE = "/wails/transport.js"; (EditorView as any).EDIT_CONTEXT = false; -const pinia = createPinia(); -pinia.use(piniaPluginPersistedstate); +async function installTransport() { + const { default: transport } = await import( + /* @vite-ignore */ WAILS_TRANSPORT_MODULE + ); + setTransport(transport); +} + +async function bootstrap() { + await installTransport(); + + const pinia = createPinia(); + pinia.use(piniaPluginPersistedstate); + + const app = createApp(App); + app.use(pinia); + app.use(i18n); + app.use(router); + registerDirectives(app); + app.mount("#app"); +} -const app = createApp(App); -app.use(pinia); -app.use(i18n); -app.use(router); -registerDirectives(app); -app.mount('#app'); \ No newline at end of file +void bootstrap().catch((error) => { + console.error("[main] Failed to bootstrap application", error); +}); \ No newline at end of file diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index a40d9990..deb37135 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -7,7 +7,7 @@ import AppearancePage from '@/views/settings/pages/AppearancePage.vue'; import KeyBindingsPage from '@/views/settings/pages/KeyBindingsPage.vue'; import UpdatesPage from '@/views/settings/pages/UpdatesPage.vue'; import ExtensionsPage from '@/views/settings/pages/ExtensionsPage.vue'; -import BackupPage from '@/views/settings/pages/BackupPage.vue'; +import SyncPage from '@/views/settings/pages/SyncPage.vue'; // 测试页面 import TestPage from '@/views/settings/pages/TestPage.vue'; @@ -44,9 +44,9 @@ const settingsChildren: RouteRecordRaw[] = [ component: UpdatesPage }, { - path: 'backup', - name: 'SettingsBackup', - component: BackupPage + path: 'sync', + name: 'SettingsSync', + component: SyncPage } ]; @@ -79,4 +79,4 @@ const router = createRouter({ routes: routes }); -export default router; \ No newline at end of file +export default router; diff --git a/frontend/src/stores/backupStore.ts b/frontend/src/stores/backupStore.ts deleted file mode 100644 index 7ab02b6c..00000000 --- a/frontend/src/stores/backupStore.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { defineStore } from 'pinia'; -import { ref } from 'vue'; -import { BackupService } from '@/../bindings/voidraft/internal/services'; - -export const useBackupStore = defineStore('backup', () => { - const isSyncing = ref(false); - - const sync = async (): Promise => { - if (isSyncing.value) { - return; - } - - isSyncing.value = true; - - try { - await BackupService.Sync(); - } catch (e) { - throw e; - } finally { - isSyncing.value = false; - } - }; - - return { - isSyncing, - sync - }; -}); \ No newline at end of file diff --git a/frontend/src/stores/configStore.ts b/frontend/src/stores/configStore.ts index 259ebd3e..6a91337e 100644 --- a/frontend/src/stores/configStore.ts +++ b/frontend/src/stores/configStore.ts @@ -1,113 +1,110 @@ import {defineStore} from 'pinia'; import {computed, reactive} from 'vue'; import {ConfigService, StartupService} from '@/../bindings/voidraft/internal/services'; -import {AppConfig, AuthMethod, LanguageType, SystemThemeType, TabType} from '@/../bindings/voidraft/internal/models/models'; +import { + AppConfig, + AuthMethod, + SyncTarget, + LanguageType, + SystemThemeType, +} from '@/../bindings/voidraft/internal/models/models'; import {useI18n} from 'vue-i18n'; import {ConfigUtils} from '@/common/utils/configUtils'; import {FONT_OPTIONS} from '@/common/constant/fonts'; -import { - CONFIG_KEY_MAP, - CONFIG_LIMITS, - ConfigKey, - ConfigSection, - DEFAULT_CONFIG, - NumberConfigKey -} from '@/common/constant/config'; +import {CONFIG_KEY_MAP, CONFIG_LIMITS, ConfigKey, DEFAULT_CONFIG, NumberConfigKey} from '@/common/constant/config'; import * as runtime from '@wailsio/runtime'; export const useConfigStore = defineStore('config', () => { const {locale} = useI18n(); - // 响应式状态 const state = reactive({ - config: {...DEFAULT_CONFIG} as AppConfig, + config: structuredClone(DEFAULT_CONFIG) as AppConfig, isLoading: false, configLoaded: false }); - // Font options (no longer localized) const fontOptions = computed(() => FONT_OPTIONS); - // 统一配置更新方法 - const updateConfig = async (key: K, value: any): Promise => { + const applyConfig = (appConfig?: AppConfig | null): void => { + const nextConfig = structuredClone(DEFAULT_CONFIG) as AppConfig; + + if (appConfig?.general) Object.assign(nextConfig.general, appConfig.general); + if (appConfig?.editing) Object.assign(nextConfig.editing, appConfig.editing); + if (appConfig?.appearance) Object.assign(nextConfig.appearance, appConfig.appearance); + if (appConfig?.updates) Object.assign(nextConfig.updates, appConfig.updates); + if (appConfig?.sync) Object.assign(nextConfig.sync, appConfig.sync); + if (appConfig?.metadata) Object.assign(nextConfig.metadata, appConfig.metadata); + + state.config = nextConfig; + }; + + const ensureConfigLoaded = async (): Promise => { if (!state.configLoaded && !state.isLoading) { await initConfig(); } + }; - const backendKey = CONFIG_KEY_MAP[key]; - if (!backendKey) { - throw new Error(`No backend key mapping found for ${String(key)}`); + const setValueByPath = (target: Record, path: string, value: unknown): void => { + const segments = path.split('.'); + const lastIndex = segments.length - 1; + + let current: Record = target; + for (let index = 0; index < lastIndex; index++) { + current = current[segments[index]]; } + current[segments[lastIndex]] = value; + }; - // 从 backendKey 提取 section(例如 'general.alwaysOnTop' -> 'general') - const section = backendKey.split('.')[0] as ConfigSection; + const getValueByPath = (target: Record, path: string): unknown => { + return path.split('.').reduce((current, segment) => (current as Record)[segment], target); + }; - await ConfigService.Set(backendKey, value); - (state.config[section] as any)[key] = value; + const updateConfig = async (key: K, value: unknown): Promise => { + await ensureConfigLoaded(); + const path = CONFIG_KEY_MAP[key]; + await ConfigService.Set(path, value); + setValueByPath(state.config as Record, path, value); }; - // 只更新本地状态,不保存到后端 - const updateConfigLocal = (key: K, value: any): void => { - const backendKey = CONFIG_KEY_MAP[key]; - const section = backendKey.split('.')[0] as ConfigSection; - (state.config[section] as any)[key] = value; + const updateConfigLocal = (key: K, value: unknown): void => { + setValueByPath(state.config as Record, CONFIG_KEY_MAP[key], value); }; - // 保存指定配置到后端 const saveConfig = async (key: K): Promise => { - const backendKey = CONFIG_KEY_MAP[key]; - const section = backendKey.split('.')[0] as ConfigSection; - await ConfigService.Set(backendKey, (state.config[section] as any)[key]); + const path = CONFIG_KEY_MAP[key]; + await ConfigService.Set(path, getValueByPath(state.config as Record, path)); }; - // 加载配置 const initConfig = async (): Promise => { if (state.isLoading) return; state.isLoading = true; try { - const appConfig = await ConfigService.GetConfig(); - - if (appConfig) { - // 合并配置 - if (appConfig.general) Object.assign(state.config.general, appConfig.general); - if (appConfig.editing) Object.assign(state.config.editing, appConfig.editing); - if (appConfig.appearance) Object.assign(state.config.appearance, appConfig.appearance); - if (appConfig.updates) Object.assign(state.config.updates, appConfig.updates); - if (appConfig.backup) Object.assign(state.config.backup, appConfig.backup); - if (appConfig.metadata) Object.assign(state.config.metadata, appConfig.metadata); - } - + applyConfig(await ConfigService.GetConfig()); state.configLoaded = true; - } finally { state.isLoading = false; } }; - // 重置配置 const resetConfig = async (): Promise => { if (state.isLoading) return; state.isLoading = true; try { await ConfigService.ResetConfig(); - const appConfig = await ConfigService.GetConfig(); - if (appConfig) { - state.config = JSON.parse(JSON.stringify(appConfig)) as AppConfig; - } + applyConfig(await ConfigService.GetConfig()); + state.configLoaded = true; } finally { state.isLoading = false; } }; - // 辅助函数:限制数值范围 const clampValue = (value: number, key: NumberConfigKey): number => { const limit = CONFIG_LIMITS[key]; return ConfigUtils.clamp(value, limit.min, limit.max); }; - // 计算属性 const fontConfig = computed(() => ({ fontSize: state.config.editing.fontSize, fontFamily: state.config.editing.fontFamily, @@ -122,7 +119,6 @@ export const useConfigStore = defineStore('config', () => { })); return { - // 状态 config: computed(() => state.config), configLoaded: computed(() => state.configLoaded), isLoading: computed(() => state.isLoading), @@ -130,31 +126,25 @@ export const useConfigStore = defineStore('config', () => { fontConfig, tabConfig, - // 核心方法 initConfig, resetConfig, - // 语言相关方法 - setLanguage: (value: LanguageType) => { - updateConfig('language', value); + setLanguage: async (value: LanguageType) => { + await updateConfig('language', value); locale.value = value as any; }, - // 主题相关方法 setSystemTheme: (value: SystemThemeType) => updateConfig('systemTheme', value), setCurrentTheme: (value: string) => updateConfig('currentTheme', value), - // 字体大小操作 setFontSize: async (value: number) => { await updateConfig('fontSize', clampValue(value, 'fontSize')); }, increaseFontSize: async () => { - const newValue = state.config.editing.fontSize + 1; - await updateConfig('fontSize', clampValue(newValue, 'fontSize')); + await updateConfig('fontSize', clampValue(state.config.editing.fontSize + 1, 'fontSize')); }, decreaseFontSize: async () => { - const newValue = state.config.editing.fontSize - 1; - await updateConfig('fontSize', clampValue(newValue, 'fontSize')); + await updateConfig('fontSize', clampValue(state.config.editing.fontSize - 1, 'fontSize')); }, resetFontSize: async () => { await updateConfig('fontSize', CONFIG_LIMITS.fontSize.default); @@ -165,93 +155,78 @@ export const useConfigStore = defineStore('config', () => { decreaseFontSizeLocal: () => { updateConfigLocal('fontSize', clampValue(state.config.editing.fontSize - 1, 'fontSize')); }, + adjustFontSizeLocal: (delta: number): boolean => { + if (!Number.isFinite(delta) || delta === 0) { + return false; + } + + const nextSize = clampValue(state.config.editing.fontSize + delta, 'fontSize'); + if (nextSize === state.config.editing.fontSize) { + return false; + } + + updateConfigLocal('fontSize', nextSize); + return true; + }, saveFontSize: async () => { await saveConfig('fontSize'); }, - // 字体操作 setFontFamily: (value: string) => updateConfig('fontFamily', value), setFontWeight: (value: string) => updateConfig('fontWeight', value), - - // 行高操作 setLineHeight: async (value: number) => { await updateConfig('lineHeight', clampValue(value, 'lineHeight')); }, - // Tab操作 setEnableTabIndent: (value: boolean) => updateConfig('enableTabIndent', value), setTabSize: async (value: number) => { await updateConfig('tabSize', clampValue(value, 'tabSize')); }, increaseTabSize: async () => { - const newValue = state.config.editing.tabSize + 1; - await updateConfig('tabSize', clampValue(newValue, 'tabSize')); + await updateConfig('tabSize', clampValue(state.config.editing.tabSize + 1, 'tabSize')); }, decreaseTabSize: async () => { - const newValue = state.config.editing.tabSize - 1; - await updateConfig('tabSize', clampValue(newValue, 'tabSize')); + await updateConfig('tabSize', clampValue(state.config.editing.tabSize - 1, 'tabSize')); }, toggleTabType: async () => { const values = CONFIG_LIMITS.tabType.values; const currentIndex = values.indexOf(state.config.editing.tabType as typeof values[number]); - const nextIndex = (currentIndex + 1) % values.length; - await updateConfig('tabType', values[nextIndex]); + await updateConfig('tabType', values[(currentIndex + 1) % values.length]); }, - // 窗口操作 toggleAlwaysOnTop: async () => { await updateConfig('alwaysOnTop', !state.config.general.alwaysOnTop); await runtime.Window.SetAlwaysOnTop(state.config.general.alwaysOnTop); }, setAlwaysOnTop: (value: boolean) => updateConfig('alwaysOnTop', value), - - // 路径操作 setDataPath: (value: string) => updateConfigLocal('dataPath', value), - - // 保存配置相关方法 setAutoSaveDelay: (value: number) => updateConfig('autoSaveDelay', value), - - // 热键配置相关方法 setEnableGlobalHotkey: (value: boolean) => updateConfig('enableGlobalHotkey', value), setGlobalHotkey: (hotkey: any) => updateConfig('globalHotkey', hotkey), - - // 系统托盘配置相关方法 setEnableSystemTray: (value: boolean) => updateConfig('enableSystemTray', value), - - // 开机启动配置相关方法 setStartAtLogin: async (value: boolean) => { await updateConfig('startAtLogin', value); await StartupService.SetEnabled(value); }, - - // 窗口吸附配置相关方法 setEnableWindowSnap: (value: boolean) => updateConfig('enableWindowSnap', value), - - // 加载动画配置相关方法 setEnableLoadingAnimation: (value: boolean) => updateConfig('enableLoadingAnimation', value), - - // 标签页配置相关方法 setEnableTabs: (value: boolean) => updateConfig('enableTabs', value), - - // 内存监视器配置相关方法 setEnableMemoryMonitor: (value: boolean) => updateConfig('enableMemoryMonitor', value), - - // 快捷键模式配置相关方法 setKeymapMode: (value: any) => updateConfig('keymapMode', value), - - // 更新配置相关方法 setAutoUpdate: (value: boolean) => updateConfig('autoUpdate', value), - // 备份配置相关方法 - setEnableBackup: (value: boolean) => updateConfig('enabled', value), - setAutoBackup: (value: boolean) => updateConfig('auto_backup', value), - setRepoUrl: (value: string) => updateConfig('repo_url', value), - setAuthMethod: (value: AuthMethod) => updateConfig('auth_method', value), - setUsername: (value: string) => updateConfig('username', value), - setPassword: (value: string) => updateConfig('password', value), - setToken: (value: string) => updateConfig('token', value), - setSshKeyPath: (value: string) => updateConfig('ssh_key_path', value), - setSshKeyPassphrase: (value: string) => updateConfig('ssh_key_passphrase', value), - setBackupInterval: (value: number) => updateConfig('backup_interval', value), + setSyncTarget: (value: SyncTarget) => updateConfig('sync_target', value), + setEnableSync: (value: boolean) => updateConfig('sync_enabled', value), + setAutoSync: (value: boolean) => updateConfig('sync_auto_sync', value), + setSyncInterval: (value: number) => updateConfig('sync_sync_interval', Math.max(1, value)), + setRepoUrl: (value: string) => updateConfig('git_repo_url', value), + setGitBranch: (value: string) => updateConfig('git_branch', value.trim() || 'main'), + setAuthMethod: (value: AuthMethod) => updateConfig('git_auth_method', value), + setUsername: (value: string) => updateConfig('git_username', value), + setPassword: (value: string) => updateConfig('git_password', value), + setToken: (value: string) => updateConfig('git_token', value), + setSshKeyPath: (value: string) => updateConfig('git_ssh_key_path', value), + setSshKeyPassphrase: (value: string) => updateConfig('git_ssh_key_passphrase', value), + setLocalFSRootPath: (value: string) => updateConfig('localfs_root_path', value), }; -}); \ No newline at end of file +}); diff --git a/frontend/src/stores/documentStore.ts b/frontend/src/stores/documentStore.ts index e1cf3f5f..2de8b0fd 100644 --- a/frontend/src/stores/documentStore.ts +++ b/frontend/src/stores/documentStore.ts @@ -1,230 +1,338 @@ import {defineStore} from 'pinia'; import {ref} from 'vue'; import {DocumentService} from '@/../bindings/voidraft/internal/services'; -import {OpenDocumentWindow} from '@/../bindings/voidraft/internal/services/windowservice'; +import {DocumentSaveResult} from '@/../bindings/voidraft/internal/services/models'; import {Document} from '@/../bindings/voidraft/internal/models/ent/models'; -import type {TimerManager} from '@/common/utils/timerUtils'; +import {OpenDocumentWindow} from '@/../bindings/voidraft/internal/services/windowservice'; +import {generateContentHash} from '@/common/utils/hashUtils'; +import {createLatestTaskQueue} from '@/common/utils/latestTaskQueue'; import {createTimerManager} from '@/common/utils/timerUtils'; +import type {TimerManager} from '@/common/utils/timerUtils'; + +interface DocumentSavePayload { + content: string; + baseUpdatedAt: string; + localHash: string; +} + +interface DocumentSaveMeta { + lastResult: DocumentSaveResult | null; + lastSavedLocalHash: string | null; +} export const useDocumentStore = defineStore('document', () => { + const currentDocumentId = ref(null); + const currentDocument = ref(null); + const autoSaveTimers = ref>(new Map()); + const showDocumentSelector = ref(false); + const isLoading = ref(false); + const saveQueue = createLatestTaskQueue({ + createMeta: () => ({ + lastResult: null, + lastSavedLocalHash: null + }), + run: async (docId, payload) => { + const result = await DocumentService.UpdateDocumentContent(docId, payload.content, payload.baseUpdatedAt); + if (!result) { + throw new Error(`Document ${docId} save returned no result`); + } + return result; + }, + same: (current, incoming) => { + return current.localHash === incoming.localHash && current.baseUpdatedAt === incoming.baseUpdatedAt; + }, + merge: (_current, incoming) => incoming, + next: (pending, lastResult) => { + if (!lastResult.updated_at) { + return pending; + } + return { + ...pending, + baseUpdatedAt: lastResult.updated_at + }; + }, + onSuccess: (docId, payload, result, meta) => { + meta.lastResult = result; + meta.lastSavedLocalHash = payload.localHash; + syncCurrentDocumentAfterSave(docId, payload.content, result); + } + }); - const currentDocumentId = ref(null); - const currentDocument = ref(null); - - // 自动保存定时器 - const autoSaveTimers = ref>(new Map()); + const openDocumentSelector = () => { + showDocumentSelector.value = true; + }; - // === UI状态 === - const showDocumentSelector = ref(false); - const isLoading = ref(false); + const closeDocumentSelector = () => { + showDocumentSelector.value = false; + }; - // === UI控制方法 === - const openDocumentSelector = () => { - showDocumentSelector.value = true; - }; + const getDocumentList = async (): Promise => { + try { + isLoading.value = true; + const docs = await DocumentService.ListAllDocumentsMeta(); + return docs?.filter((doc): doc is Document => doc !== null) || []; + } catch (_error) { + return []; + } finally { + isLoading.value = false; + } + }; - const closeDocumentSelector = () => { - showDocumentSelector.value = false; - }; + const getDocument = async (docId: number): Promise => { + try { + return await DocumentService.GetDocumentByID(docId); + } catch (error) { + console.error('Failed to get document:', error); + return null; + } + }; + const saveDocument = async (docId: number, content: string, baseUpdatedAt?: string): Promise => { + const localHash = generateContentHash(content); + const cachedResult = getCachedSaveResult(docId, content, localHash); - // 获取文档列表 - const getDocumentList = async (): Promise => { - try { - isLoading.value = true; - const docs = await DocumentService.ListAllDocumentsMeta(); - return docs?.filter((doc): doc is Document => doc !== null) || []; - } catch (_error) { - return []; - } finally { - isLoading.value = false; - } - }; - - // 获取单个文档 - const getDocument = async (docId: number): Promise => { - try { - return await DocumentService.GetDocumentByID(docId); - } catch (error) { - console.error('Failed to get document:', error); - return null; - } - }; - - // 保存文档内容 - const saveDocument = async (docId: number, content: string): Promise => { - try { - await DocumentService.UpdateDocumentContent(docId, content); - return await DocumentService.GetDocumentByID(docId); - } catch (error) { - console.error('Failed to save document:', error); - throw error; - } - }; + if (cachedResult) { + return cachedResult; + } - // 在新窗口中打开文档 - const openDocumentInNewWindow = async (docId: number): Promise => { - try { - await OpenDocumentWindow(docId); - return true; - } catch (error) { - console.error('Failed to open document in new window:', error); - return false; - } - }; + return await saveQueue.enqueue(docId, { + content, + baseUpdatedAt: resolveBaseUpdatedAt(docId, baseUpdatedAt), + localHash + }); + }; - // 创建新文档 - const createNewDocument = async (title: string): Promise => { - try { - const doc = await DocumentService.CreateDocument(title); - return doc || null; - } catch (error) { - console.error('Failed to create document:', error); - return null; - } - }; + const openDocumentInNewWindow = async (docId: number): Promise => { + try { + await OpenDocumentWindow(docId); + return true; + } catch (error) { + console.error('Failed to open document in new window:', error); + return false; + } + }; - // 打开文档 - const openDocument = async (docId: number): Promise => { - try { - const doc = await DocumentService.GetDocumentByID(docId); - if (!doc) { - throw new Error(`Document ${docId} not found`); - } - - currentDocumentId.value = docId; - currentDocument.value = doc; - return true; - } catch (error) { - console.error('Failed to open document:', error); - return false; - } - }; + const createNewDocument = async (title: string): Promise => { + try { + const doc = await DocumentService.CreateDocument(title); + return doc || null; + } catch (error) { + console.error('Failed to create document:', error); + return null; + } + }; - // 更新文档标题 - const updateDocumentTitle = async (docId: number, title: string): Promise => { - try { - await DocumentService.UpdateDocumentTitle(docId, title); - - // 更新当前文档状态 - if (currentDocument.value?.id === docId) { - currentDocument.value.title = title; - currentDocument.value.updated_at = new Date().toISOString(); - } - - return true; - } catch (error) { - console.error('Failed to update document title:', error); - return false; - } - }; + const openDocument = async (docId: number): Promise => { + try { + const doc = await DocumentService.GetDocumentByID(docId); + if (!doc) { + throw new Error(`Document ${docId} not found`); + } - // 删除文档 - const deleteDocument = async (docId: number): Promise => { - try { - await DocumentService.DeleteDocument(docId); - - // 清理定时器 - const timer = autoSaveTimers.value.get(docId); - if (timer) { - timer.clear(); - autoSaveTimers.value.delete(docId); - } - - // 如果删除的是当前文档,切换到第一个可用文档 - if (currentDocumentId.value === docId) { - const docs = await getDocumentList(); - if (docs.length > 0 && docs[0].id !== undefined) { - await openDocument(docs[0].id); - } else { - currentDocumentId.value = null; - currentDocument.value = null; - } - } - - return true; - } catch (error) { - console.error('Failed to delete document:', error); - return false; - } - }; - - // 调度自动保存 - const scheduleAutoSave = (docId: number, saveCallback: () => Promise, delay: number = 2000) => { - let timer = autoSaveTimers.value.get(docId); - if (!timer) { - timer = createTimerManager(); - autoSaveTimers.value.set(docId, timer); - } - - timer.set(async () => { - try { - await saveCallback(); - } catch (error) { - console.error(`auto save for document ${docId} failed:`, error); - } - }, delay); - }; - - // 取消自动保存 - const cancelAutoSave = (docId: number) => { - const timer = autoSaveTimers.value.get(docId); - if (timer) { - timer.clear(); + currentDocumentId.value = docId; + currentDocument.value = doc; + syncSaveQueueFromDocument(docId, doc); + return true; + } catch (error) { + console.error('Failed to open document:', error); + return false; + } + }; + + const reloadCurrentDocument = async (): Promise => { + if (!currentDocumentId.value) { + return null; + } + + const doc = await getDocument(currentDocumentId.value); + if (!doc) { + return null; + } + + currentDocument.value = doc; + syncSaveQueueFromDocument(currentDocumentId.value, doc); + return doc; + }; + + const updateDocumentTitle = async (docId: number, title: string): Promise => { + try { + await DocumentService.UpdateDocumentTitle(docId, title); + resetSaveMeta(docId); + + if (currentDocument.value?.id === docId) { + const refreshedDoc = await DocumentService.GetDocumentByID(docId); + if (refreshedDoc) { + currentDocument.value = refreshedDoc; + syncSaveQueueFromDocument(docId, refreshedDoc); + } else { + currentDocument.value.title = title; + currentDocument.value.updated_at = ''; } - }; + } - // 初始化文档 - const initDocument = async (urlDocumentId?: number): Promise => { - try { - const docs = await getDocumentList(); - - // 优先使用URL参数中的文档ID - if (urlDocumentId) { - await openDocument(urlDocumentId); - } else if (currentDocumentId.value) { - // 使用持久化的文档ID - await openDocument(currentDocumentId.value); - } else if (docs.length > 0 && docs[0].id !== undefined) { - // 打开第一个文档 - await openDocument(docs[0].id); - } - } catch (error) { - console.error('Failed to initialize document store:', error); + return true; + } catch (error) { + console.error('Failed to update document:', error); + return false; + } + }; + + const deleteDocument = async (docId: number): Promise => { + try { + await DocumentService.DeleteDocument(docId); + + const timer = autoSaveTimers.value.get(docId); + if (timer) { + timer.clear(); + autoSaveTimers.value.delete(docId); + } + saveQueue.remove(docId); + + if (currentDocumentId.value === docId) { + const docs = await getDocumentList(); + if (docs.length > 0 && docs[0].id !== undefined) { + await openDocument(docs[0].id); + } else { + currentDocumentId.value = null; + currentDocument.value = null; } - }; + } + + return true; + } catch (error) { + console.error('Failed to delete document:', error); + return false; + } + }; + + const scheduleAutoSave = (docId: number, saveCallback: () => Promise, delay: number = 2000) => { + let timer = autoSaveTimers.value.get(docId); + if (!timer) { + timer = createTimerManager(); + autoSaveTimers.value.set(docId, timer); + } + + timer.set(async () => { + try { + await saveCallback(); + } catch (error) { + console.error(`auto save for document ${docId} failed:`, error); + } + }, delay); + }; + + const cancelAutoSave = (docId: number) => { + const timer = autoSaveTimers.value.get(docId); + if (timer) { + timer.clear(); + } + }; + + const initDocument = async (urlDocumentId?: number): Promise => { + try { + const docs = await getDocumentList(); + + if (urlDocumentId) { + await openDocument(urlDocumentId); + } else if (currentDocumentId.value) { + await openDocument(currentDocumentId.value); + } else if (docs.length > 0 && docs[0].id !== undefined) { + await openDocument(docs[0].id); + } + } catch (error) { + console.error('Failed to initialize document store:', error); + } + }; + + return { + currentDocumentId, + currentDocument, + showDocumentSelector, + isLoading, + getDocumentList, + getDocument, + saveDocument, + createNewDocument, + updateDocumentTitle, + deleteDocument, + openDocument, + reloadCurrentDocument, + openDocumentInNewWindow, + scheduleAutoSave, + cancelAutoSave, + openDocumentSelector, + closeDocumentSelector, + initDocument, + }; + + function getCachedSaveResult(docId: number, content: string, localHash: string): DocumentSaveResult | null { + if (!saveQueue.isIdle(docId)) { + return null; + } + + const meta = saveQueue.getMeta(docId); + if (!meta?.lastResult || meta.lastSavedLocalHash !== localHash) { + return null; + } + + syncCurrentDocumentAfterSave(docId, content, meta.lastResult); + return meta.lastResult; + } + + function resolveBaseUpdatedAt(docId: number, fallback?: string): string { + if (fallback) { + return fallback; + } + const meta = saveQueue.getMeta(docId); + if (meta?.lastResult?.updated_at) { + return meta.lastResult.updated_at; + } + if (currentDocument.value?.id === docId) { + return currentDocument.value.updated_at || ''; + } + return ''; + } + + function resetSaveMeta(docId: number) { + const meta = saveQueue.getMeta(docId); + if (!meta) { + return; + } + + meta.lastResult = null; + meta.lastSavedLocalHash = null; + } + function syncCurrentDocumentAfterSave(docId: number, content: string, result: DocumentSaveResult) { + if (currentDocument.value?.id !== docId) { + return; + } + currentDocument.value.content = content; + currentDocument.value.updated_at = result.updated_at; + } + + function syncSaveQueueFromDocument(docId: number, doc: Document) { + const content = doc.content || ''; + saveQueue.updateMeta(docId, (meta) => { + meta.lastResult = buildSaveResultFromDocument(docId, doc, content); + meta.lastSavedLocalHash = generateContentHash(content); + }); + } + + function buildSaveResultFromDocument(docId: number, doc: Document, content: string): DocumentSaveResult { return { - // 状态 - currentDocumentId, - currentDocument, - showDocumentSelector, - isLoading, - - getDocumentList, - getDocument, - saveDocument, - createNewDocument, - updateDocumentTitle, - deleteDocument, - openDocument, - openDocumentInNewWindow, - - // 自动保存 - scheduleAutoSave, - cancelAutoSave, - - // UI 控制 - openDocumentSelector, - closeDocumentSelector, - - // 初始化 - initDocument, + document_id: docId, + updated_at: doc.updated_at || '', + content_length: content.length, + content_hash: generateContentHash(content), + saved_at: doc.updated_at || '', + changed: false }; + } }, { - persist: { - key: 'voidraft-document', - storage: localStorage, - pick: ['currentDocumentId'] - } -}); \ No newline at end of file + persist: { + key: 'voidraft-document', + storage: localStorage, + pick: ['currentDocumentId'] + } +}); diff --git a/frontend/src/stores/editorStore.ts b/frontend/src/stores/editorStore.ts index 10678fca..39edbf15 100644 --- a/frontend/src/stores/editorStore.ts +++ b/frontend/src/stores/editorStore.ts @@ -10,7 +10,7 @@ import {createThemeExtension, updateEditorTheme} from '@/views/editor/basic/them import {getTabExtensions, updateTabConfig} from '@/views/editor/basic/tabExtension'; import {createFontExtensionFromBackend, updateFontConfig} from '@/views/editor/basic/fontExtension'; import {createStatsUpdateExtension} from '@/views/editor/basic/statsExtension'; -import {createContentChangePlugin} from '@/views/editor/basic/contentChangeExtension'; +import {createContentChangePlugin, externalDocumentUpdateAnnotation} from '@/views/editor/basic/contentChangeExtension'; import {createWheelZoomExtension} from '@/views/editor/basic/wheelZoomExtension'; import {createCursorPositionExtension, scrollToCursor} from '@/views/editor/basic/cursorPositionExtension'; import {createFoldStateExtension, restoreFoldState} from '@/views/editor/basic/foldStateExtension'; @@ -24,15 +24,15 @@ import createCodeBlockExtension from "@/views/editor/extensions/codeblock"; import {LruCache} from '@/common/utils/lruCache'; import {EDITOR_CONFIG} from '@/common/constant/editor'; import {useEditorStateStore, type DocumentStats} from './editorStateStore'; +import {DocumentSaveResult} from '@/../bindings/voidraft/internal/services/models'; // 编辑器实例 interface EditorInstance { view: EditorView; documentId: number; - contentTimestamp: string; // 文档时间戳 + baseUpdatedAt: string; contentLength: number; // 内容长度 isDirty: boolean; - lastModified: number; } export const useEditorStore = defineStore('editor', () => { @@ -48,7 +48,7 @@ export const useEditorStore = defineStore('editor', () => { // 验证缓存是否有效 const isCacheValid = (cached: EditorInstance, doc: Document): boolean => { - return cached.contentTimestamp === doc.updated_at + return cached.baseUpdatedAt === doc.updated_at && cached.contentLength === (doc.content || '').length; }; @@ -92,8 +92,13 @@ export const useEditorStore = defineStore('editor', () => { // 滚轮缩放扩展 const wheelZoomExtension = createWheelZoomExtension({ - increaseFontSize: () => configStore.increaseFontSizeLocal(), - decreaseFontSize: () => configStore.decreaseFontSizeLocal(), + adjustFontSize: (delta) => { + const changed = configStore.adjustFontSizeLocal(delta); + if (!changed) { + return; + } + applyFontSettings(); + }, onSave: () => configStore.saveFontSize(), saveDelay: 1000 }); @@ -163,13 +168,23 @@ export const useEditorStore = defineStore('editor', () => { return { view, documentId: docId, - contentTimestamp: doc.updated_at || '', + baseUpdatedAt: doc.updated_at || '', contentLength: content.length, - isDirty: false, - lastModified: Date.now() + isDirty: false }; }; + const getFontConfigSnapshot = () => ({ + fontFamily: configStore.config.editing.fontFamily, + fontSize: configStore.config.editing.fontSize, + lineHeight: configStore.config.editing.lineHeight, + fontWeight: configStore.config.editing.fontWeight + }); + + const applyFontSettingsToView = (view: EditorView) => { + updateFontConfig(view, getFontConfigSnapshot()); + }; + // 更新编辑器内容 const updateEditorContent = (instance: EditorInstance, doc: Document) => { const currentContent = instance.view.state.doc.toString(); @@ -177,7 +192,7 @@ export const useEditorStore = defineStore('editor', () => { // 如果内容相同,只更新元数据 if (currentContent === newContent) { - instance.contentTimestamp = doc.updated_at || ''; + instance.baseUpdatedAt = doc.updated_at || ''; instance.contentLength = newContent.length; return; } @@ -191,7 +206,8 @@ export const useEditorStore = defineStore('editor', () => { from: 0, to: instance.view.state.doc.length, insert: newContent - } + }, + annotations: externalDocumentUpdateAnnotation.of(true) }); // 智能恢复光标位置 @@ -205,7 +221,7 @@ export const useEditorStore = defineStore('editor', () => { } // 同步元数据 - instance.contentTimestamp = doc.updated_at || ''; + instance.baseUpdatedAt = doc.updated_at || ''; instance.contentLength = newContent.length; instance.isDirty = false; }; @@ -215,6 +231,7 @@ export const useEditorStore = defineStore('editor', () => { if (!containerElement.value) return; try { + applyFontSettingsToView(instance.view); // 移除当前编辑器 DOM const currentEditor = editorCache.get(currentEditorId.value || 0); if (currentEditor && currentEditor.view.dom && currentEditor.view.dom.parentElement) { @@ -251,27 +268,90 @@ export const useEditorStore = defineStore('editor', () => { // 标记为脏数据 instance.isDirty = true; - instance.lastModified = Date.now(); // 调度自动保存 const autoSaveDelay = configStore.config.editing.autoSaveDelay; documentStore.scheduleAutoSave( docId, async () => { - const content = instance.view.state.doc.toString(); - const savedDoc = await documentStore.saveDocument(docId, content); - - // 同步版本信息 - if (savedDoc) { - instance.contentTimestamp = savedDoc.updated_at || ''; - instance.contentLength = (savedDoc.content || '').length; - instance.isDirty = false; - } + await saveEditorInstance(instance); }, autoSaveDelay ); }; + const saveDirtyEditor = async (docId: number): Promise => { + const instance = editorCache.get(docId); + if (!instance) { + return null; + } + return await saveEditorInstance(instance); + }; + + const saveAllDirtyEditors = async (): Promise => { + const dirtyEditors = editorCache.values().filter(instance => instance.isDirty); + for (const instance of dirtyEditors) { + await saveEditorInstance(instance); + } + }; + + const renderEditor = async (docId: number, doc: Document) => { + const cached = editorCache.get(docId); + + if (cached) { + // 场景1:缓存有效 + if (isCacheValid(cached, doc)) { + showEditor(cached); + return; + } + + // 场景2:有未保存修改 + if (cached.isDirty) { + // 检查内容是否真的不同 + if (!hasContentChanged(cached, doc)) { + // 内容实际相同,只是元数据变了,同步元数据 + cached.baseUpdatedAt = doc.updated_at || ''; + cached.contentLength = (doc.content || '').length; + cached.isDirty = false; + } + // 内容不同,保留用户编辑 + showEditor(cached); + return; + } + + // 场景3:缓存失效且无脏数据,更新内容 + updateEditorContent(cached, doc); + showEditor(cached); + return; + } + + // 场景4:创建新编辑器 + const editor = await createEditorInstance(docId, doc); + + // 添加到缓存 + editorCache.set(docId, editor, (_evictedKey, evictedInstance) => { + disposeEditorInstance(evictedInstance); + }); + + showEditor(editor); + }; + + const loadEditor = async (doc: Document) => { + if (doc.id === undefined) { + return; + } + isLoading.value = true; + + try { + await renderEditor(doc.id, doc); + } catch (error) { + console.error('Failed to load editor:', error); + } finally { + setTimeout(() => { + isLoading.value = false; + }, EDITOR_CONFIG.LOADING_DELAY); + } + }; // 切换到指定编辑器 const switchToEditor = async (docId: number) => { @@ -284,56 +364,7 @@ export const useEditorStore = defineStore('editor', () => { throw new Error(`Failed to load document ${docId}`); } - const cached = editorCache.get(docId); - - if (cached) { - // 场景1:缓存有效 - if (isCacheValid(cached, doc)) { - showEditor(cached); - return; - } - - // 场景2:有未保存修改 - if (cached.isDirty) { - // 检查内容是否真的不同 - if (!hasContentChanged(cached, doc)) { - // 内容实际相同,只是元数据变了,同步元数据 - cached.contentTimestamp = doc.updated_at || ''; - cached.contentLength = (doc.content || '').length; - cached.isDirty = false; - } - // 内容不同,保留用户编辑 - showEditor(cached); - return; - } - - // 场景3:缓存失效且无脏数据,更新内容 - updateEditorContent(cached, doc); - showEditor(cached); - } else { - // 场景4:创建新编辑器 - const editor = await createEditorInstance(docId, doc); - - // 添加到缓存 - editorCache.set(docId, editor, (_evictedKey, evictedInstance) => { - // 保存光标位置 - const cursorPos = evictedInstance.view.state.selection.main.head; - editorStateStore.saveCursorPosition(evictedInstance.documentId, cursorPos); - - // 从扩展管理器移除 - removeExtensionManagerView(evictedInstance.documentId); - - // 移除 DOM - if (evictedInstance.view.dom.parentElement) { - evictedInstance.view.dom.remove(); - } - - // 销毁编辑器 - evictedInstance.view.destroy(); - }); - - showEditor(editor); - } + await renderEditor(docId, doc); } catch (error) { console.error('Failed to switch editor:', error); } finally { @@ -363,39 +394,13 @@ export const useEditorStore = defineStore('editor', () => { return instance?.isDirty || false; }; - // 同步保存后的版本信息 - const syncAfterSave = async (docId: number) => { - const instance = editorCache.get(docId); - if (!instance) return; - - const doc = await documentStore.getDocument(docId); - if (doc) { - instance.contentTimestamp = doc.updated_at || ''; - instance.contentLength = (doc.content || '').length; - instance.isDirty = false; - } - }; - // 销毁编辑器 const destroyEditor = async (docId: number) => { const instance = editorCache.get(docId); if (!instance) return; try { - // 保存光标位置 - const cursorPos = instance.view.state.selection.main.head; - editorStateStore.saveCursorPosition(docId, cursorPos); - - // 从扩展管理器移除 - removeExtensionManagerView(docId); - - // 移除 DOM - if (instance.view.dom && instance.view.dom.parentElement) { - instance.view.dom.remove(); - } - - // 销毁编辑器 - instance.view.destroy(); + disposeEditorInstance(instance); // 从缓存删除 editorCache.delete(docId); @@ -410,22 +415,9 @@ export const useEditorStore = defineStore('editor', () => { }; // 清空所有编辑器 - const destroyAllEditors = () => { + const clearEditorCache = () => { editorCache.clear((_documentId, instance) => { - // 保存光标位置 - const cursorPos = instance.view.state.selection.main.head; - editorStateStore.saveCursorPosition(instance.documentId, cursorPos); - - // 从扩展管理器移除 - removeExtensionManagerView(instance.documentId); - - // 移除 DOM - if (instance.view.dom.parentElement) { - instance.view.dom.remove(); - } - - // 销毁编辑器 - instance.view.destroy(); + disposeEditorInstance(instance); }); currentEditorId.value = null; @@ -440,12 +432,10 @@ export const useEditorStore = defineStore('editor', () => { // 应用字体设置 const applyFontSettings = () => { editorCache.values().forEach(instance => { - updateFontConfig(instance.view, { - fontFamily: configStore.config.editing.fontFamily, - fontSize: configStore.config.editing.fontSize, - lineHeight: configStore.config.editing.lineHeight, - fontWeight: configStore.config.editing.fontWeight - }); + if (!instance.view.dom.isConnected) { + return; + } + applyFontSettingsToView(instance.view); }); }; @@ -493,15 +483,17 @@ export const useEditorStore = defineStore('editor', () => { // 编辑器管理 setEditorContainer, + loadEditor, switchToEditor, destroyEditor, - destroyAllEditors, + clearEditorCache, // 查询方法 getCurrentContent, getCurrentCursorPosition, hasUnsavedChanges, - syncAfterSave, + saveDirtyEditor, + saveAllDirtyEditors, // 配置应用 applyFontSettings, @@ -509,4 +501,36 @@ export const useEditorStore = defineStore('editor', () => { applyTabSettings, applyKeymapSettings, }; + + async function saveEditorInstance(instance: EditorInstance): Promise { + if (editorCache.get(instance.documentId) !== instance || !instance.isDirty) { + return null; + } + + const content = instance.view.state.doc.toString(); + const savedDoc = await documentStore.saveDocument(instance.documentId, content, instance.baseUpdatedAt); + + instance.baseUpdatedAt = savedDoc.updated_at || ''; + instance.contentLength = savedDoc.content_length ?? content.length; + + if (instance.view.state.doc.toString() === content) { + instance.isDirty = false; + documentStore.cancelAutoSave(instance.documentId); + } + + return savedDoc; + } + + function disposeEditorInstance(instance: EditorInstance) { + const cursorPos = instance.view.state.selection.main.head; + editorStateStore.saveCursorPosition(instance.documentId, cursorPos); + documentStore.cancelAutoSave(instance.documentId); + removeExtensionManagerView(instance.documentId); + + if (instance.view.dom.parentElement) { + instance.view.dom.remove(); + } + + instance.view.destroy(); + } }); diff --git a/frontend/src/stores/syncStore.ts b/frontend/src/stores/syncStore.ts new file mode 100644 index 00000000..d740db8e --- /dev/null +++ b/frontend/src/stores/syncStore.ts @@ -0,0 +1,80 @@ +import {defineStore} from 'pinia'; +import {computed, ref} from 'vue'; +import {SyncService} from '@/../bindings/voidraft/internal/services'; +import {SyncRunRecord} from '@/../bindings/voidraft/internal/models/models'; + +const DEFAULT_RUNS_PAGE_SIZE = 8; + +export const useSyncStore = defineStore('sync', () => { + const isSyncing = ref(false); + const isLoadingRuns = ref(false); + const runs = ref([]); + const currentPage = ref(1); + const hasMoreRuns = ref(false); + const canLoadPrevRuns = computed(() => currentPage.value > 1); + const canLoadNextRuns = computed(() => hasMoreRuns.value); + + /** Loads recent sync records. */ + const loadRuns = async (page = 1): Promise => { + const safePage = Math.max(page, 1); + isLoadingRuns.value = true; + try { + const items = await SyncService.ListSyncRunLogs(safePage, DEFAULT_RUNS_PAGE_SIZE + 1); + hasMoreRuns.value = items.length > DEFAULT_RUNS_PAGE_SIZE; + runs.value = hasMoreRuns.value ? items.slice(0, DEFAULT_RUNS_PAGE_SIZE) : items; + currentPage.value = safePage; + } finally { + isLoadingRuns.value = false; + } + }; + + /** Loads previous sync log page. */ + const loadPrevRunsPage = async (): Promise => { + if (!canLoadPrevRuns.value || isLoadingRuns.value) { + return; + } + + await loadRuns(currentPage.value - 1); + }; + + /** Loads next sync log page. */ + const loadNextRunsPage = async (): Promise => { + if (!canLoadNextRuns.value || isLoadingRuns.value) { + return; + } + + await loadRuns(currentPage.value + 1); + }; + + /** Runs one sync. */ + const sync = async (): Promise => { + if (isSyncing.value) { + return; + } + + isSyncing.value = true; + try { + await SyncService.Sync(); + } finally { + isSyncing.value = false; + try { + await loadRuns(1); + } catch { + } + } + }; + + return { + isSyncing, + isLoadingRuns, + runs, + currentPage, + hasMoreRuns, + canLoadPrevRuns, + canLoadNextRuns, + loadRuns, + loadPrevRunsPage, + loadNextRunsPage, + sync + }; +}); diff --git a/frontend/src/stores/themeStore.ts b/frontend/src/stores/themeStore.ts index 2eeea5ff..05730ebb 100644 --- a/frontend/src/stores/themeStore.ts +++ b/frontend/src/stores/themeStore.ts @@ -62,11 +62,15 @@ export const useThemeStore = defineStore('theme', () => { // 从服务器获取主题颜色 const fetchThemeColors = async (themeName: string): Promise => { const safeName = resolveThemeName(themeName); - const theme = await ThemeService.GetThemeByName(safeName); - if (theme?.colors) { - const colors = cloneThemeColors(theme.colors as ThemeColors); - colors.themeName = safeName; - return colors; + try { + const theme = await ThemeService.GetThemeByName(safeName); + if (theme?.colors) { + const colors = cloneThemeColors(theme.colors as ThemeColors); + colors.themeName = safeName; + return colors; + } + } catch (error) { + console.warn('Failed to load custom theme, using preset:', safeName, error); } return getPresetColors(safeName); }; diff --git a/frontend/src/stores/translationStore.ts b/frontend/src/stores/translationStore.ts index 4bbcbf72..8bffaed6 100644 --- a/frontend/src/stores/translationStore.ts +++ b/frontend/src/stores/translationStore.ts @@ -42,7 +42,7 @@ export const useTranslationStore = defineStore('translation', () => { try { const languages = await TranslationService.GetTranslatorLanguages(translatorType as any); if (languages) { - translatorLanguages.value[translatorType] = languages; + translatorLanguages.value[translatorType] = languages as Record; } } catch (err) { console.error(`Failed to preload languages for ${translatorType}:`, err); @@ -63,7 +63,7 @@ export const useTranslationStore = defineStore('translation', () => { try { const languages = await TranslationService.GetTranslatorLanguages(translatorType as any); if (languages) { - translatorLanguages.value[translatorType] = languages; + translatorLanguages.value[translatorType] = languages as Record; } } catch (err) { console.error(`Failed to load languages for ${translatorType}:`, err); diff --git a/frontend/src/views/editor/Editor.vue b/frontend/src/views/editor/Editor.vue index 201a5eec..ab607d59 100644 --- a/frontend/src/views/editor/Editor.vue +++ b/frontend/src/views/editor/Editor.vue @@ -11,6 +11,8 @@ import ContextMenu from '@/views/editor/extensions/contextMenu/ContextMenu.vue'; import {contextMenuManager} from '@/views/editor/extensions/contextMenu/manager'; import TranslatorDialog from './extensions/translator/TranslatorDialog.vue'; import {translatorManager} from './extensions/translator/manager'; +import DrawImageDialog from '@/components/inlineImage/DrawImageDialog.vue'; +import {inlineImageDrawManager} from './extensions/inlineImage/manager'; const editorStore = useEditorStore(); @@ -19,6 +21,7 @@ const configStore = useConfigStore(); const windowStore = useWindowStore(); const tabStore = useTabStore(); +const editorContainerElement = ref(null); const editorElement = ref(null); const enableLoadingAnimation = computed(() => configStore.config.general.enableLoadingAnimation); @@ -43,11 +46,12 @@ onMounted(async () => { onBeforeUnmount(() => { contextMenuManager.destroy(); translatorManager.destroy(); + inlineImageDrawManager.destroy(); }); diff --git a/frontend/src/views/editor/basic/contentChangeExtension.ts b/frontend/src/views/editor/basic/contentChangeExtension.ts index 811a8812..001d90d8 100644 --- a/frontend/src/views/editor/basic/contentChangeExtension.ts +++ b/frontend/src/views/editor/basic/contentChangeExtension.ts @@ -1,5 +1,7 @@ import {EditorView, ViewPlugin, ViewUpdate} from '@codemirror/view'; -import type {Text} from '@codemirror/state'; +import {Annotation, type Text} from '@codemirror/state'; + +export const externalDocumentUpdateAnnotation = Annotation.define(); /** * 内容变化监听扩展 @@ -21,6 +23,11 @@ export function createContentChangePlugin(onContentChange: () => void) { return; } + if (update.transactions.some(transaction => transaction.annotation(externalDocumentUpdateAnnotation))) { + this.lastDoc = update.state.doc; + return; + } + this.lastDoc = update.state.doc; this.scheduleNotification(); } diff --git a/frontend/src/views/editor/basic/fontExtension.ts b/frontend/src/views/editor/basic/fontExtension.ts index 7df495bf..154375bd 100644 --- a/frontend/src/views/editor/basic/fontExtension.ts +++ b/frontend/src/views/editor/basic/fontExtension.ts @@ -1,7 +1,6 @@ -import { EditorView } from '@codemirror/view'; -import { Extension, Compartment } from '@codemirror/state'; +import {Compartment, Extension} from '@codemirror/state'; +import {EditorView} from '@codemirror/view'; -// 字体配置接口 export interface FontConfig { fontFamily: string; fontSize?: number; @@ -9,10 +8,8 @@ export interface FontConfig { fontWeight?: string; } -// 创建字体配置compartment export const fontCompartment = new Compartment(); -// 默认字体配置 export const DEFAULT_FONT_CONFIG: FontConfig = { fontFamily: 'HarmonyOS', fontSize: 13, @@ -20,7 +17,19 @@ export const DEFAULT_FONT_CONFIG: FontConfig = { fontWeight: '400' }; -// 从后端配置创建字体配置 +const appliedFontConfigCache = new WeakMap(); + +function normalizeFontConfig(config: Partial): FontConfig { + return {...DEFAULT_FONT_CONFIG, ...config}; +} + +function isSameFontConfig(previous: FontConfig | undefined, next: FontConfig): boolean { + return previous?.fontFamily === next.fontFamily + && previous?.fontSize === next.fontSize + && previous?.lineHeight === next.lineHeight + && previous?.fontWeight === next.fontWeight; +} + export function createFontConfigFromBackend(backendConfig: { fontFamily?: string; fontSize?: number; @@ -35,22 +44,21 @@ export function createFontConfigFromBackend(backendConfig: { }; } -// 创建字体样式扩展 export function createFontExtension(config: Partial = {}): Extension { - const fontConfig = { ...DEFAULT_FONT_CONFIG, ...config }; - + const fontConfig = normalizeFontConfig(config); + const styles: Record = { '&': { fontFamily: fontConfig.fontFamily, - ...(fontConfig.fontSize && { fontSize: `${fontConfig.fontSize}px` }), - ...(fontConfig.lineHeight && { lineHeight: fontConfig.lineHeight.toString() }), - ...(fontConfig.fontWeight && { fontWeight: fontConfig.fontWeight }), + ...(fontConfig.fontSize && {fontSize: `${fontConfig.fontSize}px`}), + ...(fontConfig.lineHeight && {lineHeight: fontConfig.lineHeight.toString()}), + ...(fontConfig.fontWeight && {fontWeight: fontConfig.fontWeight}), }, '.cm-content': { fontFamily: fontConfig.fontFamily, - ...(fontConfig.fontSize && { fontSize: `${fontConfig.fontSize}px` }), - ...(fontConfig.lineHeight && { lineHeight: fontConfig.lineHeight.toString() }), - ...(fontConfig.fontWeight && { fontWeight: fontConfig.fontWeight }), + ...(fontConfig.fontSize && {fontSize: `${fontConfig.fontSize}px`}), + ...(fontConfig.lineHeight && {lineHeight: fontConfig.lineHeight.toString()}), + ...(fontConfig.fontWeight && {fontWeight: fontConfig.fontWeight}), }, '.cm-editor': { fontFamily: fontConfig.fontFamily, @@ -60,15 +68,15 @@ export function createFontExtension(config: Partial = {}): Extension }, '.cm-gutters': { fontFamily: fontConfig.fontFamily, - ...(fontConfig.fontSize && { fontSize: `${fontConfig.fontSize}px` }), + ...(fontConfig.fontSize && {fontSize: `${fontConfig.fontSize}px`}), }, '.cm-lineNumbers': { fontFamily: fontConfig.fontFamily, - ...(fontConfig.fontSize && { fontSize: `${Math.max(10, fontConfig.fontSize - 1)}px` }), + ...(fontConfig.fontSize && {fontSize: `${Math.max(10, fontConfig.fontSize - 1)}px`}), }, '.cm-tooltip': { fontFamily: fontConfig.fontFamily, - ...(fontConfig.fontSize && { fontSize: `${Math.max(12, fontConfig.fontSize - 1)}px` }), + ...(fontConfig.fontSize && {fontSize: `${Math.max(12, fontConfig.fontSize - 1)}px`}), }, '.cm-completionLabel': { fontFamily: fontConfig.fontFamily, @@ -77,11 +85,10 @@ export function createFontExtension(config: Partial = {}): Extension fontFamily: fontConfig.fontFamily, } }; - + return EditorView.theme(styles); } -// 从后端配置创建字体扩展 export function createFontExtensionFromBackend(backendConfig: { fontFamily?: string; fontSize?: number; @@ -92,12 +99,14 @@ export function createFontExtensionFromBackend(backendConfig: { return fontCompartment.of(createFontExtension(fontConfig)); } -// 动态更新字体配置 export function updateFontConfig(view: EditorView, config: Partial): void { - const newFontExtension = createFontExtension(config); - - // 使用compartment重新配置字体扩展 + const nextFontConfig = normalizeFontConfig(config); + if (isSameFontConfig(appliedFontConfigCache.get(view), nextFontConfig)) { + return; + } + + appliedFontConfigCache.set(view, nextFontConfig); view.dispatch({ - effects: fontCompartment.reconfigure(newFontExtension) + effects: fontCompartment.reconfigure(createFontExtension(nextFontConfig)) }); -} \ No newline at end of file +} diff --git a/frontend/src/views/editor/basic/wheelZoomExtension.ts b/frontend/src/views/editor/basic/wheelZoomExtension.ts index 59110b31..1c2f25cd 100644 --- a/frontend/src/views/editor/basic/wheelZoomExtension.ts +++ b/frontend/src/views/editor/basic/wheelZoomExtension.ts @@ -1,25 +1,22 @@ -import {EditorView} from '@codemirror/view'; import type {Extension} from '@codemirror/state'; +import {EditorView, ViewPlugin} from '@codemirror/view'; import {createDebounce} from '@/common/utils/debounce'; type FontAdjuster = () => void; +type FontDeltaAdjuster = (delta: number) => void; type SaveCallback = () => Promise | void; export interface WheelZoomOptions { - /** 增加字体大小的回调(立即执行) */ - increaseFontSize: FontAdjuster; - /** 减少字体大小的回调(立即执行) */ - decreaseFontSize: FontAdjuster; - /** 保存回调(防抖执行),在滚动结束后调用 */ + increaseFontSize?: FontAdjuster; + decreaseFontSize?: FontAdjuster; + adjustFontSize?: FontDeltaAdjuster; onSave?: SaveCallback; - /** 保存防抖延迟(毫秒),默认 300ms */ saveDelay?: number; } export const createWheelZoomExtension = (options: WheelZoomOptions): Extension => { - const {increaseFontSize, decreaseFontSize, onSave, saveDelay = 300} = options; + const {increaseFontSize, decreaseFontSize, adjustFontSize, onSave, saveDelay = 300} = options; - // 如果有 onSave 回调,创建防抖版本 const {debouncedFn: debouncedSave} = onSave ? createDebounce(() => { try { @@ -35,27 +32,72 @@ export const createWheelZoomExtension = (options: WheelZoomOptions): Extension = }, {delay: saveDelay}) : {debouncedFn: null}; - return EditorView.domEventHandlers({ - wheel(event) { + return ViewPlugin.fromClass(class { + private pendingDelta = 0; + private frameId: number | null = null; + private readonly domWindow: Window; + private readonly onWheel = (event: WheelEvent) => { if (!event.ctrlKey) { - return false; + return; } event.preventDefault(); + event.stopPropagation(); - // 立即更新字体大小 if (event.deltaY < 0) { - increaseFontSize(); + this.pendingDelta += 1; } else if (event.deltaY > 0) { - decreaseFontSize(); + this.pendingDelta -= 1; + } + + if (this.pendingDelta !== 0 && this.frameId === null) { + this.frameId = this.domWindow.requestAnimationFrame(this.flushPendingDelta); } - // 防抖保存 if (debouncedSave) { debouncedSave(); } + }; + + constructor(private readonly view: EditorView) { + this.domWindow = this.view.dom.ownerDocument.defaultView ?? window; + this.view.dom.addEventListener('wheel', this.onWheel, { + capture: true, + passive: false, + }); + } - return true; + destroy() { + this.view.dom.removeEventListener('wheel', this.onWheel, true); + + if (this.frameId !== null) { + this.domWindow.cancelAnimationFrame(this.frameId); + } } + + private readonly flushPendingDelta = () => { + this.frameId = null; + + if (this.pendingDelta === 0) { + return; + } + + const delta = this.pendingDelta; + this.pendingDelta = 0; + + if (adjustFontSize) { + adjustFontSize(delta); + return; + } + + const applyStep = delta > 0 ? increaseFontSize : decreaseFontSize; + if (!applyStep) { + return; + } + + for (let index = 0; index < Math.abs(delta); index++) { + applyStep(); + } + }; }); }; diff --git a/frontend/src/views/editor/extensions/blockImage/clipboard.ts b/frontend/src/views/editor/extensions/blockImage/clipboard.ts new file mode 100644 index 00000000..dd3a4754 --- /dev/null +++ b/frontend/src/views/editor/extensions/blockImage/clipboard.ts @@ -0,0 +1,23 @@ +export function canvasToPngBlob(canvas: HTMLCanvasElement): Promise { + return new Promise((resolve, reject) => { + canvas.toBlob(blob => { + if (blob) { + resolve(blob); + return; + } + reject(new Error('Canvas toBlob returned null')); + }, 'image/png'); + }); +} + +export async function writeImageToClipboard(blob: Blob, mimeType = blob.type || 'image/png'): Promise { + const ClipboardItemCtor = globalThis.ClipboardItem; + if (!ClipboardItemCtor || !navigator.clipboard?.write) { + throw new Error('Clipboard image write is not supported in this environment'); + } + + const resolvedType = mimeType.startsWith('image/') ? mimeType : 'image/png'; + await navigator.clipboard.write([ + new ClipboardItemCtor({[resolvedType]: blob}), + ]); +} diff --git a/frontend/src/views/editor/extensions/blockImage/contextMenu.ts b/frontend/src/views/editor/extensions/blockImage/contextMenu.ts index 2e662b50..9bd8d2fc 100644 --- a/frontend/src/views/editor/extensions/blockImage/contextMenu.ts +++ b/frontend/src/views/editor/extensions/blockImage/contextMenu.ts @@ -1,5 +1,5 @@ import type {MenuSchemaNode} from '../contextMenu/menuSchema'; -import {getActiveNoteBlock} from '../codeblock/state'; +import {getMenuBlock, runCommandInMenuBlock} from '../contextMenu/blockContext'; import {blockImageEnabledFacet, copyBlockImageCommand} from './index'; @@ -7,13 +7,13 @@ export const blockImageMenuNodes: MenuSchemaNode[] = [ { id: 'copy-block-image', labelKey: 'extensions.blockImage.copyMenu', - command: copyBlockImageCommand, + command: runCommandInMenuBlock(copyBlockImageCommand), visible: context => context.view.state.facet(blockImageEnabledFacet) && - Boolean(getActiveNoteBlock(context.view.state)), + Boolean(getMenuBlock(context)), enabled: context => context.view.state.facet(blockImageEnabledFacet) && - Boolean(getActiveNoteBlock(context.view.state)), + Boolean(getMenuBlock(context)), }, ]; diff --git a/frontend/src/views/editor/extensions/blockImage/exportPreset.ts b/frontend/src/views/editor/extensions/blockImage/exportPreset.ts new file mode 100644 index 00000000..b8dc3c9b --- /dev/null +++ b/frontend/src/views/editor/extensions/blockImage/exportPreset.ts @@ -0,0 +1,252 @@ +import {EditorState, Extension, RangeSetBuilder, StateField} from '@codemirror/state'; +import { + Decoration, + type DecorationSet, + EditorView, + highlightTrailingWhitespace, + highlightWhitespace, + WidgetType, +} from '@codemirror/view'; +import {ExtensionName} from '@/../bindings/voidraft/internal/models/models'; +import {createFontExtension} from '@/views/editor/basic/fontExtension'; +import {createThemeByColors} from '@/views/editor/theme'; +import {useConfigStore} from '@/stores/configStore'; +import {useExtensionStore} from '@/stores/extensionStore'; +import {useThemeStore} from '@/stores/themeStore'; +import {blockState} from '../codeblock/state'; +import {colorTheme, colorView} from '../colorSelector'; +import {getCodeBlockLanguageExtension} from '../codeblock/lang-parser'; +import {getMathBlockExtensions} from '../codeblock/mathBlock'; +import type {Block, BlockAccess} from '../codeblock/types'; +import {hyperLink} from '../hyperlink'; +import {headingSlugField} from '../markdown/state/heading-slug'; +import {render} from '../markdown/plugins/render'; +import {Theme as markdownTheme} from '../markdown/plugins/theme'; +import rainbowBrackets from '../rainbowBracket'; +import type {BlockImageExportAppearance, BlockImageExportPreset} from './types'; + +class BlockStartWidget extends WidgetType { + constructor(private readonly isFirst: boolean) { + super(); + } + + override eq(other: BlockStartWidget): boolean { + return this.isFirst === other.isFirst; + } + + override toDOM(): HTMLElement { + const wrap = document.createElement('div'); + wrap.className = `code-block-start${this.isFirst ? ' first' : ''}`; + return wrap; + } + + override ignoreEvent(): boolean { + return true; + } +} + +const BASE_CAPTURE_HIDDEN_SELECTORS = [ + '.cm-cursorLayer', + '.cm-selectionLayer', + '.cm-tooltip', + '.cm-panels', +] as const; + +type ExportContribution = { + extension: Extension; + hiddenSelectors?: readonly string[]; +}; + +type ExportContributionFactory = (config: Record) => ExportContribution; + +const EXPORT_CONTRIBUTIONS = { + [ExtensionName.Markdown]: () => ({ + extension: [headingSlugField, render(), markdownTheme], + hiddenSelectors: ['.cm-code-block-copy-btn', '.cm-image-indicator', '.cm-html-indicator'], + }), + [ExtensionName.RainbowBrackets]: () => ({ + extension: rainbowBrackets(), + }), + [ExtensionName.Hyperlink]: () => ({ + extension: hyperLink, + hiddenSelectors: ['.cm-hyper-link-icon'], + }), + [ExtensionName.ColorSelector]: () => ({ + extension: [colorView(false), colorTheme], + hiddenSelectors: ['span[data-color] input[type="color"]'], + }), + [ExtensionName.HighlightWhitespace]: () => ({ + extension: highlightWhitespace(), + }), + [ExtensionName.HighlightTrailingWhitespace]: () => ({ + extension: highlightTrailingWhitespace(), + }), +} satisfies Partial>; + +function createDelimiterPresentationDecorations(state: EditorState): DecorationSet { + const blocks = state.field(blockState, false) ?? []; + const builder = new RangeSetBuilder(); + + for (const block of blocks) { + if (block.delimiter.to <= block.delimiter.from) { + continue; + } + + const from = block.delimiter.from === 0 ? block.delimiter.from : block.delimiter.from + 1; + const to = Math.max(from, block.delimiter.to - 1); + builder.add(from, to, Decoration.replace({ + widget: new BlockStartWidget(block.delimiter.from === 0), + inclusive: true, + block: true, + side: 0, + })); + } + + return builder.finish(); +} + +const delimiterPresentationField = StateField.define({ + create: createDelimiterPresentationDecorations, + update(decorations, transaction) { + if (transaction.docChanged) { + return createDelimiterPresentationDecorations(transaction.state); + } + return decorations; + }, + provide: field => EditorView.decorations.from(field), +}); + +function createExportChromeTheme(): Extension { + return EditorView.theme({ + '&': { + backgroundColor: 'transparent', + }, + '.cm-editor': { + height: 'auto', + width: '100%', + backgroundColor: 'transparent', + }, + '.cm-scroller': { + overflow: 'visible', + fontFamily: 'inherit', + }, + '.cm-content': { + minHeight: '0', + padding: '0', + caretColor: 'transparent', + }, + '.cm-gutters': { + display: 'none', + }, + '.cm-activeLine, .cm-activeLineGutter': { + backgroundColor: 'transparent', + }, + '.cm-searchMatch, .cm-searchMatch-selected': { + backgroundColor: 'transparent !important', + outline: 'none', + }, + '.cm-panels, .cm-tooltip': { + display: 'none !important', + }, + '.cm-code-block-copy-btn, .cm-image-indicator, .cm-html-indicator, .cm-hyper-link-icon': { + display: 'none !important', + }, + 'span[data-color] input[type="color"]': { + display: 'none !important', + }, + '.cm-dropCursor': { + display: 'none', + }, + }); +} + +function getReadonlyBlockBackground(isDark: boolean, access: BlockAccess, isEvenBlock: boolean): string | null { + if (access !== 'read') { + return null; + } + + if (isDark) { + return isEvenBlock ? 'rgba(124, 124, 138, 0.16)' : 'rgba(110, 110, 124, 0.22)'; + } + + return isEvenBlock ? 'rgba(0, 0, 0, 0.06)' : 'rgba(0, 0, 0, 0.09)'; +} + +function resolveBlockBackground(block: Block, sourceIndex: number): string { + const themeStore = useThemeStore(); + const colors = themeStore.getEffectiveColors(); + const isEvenBlock = sourceIndex % 2 === 0; + + return getReadonlyBlockBackground(colors.dark, block.access, isEvenBlock) + ?? (isEvenBlock ? colors.background : colors.backgroundSecondary); +} + +function createAppearance(block: Block, sourceIndex: number): BlockImageExportAppearance { + const configStore = useConfigStore(); + const themeStore = useThemeStore(); + const colors = themeStore.getEffectiveColors(); + + return { + backgroundColor: resolveBlockBackground(block, sourceIndex), + borderColor: colors.borderColor, + shadow: colors.dark + ? '0 14px 32px rgba(0, 0, 0, 0.34)' + : '0 10px 28px rgba(15, 23, 42, 0.12)', + foregroundColor: colors.foreground, + fontFamily: configStore.config.editing.fontFamily, + fontSize: configStore.config.editing.fontSize, + lineHeight: configStore.config.editing.lineHeight, + fontWeight: configStore.config.editing.fontWeight, + languageLabel: block.language.name && block.language.name !== 'text' + ? block.language.name + : null, + }; +} + +async function getOptionalRenderExtensions(): Promise { + const extensionStore = useExtensionStore(); + if (extensionStore.extensions.length === 0) { + await extensionStore.loadExtensions(); + } + + return extensionStore.extensions + .filter(extension => extension.enabled) + .map(extension => { + const build = EXPORT_CONTRIBUTIONS[extension.name as ExtensionName]; + return build?.(extension.config ?? {}); + }) + .filter((contribution): contribution is ExportContribution => Boolean(contribution)); +} + +export async function createBlockImageExportPreset(block: Block, sourceIndex: number): Promise { + const themeStore = useThemeStore(); + const configStore = useConfigStore(); + const colors = themeStore.getEffectiveColors(); + const optionalContributions = await getOptionalRenderExtensions(); + + return { + appearance: createAppearance(block, sourceIndex), + extensions: [ + EditorState.readOnly.of(true), + EditorView.editable.of(false), + EditorView.lineWrapping, + createThemeByColors(colors), + createFontExtension({ + fontFamily: configStore.config.editing.fontFamily, + fontSize: configStore.config.editing.fontSize, + lineHeight: configStore.config.editing.lineHeight, + fontWeight: configStore.config.editing.fontWeight, + }), + blockState, + ...getCodeBlockLanguageExtension(), + delimiterPresentationField, + ...getMathBlockExtensions(), + createExportChromeTheme(), + ...optionalContributions.map(({extension}) => extension), + ], + hiddenSelectors: [ + ...BASE_CAPTURE_HIDDEN_SELECTORS, + ...optionalContributions.flatMap(({hiddenSelectors = []}) => hiddenSelectors), + ], + }; +} diff --git a/frontend/src/views/editor/extensions/blockImage/index.ts b/frontend/src/views/editor/extensions/blockImage/index.ts index 07817e86..7f16f811 100644 --- a/frontend/src/views/editor/extensions/blockImage/index.ts +++ b/frontend/src/views/editor/extensions/blockImage/index.ts @@ -1,255 +1,39 @@ import {snapdom} from '@zumer/snapdom'; -import {syntaxTree, highlightingFor} from '@codemirror/language'; -import {Highlighter, highlightTree} from '@lezer/highlight'; import {Facet, type Extension} from '@codemirror/state'; -import {EditorView, Command} from '@codemirror/view'; -import type {Block} from '../codeblock/types'; -import {blockState, getActiveNoteBlock} from '../codeblock/state'; - -/** - * 高亮片段信息 - */ -interface HighlightSpan { - from: number; - to: number; - cssClass: string; -} - -/** - * 从语法树获取指定范围的高亮信息 - */ -function getHighlights(view: EditorView, from: number, to: number): HighlightSpan[] { - const tree = syntaxTree(view.state); - const highlights: HighlightSpan[] = []; - - if (tree.length === 0) { - return highlights; - } - - const highlighter: Highlighter = { - style: tags => highlightingFor(view.state, tags), +import {type Command, EditorView} from '@codemirror/view'; +import {getActiveNoteBlock} from '../codeblock/state'; +import {canvasToPngBlob, writeImageToClipboard} from './clipboard'; +import {OffscreenBlockExportSession} from './session'; +import type {BlockImageExtensionOptions} from './types'; + +const DEFAULT_BLOCK_IMAGE_OPTIONS = { + minWidth: 360, + maxWidth: 1200, + scale: 2, + captureExcludeSelectors: [], +} as const satisfies BlockImageExtensionOptions; + +function createDefaultBlockImageOptions(): BlockImageExtensionOptions { + return { + ...DEFAULT_BLOCK_IMAGE_OPTIONS, + captureExcludeSelectors: [...DEFAULT_BLOCK_IMAGE_OPTIONS.captureExcludeSelectors], }; - - highlightTree( - tree, - highlighter, - (hlFrom, hlTo, cssClass) => { - if (hlFrom < to && hlTo > from) { - highlights.push({ - from: Math.max(hlFrom, from), - to: Math.min(hlTo, to), - cssClass: cssClass || '', - }); - } - }, - from, - to, - ); - - return highlights; -} - -/** - * 构建带高亮的单行元素 - */ -function createHighlightedLine( - lineText: string, - lineFrom: number, - lineTo: number, - highlights: HighlightSpan[], -): HTMLElement { - const lineElement = document.createElement('div'); - lineElement.className = 'cm-line'; - lineElement.style.whiteSpace = 'pre'; - - if (highlights.length === 0 || lineText.length === 0) { - lineElement.textContent = lineText || ' '; - return lineElement; - } - - const spans: Array<{text: string; cssClass: string}> = []; - let pos = lineFrom; - - const lineHighlights = highlights - .filter(h => h.from < lineTo && h.to > lineFrom) - .sort((a, b) => a.from - b.from); - - for (const hl of lineHighlights) { - if (hl.from > pos) { - spans.push({ - text: lineText.slice(pos - lineFrom, hl.from - lineFrom), - cssClass: '', - }); - } - - const hlStart = Math.max(hl.from, lineFrom); - const hlEnd = Math.min(hl.to, lineTo); - spans.push({ - text: lineText.slice(hlStart - lineFrom, hlEnd - lineFrom), - cssClass: hl.cssClass, - }); - - pos = hlEnd; - } - - if (pos < lineTo) { - spans.push({ - text: lineText.slice(pos - lineFrom), - cssClass: '', - }); - } - - for (const span of spans) { - if (span.cssClass) { - const spanElement = document.createElement('span'); - spanElement.className = span.cssClass; - spanElement.textContent = span.text; - lineElement.appendChild(spanElement); - } else { - lineElement.appendChild(document.createTextNode(span.text)); - } - } - - return lineElement; } -/** - * 构建用于截图的块 DOM - */ -function inlineStyle(style: CSSStyleDeclaration, props: string[]): string { - return props - .map(prop => { - const val = style.getPropertyValue(prop); - return val ? `${prop}:${val};` : ''; - }) - .join(''); -} - -function getBlockDomElement(view: EditorView, block: Block): HTMLElement | null { - try { - const blocks = view.state.field(blockState, false); - if (!blocks) return null; - - const blockIndex = blocks.indexOf(block); - const isEvenBlock = blockIndex % 2 === 0; - - const blockLayerElem = view.dom.querySelector( - `.code-blocks-layer .${isEvenBlock ? 'block-even' : 'block-odd'}`, - ) as HTMLElement | null; - const backgroundColor = - blockLayerElem?.ownerDocument - ? getComputedStyle(blockLayerElem).backgroundColor - : isEvenBlock - ? '#252B37' - : '#213644'; - - const contentDom = view.dom.querySelector('.cm-content') as HTMLElement | null; - const sourceStyle = contentDom ? getComputedStyle(contentDom) : getComputedStyle(view.dom); - - const container = document.createElement('div'); - container.className = 'cm-editor cm-focused block-export-wrapper'; - container.style.cssText = ` - padding: 18px 22px; - background-color: ${backgroundColor}; - border-radius: 8px; - box-shadow: 0 2px 12px rgba(0, 0, 0, 0.25); - display: inline-block; - min-width: 360px; - max-width: 960px; - color: ${sourceStyle.color}; - font-family: ${sourceStyle.fontFamily}; - font-size: ${sourceStyle.fontSize}; - line-height: ${sourceStyle.lineHeight}; - position: relative; - `; - - const contentWrapper = document.createElement('div'); - contentWrapper.className = 'cm-content'; - contentWrapper.style.whiteSpace = 'pre'; - contentWrapper.style.cssText += inlineStyle(sourceStyle, [ - 'color', - 'font-family', - 'font-size', - 'font-weight', - 'font-style', - 'line-height', - 'letter-spacing', - 'tab-size', - 'text-rendering', - 'background', - 'background-color', - 'text-shadow', - ]); - - const highlights = getHighlights(view, block.content.from, block.content.to); - const fromLine = view.state.doc.lineAt(block.content.from); - const toLine = view.state.doc.lineAt(block.content.to); - for (let lineNum = fromLine.number; lineNum <= toLine.number; lineNum++) { - const line = view.state.doc.line(lineNum); - const lineElement = createHighlightedLine(line.text, line.from, line.to, highlights); - contentWrapper.appendChild(lineElement); - } - - if (block.language.name && block.language.name !== 'text') { - const langLabel = document.createElement('div'); - langLabel.className = 'block-language-label'; - langLabel.textContent = block.language.name; - langLabel.style.cssText = ` - position: absolute; - top: 6px; - right: 10px; - padding: 3px 8px; - background-color: rgba(0, 0, 0, 0.35); - color: rgba(255, 255, 255, 0.85); - font-size: 11px; - font-family: system-ui, -apple-system, sans-serif; - font-weight: 600; - border-radius: 4px; - text-transform: uppercase; - letter-spacing: 0.5px; - pointer-events: none; - `; - container.appendChild(langLabel); - } - - container.appendChild(contentWrapper); - return container; - } catch (error) { - console.error('[blockImage] Failed to build block DOM:', error); - return null; - } -} - -/** - * 将 Canvas 转换为 PNG Blob - */ -function canvasToPngBlob(canvas: HTMLCanvasElement): Promise { - return new Promise((resolve, reject) => { - canvas.toBlob(blob => { - if (blob) { - resolve(blob); - } else { - reject(new Error('Canvas toBlob returned null')); - } - }, 'image/png'); - }); -} - -/** - * 写入剪贴板(PNG) - */ -async function writeImageToClipboard(blob: Blob): Promise { - const ClipboardItemCtor = (window as any).ClipboardItem; - if (ClipboardItemCtor && navigator.clipboard?.write) { - const item = new ClipboardItemCtor({'image/png': blob}); - await navigator.clipboard.write([item]); - return; - } +function mergeBlockImageOptions( + base: BlockImageExtensionOptions, + patch: Partial, +): BlockImageExtensionOptions { + return { + ...base, + ...patch, + captureExcludeSelectors: [ + ...base.captureExcludeSelectors, + ...(patch.captureExcludeSelectors ?? []), + ], + }; } -/** - * 将当前活动块导出为图片并复制到剪贴板 - */ async function copyActiveBlockAsImage(view: EditorView): Promise { const activeBlock = getActiveNoteBlock(view.state); if (!activeBlock) { @@ -257,30 +41,25 @@ async function copyActiveBlockAsImage(view: EditorView): Promise { return false; } + const options = view.state.facet(blockImageOptionsFacet); const targetDom = view.scrollDOM || document.body; const prevCursor = (targetDom as HTMLElement).style.cursor; (targetDom as HTMLElement).style.cursor = 'progress'; - const blockDom = getBlockDomElement(view, activeBlock); - if (!blockDom) { - console.warn('[blockImage] Cannot create block DOM'); - (targetDom as HTMLElement).style.cursor = prevCursor; - return false; - } - - // 将节点挂到文档外层,确保样式可用 - const mount = document.createElement('div'); - mount.style.cssText = 'position: fixed; left: -10000px; top: -10000px; pointer-events: none; z-index: -1;'; - mount.appendChild(blockDom); - document.body.appendChild(mount); + let session: OffscreenBlockExportSession | null = null; try { - const canvas = await snapdom.toCanvas(blockDom, { - scale: 2, - dpr: window.devicePixelRatio || 1, + session = await OffscreenBlockExportSession.create(view, activeBlock, options); + await session.settle(); + + const canvas = await snapdom.toCanvas(session.root, { + scale: options.scale, + dpr: 1, cache: 'auto', - backgroundColor: getComputedStyle(blockDom).backgroundColor, - outerShadows: false, + embedFonts: true, + backgroundColor: getComputedStyle(session.root).backgroundColor, + outerShadows: true, + exclude: [...session.captureExcludeSelectors], }); const blob = await canvasToPngBlob(canvas); @@ -290,14 +69,11 @@ async function copyActiveBlockAsImage(view: EditorView): Promise { console.error('[blockImage] Failed to copy block image:', error); return false; } finally { - mount.remove(); + session?.destroy(); (targetDom as HTMLElement).style.cursor = prevCursor; } } -/** - * 命令:复制当前块为图片 - */ export const copyBlockImageCommand: Command = view => { void copyActiveBlockAsImage(view); return true; @@ -307,12 +83,18 @@ export const blockImageEnabledFacet = Facet.define({ combine: values => values.some(Boolean), }); -/** - * BlockImage 扩展入口 - */ -export function createBlockImageExtension(): Extension { +export const blockImageOptionsFacet = Facet.define, BlockImageExtensionOptions>({ + combine: values => + values.reduce( + (merged, value) => mergeBlockImageOptions(merged, value), + createDefaultBlockImageOptions(), + ), +}); + +export function createBlockImageExtension(options: Partial = {}): Extension { return [ blockImageEnabledFacet.of(true), + blockImageOptionsFacet.of(options), ]; } diff --git a/frontend/src/views/editor/extensions/blockImage/session.ts b/frontend/src/views/editor/extensions/blockImage/session.ts new file mode 100644 index 00000000..2594917b --- /dev/null +++ b/frontend/src/views/editor/extensions/blockImage/session.ts @@ -0,0 +1,228 @@ +import {forceParsing, syntaxParserRunning, syntaxTreeAvailable} from '@codemirror/language'; +import {EditorState} from '@codemirror/state'; +import {EditorView} from '@codemirror/view'; +import {blockState} from '../codeblock/state'; +import type {Block} from '../codeblock/types'; +import {createBlockImageExportPreset} from './exportPreset'; +import type { + BlockImageExportDescriptor, + BlockImageExtensionOptions, +} from './types'; + +const DEFAULT_EXPORT_WIDTH = 720; +const HIDDEN_MOUNT_STYLE = [ + 'position:fixed', + 'inset:0', + 'overflow:hidden', + 'pointer-events:none', + 'opacity:0', + 'z-index:-1', + 'contain:layout style paint', +].join(';'); + +type InternalEditorView = EditorView & { + viewState?: { + printing?: boolean; + }; +}; + +function clamp(value: number, min: number, max: number): number { + return Math.min(max, Math.max(min, value)); +} + +function nextAnimationFrame(): Promise { + return new Promise(resolve => requestAnimationFrame(() => resolve())); +} + +async function waitForAnimationFrames(count: number): Promise { + for (let index = 0; index < count; index++) { + await nextAnimationFrame(); + } +} + +function waitForEditorMeasure(view: EditorView): Promise { + return new Promise(resolve => { + view.requestMeasure({ + read() {}, + write() { + resolve(); + }, + }); + }); +} + +async function waitForFonts(): Promise { + await document.fonts?.ready; +} + +async function waitForImages(root: ParentNode): Promise { + const images = Array.from(root.querySelectorAll('img')); + if (images.length === 0) { + return; + } + + await Promise.allSettled(images.map(image => new Promise(resolve => { + try { + image.loading = 'eager'; + image.decoding = 'sync'; + } catch { + // ignore readonly browser fields + } + + if (image.complete) { + resolve(); + return; + } + + const complete = () => resolve(); + image.addEventListener('load', complete, {once: true}); + image.addEventListener('error', complete, {once: true}); + }))); +} + +function setFullDocumentRendering(view: EditorView, enabled: boolean): void { + const internalView = view as InternalEditorView; + if (!internalView.viewState) { + return; + } + + internalView.viewState.printing = enabled; + view.requestMeasure(); +} + +async function ensureFullSyntaxTree(view: EditorView): Promise { + const target = view.state.doc.length; + if (target === 0) { + return; + } + + for (let attempt = 0; attempt < 6; attempt++) { + if (syntaxTreeAvailable(view.state, target)) { + return; + } + + forceParsing(view, target, 250); + await waitForEditorMeasure(view); + await nextAnimationFrame(); + + if (!syntaxParserRunning(view) && syntaxTreeAvailable(view.state, target)) { + return; + } + } +} + +function resolveDescriptor( + sourceView: EditorView, + block: Block, + options: BlockImageExtensionOptions, +): BlockImageExportDescriptor { + const blocks = sourceView.state.field(blockState, false) ?? []; + const sourceIndex = Math.max(0, blocks.indexOf(block)); + const contentWidth = sourceView.contentDOM.clientWidth || sourceView.dom.clientWidth || DEFAULT_EXPORT_WIDTH; + + return { + source: block, + sourceIndex, + document: sourceView.state.doc.sliceString(block.range.from, block.range.to), + width: clamp(contentWidth, options.minWidth, options.maxWidth), + }; +} + + +export class OffscreenBlockExportSession { + readonly root: HTMLElement; + readonly captureExcludeSelectors: readonly string[]; + + readonly #mount: HTMLDivElement; + readonly #view: EditorView; + + private constructor(mount: HTMLDivElement, root: HTMLElement, view: EditorView, captureExcludeSelectors: readonly string[]) { + this.#mount = mount; + this.root = root; + this.#view = view; + this.captureExcludeSelectors = captureExcludeSelectors; + } + + static create( + sourceView: EditorView, + block: Block, + options: BlockImageExtensionOptions, + ): Promise { + return OffscreenBlockExportSession.#create(sourceView, block, options); + } + + static async #create( + sourceView: EditorView, + block: Block, + options: BlockImageExtensionOptions, + ): Promise { + const descriptor = resolveDescriptor(sourceView, block, options); + const preset = await createBlockImageExportPreset(block, descriptor.sourceIndex); + + const mount = document.createElement('div'); + mount.style.cssText = HIDDEN_MOUNT_STYLE; + + const root = document.createElement('div'); + root.className = 'block-export-capture-root'; + root.style.cssText = [ + `display:inline-block`, + `position:relative`, + `box-sizing:border-box`, + `padding:18px 22px`, + `color:${preset.appearance.foregroundColor}`, + `font-family:${preset.appearance.fontFamily}`, + `font-size:${preset.appearance.fontSize}px`, + `line-height:${preset.appearance.lineHeight}`, + `font-weight:${preset.appearance.fontWeight}`, + ].join(';'); + + const host = document.createElement('div'); + host.className = 'block-export-editor-host'; + host.style.cssText = [ + 'position:relative', + `width:${descriptor.width}px`, + 'max-width:100%', + ].join(';'); + + root.appendChild(host); + mount.appendChild(root); + document.body.appendChild(mount); + + const state = EditorState.create({ + doc: descriptor.document, + extensions: preset.extensions, + selection: {anchor: 0}, + }); + + const view = new EditorView({ + state, + parent: host, + }); + + setFullDocumentRendering(view, true); + + return new OffscreenBlockExportSession( + mount, + root, + view, + [...preset.hiddenSelectors, ...options.captureExcludeSelectors], + ); + } + + async settle(): Promise { + setFullDocumentRendering(this.#view, true); + await waitForEditorMeasure(this.#view); + await waitForFonts(); + await ensureFullSyntaxTree(this.#view); + await waitForImages(this.root); + await waitForEditorMeasure(this.#view); + await waitForAnimationFrames(2); + await waitForAnimationFrames(1); + } + + destroy(): void { + setFullDocumentRendering(this.#view, false); + this.#view.destroy(); + this.#mount.remove(); + } +} diff --git a/frontend/src/views/editor/extensions/blockImage/types.ts b/frontend/src/views/editor/extensions/blockImage/types.ts new file mode 100644 index 00000000..b8a9c771 --- /dev/null +++ b/frontend/src/views/editor/extensions/blockImage/types.ts @@ -0,0 +1,34 @@ +import type {Extension} from '@codemirror/state'; +import type {Block} from '../codeblock/types'; + +export interface BlockImageExtensionOptions { + minWidth: number; + maxWidth: number; + scale: number; + captureExcludeSelectors: readonly string[]; +} + +export interface BlockImageExportDescriptor { + readonly source: Block; + readonly sourceIndex: number; + readonly document: string; + readonly width: number; +} + +export interface BlockImageExportAppearance { + readonly backgroundColor: string; + readonly borderColor: string; + readonly shadow: string; + readonly foregroundColor: string; + readonly fontFamily: string; + readonly fontSize: number; + readonly lineHeight: number; + readonly fontWeight: string; + readonly languageLabel: string | null; +} + +export interface BlockImageExportPreset { + readonly appearance: BlockImageExportAppearance; + readonly extensions: Extension[]; + readonly hiddenSelectors: readonly string[]; +} diff --git a/frontend/src/views/editor/extensions/blockReadonly/contextMenu.ts b/frontend/src/views/editor/extensions/blockReadonly/contextMenu.ts new file mode 100644 index 00000000..69804596 --- /dev/null +++ b/frontend/src/views/editor/extensions/blockReadonly/contextMenu.ts @@ -0,0 +1,42 @@ +import type { MenuContext, MenuSchemaNode } from '../contextMenu/menuSchema'; +import {getMenuBlock as getTargetMenuBlock, runCommandInMenuBlock} from '../contextMenu/blockContext'; +import { + blockReadonlyEnabledFacet, + setActiveBlockReadOnlyCommand, + setActiveBlockWritableCommand, +} from './index'; + +function getMenuBlock(context: MenuContext) { + return getTargetMenuBlock(context); +} + +function isReadonlyExtensionEnabled(context: MenuContext) { + return context.view.state.facet(blockReadonlyEnabledFacet); +} + +export const blockReadonlyMenuNodes: MenuSchemaNode[] = [ + { + id: 'set-block-readonly', + labelKey: 'extensions.blockReadonly.markReadonly', + command: runCommandInMenuBlock(setActiveBlockReadOnlyCommand), + visible: context => + isReadonlyExtensionEnabled(context) && + Boolean(getMenuBlock(context)?.delimiter.to), + enabled: context => { + const block = getMenuBlock(context); + return Boolean(block?.delimiter.to) && block?.access !== 'read'; + }, + }, + { + id: 'set-block-writable', + labelKey: 'extensions.blockReadonly.markWritable', + command: runCommandInMenuBlock(setActiveBlockWritableCommand), + visible: context => + isReadonlyExtensionEnabled(context) && + Boolean(getMenuBlock(context)?.delimiter.to), + enabled: context => { + const block = getMenuBlock(context); + return Boolean(block?.delimiter.to) && block?.access !== 'write'; + }, + }, +]; diff --git a/frontend/src/views/editor/extensions/blockReadonly/index.test.ts b/frontend/src/views/editor/extensions/blockReadonly/index.test.ts new file mode 100644 index 00000000..3707f0d2 --- /dev/null +++ b/frontend/src/views/editor/extensions/blockReadonly/index.test.ts @@ -0,0 +1,90 @@ +import { describe, expect, it, vi } from 'vitest'; +import { EditorState } from '@codemirror/state'; +import { blockState } from '../codeblock/state'; +import { codeBlockEvent, LANGUAGE_CHANGE } from '../codeblock/annotation'; +import { + DELIMITER_PREFIX, + DELIMITER_SUFFIX, + READONLY_SUFFIX, + WRITABLE_SUFFIX, +} from '../codeblock/types'; +import { createBlockReadonlyExtension } from './index'; + +vi.mock('../codeblock/lang-parser/languages', () => ({ + LANGUAGES: [{ token: 'text' }], +})); + +function createTestState(doc: string) { + return EditorState.create({ + doc, + extensions: [ + blockState, + createBlockReadonlyExtension(), + ], + }); +} + +function createDelimiter(access: 'read' | 'write') { + const accessSuffix = access === 'read' ? READONLY_SUFFIX : WRITABLE_SUFFIX; + return `${DELIMITER_PREFIX}text${accessSuffix}${DELIMITER_SUFFIX}`; +} + +function createTestDocument() { + return [ + createDelimiter('write'), + 'const writable = 1;\n', + createDelimiter('read'), + 'const readonlyValue = 2;\n', + createDelimiter('write'), + 'const tail = 3;\n', + ].join(''); +} + +describe('block readonly protection', () => { + it('blocks edits inside readonly block content', () => { + const state = createTestState(createTestDocument()); + const readonlyBlock = state.field(blockState)[1]!; + + const transaction = state.update({ + changes: { + from: readonlyBlock.content.from, + to: readonlyBlock.content.from + 5, + insert: 'let', + }, + }); + + expect(transaction.state.doc.toString()).toBe(state.doc.toString()); + }); + + it('blocks deleting a readonly block from its start boundary', () => { + const state = createTestState(createTestDocument()); + const readonlyBlock = state.field(blockState)[1]!; + + const transaction = state.update({ + changes: { + from: readonlyBlock.content.from - 1, + to: readonlyBlock.content.from, + insert: '', + }, + }); + + expect(transaction.state.doc.toString()).toBe(state.doc.toString()); + }); + + it('still allows updating a readonly block delimiter through internal commands', () => { + const state = createTestState(createTestDocument()); + const readonlyBlock = state.field(blockState)[1]!; + const writableDelimiter = createDelimiter('write'); + + const transaction = state.update({ + changes: { + from: readonlyBlock.delimiter.from, + to: readonlyBlock.delimiter.to, + insert: writableDelimiter, + }, + annotations: [codeBlockEvent.of(LANGUAGE_CHANGE)], + }); + + expect(transaction.state.field(blockState)[1]?.access).toBe('write'); + }); +}); diff --git a/frontend/src/views/editor/extensions/blockReadonly/index.ts b/frontend/src/views/editor/extensions/blockReadonly/index.ts new file mode 100644 index 00000000..b59428a5 --- /dev/null +++ b/frontend/src/views/editor/extensions/blockReadonly/index.ts @@ -0,0 +1,52 @@ +import { Facet, Extension, EditorState } from '@codemirror/state'; +import { Command } from '@codemirror/view'; +import { blockState, getActiveNoteBlock } from '../codeblock/state'; +import { changeCurrentBlockAccess } from '../codeblock/commands'; +import { codeBlockEvent, LANGUAGE_CHANGE } from '../codeblock/annotation'; + +const readonlyRangeFilter = EditorState.changeFilter.of(transaction => { + if (transaction.annotation(codeBlockEvent) === LANGUAGE_CHANGE) { + return true; + } + + const blocks = transaction.startState.field(blockState, false); + if (!blocks?.length) { + return true; + } + + const protectedRanges: number[] = []; + for (const block of blocks) { + if (block.access !== 'read') { + continue; + } + protectedRanges.push(block.range.from, block.range.to); + } + + return protectedRanges.length > 0 ? protectedRanges : true; +}); + +export const blockReadonlyEnabledFacet = Facet.define({ + combine: values => values.some(Boolean), +}); + +export function isActiveBlockReadonly(state: EditorState): boolean { + return getActiveNoteBlock(state)?.access === 'read'; +} + +export const setActiveBlockReadOnlyCommand: Command = view => + changeCurrentBlockAccess(view.state, view.dispatch, 'read'); + +export const setActiveBlockWritableCommand: Command = view => + changeCurrentBlockAccess(view.state, view.dispatch, 'write'); + +/** + * Block 只读扩展入口 + */ +export function createBlockReadonlyExtension(): Extension { + return [ + blockReadonlyEnabledFacet.of(true), + readonlyRangeFilter, + ]; +} + +export default createBlockReadonlyExtension; diff --git a/frontend/src/views/editor/extensions/codeblock/commands.ts b/frontend/src/views/editor/extensions/codeblock/commands.ts index 2bbf0b42..c43132fa 100644 --- a/frontend/src/views/editor/extensions/codeblock/commands.ts +++ b/frontend/src/views/editor/extensions/codeblock/commands.ts @@ -5,15 +5,50 @@ import { EditorSelection, Transaction } from "@codemirror/state"; import { Command } from "@codemirror/view"; import { blockState, getActiveNoteBlock, getFirstNoteBlock, getLastNoteBlock, getNoteBlockFromPos } from "./state"; -import { Block, EditorOptions, DELIMITER_REGEX } from "./types"; +import type { Block, BlockAccess, EditorOptions, SupportedLanguage } from "./types"; import { formatBlockContent } from "./formatCode"; +import { createDelimiter } from "./parser"; import { codeBlockEvent, LANGUAGE_CHANGE, ADD_NEW_BLOCK, MOVE_BLOCK, DELETE_BLOCK, CURRENCIES_LOADED, USER_EVENTS } from "./annotation"; /** * 获取块分隔符 */ -export function getBlockDelimiter(defaultToken: string, autoDetect: boolean): string { - return `\n∞∞∞${autoDetect ? defaultToken + '-a' : defaultToken}\n`; +export function getBlockDelimiter( + defaultToken: string, + autoDetect: boolean, + access: BlockAccess = 'write', +): string { + return createDelimiter(defaultToken as SupportedLanguage, autoDetect, access); +} + +function getDefaultAccess(options: EditorOptions): BlockAccess { + return options.defaultBlockAccess ?? 'write'; +} + +function replaceBlockDelimiter( + state: any, + dispatch: any, + block: Block, + language: string, + auto: boolean, + access: BlockAccess, +) { + if (state.readOnly || block.delimiter.to <= block.delimiter.from) { + return false; + } + + const newDelimiter = createDelimiter(language as SupportedLanguage, auto, access); + + dispatch({ + changes: { + from: block.delimiter.from, + to: block.delimiter.to, + insert: newDelimiter, + }, + annotations: [codeBlockEvent.of(LANGUAGE_CHANGE)], + }); + + return true; } /** @@ -23,14 +58,18 @@ export const insertNewBlockAtCursor = (options: EditorOptions): Command => ({ st if (state.readOnly) return false; const currentBlock = getActiveNoteBlock(state); - let delimText: string; - - if (currentBlock) { - delimText = `\n∞∞∞${currentBlock.language.name}${currentBlock.language.auto ? "-a" : ""}\n`; - } else { - delimText = getBlockDelimiter(options.defaultBlockToken, options.defaultBlockAutoDetect); - } - + const delimText = currentBlock + ? createDelimiter( + currentBlock.language.name as SupportedLanguage, + currentBlock.language.auto, + currentBlock.access, + ) + : getBlockDelimiter( + options.defaultBlockToken, + options.defaultBlockAutoDetect, + getDefaultAccess(options), + ); + dispatch(state.replaceSelection(delimText), { scrollIntoView: true, userEvent: USER_EVENTS.INPUT, @@ -47,10 +86,14 @@ export const addNewBlockBeforeCurrent = (options: EditorOptions): Command => ({ const block = getActiveNoteBlock(state); if (!block) return false; - - const delimText = getBlockDelimiter(options.defaultBlockToken, options.defaultBlockAutoDetect); - dispatch(state.update({ + const delimText = getBlockDelimiter( + options.defaultBlockToken, + options.defaultBlockAutoDetect, + getDefaultAccess(options), + ); + + dispatch(state.update({ changes: { from: block.delimiter.from, insert: delimText, @@ -61,7 +104,7 @@ export const addNewBlockBeforeCurrent = (options: EditorOptions): Command => ({ scrollIntoView: true, userEvent: USER_EVENTS.INPUT, })); - + return true; }; @@ -73,10 +116,14 @@ export const addNewBlockAfterCurrent = (options: EditorOptions): Command => ({ s const block = getActiveNoteBlock(state); if (!block) return false; - - const delimText = getBlockDelimiter(options.defaultBlockToken, options.defaultBlockAutoDetect); - dispatch(state.update({ + const delimText = getBlockDelimiter( + options.defaultBlockToken, + options.defaultBlockAutoDetect, + getDefaultAccess(options), + ); + + dispatch(state.update({ changes: { from: block.content.to, insert: delimText, @@ -87,7 +134,7 @@ export const addNewBlockAfterCurrent = (options: EditorOptions): Command => ({ s scrollIntoView: true, userEvent: USER_EVENTS.INPUT, })); - + return true; }; @@ -99,10 +146,14 @@ export const addNewBlockBeforeFirst = (options: EditorOptions): Command => ({ st const block = getFirstNoteBlock(state); if (!block) return false; - - const delimText = getBlockDelimiter(options.defaultBlockToken, options.defaultBlockAutoDetect); - dispatch(state.update({ + const delimText = getBlockDelimiter( + options.defaultBlockToken, + options.defaultBlockAutoDetect, + getDefaultAccess(options), + ); + + dispatch(state.update({ changes: { from: block.delimiter.from, insert: delimText, @@ -113,7 +164,7 @@ export const addNewBlockBeforeFirst = (options: EditorOptions): Command => ({ st scrollIntoView: true, userEvent: USER_EVENTS.INPUT, })); - + return true; }; @@ -122,13 +173,17 @@ export const addNewBlockBeforeFirst = (options: EditorOptions): Command => ({ st */ export const addNewBlockAfterLast = (options: EditorOptions): Command => ({ state, dispatch }) => { if (state.readOnly) return false; - + const block = getLastNoteBlock(state); if (!block) return false; - - const delimText = getBlockDelimiter(options.defaultBlockToken, options.defaultBlockAutoDetect); - dispatch(state.update({ + const delimText = getBlockDelimiter( + options.defaultBlockToken, + options.defaultBlockAutoDetect, + getDefaultAccess(options), + ); + + dispatch(state.update({ changes: { from: block.content.to, insert: delimText, @@ -139,7 +194,7 @@ export const addNewBlockAfterLast = (options: EditorOptions): Command => ({ stat scrollIntoView: true, userEvent: USER_EVENTS.INPUT, })); - + return true; }; @@ -147,20 +202,14 @@ export const addNewBlockAfterLast = (options: EditorOptions): Command => ({ stat * 更改块语言 */ export function changeLanguageTo(state: any, dispatch: any, block: Block, language: string, auto: boolean) { - if (state.readOnly) return false; - - const newDelimiter = `\n∞∞∞${language}${auto ? '-a' : ''}\n`; - - dispatch({ - changes: { - from: block.delimiter.from, - to: block.delimiter.to, - insert: newDelimiter, - }, - annotations: [codeBlockEvent.of(LANGUAGE_CHANGE)], - }); - - return true; + return replaceBlockDelimiter( + state, + dispatch, + block, + language, + auto, + block.access, + ); } /** @@ -172,16 +221,43 @@ export function changeCurrentBlockLanguage(state: any, dispatch: any, language: console.warn("No active block found"); return false; } - - // 如果 language 为 null,我们只想更改自动检测标志 - if (language === null) { - language = block.language.name; + + return changeLanguageTo( + state, + dispatch, + block, + language ?? block.language.name, + auto, + ); +} + +/** + * 更改块访问模式 + */ +export function changeBlockAccessTo(state: any, dispatch: any, block: Block, access: BlockAccess) { + return replaceBlockDelimiter( + state, + dispatch, + block, + block.language.name, + block.language.auto, + access, + ); +} + +/** + * 更改当前块访问模式 + */ +export function changeCurrentBlockAccess(state: any, dispatch: any, access: BlockAccess) { + const block = getActiveNoteBlock(state); + if (!block) { + console.warn("No active block found"); + return false; } - - return changeLanguageTo(state, dispatch, block, language, auto); + + return changeBlockAccessTo(state, dispatch, block, access); } -// 选择和移动辅助函数 function updateSel(sel: EditorSelection, by: (range: any) => any): EditorSelection { return EditorSelection.create(sel.ranges.map(by), sel.mainIndex); } @@ -211,28 +287,28 @@ function previousBlock(state: any, range: any) { const blocks = state.field(blockState); const block = getNoteBlockFromPos(state, range.head); if (!block) return EditorSelection.cursor(0); - + if (range.head === block.content.from) { const index = blocks.indexOf(block); const previousBlockIndex = index > 0 ? index - 1 : 0; return EditorSelection.cursor(blocks[previousBlockIndex].content.from); - } else { - return EditorSelection.cursor(block.content.from); } + + return EditorSelection.cursor(block.content.from); } function nextBlock(state: any, range: any) { const blocks = state.field(blockState); const block = getNoteBlockFromPos(state, range.head); if (!block) return EditorSelection.cursor(state.doc.length); - + if (range.head === block.content.to) { const index = blocks.indexOf(block); const nextBlockIndex = index < blocks.length - 1 ? index + 1 : index; return EditorSelection.cursor(blocks[nextBlockIndex].content.to); - } else { - return EditorSelection.cursor(block.content.to); } + + return EditorSelection.cursor(block.content.to); } /** @@ -268,33 +344,28 @@ export function selectPreviousBlock({ state, dispatch }: any) { */ export const deleteBlock = (_options: EditorOptions): Command => ({ state, dispatch }) => { if (state.readOnly) return false; - + const block = getActiveNoteBlock(state); if (!block) return false; - + const blocks = state.field(blockState); - if (blocks.length <= 1) return false; // 不能删除最后一个块 - + if (blocks.length <= 1) return false; + const blockIndex = blocks.indexOf(block); let newCursorPos: number; - + if (blockIndex === blocks.length - 1) { - // 如果是最后一个块,将光标移到前一个块的末尾 - // 需要计算删除后的位置 const prevBlock = blocks[blockIndex - 1]; newCursorPos = prevBlock.content.to; } else { - // 否则移到下一个块的开始 - // 需要计算删除后的位置,下一个块会向前移动 const nextBlock = blocks[blockIndex + 1]; const blockLength = block.range.to - block.range.from; newCursorPos = nextBlock.content.from - blockLength; } - - // 确保光标位置在有效范围内 + const docLengthAfterDelete = state.doc.length - (block.range.to - block.range.from); newCursorPos = Math.max(0, Math.min(newCursorPos, docLengthAfterDelete)); - + dispatch(state.update({ changes: { from: block.range.from, @@ -307,7 +378,7 @@ export const deleteBlock = (_options: EditorOptions): Command => ({ state, dispa scrollIntoView: true, userEvent: USER_EVENTS.DELETE })); - + return true; }; @@ -327,23 +398,21 @@ export function moveCurrentBlockDown({ state, dispatch }: any) { function moveCurrentBlock(state: any, dispatch: any, up: boolean) { if (state.readOnly) return false; - + const block = getActiveNoteBlock(state); if (!block) return false; - + const blocks = state.field(blockState); const blockIndex = blocks.indexOf(block); - + const targetIndex = up ? blockIndex - 1 : blockIndex + 1; if (targetIndex < 0 || targetIndex >= blocks.length) return false; - + const targetBlock = blocks[targetIndex]; - - // 获取两个块的完整内容 + const currentBlockContent = state.doc.sliceString(block.range.from, block.range.to); const targetBlockContent = state.doc.sliceString(targetBlock.range.from, targetBlock.range.to); - - // 交换块的位置 + const changes = up ? [ { from: targetBlock.range.from, @@ -357,12 +426,11 @@ function moveCurrentBlock(state: any, dispatch: any, up: boolean) { insert: targetBlockContent + currentBlockContent } ]; - - // 计算新的光标位置 - const newCursorPos = up ? - targetBlock.range.from + (block.range.to - block.range.from) + (block.content.from - block.range.from) : - block.range.from + (targetBlock.range.to - targetBlock.range.from) + (block.content.from - block.range.from); - + + const newCursorPos = up + ? targetBlock.range.from + (block.range.to - block.range.from) + (block.content.from - block.range.from) + : block.range.from + (targetBlock.range.to - targetBlock.range.from) + (block.content.from - block.range.from); + dispatch(state.update({ changes, selection: EditorSelection.cursor(newCursorPos), @@ -371,7 +439,7 @@ function moveCurrentBlock(state: any, dispatch: any, up: boolean) { scrollIntoView: true, userEvent: USER_EVENTS.MOVE })); - + return true; } diff --git a/frontend/src/views/editor/extensions/codeblock/copyPaste.ts b/frontend/src/views/editor/extensions/codeblock/copyPaste.ts index 0f51a62c..63747ec4 100644 --- a/frontend/src/views/editor/extensions/codeblock/copyPaste.ts +++ b/frontend/src/views/editor/extensions/codeblock/copyPaste.ts @@ -1,193 +1,262 @@ /** * 代码块复制粘贴扩展 - * 防止复制分隔符标记,自动替换为换行符 + * 防止复制分隔符标记,并接入 inlineImage 的图片复制/粘贴能力。 */ -import { EditorState, EditorSelection } from "@codemirror/state"; -import { EditorView } from "@codemirror/view"; -import { Command } from "@codemirror/view"; -import { LANGUAGES } from "./lang-parser/languages"; -import { USER_EVENTS, codeBlockEvent, CONTENT_EDIT } from "./annotation"; +import {EditorSelection, EditorState} from "@codemirror/state"; +import {Command, EditorView} from "@codemirror/view"; +import {LANGUAGES} from "./lang-parser/languages"; +import {codeBlockEvent, CONTENT_EDIT, USER_EVENTS} from "./annotation"; +import {inlineImageEnabledFacet, inlineImageOptionsFacet} from "../inlineImage"; +import { + copySelectedInlineImageIfNeeded, + pasteInlineImagesFromClipboardEvent, + pasteInlineImagesFromSystemClipboard +} from "../inlineImage/clipboardIntegration"; +import {WIDGET_TAG_REGEX} from "../inlineImage/inlineImageParsing"; +import * as runtime from "@wailsio/runtime"; /** * 构建块分隔符正则表达式 */ const languageTokensMatcher = LANGUAGES.map(lang => lang.token).join("|"); -const blockSeparatorRegex = new RegExp(`\\n∞∞∞(${languageTokensMatcher})(-a)?\\n`, "g"); +const blockSeparatorRegex = new RegExp(`(?:^|\\n)∞∞∞(?:${languageTokensMatcher})(?:-(?:a|r|w))*\\n`, "g"); /** * 获取被复制的范围和内容 */ -function copiedRange(state: EditorState, forCut: boolean = false) { - const content: string[] = []; - const ranges: any[] = []; - - for (const range of state.selection.ranges) { - if (!range.empty) { - content.push(state.sliceDoc(range.from, range.to)); - ranges.push(range); - } - } - - if (ranges.length === 0) { - // 如果所有范围都是空的,我们想要复制每个选择的整行(唯一的) - const copiedLines: number[] = []; +function copiedRange(state: EditorState, forCut = false) { + const content: string[] = []; + const ranges: any[] = []; + for (const range of state.selection.ranges) { - if (range.empty) { - const line = state.doc.lineAt(range.head); - const lineContent = state.sliceDoc(line.from, line.to); - if (!copiedLines.includes(line.from)) { - content.push(lineContent); - // 对于剪切操作,需要包含整行范围(包括换行符) - if (forCut) { - const lineEnd = line.to < state.doc.length ? line.to + 1 : line.to; - ranges.push({ from: line.from, to: lineEnd }); - } else { + if (!range.empty) { + content.push(state.sliceDoc(range.from, range.to)); ranges.push(range); - } - copiedLines.push(line.from); } - } } - } - - return { - text: content.join(state.lineBreak), - ranges - }; + + if (ranges.length === 0) { + const copiedLines: number[] = []; + for (const range of state.selection.ranges) { + if (!range.empty) { + continue; + } + + const line = state.doc.lineAt(range.head); + const lineContent = state.sliceDoc(line.from, line.to); + if (copiedLines.includes(line.from)) { + continue; + } + + content.push(lineContent); + if (forCut) { + const lineEnd = line.to < state.doc.length ? line.to + 1 : line.to; + ranges.push({from: line.from, to: lineEnd}); + } else { + ranges.push(range); + } + copiedLines.push(line.from); + } + } + + return { + text: content.join(state.lineBreak), + ranges, + }; } -/** - * 设置浏览器复制和剪切事件处理器,将块分隔符替换为换行符 - */ -export const codeBlockCopyCut = EditorView.domEventHandlers({ - copy(event, view) { - let { text } = copiedRange(view.state); - // 将块分隔符替换为双换行符 - text = text.replaceAll(blockSeparatorRegex, "\n\n"); - - const data = event.clipboardData; - if (data) { - event.preventDefault(); - data.clearData(); - data.setData("text/plain", text); +function normalizeCopiedText(text: string): string { + return text + .replaceAll(blockSeparatorRegex, "\n\n") + .replaceAll(WIDGET_TAG_REGEX, ""); +} + +async function writeTextToClipboard(text: string, event?: ClipboardEvent): Promise { + try { + await runtime.Clipboard.SetText(text); + return; + } catch (error) { + console.error('[Clipboard] Failed to write to system clipboard:', error); } - }, - - cut(event, view) { - let { text, ranges } = copiedRange(view.state, true); - // 将块分隔符替换为双换行符 - text = text.replaceAll(blockSeparatorRegex, "\n\n"); - - const data = event.clipboardData; + + const data = event?.clipboardData; if (data) { - event.preventDefault(); - data.clearData(); - data.setData("text/plain", text); + data.clearData(); + data.setData("text/plain", text); + return; } - - if (!view.state.readOnly) { - view.dispatch({ - changes: ranges, - scrollIntoView: true, - userEvent: USER_EVENTS.DELETE_CUT, - annotations: [codeBlockEvent.of(CONTENT_EDIT)], - }); + + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(text); } - } -}); +} + +async function handleCopyCut(view: EditorView, cut: boolean, event?: ClipboardEvent): Promise { + if (!cut && view.state.facet(inlineImageEnabledFacet)) { + try { + if (await copySelectedInlineImageIfNeeded(view)) { + return true; + } + } catch (error) { + console.error('[Clipboard] Failed to copy selected image:', error); + } + } + + let {text} = copiedRange(view.state, cut); + const {ranges} = copiedRange(view.state, cut); + text = normalizeCopiedText(text); + + await writeTextToClipboard(text, event); + + if (cut && !view.state.readOnly) { + view.dispatch({ + changes: ranges, + scrollIntoView: true, + userEvent: USER_EVENTS.DELETE_CUT, + annotations: [codeBlockEvent.of(CONTENT_EDIT)], + }); + } + + return true; +} /** - * 复制和剪切的通用函数 + * 设置浏览器复制和剪切事件处理器,将块分隔符替换为换行符 */ -const copyCut = (view: EditorView, cut: boolean): boolean => { - let { text, ranges } = copiedRange(view.state, cut); - // 将块分隔符替换为双换行符 - text = text.replaceAll(blockSeparatorRegex, "\n\n"); - - if (navigator.clipboard && navigator.clipboard.writeText) { - navigator.clipboard.writeText(text); - } - - if (cut && !view.state.readOnly) { - view.dispatch({ - changes: ranges, - scrollIntoView: true, - userEvent: USER_EVENTS.DELETE_CUT, - annotations: [codeBlockEvent.of(CONTENT_EDIT)], - }); - } +export const codeBlockCopyCut = EditorView.domEventHandlers({ + copy(event, view) { + event.preventDefault(); + void handleCopyCut(view, false, event as ClipboardEvent); + }, - return true; -}; + cut(event, view) { + event.preventDefault(); + void handleCopyCut(view, true, event as ClipboardEvent); + }, + + paste(event, view) { + if (view.state.readOnly) { + return false; + } + + event.preventDefault(); + void pasteClipboard(view, event as ClipboardEvent); + + return true; + } +}); /** * 粘贴函数 */ function doPaste(view: EditorView, input: string) { - const { state } = view; - const text = state.toText(input); - const byLine = text.lines === state.selection.ranges.length; - - let changes: any; - - if (byLine) { - let i = 1; - changes = state.changeByRange(range => { - const line = text.line(i++); - return { - changes: { from: range.from, to: range.to, insert: line.text }, - range: EditorSelection.cursor(range.from + line.length) - }; + const {state} = view; + const text = state.toText(input); + const byLine = text.lines === state.selection.ranges.length; + + let changes: any; + + if (byLine) { + let i = 1; + changes = state.changeByRange(range => { + const line = text.line(i++); + return { + changes: {from: range.from, to: range.to, insert: line.text}, + range: EditorSelection.cursor(range.from + line.length) + }; + }); + } else { + changes = state.replaceSelection(text); + } + + view.dispatch(changes, { + userEvent: USER_EVENTS.INPUT_PASTE, + scrollIntoView: true, + annotations: [codeBlockEvent.of(CONTENT_EDIT)], }); - } else { - changes = state.replaceSelection(text); - } - - view.dispatch(changes, { - userEvent: USER_EVENTS.INPUT_PASTE, - scrollIntoView: true, - annotations: [codeBlockEvent.of(CONTENT_EDIT)], - }); +} + +async function readClipboardText(event?: ClipboardEvent): Promise { + try { + const text = await runtime.Clipboard.Text(); + if (text) { + return text; + } + } catch (error) { + console.error('[Clipboard] Failed to read from system clipboard:', error); + } + + const eventText = event?.clipboardData?.getData("text/plain"); + if (eventText) { + return eventText; + } + + if (navigator.clipboard?.readText) { + try { + return await navigator.clipboard.readText(); + } catch (fallbackErr) { + console.error('[Clipboard] Fallback also failed:', fallbackErr); + } + } + + return ""; +} + +async function pasteText(view: EditorView, event?: ClipboardEvent): Promise { + const text = await readClipboardText(event); + if (!text) { + return false; + } + + doPaste(view, text); + return true; +} + +async function pasteClipboard(view: EditorView, event?: ClipboardEvent): Promise { + if (view.state.facet(inlineImageEnabledFacet)) { + const options = view.state.facet(inlineImageOptionsFacet); + const pasted = event + ? await pasteInlineImagesFromClipboardEvent(view, event, options.maxDisplayHeight) + : await pasteInlineImagesFromSystemClipboard(view, options.maxDisplayHeight); + + if (pasted) { + return true; + } + } + + return pasteText(view, event); } /** * 复制命令 */ -export const copyCommand: Command = (view) => { - return copyCut(view, false); +export const copyCommand: Command = view => { + void handleCopyCut(view, false); + return true; }; /** * 剪切命令 */ -export const cutCommand: Command = (view) => { - return copyCut(view, true); +export const cutCommand: Command = view => { + void handleCopyCut(view, true); + return true; }; /** * 粘贴命令 */ -export const pasteCommand: Command = (view) => { - if (navigator.clipboard && navigator.clipboard.readText) { - navigator.clipboard.readText() - .then(text => { - doPaste(view, text); - }) - .catch(err => { - console.error('Failed to read from clipboard:', err); - }); - } else { - console.warn('The clipboard API is not available, please use your browser\'s native paste feature'); - } - return true; +export const pasteCommand: Command = view => { + void pasteClipboard(view); + return true; }; /** * 获取复制粘贴扩展 */ export function getCopyPasteExtensions() { - return [ - codeBlockCopyCut, - ]; + return [ + codeBlockCopyCut, + ]; } diff --git a/frontend/src/views/editor/extensions/codeblock/decorations.ts b/frontend/src/views/editor/extensions/codeblock/decorations.ts index 4f66d87b..de775c90 100644 --- a/frontend/src/views/editor/extensions/codeblock/decorations.ts +++ b/frontend/src/views/editor/extensions/codeblock/decorations.ts @@ -170,8 +170,13 @@ const blockLayer = layer({ } } + const isEvenBlock = idx++ % 2 == 0; + const blockClass = [ + isEvenBlock ? "block-even" : "block-odd", + block.access === "read" ? "block-readonly" : "", + ].filter(Boolean).join(" "); markers.push(new RectangleMarker( - idx++ % 2 == 0 ? "block-even" : "block-odd", + blockClass, 0, fromCoordsTop - (view.documentTop - view.documentPadding.top) - 1 - 6, null, // 宽度在 CSS 中设置为 100% diff --git a/frontend/src/views/editor/extensions/codeblock/index.ts b/frontend/src/views/editor/extensions/codeblock/index.ts index c9cff984..c626bd97 100644 --- a/frontend/src/views/editor/extensions/codeblock/index.ts +++ b/frontend/src/views/editor/extensions/codeblock/index.ts @@ -130,6 +130,7 @@ export function createCodeBlockExtension(options: CodeBlockOptions = {}): Extens export { // 类型定义 type Block, + type BlockAccess, type SupportedLanguage, type CreateBlockOptions, } from './types'; @@ -226,4 +227,4 @@ export { /** * 默认导出 */ -export default createCodeBlockExtension; \ No newline at end of file +export default createCodeBlockExtension; diff --git a/frontend/src/views/editor/extensions/codeblock/lang-detect/autodetect.ts b/frontend/src/views/editor/extensions/codeblock/lang-detect/autodetect.ts index 5f0bc0ec..e4a722fa 100644 --- a/frontend/src/views/editor/extensions/codeblock/lang-detect/autodetect.ts +++ b/frontend/src/views/editor/extensions/codeblock/lang-detect/autodetect.ts @@ -68,13 +68,20 @@ function createDetectionMap(): Map { LANGUAGES.forEach(lang => { if (lang.detectIds) { lang.detectIds.forEach(detectId => { - map.set(detectId, lang.token); + // 保留首个映射,避免重复 detectId 覆盖更基础的语言,例如 js -> ts。 + if (!map.has(detectId)) { + map.set(detectId, lang.token); + } }); } }); return map; } +function createWorkerUrl(): URL { + return new URL(`${import.meta.env.BASE_URL}langdetect-worker.js`, window.location.href); +} + /** * 检测ID到语言token的映射表 */ @@ -131,7 +138,7 @@ class LanguageDetectionWorker { */ private initWorker(): void { try { - this.worker = new Worker('/langdetect-worker.js'); + this.worker = new Worker(createWorkerUrl()); this.worker.onmessage = (event) => { const response: WorkerResponse = event.data; const request = this.pendingRequests.get(response.idx); @@ -316,4 +323,4 @@ export async function detectLanguages(contents: string[]): Promise l.token).join("|"); -const tokenRegEx = new RegExp(`^\\n∞∞∞(${languageTokensMatcher})(-a)?\\n`, "g"); +const escapeForRegex = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +const tokenRegEx = new RegExp( + `^${escapeForRegex(DELIMITER_PREFIX)}(?:${languageTokensMatcher})(?:-(?:a|r|w))*${escapeForRegex(DELIMITER_SUFFIX)}`, + "g", +); +const maxDelimiterLookahead = DELIMITER_PREFIX.length + + Math.max(...LANGUAGES.map(lang => lang.token.length)) + + "-a-w".length + + DELIMITER_SUFFIX.length; /** * 代码块内容标记器 - * 识别 ∞∞∞ 分隔符之间的内容 + * 识别分隔符之间的内容 */ export const blockContent = new ExternalTokenizer((input) => { let current = input.peek(0); @@ -29,23 +36,29 @@ export const blockContent = new ExternalTokenizer((input) => { } while (true) { - // 除非前两个字符是换行符和"∞"字符,否则我们没有代码块内容标记 - // 所以我们不需要检查标记的其余部分 if (current === FIRST_TOKEN_CHAR && next === SECOND_TOKEN_CHAR) { - let potentialLang = ""; - for (let i = 0; i < 18; i++) { - potentialLang += String.fromCharCode(input.peek(i)); + let potentialDelimiter = ""; + for (let i = 0; i < maxDelimiterLookahead; i++) { + const char = input.peek(i); + if (char === EOF) { + break; + } + potentialDelimiter += String.fromCharCode(char); } - if (potentialLang.match(tokenRegEx)) { + + tokenRegEx.lastIndex = 0; + if (tokenRegEx.test(potentialDelimiter)) { input.acceptToken(BlockContent); return; } } + if (next === EOF) { input.acceptToken(BlockContent, 1); return; } + current = input.advance(1); next = input.peek(1); } -}); \ No newline at end of file +}); diff --git a/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts b/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts index 6234f770..b98bbad0 100644 --- a/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts +++ b/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts @@ -56,7 +56,6 @@ import yamlPrettierPlugin from "prettier/plugins/yaml"; import goPrettierPlugin from "@/common/prettier/plugins/go"; import sqlPrettierPlugin from "@/common/prettier/plugins/sql"; import phpPrettierPlugin from "@/common/prettier/plugins/php"; -import javaPrettierPlugin from "@/common/prettier/plugins/java"; import xmlPrettierPlugin from "@prettier/plugin-xml"; import shellPrettierPlugin from "@/common/prettier/plugins/shell"; import dockerfilePrettierPlugin from "@/common/prettier/plugins/docker"; @@ -134,8 +133,12 @@ export const LANGUAGES: LanguageInfo[] = [ plugins: [markdownPrettierPlugin] }), new LanguageInfo("java", "Java", javaLanguage.parser, ["java"], { - parser: "java", - plugins: [javaPrettierPlugin] + parser: "clang-format", + plugins: [clangPrettierPlugin], + options: { + filename: "Main.java", + clangStyle: "Google" + } }), new LanguageInfo("php", "PHP", phpLanguage.configure({top: "Program"}).parser, ["php"], { parser: "php", @@ -268,4 +271,4 @@ export function getLanguage(token: SupportedLanguage): LanguageInfo | undefined */ export function getAllSupportedLanguages(): SupportedLanguage[] { return ['auto', ...LANGUAGES.map(lang => lang.token)]; -} \ No newline at end of file +} diff --git a/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.terms.ts b/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.terms.ts index d5514f68..d6f1e180 100644 --- a/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.terms.ts +++ b/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.terms.ts @@ -5,4 +5,7 @@ export const Block = 3, BlockDelimiter = 4, BlockLanguage = 5, - Auto = 6 + BlockFlag = 6, + Auto = 7, + ReadOnly = 8, + Writable = 9 diff --git a/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.ts b/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.ts index 8e405c7e..491c905f 100644 --- a/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.ts +++ b/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.ts @@ -3,14 +3,14 @@ import {LRParser} from "@lezer/lr" import {blockContent} from "./external-tokens.js" export const parser = LRParser.deserialize({ version: 14, - states: "!jQQOQOOOVOQO'#C`O#{OPO'#C_OOOO'#Cc'#CcQQOQOOOOOO'#Ca'#CaO$QOSO,58zOOOO,58y,58yOOOO-E6a-E6aOOOP1G.f1G.fO$YOSO1G.fOOOP7+$Q7+$Q", - stateData: "$_~OXPO~OYTOZTO[TO]TO^TO_TO`TOaTObTOcTOdTOeTOfTOgTOhTOiTOjTOkTOlTOmTOnTOoTOpTOqTOrTOsTOtTOuTOvTOwTOxTOyTOzTO{TO|TO}TO!OTO!PTO!QTO!RTO!STO!TTO~OPVO~OUYO!UXO~O!UZO~O", - goto: "jWPPPX]aPdTROSTQOSRUPQSORWS", - nodeNames: "⚠ BlockContent Document Block BlockDelimiter BlockLanguage Auto", - maxTerm: 52, + states: "!|QQOQOOOVOQO'#C`O#{OPO'#C_OOOO'#Cf'#CfQQOQOOOOOO'#Ca'#CaO$QOSO,58zOOOO,58y,58yOOOO-E6d-E6dOOOO'#Cb'#CbOOOO'#Cg'#CgO$`OSO1G.fOOOP1G.f1G.fOOOO-E6e-E6eOOOP7+$Q7+$Q", + stateData: "$n~O]PO~O^TO_TO`TOaTObTOcTOdTOeTOfTOgTOhTOiTOjTOkTOlTOmTOnTOoTOpTOqTOrTOsTOtTOuTOvTOwTOxTOyTOzTO{TO|TO}TO!OTO!PTO!QTO!RTO!STO!TTO!UTO!VTO!WTO!XTO~OPVO~OVXOWXOXXO!Y[O~OVXOWXOXXO!Y^O~O", + goto: "x[PPP]aehPPPlrTROSTQOSRUPTYUZQSORWSQZUR]Z", + nodeNames: "⚠ BlockContent Document Block BlockDelimiter BlockLanguage BlockFlag Auto ReadOnly Writable", + maxTerm: 56, skippedNodes: [0], - repeatNodeCount: 1, - tokenData: "4m~RdYZ!a}!O!z#T#U#V#V#W$Q#W#X%R#X#Y&t#Z#['_#[#]([#^#_)R#_#`*Q#`#a*]#a#b,Y#d#e-q#f#g.j#g#h.}#h#i1t#j#k2y#k#l3[#l#m3s#m#n4UR!fP!UQ%&x%&y!iP!lP%&x%&y!oP!rP%&x%&y!uP!zOXP~!}P#T#U#Q~#VOU~~#YP#b#c#]~#`P#Z#[#c~#fP#i#j#i~#lP#`#a#o~#rP#T#U#u~#xP#f#g#{~$QO!Q~~$TR#`#a$^#d#e$i#g#h$t~$aP#^#_$d~$iOl~~$lP#d#e$o~$tOd~~$yPf~#g#h$|~%ROb~~%UQ#T#U%[#c#d%m~%_P#f#g%b~%eP#h#i%h~%mOu~~%pP#V#W%s~%vP#_#`%y~%|P#X#Y&P~&SP#f#g&V~&YP#Y#Z&]~&`P#]#^&c~&fP#`#a&i~&lP#X#Y&o~&tOx~~&wQ#f#g&}#l#m'Y~'QP#`#a'T~'YOn~~'_Om~~'bQ#c#d'h#f#g'm~'mOk~~'pP#c#d's~'vP#c#d'y~'|P#j#k(P~(SP#m#n(V~([Os~~(_P#h#i(b~(eQ#a#b(k#h#i(v~(nP#`#a(q~(vO]~~(yP#d#e(|~)RO!S~~)UQ#T#U)[#g#h)m~)_P#j#k)b~)eP#T#U)h~)mO`~~)rPo~#c#d)u~)xP#b#c){~*QOZ~~*TP#h#i*W~*]Or~~*`R#X#Y*i#]#^+`#i#j+}~*lQ#g#h*r#n#o*}~*uP#g#h*x~*}O!P~~+QP#X#Y+T~+WP#f#g+Z~+`O{~~+cP#e#f+f~+iP#i#j+l~+oP#]#^+r~+uP#W#X+x~+}O|~~,QP#T#U,T~,YOy~~,]R#T#U,f#W#X,w#X#Y,|~,iP#h#i,l~,oP#[#],r~,wOw~~,|O_~~-PP#f#g-S~-VP#a#b-Y~-]P#T#U-`~-cP#]#^-f~-iP#W#X-l~-qO!T~~-tR#[#]-}#g#h.Y#m#n.e~.QP#d#e.T~.YOa~~.]P!R!S.`~.eOt~~.jO[~~.mQ#U#V.s#g#h.x~.xOg~~.}Oe~~/QU#T#U/d#V#W/u#[#]0^#e#f0c#j#k0n#k#l1]~/gP#g#h/j~/mP#g#h/p~/uO!O~~/xP#T#U/{~0OP#`#a0R~0UP#T#U0X~0^Ov~~0cOh~~0fP#`#a0i~0nO^~~0qP#X#Y0t~0wP#`#a0z~0}P#h#i1Q~1TP#X#Y1W~1]O!R~~1`P#]#^1c~1fP#Y#Z1i~1lP#h#i1o~1tOq~~1wR#X#Y2Q#c#d2c#g#h2t~2TP#l#m2W~2ZP#h#i2^~2cOY~~2fP#a#b2i~2lP#`#a2o~2tOj~~2yOp~~2|P#i#j3P~3SP#X#Y3V~3[Oz~~3_P#T#U3b~3eP#g#h3h~3kP#h#i3n~3sO}~~3vP#a#b3y~3|P#`#a4P~4UOc~~4XP#T#U4[~4_P#a#b4b~4eP#`#a4h~4mOi~", + repeatNodeCount: 2, + tokenData: "4}~RdYZ!a}!O!z#T#U#g#V#W$b#W#X%c#X#Y'U#Z#['o#[#](l#^#_)c#_#`*b#`#a*m#a#b,j#d#e.R#f#g.z#g#h/_#h#i2U#j#k3Z#k#l3l#l#m4T#m#n4fR!fP!YQ%&x%&y!iP!lP%&x%&y!oP!rP%&x%&y!uP!zO]P~!}R#T#U#W#f#g#]#k#l#b~#]OV~~#bOW~~#gOX~~#jP#b#c#m~#pP#Z#[#s~#vP#i#j#y~#|P#`#a$P~$SP#T#U$V~$YP#f#g$]~$bO!U~~$eR#`#a$n#d#e$y#g#h%U~$qP#^#_$t~$yOp~~$|P#d#e%P~%UOh~~%ZPj~#g#h%^~%cOf~~%fQ#T#U%l#c#d%}~%oP#f#g%r~%uP#h#i%x~%}Oy~~&QP#V#W&T~&WP#_#`&Z~&^P#X#Y&a~&dP#f#g&g~&jP#Y#Z&m~&pP#]#^&s~&vP#`#a&y~&|P#X#Y'P~'UO|~~'XQ#f#g'_#l#m'j~'bP#`#a'e~'jOr~~'oOq~~'rQ#c#d'x#f#g'}~'}Oo~~(QP#c#d(T~(WP#c#d(Z~(^P#j#k(a~(dP#m#n(g~(lOw~~(oP#h#i(r~(uQ#a#b({#h#i)W~)OP#`#a)R~)WOa~~)ZP#d#e)^~)cO!W~~)fQ#T#U)l#g#h)}~)oP#j#k)r~)uP#T#U)x~)}Od~~*SPs~#c#d*V~*YP#b#c*]~*bO_~~*eP#h#i*h~*mOv~~*pR#X#Y*y#]#^+p#i#j,_~*|Q#g#h+S#n#o+_~+VP#g#h+Y~+_O!T~~+bP#X#Y+e~+hP#f#g+k~+pO!P~~+sP#e#f+v~+yP#i#j+|~,PP#]#^,S~,VP#W#X,Y~,_O!Q~~,bP#T#U,e~,jO}~~,mR#T#U,v#W#X-X#X#Y-^~,yP#h#i,|~-PP#[#]-S~-XO{~~-^Oc~~-aP#f#g-d~-gP#a#b-j~-mP#T#U-p~-sP#]#^-v~-yP#W#X-|~.RO!X~~.UR#[#]._#g#h.j#m#n.u~.bP#d#e.e~.jOe~~.mP!R!S.p~.uOx~~.zO`~~.}Q#U#V/T#g#h/Y~/YOk~~/_Oi~~/bU#T#U/t#V#W0V#[#]0n#e#f0s#j#k1O#k#l1m~/wP#g#h/z~/}P#g#h0Q~0VO!S~~0YP#T#U0]~0`P#`#a0c~0fP#T#U0i~0nOz~~0sOl~~0vP#`#a0y~1OOb~~1RP#X#Y1U~1XP#`#a1[~1_P#h#i1b~1eP#X#Y1h~1mO!V~~1pP#]#^1s~1vP#Y#Z1y~1|P#h#i2P~2UOu~~2XR#X#Y2b#c#d2s#g#h3U~2eP#l#m2h~2kP#h#i2n~2sO^~~2vP#a#b2y~2|P#`#a3P~3UOn~~3ZOt~~3^P#i#j3a~3dP#X#Y3g~3lO!O~~3oP#T#U3r~3uP#g#h3x~3{P#h#i4O~4TO!R~~4WP#a#b4Z~4^P#`#a4a~4fOg~~4iP#T#U4l~4oP#a#b4r~4uP#`#a4x~4}Om~", tokenizers: [blockContent, 0, 1], topRules: {"Document":[0,2]}, tokenPrec: 0 diff --git a/frontend/src/views/editor/extensions/codeblock/parser.test.ts b/frontend/src/views/editor/extensions/codeblock/parser.test.ts new file mode 100644 index 00000000..5d3fbd65 --- /dev/null +++ b/frontend/src/views/editor/extensions/codeblock/parser.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, it } from 'vitest'; +import { EditorState } from '@codemirror/state'; +import { createDelimiter, getBlocksFromString, parseDelimiter } from './parser'; + +describe('codeblock delimiter access', () => { + it('parses readonly delimiters with auto-detect', () => { + expect(parseDelimiter(createDelimiter('ts', true, 'read'))).toEqual({ + language: 'ts', + auto: true, + access: 'read', + }); + }); + + it('keeps legacy delimiters writable by default', () => { + expect(parseDelimiter('\n∞∞∞go-a\n')).toEqual({ + language: 'go', + auto: true, + access: 'write', + }); + }); + + it('parses a first delimiter without the leading newline', () => { + expect(parseDelimiter('∞∞∞text-a-w\n')).toEqual({ + language: 'text', + auto: true, + access: 'write', + }); + + const state = EditorState.create({ doc: '∞∞∞text-a-w\nhello' }); + const blocks = getBlocksFromString(state); + + expect(blocks).toHaveLength(1); + expect(blocks[0]?.delimiter).toEqual({ from: 0, to: '∞∞∞text-a-w\n'.length }); + expect(blocks[0]?.content).toEqual({ from: '∞∞∞text-a-w\n'.length, to: '∞∞∞text-a-w\nhello'.length }); + }); + + it('builds block access from string parsing', () => { + const document = [ + createDelimiter('ts', false, 'read'), + 'const value = 1;\n', + createDelimiter('json', true, 'write'), + '{}', + ].join(''); + + const state = EditorState.create({ doc: document }); + const blocks = getBlocksFromString(state); + + expect(blocks).toHaveLength(2); + expect(blocks[0]?.access).toBe('read'); + expect(blocks[0]?.language).toEqual({ name: 'ts', auto: false }); + expect(blocks[1]?.access).toBe('write'); + expect(blocks[1]?.language).toEqual({ name: 'json', auto: true }); + }); +}); diff --git a/frontend/src/views/editor/extensions/codeblock/parser.ts b/frontend/src/views/editor/extensions/codeblock/parser.ts index 07a67ed1..498a5fbe 100644 --- a/frontend/src/views/editor/extensions/codeblock/parser.ts +++ b/frontend/src/views/editor/extensions/codeblock/parser.ts @@ -5,18 +5,36 @@ import { EditorState } from '@codemirror/state'; import { syntaxTree, ensureSyntaxTree } from '@codemirror/language'; import type { Tree } from '@lezer/common'; -import { Block as BlockNode, BlockDelimiter, BlockContent, BlockLanguage } from './lang-parser/parser.terms.js'; import { - SupportedLanguage, - DELIMITER_REGEX, + Block as BlockNode, + BlockDelimiter, + BlockContent, +} from './lang-parser/parser.terms.js'; +import { + type Block, + type BlockAccess, + type BlockDelimiterInfo, + type SupportedLanguage, + AUTO_DETECT_SUFFIX, DELIMITER_PREFIX, + DELIMITER_REGEX, + DELIMITER_START, DELIMITER_SUFFIX, - AUTO_DETECT_SUFFIX, - Block + READONLY_SUFFIX, + WRITABLE_SUFFIX, } from './types'; import { LANGUAGES } from './lang-parser/languages'; -const DEFAULT_LANGUAGE = (LANGUAGES[0]?.token || 'text') as string; +const DEFAULT_LANGUAGE = (LANGUAGES[0]?.token || 'text') as SupportedLanguage; +const DEFAULT_ACCESS: BlockAccess = 'write'; + +function getDefaultDelimiterInfo(): BlockDelimiterInfo { + return { + language: DEFAULT_LANGUAGE, + auto: false, + access: DEFAULT_ACCESS, + }; +} /** * 从语法树解析代码块 @@ -35,52 +53,42 @@ function collectBlocksFromTree(tree: Tree, state: EditorState): Block[] | null { tree.iterate({ enter(node) { - if (node.type.id === BlockNode) { - let delimiter: { from: number; to: number } | null = null; - let content: { from: number; to: number } | null = null; - let language: string = DEFAULT_LANGUAGE; - let auto = false; - - const blockNode = node.node; - blockNode.firstChild?.cursor().iterate(child => { - if (child.type.id === BlockDelimiter) { - delimiter = { from: child.from, to: child.to }; - const delimiterText = doc.sliceString(child.from, child.to); - const match = delimiterText.match(/∞∞∞([a-zA-Z0-9_-]+)(-a)?\n/); - if (match) { - language = match[1] || DEFAULT_LANGUAGE; - auto = match[2] === '-a'; - } else { - child.node.firstChild?.cursor().iterate(langChild => { - if (langChild.type.id === BlockLanguage) { - const langText = doc.sliceString(langChild.from, langChild.to); - language = langText || DEFAULT_LANGUAGE; - } - if (doc.sliceString(langChild.from, langChild.to) === AUTO_DETECT_SUFFIX) { - auto = true; - } - }); - } - } else if (child.type.id === BlockContent) { - content = { from: child.from, to: child.to }; - } - }); - - if (delimiter && content) { - blocks.push({ - language: { - name: language as SupportedLanguage, - auto: auto, - }, - content: content, - delimiter: delimiter, - range: { - from: node.from, - to: node.to, - }, - }); + if (node.type.id !== BlockNode) { + return; + } + + let delimiter: { from: number; to: number } | null = null; + let content: { from: number; to: number } | null = null; + let delimiterInfo = getDefaultDelimiterInfo(); + + const blockNode = node.node; + blockNode.firstChild?.cursor().iterate(child => { + if (child.type.id === BlockDelimiter) { + delimiter = { from: child.from, to: child.to }; + const delimiterText = doc.sliceString(child.from, child.to); + delimiterInfo = parseDelimiter(delimiterText) ?? getDefaultDelimiterInfo(); + } else if (child.type.id === BlockContent) { + content = { from: child.from, to: child.to }; } + }); + + if (!delimiter || !content) { + return; } + + blocks.push({ + language: { + name: delimiterInfo.language, + auto: delimiterInfo.auto, + }, + access: delimiterInfo.access, + content, + delimiter, + range: { + from: node.from, + to: node.to, + }, + }); } }); @@ -99,60 +107,67 @@ export let firstBlockDelimiterSize: number | undefined; * 从文档字符串内容解析块,使用 String.indexOf() */ export function getBlocksFromString(state: EditorState): Block[] { - const blocks: Block[] = []; - const doc = state.doc; - - if (doc.length === 0) { - return [createPlainTextBlock(0, 0)]; - } - - const content = doc.sliceString(0, doc.length); - const delimiter = DELIMITER_PREFIX; - const suffixLength = DELIMITER_SUFFIX.length; - - let pos = content.indexOf(delimiter); - - if (pos === -1) { - firstBlockDelimiterSize = 0; - return [createPlainTextBlock(0, doc.length)]; - } - - if (pos > 0) { - blocks.push(createPlainTextBlock(0, pos)); - } - - while (pos !== -1 && pos < doc.length) { - const blockStart = pos; - const langStart = blockStart + delimiter.length; - const delimiterEnd = content.indexOf(DELIMITER_SUFFIX, langStart); - if (delimiterEnd === -1) break; - - const delimiterText = content.slice(blockStart, delimiterEnd + suffixLength); - const delimiterInfo = parseDelimiter(delimiterText); - if (!delimiterInfo) break; - - const contentStart = delimiterEnd + suffixLength; - const nextDelimiter = content.indexOf(delimiter, contentStart); - const contentEnd = nextDelimiter === -1 ? doc.length : nextDelimiter; - - blocks.push({ - language: { name: delimiterInfo.language, auto: delimiterInfo.auto }, - content: { from: contentStart, to: contentEnd }, - delimiter: { from: blockStart, to: delimiterEnd + suffixLength }, - range: { from: blockStart, to: contentEnd }, - }); + const blocks: Block[] = []; + const doc = state.doc; + + if (doc.length === 0) { + return [createPlainTextBlock(0, 0)]; + } - pos = nextDelimiter; - } + const content = doc.sliceString(0, doc.length); + const suffixLength = DELIMITER_SUFFIX.length; - if (blocks.length === 0) { - blocks.push(createPlainTextBlock(0, doc.length)); - firstBlockDelimiterSize = 0; - } else { - firstBlockDelimiterSize = blocks[0].delimiter.to; - } + let pos = findDelimiter(content, 0); + + if (pos === -1) { + firstBlockDelimiterSize = 0; + return [createPlainTextBlock(0, doc.length)]; + } + + if (pos > 0) { + blocks.push(createPlainTextBlock(0, pos)); + } + + while (pos !== -1 && pos < doc.length) { + const blockStart = pos; + const langStart = blockStart + delimiterPrefixLength(content, blockStart); + const delimiterEnd = content.indexOf(DELIMITER_SUFFIX, langStart); + if (delimiterEnd === -1) { + break; + } - return blocks; + const delimiterText = content.slice(blockStart, delimiterEnd + suffixLength); + const delimiterInfo = parseDelimiter(delimiterText); + if (!delimiterInfo) { + break; + } + + const contentStart = delimiterEnd + suffixLength; + const nextDelimiter = findDelimiter(content, contentStart); + const contentEnd = nextDelimiter === -1 ? doc.length : nextDelimiter; + + blocks.push({ + language: { + name: delimiterInfo.language, + auto: delimiterInfo.auto, + }, + access: delimiterInfo.access, + content: { from: contentStart, to: contentEnd }, + delimiter: { from: blockStart, to: delimiterEnd + suffixLength }, + range: { from: blockStart, to: contentEnd }, + }); + + pos = nextDelimiter; + } + + if (blocks.length === 0) { + blocks.push(createPlainTextBlock(0, doc.length)); + firstBlockDelimiterSize = 0; + } else { + firstBlockDelimiterSize = blocks[0].delimiter.to; + } + + return blocks; } /** @@ -171,7 +186,7 @@ export function getBlocks(state: EditorState): Block[] { return blocks; } } - + return getBlocksFromString(state); } @@ -181,7 +196,7 @@ export function getBlocks(state: EditorState): Block[] { export function getActiveBlock(state: EditorState): Block | undefined { const range = state.selection.asSingle().ranges[0]; const blocks = getBlocks(state); - return blocks.find(block => + return blocks.find(block => block.range.from <= range.head && block.range.to >= range.head ); } @@ -207,10 +222,10 @@ export function getLastBlock(state: EditorState): Block | undefined { */ export function getBlockFromPos(state: EditorState, pos: number): Block | undefined { const blocks = getBlocks(state); - return blocks.find(block => + return blocks.find(block => block.range.from <= pos && block.range.to >= pos ); - } +} /** * 获取块的行信息 @@ -218,7 +233,7 @@ export function getBlockFromPos(state: EditorState, pos: number): Block | undefi export function getBlockLineFromPos(state: EditorState, pos: number) { const line = state.doc.lineAt(pos); const block = getBlockFromPos(state, pos); - + if (block) { const firstBlockLine = state.doc.lineAt(block.content.from).number; return { @@ -227,7 +242,7 @@ export function getBlockLineFromPos(state: EditorState, pos: number) { length: line.length, }; } - + return { line: line.number, col: pos - line.from, @@ -238,50 +253,83 @@ export function getBlockLineFromPos(state: EditorState, pos: number) { /** * 创建新的分隔符文本 */ -export function createDelimiter(language: SupportedLanguage, autoDetect = false): string { - const suffix = autoDetect ? AUTO_DETECT_SUFFIX : ''; - return `${DELIMITER_PREFIX}${language}${suffix}${DELIMITER_SUFFIX}`; +export function createDelimiter( + language: SupportedLanguage, + autoDetect = false, + access: BlockAccess = DEFAULT_ACCESS, +): string { + const suffixes: string[] = []; + if (autoDetect) { + suffixes.push(AUTO_DETECT_SUFFIX); + } + suffixes.push(access === 'read' ? READONLY_SUFFIX : WRITABLE_SUFFIX); + return `${DELIMITER_PREFIX}${language}${suffixes.join('')}${DELIMITER_SUFFIX}`; } /** * 验证分隔符格式 */ export function isValidDelimiter(text: string): boolean { - DELIMITER_REGEX.lastIndex = 0; - return DELIMITER_REGEX.test(text); + return parseDelimiter(text) !== null; } /** * 解析分隔符信息 */ -export function parseDelimiter(delimiterText: string): { language: SupportedLanguage; auto: boolean } | null { - DELIMITER_REGEX.lastIndex = 0; - const match = DELIMITER_REGEX.exec(delimiterText); - +export function parseDelimiter(delimiterText: string): BlockDelimiterInfo | null { + const match = delimiterText.match(DELIMITER_REGEX); if (!match) { return null; } - const languageName = match[1]; - const isAuto = match[2] === AUTO_DETECT_SUFFIX; - + const [, languageName, rawFlags = ''] = match; const validLanguage = LANGUAGES.some(lang => lang.token === languageName) ? languageName as SupportedLanguage - : DEFAULT_LANGUAGE as SupportedLanguage; + : DEFAULT_LANGUAGE; + + const flags = rawFlags.match(/-(?:a|r|w)/g) ?? []; + let auto = false; + let access: BlockAccess = DEFAULT_ACCESS; + + for (const flag of flags) { + if (flag === AUTO_DETECT_SUFFIX) { + auto = true; + } else if (flag === READONLY_SUFFIX) { + access = 'read'; + } else if (flag === WRITABLE_SUFFIX) { + access = 'write'; + } else { + return null; + } + } return { language: validLanguage, - auto: isAuto + auto, + access, }; } function createPlainTextBlock(from: number, to: number): Block { - return { - language: { name: DEFAULT_LANGUAGE, auto: false }, - content: { from, to }, - delimiter: { from: 0, to: 0 }, - range: { from, to }, - }; + return { + language: { name: DEFAULT_LANGUAGE, auto: false }, + access: DEFAULT_ACCESS, + content: { from, to }, + delimiter: { from: 0, to: 0 }, + range: { from, to }, + }; } +function findDelimiter(content: string, from: number): number { + if (from <= 0 && content.startsWith(DELIMITER_START)) { + return 0; + } + return content.indexOf(DELIMITER_PREFIX, Math.max(from, 0)); +} +function delimiterPrefixLength(content: string, from: number): number { + if (content.startsWith(DELIMITER_PREFIX, from)) { + return DELIMITER_PREFIX.length; + } + return DELIMITER_START.length; +} diff --git a/frontend/src/views/editor/extensions/codeblock/types.ts b/frontend/src/views/editor/extensions/codeblock/types.ts index 47c49896..7bc8ad3d 100644 --- a/frontend/src/views/editor/extensions/codeblock/types.ts +++ b/frontend/src/views/editor/extensions/codeblock/types.ts @@ -6,6 +6,7 @@ export interface Block { name: string; auto: boolean; }; + access: BlockAccess; content: { from: number; to: number; @@ -20,38 +21,52 @@ export interface Block { }; } +/** + * 代码块访问模式 + */ +export type BlockAccess = 'read' | 'write'; + +/** + * 分隔符解析结果 + */ +export interface BlockDelimiterInfo { + language: SupportedLanguage; + auto: boolean; + access: BlockAccess; +} + /** * 支持的语言类型 */ export type SupportedLanguage = - | 'auto' // 自动检测 + | 'auto' | 'text' | 'json' - | 'py' // Python + | 'py' | 'html' | 'sql' - | 'md' // Markdown + | 'md' | 'java' | 'php' | 'css' | 'xml' - | 'cpp' // C++ - | 'rs' // Rust - | 'cs' // C# - | 'rb' // Ruby - | 'sh' // Shell + | 'cpp' + | 'rs' + | 'cs' + | 'rb' + | 'sh' | 'yaml' | 'toml' | 'go' - | 'clj' // Clojure - | 'ex' // Elixir - | 'erl' // Erlang - | 'js' // JavaScript - | 'ts' // TypeScript + | 'clj' + | 'ex' + | 'erl' + | 'js' + | 'ts' | 'swift' - | 'kt' // Kotlin + | 'kt' | 'groovy' - | 'ps1' // PowerShell + | 'ps1' | 'dart' | 'scala' | 'dockerfile' @@ -65,8 +80,8 @@ export type SupportedLanguage = | 'less' | 'angular' | 'svelte' - | 'http' // HTTP Client - | 'mermaid' + | 'http' + | 'mermaid'; /** * 创建块的选项 @@ -74,6 +89,7 @@ export type SupportedLanguage = export interface CreateBlockOptions { language?: SupportedLanguage; auto?: boolean; + access?: BlockAccess; content?: string; } @@ -83,11 +99,14 @@ export interface CreateBlockOptions { export interface EditorOptions { defaultBlockToken: string; defaultBlockAutoDetect: boolean; + defaultBlockAccess?: BlockAccess; } - -// 分隔符格式常量 -export const DELIMITER_REGEX = /^\n∞∞∞([a-zA-Z0-9_-]+)(-a)?\n/gm; export const DELIMITER_PREFIX = '\n∞∞∞'; +export const DELIMITER_START = '∞∞∞'; export const DELIMITER_SUFFIX = '\n'; export const AUTO_DETECT_SUFFIX = '-a'; +export const READONLY_SUFFIX = '-r'; +export const WRITABLE_SUFFIX = '-w'; + +export const DELIMITER_REGEX = /^(?:\n)?∞∞∞([a-zA-Z0-9_]+)((?:-(?:a|r|w))*)\n$/m; diff --git a/frontend/src/views/editor/extensions/contextMenu/blockContext.test.ts b/frontend/src/views/editor/extensions/contextMenu/blockContext.test.ts new file mode 100644 index 00000000..7761fb91 --- /dev/null +++ b/frontend/src/views/editor/extensions/contextMenu/blockContext.test.ts @@ -0,0 +1,58 @@ +import {EditorState} from '@codemirror/state'; +import {describe, expect, it, vi} from 'vitest'; +vi.mock('../codeblock/lang-parser/languages', () => ({ + LANGUAGES: [ + {token: 'text'}, + {token: 'http'}, + ], +})); + +import {getActiveNoteBlock} from '../codeblock/state'; +import {createDelimiter} from '../codeblock/parser'; +import {blockState} from '../codeblock/state'; +import {runCommandInMenuBlock} from './blockContext'; +import type {MenuContext} from './menuSchema'; + +describe('runCommandInMenuBlock', () => { + it('executes the command against the block targeted by the context menu', () => { + let state = EditorState.create({ + doc: [ + createDelimiter('text', false, 'write'), + 'first block\n', + createDelimiter('http', false, 'write'), + 'GET https://example.com', + ].join(''), + selection: {anchor: 1}, + extensions: [blockState], + }); + + const blocks = state.field(blockState); + const targetBlock = blocks[1]; + const command = vi.fn(view => getActiveNoteBlock(view.state)?.language.name === 'http'); + const wrapped = runCommandInMenuBlock(command); + + const view = { + get state() { + return state; + }, + dispatch(spec: Parameters[0]) { + state = state.update(spec).state; + }, + }; + + const context: MenuContext = { + view: view as never, + event: {} as MouseEvent, + targetPos: targetBlock.content.from, + targetBlock, + hasSelection: false, + selectionText: '', + isEditable: true, + }; + + expect(wrapped(view as never, context)).toBe(true); + expect(command).toHaveBeenCalledOnce(); + expect(getActiveNoteBlock(state)?.language.name).toBe('http'); + expect(state.selection.main.head).toBe(targetBlock.content.from); + }); +}); diff --git a/frontend/src/views/editor/extensions/contextMenu/blockContext.ts b/frontend/src/views/editor/extensions/contextMenu/blockContext.ts new file mode 100644 index 00000000..77c779f7 --- /dev/null +++ b/frontend/src/views/editor/extensions/contextMenu/blockContext.ts @@ -0,0 +1,40 @@ +import {EditorSelection, Transaction} from '@codemirror/state'; +import type {Command, EditorView} from '@codemirror/view'; +import {getNoteBlockFromPos} from '../codeblock/state'; +import type {Block} from '../codeblock/types'; +import type {MenuContext} from './menuSchema'; + +function containsPos(block: Block, pos: number): boolean { + return block.range.from <= pos && pos <= block.range.to; +} + +function getCurrentMenuBlock(view: EditorView, context: MenuContext): Block | null { + if (context.targetPos === null) { + return context.targetBlock; + } + + return getNoteBlockFromPos(view.state, Math.min(context.targetPos, view.state.doc.length)) ?? null; +} + +export function getMenuBlock(context: MenuContext): Block | null { + return context.targetBlock; +} + +export function runCommandInMenuBlock(command: Command) { + return (view: EditorView, context: MenuContext): boolean => { + const block = getCurrentMenuBlock(view, context); + if (!block) { + return false; + } + + const currentHead = view.state.selection.main.head; + if (!containsPos(block, currentHead)) { + view.dispatch({ + selection: EditorSelection.cursor(block.content.from), + annotations: [Transaction.addToHistory.of(false)], + }); + } + + return command(view); + }; +} diff --git a/frontend/src/views/editor/extensions/contextMenu/constants.ts b/frontend/src/views/editor/extensions/contextMenu/constants.ts new file mode 100644 index 00000000..b945c6be --- /dev/null +++ b/frontend/src/views/editor/extensions/contextMenu/constants.ts @@ -0,0 +1,2 @@ +export const CONTEXT_MENU_BLOCK_ANCHOR_DATASET = 'blockAnchor'; +export const CONTEXT_MENU_BLOCK_ANCHOR_SELECTOR = '[data-block-anchor]'; diff --git a/frontend/src/views/editor/extensions/contextMenu/index.ts b/frontend/src/views/editor/extensions/contextMenu/index.ts index 11aa6468..fd3ecaec 100644 --- a/frontend/src/views/editor/extensions/contextMenu/index.ts +++ b/frontend/src/views/editor/extensions/contextMenu/index.ts @@ -10,6 +10,7 @@ import {showContextMenu} from './manager'; import type {MenuSchemaNode} from './menuSchema'; import {buildRegisteredMenu, createMenuContext, registerMenuNodes} from './menuSchema'; import {blockImageMenuNodes} from '../blockImage/contextMenu'; +import {blockReadonlyMenuNodes} from '../blockReadonly/contextMenu'; function t(key: string): string { @@ -106,7 +107,11 @@ let builtinMenuRegistered = false; function ensureBuiltinMenuRegistered(): void { if (builtinMenuRegistered) return; - registerMenuNodes([...builtinMenuNodes(), ...blockImageMenuNodes]); + registerMenuNodes([ + ...builtinMenuNodes(), + ...blockReadonlyMenuNodes, + ...blockImageMenuNodes, + ]); builtinMenuRegistered = true; } diff --git a/frontend/src/views/editor/extensions/contextMenu/menuSchema.ts b/frontend/src/views/editor/extensions/contextMenu/menuSchema.ts index 88b1adfa..29cf9bed 100644 --- a/frontend/src/views/editor/extensions/contextMenu/menuSchema.ts +++ b/frontend/src/views/editor/extensions/contextMenu/menuSchema.ts @@ -1,21 +1,28 @@ -import type { EditorView } from '@codemirror/view'; +import type {Command, EditorView} from '@codemirror/view'; import { EditorState } from '@codemirror/state'; +import {getNoteBlockFromPos} from '../codeblock/state'; +import type {Block} from '../codeblock/types'; +import {CONTEXT_MENU_BLOCK_ANCHOR_DATASET, CONTEXT_MENU_BLOCK_ANCHOR_SELECTOR} from './constants'; import { KeyBindingName } from '@/../bindings/voidraft/internal/models/models'; export interface MenuContext { view: EditorView; event: MouseEvent; + targetPos: number | null; + targetBlock: Block | null; hasSelection: boolean; selectionText: string; isEditable: boolean; } +export type MenuCommand = (view: EditorView, context: MenuContext) => boolean; + export type MenuSchemaNode = | { id: string; type?: "action"; labelKey: string; - command?: (view: EditorView) => boolean; + command?: Command | MenuCommand; keyBindingName?: KeyBindingName; visible?: (context: MenuContext) => boolean; enabled?: (context: MenuContext) => boolean; @@ -42,8 +49,48 @@ interface MenuBuildOptions { const menuRegistry: MenuSchemaNode[] = []; +function getAnchorPosFromTarget(target: EventTarget | null): number | null { + if (!(target instanceof HTMLElement)) { + return null; + } + + const anchor = target.closest(CONTEXT_MENU_BLOCK_ANCHOR_SELECTOR)?.dataset[CONTEXT_MENU_BLOCK_ANCHOR_DATASET]; + if (!anchor) { + return null; + } + + const pos = Number(anchor); + return Number.isInteger(pos) && pos >= 0 ? pos : null; +} + +function getTargetPos(view: EditorView, event: MouseEvent): number | null { + const anchorPos = getAnchorPosFromTarget(event.target); + if (anchorPos !== null) { + return anchorPos; + } + + const posAtCoords = view.posAtCoords({ + x: event.clientX, + y: event.clientY, + }); + if (posAtCoords !== null) { + return posAtCoords; + } + + if (!(event.target instanceof Node) || !view.dom.contains(event.target)) { + return null; + } + + try { + return view.posAtDOM(event.target, 0); + } catch { + return null; + } +} + export function createMenuContext(view: EditorView, event: MouseEvent): MenuContext { const { state } = view; + const targetPos = getTargetPos(view, event); const hasSelection = state.selection.ranges.some((range) => !range.empty); const selectionText = hasSelection ? state.sliceDoc(state.selection.main.from, state.selection.main.to) @@ -53,6 +100,8 @@ export function createMenuContext(view: EditorView, event: MouseEvent): MenuCont return { view, event, + targetPos, + targetBlock: targetPos !== null ? getNoteBlockFromPos(state, targetPos) ?? null : null, hasSelection, selectionText, isEditable @@ -90,6 +139,9 @@ function convertNode( const disabled = node.enabled ? !node.enabled(context) : false; const shortcut = options.formatShortcut(node.keyBindingName); + const command = node.command + ? (view: EditorView) => (node.command as (view: EditorView, context: MenuContext) => boolean)(view, context) + : undefined; return { id: node.id, @@ -97,6 +149,6 @@ function convertNode( label: options.translate(node.labelKey), shortcut: shortcut || undefined, disabled, - command: node.command + command }; } diff --git a/frontend/src/views/editor/extensions/httpclient/widgets/run-gutter.ts b/frontend/src/views/editor/extensions/httpclient/widgets/run-gutter.ts index 063ca0b6..7ce79c9f 100644 --- a/frontend/src/views/editor/extensions/httpclient/widgets/run-gutter.ts +++ b/frontend/src/views/editor/extensions/httpclient/widgets/run-gutter.ts @@ -173,7 +173,7 @@ class RunButtonMarker extends GutterMarker { time: response.time, requestSize: response.requestSize, body: response.body, - headers: response.headers, + headers: response.headers as { [_: string]: string[] }, timestamp: response.timestamp ? new Date(response.timestamp) : new Date(), error: response.error }; diff --git a/frontend/src/views/editor/extensions/inlineImage/clipboard.ts b/frontend/src/views/editor/extensions/inlineImage/clipboard.ts new file mode 100644 index 00000000..6b65444d --- /dev/null +++ b/frontend/src/views/editor/extensions/inlineImage/clipboard.ts @@ -0,0 +1,177 @@ +import * as MediaHTTPService from '@/../bindings/voidraft/internal/services/mediahttpservice'; +import type {ImageAsset, ImageDeleteResult} from '@/../bindings/voidraft/internal/services/models'; +import {createInlineImageTag} from './inlineImageParsing'; +import type {InlineImageData} from './types'; + +export const IMAGE_MIME_TYPES = [ + 'image/png', + 'image/jpeg', + 'image/gif', + 'image/webp', + 'image/svg+xml', + 'image/apng', + 'image/avif', + 'image/bmp', + 'image/tiff', +] as const; + +export function canvasToPngBlob(canvas: HTMLCanvasElement): Promise { + return new Promise((resolve, reject) => { + canvas.toBlob(blob => { + if (blob) { + resolve(blob); + return; + } + reject(new Error('Canvas toBlob returned null')); + }, 'image/png'); + }); +} + +async function readBlobAsDataURL(blob: Blob): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onerror = () => reject(reader.error || new Error('Failed to read image blob')); + reader.onload = () => resolve(String(reader.result || '')); + reader.readAsDataURL(blob); + }); +} + +function fallbackVersion(value: string | undefined): string { + return value && value.trim() ? value.trim() : String(Date.now()); +} + +function fallbackFilename(mimeType: string): string { + switch (mimeType.toLowerCase()) { + case 'image/jpeg': + return 'clipboard-image.jpg'; + case 'image/webp': + return 'clipboard-image.webp'; + case 'image/gif': + return 'clipboard-image.gif'; + case 'image/svg+xml': + return 'clipboard-image.svg'; + case 'image/avif': + return 'clipboard-image.avif'; + case 'image/bmp': + return 'clipboard-image.bmp'; + case 'image/tiff': + return 'clipboard-image.tiff'; + case 'image/apng': + return 'clipboard-image.apng'; + default: + return 'clipboard-image.png'; + } +} + +async function blobToPngBlob(blob: Blob): Promise { + const image = new Image(); + const blobUrl = URL.createObjectURL(blob); + + try { + await new Promise((resolve, reject) => { + image.onload = () => resolve(); + image.onerror = () => reject(new Error('Failed to decode image blob')); + image.src = blobUrl; + }); + + const canvas = document.createElement('canvas'); + canvas.width = image.naturalWidth; + canvas.height = image.naturalHeight; + + const ctx = canvas.getContext('2d'); + if (!ctx) { + throw new Error('Failed to create canvas context'); + } + + ctx.drawImage(image, 0, 0); + return await canvasToPngBlob(canvas); + } finally { + URL.revokeObjectURL(blobUrl); + } +} + +export async function writeImageToClipboard(blob: Blob, mimeType = blob.type || 'image/png'): Promise { + const ClipboardItemCtor = globalThis.ClipboardItem; + if (!ClipboardItemCtor || !navigator.clipboard?.write) { + throw new Error('Clipboard image write is not supported in this environment'); + } + + const resolvedType = mimeType.startsWith('image/') ? mimeType : 'image/png'; + if (typeof ClipboardItemCtor.supports === 'function' && ClipboardItemCtor.supports(resolvedType)) { + await navigator.clipboard.write([ + new ClipboardItemCtor({[resolvedType]: blob}), + ]); + return; + } + + const pngBlob = resolvedType === 'image/png' ? blob : await blobToPngBlob(blob); + await navigator.clipboard.write([ + new ClipboardItemCtor({'image/png': pngBlob}), + ]); +} + +export async function copyImage(url: string): Promise { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Fetch failed: ${response.status}`); + } + + const blob = await response.blob(); + if (!blob.type.startsWith('image/')) { + throw new Error(`Not an image content type. Got: ${blob.type}`); + } + + await writeImageToClipboard(blob, blob.type); +} + +export async function importImageBlob(blob: Blob, filename?: string): Promise { + const dataBase64 = await readBlobAsDataURL(blob); + const asset = await MediaHTTPService.ImportImage({ + filename: filename || fallbackFilename(blob.type || 'image/png'), + mime_type: blob.type || undefined, + data_base64: dataBase64, + }); + + if (!asset) { + throw new Error('ImportImage returned no asset'); + } + + return asset; +} + +export async function deleteImageAsset(imageRef: string): Promise { + const result = await MediaHTTPService.DeleteImage(imageRef); + + if (!result) { + throw new Error('DeleteImage returned no result'); + } + + return result; +} + +export function buildVersionedInlineImageUrl(asset: ImageAsset): string { + const separator = asset.url.includes('?') ? '&' : '?'; + return `${asset.url}${separator}v=${encodeURIComponent(fallbackVersion(asset.updated_at))}`; +} + +export function createInlineImageDataFromAsset(asset: ImageAsset, maxDisplayHeight: number): InlineImageData { + const image: InlineImageData = { + id: crypto.randomUUID(), + assetRef: asset.id, + file: buildVersionedInlineImageUrl(asset), + width: asset.width, + height: asset.height, + }; + + const aspect = asset.height > 0 ? asset.width / asset.height : 1; + if ((asset.height / window.devicePixelRatio) > maxDisplayHeight) { + image.displayHeight = maxDisplayHeight; + image.displayWidth = maxDisplayHeight * aspect; + } + + return image; +} + +export function createInlineImageTagFromAsset(asset: ImageAsset, maxDisplayHeight: number): string { + return createInlineImageTag(createInlineImageDataFromAsset(asset, maxDisplayHeight)); +} diff --git a/frontend/src/views/editor/extensions/inlineImage/clipboardIntegration.ts b/frontend/src/views/editor/extensions/inlineImage/clipboardIntegration.ts new file mode 100644 index 00000000..90058b8d --- /dev/null +++ b/frontend/src/views/editor/extensions/inlineImage/clipboardIntegration.ts @@ -0,0 +1,180 @@ +import {EditorSelection, type Extension} from '@codemirror/state'; +import {EditorView} from '@codemirror/view'; +import {codeBlockEvent, CONTENT_EDIT, USER_EVENTS} from '../codeblock/annotation'; +import {copyImage, createInlineImageTagFromAsset, IMAGE_MIME_TYPES, importImageBlob} from './clipboard'; +import {inlineImageIsSelected, inlineImageState} from './inlineImage'; + +function isImageMimeType(type: string): boolean { + return type.startsWith('image/') || IMAGE_MIME_TYPES.includes(type as (typeof IMAGE_MIME_TYPES)[number]); +} + +function extractImageBlobsFromClipboardData(data: DataTransfer | null): Blob[] { + if (!data) { + return []; + } + + const fromItems = Array.from(data.items || []) + .filter(item => item.kind === 'file' && isImageMimeType(item.type)) + .map(item => item.getAsFile()) + .filter((file): file is File => Boolean(file)); + + if (fromItems.length > 0) { + return fromItems; + } + + return Array.from(data.files || []).filter(file => isImageMimeType(file.type)); +} + +async function buildInlineImageTags(blobs: readonly Blob[], maxDisplayHeight: number): Promise { + const tags: string[] = []; + + for (const blob of blobs) { + try { + const asset = await importImageBlob(blob); + tags.push(createInlineImageTagFromAsset(asset, maxDisplayHeight)); + } catch (error) { + console.error('[inlineImage] Failed to import pasted image:', error); + } + } + + return tags; +} + +function insertInlineImageTagsAtSelection(view: EditorView, tags: readonly string[], userEvent: string): boolean { + if (tags.length === 0) { + return false; + } + + const insert = tags.join(''); + const selection = view.state.selection.main; + + view.dispatch(view.state.update({ + changes: { + from: selection.from, + to: selection.to, + insert, + }, + selection: EditorSelection.cursor(selection.from + insert.length), + userEvent, + annotations: [codeBlockEvent.of(CONTENT_EDIT)], + }, {scrollIntoView: true})); + + return true; +} + +function insertInlineImageTagsAtPosition(view: EditorView, pos: number, tags: readonly string[], userEvent: string): boolean { + if (tags.length === 0) { + return false; + } + + const insert = tags.join(''); + view.dispatch(view.state.update({ + changes: {from: pos, to: pos, insert}, + selection: EditorSelection.cursor(pos + insert.length), + userEvent, + annotations: [codeBlockEvent.of(CONTENT_EDIT)], + }, {scrollIntoView: true})); + + return true; +} + +export async function copySelectedInlineImageIfNeeded(view: EditorView): Promise { + const images = view.state.field(inlineImageState, false); + if (!images) { + return false; + } + + for (const image of images) { + if (inlineImageIsSelected(image, view.state.selection.main)) { + await copyImage(image.file); + return true; + } + } + + return false; +} + +export async function pasteInlineImagesFromClipboardEvent( + view: EditorView, + event: ClipboardEvent, + maxDisplayHeight: number, +): Promise { + if (view.state.readOnly) { + return false; + } + + const blobs = extractImageBlobsFromClipboardData(event.clipboardData); + if (blobs.length === 0) { + return false; + } + + const tags = await buildInlineImageTags(blobs, maxDisplayHeight); + return insertInlineImageTagsAtSelection(view, tags, USER_EVENTS.INPUT_PASTE); +} + +export async function pasteInlineImagesFromSystemClipboard( + view: EditorView, + maxDisplayHeight: number, +): Promise { + if (view.state.readOnly || !navigator.clipboard?.read) { + return false; + } + + try { + const items = await navigator.clipboard.read(); + const blobs: Blob[] = []; + + for (const item of items) { + for (const type of item.types) { + if (!isImageMimeType(type)) { + continue; + } + blobs.push(await item.getType(type)); + break; + } + } + + if (blobs.length === 0) { + return false; + } + + const tags = await buildInlineImageTags(blobs, maxDisplayHeight); + return insertInlineImageTagsAtSelection(view, tags, USER_EVENTS.INPUT_PASTE); + } catch (error) { + console.error('[inlineImage] Failed to read clipboard images:', error); + return false; + } +} + +export function createInlineImageDropExtension(maxDisplayHeight: number): Extension { + return EditorView.domEventHandlers({ + dragover(event) { + const files = Array.from(event.dataTransfer?.files || []); + const hasImage = files.some(file => isImageMimeType(file.type)); + if (!hasImage) { + return false; + } + + event.preventDefault(); + return true; + }, + drop(event, view) { + const files = Array.from(event.dataTransfer?.files || []).filter(file => isImageMimeType(file.type)); + if (files.length === 0 || view.state.readOnly) { + return false; + } + + event.preventDefault(); + event.stopPropagation(); + view.focus(); + + const pos = view.posAtCoords({x: event.clientX, y: event.clientY}) ?? view.state.selection.main.head; + void (async () => { + const tags = await buildInlineImageTags(files, maxDisplayHeight); + insertInlineImageTagsAtPosition(view, pos, tags, 'input.drop'); + })(); + + return true; + }, + }); +} diff --git a/frontend/src/views/editor/extensions/inlineImage/index.ts b/frontend/src/views/editor/extensions/inlineImage/index.ts new file mode 100644 index 00000000..4bd40a81 --- /dev/null +++ b/frontend/src/views/editor/extensions/inlineImage/index.ts @@ -0,0 +1,49 @@ +import {Facet, type Extension} from '@codemirror/state'; +import {createInlineImageDropExtension} from './clipboardIntegration'; +import {createInlineImageWidgetExtension} from './inlineImage'; +import type {InlineImageOptions} from './types'; + +const DEFAULT_OPTIONS = { + maxDisplayHeight: 200, +} as const satisfies InlineImageOptions; + +function createDefaultOptions(): InlineImageOptions { + return { + ...DEFAULT_OPTIONS, + }; +} + +function mergeOptions( + base: InlineImageOptions, + patch: Partial, +): InlineImageOptions { + return { + ...base, + ...patch, + }; +} + +export const inlineImageEnabledFacet = Facet.define({ + combine: values => values.some(Boolean), +}); + +export const inlineImageOptionsFacet = Facet.define, InlineImageOptions>({ + combine: values => + values.reduce( + (merged, value) => mergeOptions(merged, value), + createDefaultOptions(), + ), +}); + +export function createInlineImageExtension(options: Partial = {}): Extension { + const resolvedOptions = mergeOptions(createDefaultOptions(), options); + + return [ + inlineImageEnabledFacet.of(true), + inlineImageOptionsFacet.of(options), + createInlineImageWidgetExtension(), + createInlineImageDropExtension(resolvedOptions.maxDisplayHeight), + ]; +} + +export default createInlineImageExtension; diff --git a/frontend/src/views/editor/extensions/inlineImage/inlineImage.ts b/frontend/src/views/editor/extensions/inlineImage/inlineImage.ts new file mode 100644 index 00000000..34f0979c --- /dev/null +++ b/frontend/src/views/editor/extensions/inlineImage/inlineImage.ts @@ -0,0 +1,321 @@ +import {Compartment, RangeSetBuilder, StateField, type Extension, type SelectionRange, type Transaction} from '@codemirror/state'; +import {foldEffect, foldState, unfoldEffect} from '@codemirror/language'; +import {Decoration, type DecorationSet, EditorView, ViewPlugin} from '@codemirror/view'; +import copyDarkIconUrl from '@/assets/icons/copy-dark.svg'; +import pencilWhiteIconUrl from '@/assets/icons/pencil-white.svg'; +import resizeHandleDarkUrl from '@/assets/icons/resize-handle-se-dark.png'; +import resizeHandleLightUrl from '@/assets/icons/resize-handle-se-light.png'; +import trashWhiteIconUrl from '@/assets/icons/trash-white.svg'; +import {parseInlineImages} from './inlineImageParsing'; +import {InlineImageWidget} from './inlineImageWidget'; +import type {ParsedInlineImage} from './types'; + +export const inlineImageState = StateField.define({ + create(state) { + return parseInlineImages(state); + }, + update(images, transaction) { + if (transaction.docChanged) { + return parseInlineImages(transaction.state); + } + return images; + }, +}); + +function createAtomicRanges(view: EditorView): DecorationSet { + const builder = new RangeSetBuilder(); + + view.state.field(inlineImageState).forEach(image => { + builder.add(image.from, image.to, Decoration.mark({})); + }); + + return builder.finish(); +} + +const atomicInlineImages = ViewPlugin.fromClass(class { + atomicRanges: DecorationSet; + + constructor(view: EditorView) { + this.atomicRanges = createAtomicRanges(view); + } + + update(update: {docChanged: boolean; view: EditorView}) { + if (update.docChanged) { + this.atomicRanges = createAtomicRanges(update.view); + } + } +}, { + provide: plugin => EditorView.atomicRanges.of(view => view.plugin(plugin)?.atomicRanges || Decoration.none), +}); + +function transactionHasFoldEffect(transaction: Transaction): boolean { + return transaction.effects.some(effect => effect.is(foldEffect) || effect.is(unfoldEffect)); +} + +function createInlineImageTheme(): Extension { + return EditorView.baseTheme({ + '&.cm-editor.resizing-image': { + cursor: 'nwse-resize', + }, + '.inline-image': { + '--outline-color': '#2482ce', + '--snapped-outline-color': '#39a363', + '--handle-color': '#ccc', + '--image-border-color': '#c9c9c9', + padding: '6px 2px', + display: 'inline-block', + position: 'relative', + verticalAlign: 'middle', + }, + '&.cm-dark .inline-image': { + '--outline-color': '#0060c7', + '--snapped-outline-color': '#39a363', + '--handle-color': '#192736', + '--image-border-color': '#252525', + }, + '.inline-image.folded': { + padding: '0', + }, + '.inline-image.folded.selected': { + padding: '0', + }, + '.inline-image.folded .resize-handle': { + display: 'none', + }, + '.inline-image .inner': { + position: 'relative', + border: '1px solid var(--image-border-color)', + }, + '.inline-image.controls-visible .buttons-container': { + opacity: '1', + }, + '.inline-image.selected .buttons-container': { + opacity: '1', + }, + '.inline-image img': { + display: 'block', + maxWidth: '100%', + minWidth: '16px', + minHeight: '16px', + objectFit: 'contain', + }, + '.inline-image .highlight-border': { + display: 'none', + position: 'absolute', + top: '-2px', + right: '-2px', + bottom: '-2px', + left: '-2px', + border: '3px solid var(--outline-color)', + boxSizing: 'border-box', + pointerEvents: 'none', + }, + '.inline-image .buttons-container': { + position: 'absolute', + inset: '0', + display: 'flex', + padding: '7px', + alignItems: 'flex-start', + justifyContent: 'left', + boxSizing: 'border-box', + gap: '4px', + opacity: '0', + overflow: 'hidden', + containerType: 'inline-size', + pointerEvents: 'none', + }, + '.inline-image .buttons-container button': { + height: 'calc(24px * var(--button-scale, 1))', + fontSize: 'calc(12px * var(--button-scale, 1))', + backgroundColor: '#646e71', + color: '#fff', + opacity: '1', + transition: 'background-color 200ms', + backgroundImage: `url("${copyDarkIconUrl}")`, + backgroundSize: 'calc(13px * var(--button-scale, 1))', + backgroundPosition: 'calc(6px * var(--button-scale, 1)) center', + backgroundRepeat: 'no-repeat', + padding: 'calc(3px * var(--button-scale, 1)) calc(7px * var(--button-scale, 1)) calc(3px * var(--button-scale, 1)) calc(22px * var(--button-scale, 1))', + border: 'none', + borderRadius: 'calc(3px * var(--button-scale, 1))', + cursor: 'pointer', + boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)', + minWidth: '0', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + flex: '0 1 auto', + maxWidth: '100%', + pointerEvents: 'auto', + }, + '.inline-image .buttons-container button span': { + display: 'block', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }, + '.inline-image .buttons-container button:hover': { + backgroundColor: '#51595c', + opacity: '1', + }, + '.inline-image .buttons-container button.draw': { + backgroundImage: `url("${pencilWhiteIconUrl}")`, + }, + '.inline-image .buttons-container button.delete': { + backgroundImage: `url("${trashWhiteIconUrl}")`, + padding: 'calc(3px * var(--button-scale, 1)) calc(8px * var(--button-scale, 1)) calc(3px * var(--button-scale, 1)) calc(22px * var(--button-scale, 1))', + backgroundColor: '#8f3d3d', + }, + '.inline-image .buttons-container button.delete:hover': { + backgroundColor: '#7c3131', + }, + '.inline-image.selected': { + '--handle-color': 'var(--outline-color)', + }, + '.inline-image.selected .highlight-border': { + display: 'block', + }, + '.inline-image.selected .resize-handle': { + opacity: '1', + }, + '.inline-image.selected .resize-handle .icon': { + backgroundImage: `url("${resizeHandleDarkUrl}")`, + }, + '.inline-image:hover .resize-handle': { + opacity: '0.5', + }, + '.inline-image:hover .resize-handle:hover': { + opacity: '1', + }, + '.inline-image .resize-handle': { + opacity: '0', + transition: 'opacity 200ms', + width: '0', + height: '0', + position: 'absolute', + right: '2px', + bottom: '6px', + borderBottom: '10px solid var(--handle-color)', + borderRight: '10px solid var(--handle-color)', + borderLeft: '10px solid transparent', + borderTop: '10px solid transparent', + cursor: 'nwse-resize', + zIndex: '10', + }, + '.inline-image .resize-handle .icon': { + position: 'absolute', + top: '-4px', + left: '-4px', + backgroundImage: `url("${resizeHandleLightUrl}")`, + backgroundSize: '100%', + width: '12px', + height: '12px', + }, + '&.cm-dark .inline-image .resize-handle .icon': { + backgroundImage: `url("${resizeHandleDarkUrl}")`, + }, + '.inline-image.resizing .buttons-container': { + display: 'none', + }, + '.inline-image.resizing .resize-handle': { + opacity: '1', + transition: 'none', + }, + '.inline-image.resizing.snapped': { + '--outline-color': 'var(--snapped-outline-color)', + }, + }); +} + +export interface InlineImageWidgetExtensionOptions { + interactive?: boolean; +} + +export function createInlineImageWidgetExtension(options: InlineImageWidgetExtensionOptions = {}): Extension { + const interactive = options.interactive ?? true; + const domEventCompartment = interactive ? new Compartment() : null; + + const decorate = (state: EditorView['state']): DecorationSet => { + const builder = new RangeSetBuilder(); + const selection = state.selection.main; + const foldStarts = new Set(); + const foldRanges = state.field(foldState, false); + + if (foldRanges) { + foldRanges.between(0, state.doc.length, from => { + foldStarts.add(from); + }); + } + + let foundSelectedInlineImage = false; + + state.field(inlineImageState).forEach(image => { + const isFolded = foldStarts.has(image.to); + const isSelected = !foundSelectedInlineImage && inlineImageIsSelected(image, selection); + + if (isSelected) { + foundSelectedInlineImage = true; + } + + builder.add(image.from, image.to, Decoration.replace({ + widget: new InlineImageWidget({ + id: image.id, + assetRef: image.assetRef, + path: image.file, + width: image.width, + height: image.height, + displayWidth: image.displayWidth, + displayHeight: image.displayHeight, + selected: isSelected, + isFolded, + interactive, + domEventCompartment: domEventCompartment ?? undefined, + }), + inclusive: false, + block: false, + side: 0, + })); + }); + + return builder.finish(); + }; + + const inlineImagesField = StateField.define({ + create(state) { + return decorate(state); + }, + update(widgets, transaction) { + if (transaction.docChanged || transaction.selection || transactionHasFoldEffect(transaction)) { + return decorate(transaction.state); + } + return widgets; + }, + provide(field) { + return EditorView.decorations.from(field); + }, + }); + + return [ + inlineImageState, + inlineImagesField, + atomicInlineImages, + createInlineImageTheme(), + ...(domEventCompartment ? [domEventCompartment.of([])] : []), + ]; +} + +export function inlineImageIsSelected(image: ParsedInlineImage, selection: SelectionRange): boolean { + if (selection.from !== selection.to) { + return false; + } + + if (selection.from === image.from) { + return selection.assoc >= 0; + } + + if (selection.from === image.to) { + return selection.assoc < 0; + } + + return false; +} diff --git a/frontend/src/views/editor/extensions/inlineImage/inlineImageParsing.test.ts b/frontend/src/views/editor/extensions/inlineImage/inlineImageParsing.test.ts new file mode 100644 index 00000000..4d3a71b0 --- /dev/null +++ b/frontend/src/views/editor/extensions/inlineImage/inlineImageParsing.test.ts @@ -0,0 +1,72 @@ +import {describe, expect, it} from "vitest"; +import {createInlineImageTag, parseInlineImagesFromString} from "./inlineImageParsing"; + +describe("inlineImage image parsing", () => { + it("parses image tags with required and optional params", () => { + const tag = "<∞img;id=abc;asset=sha256_asset;file=/media/2026/04/test.png;w=1200;h=630;dw=300;dh=200∞>"; + const content = `before ${tag} after`; + + const images = parseInlineImagesFromString(content); + + expect(images).toHaveLength(1); + expect(images[0]).toEqual({ + from: content.indexOf(tag), + to: content.indexOf(tag) + tag.length, + id: "abc", + assetRef: "sha256_asset", + file: "/media/2026/04/test.png", + width: 1200, + height: 630, + displayWidth: 300, + displayHeight: 200, + }); + }); + + it("ignores tags missing required params", () => { + const tag1 = "<∞img;id=1;file=/media/a.png;w=10;h=20∞>"; + const invalid = "<∞img;id=2;file=/media/b.png;w=10∞>"; + const tag2 = "<∞img;id=3;file=/media/c.png;w=30;h=40;dw=15∞>"; + const content = `${tag1} middle ${invalid} tail ${tag2}`; + + const images = parseInlineImagesFromString(content); + + expect(images).toHaveLength(2); + expect(images[0].id).toBe("1"); + expect(images[1].id).toBe("3"); + expect(images[1].displayWidth).toBe(15); + expect(images[1].displayHeight).toBeUndefined(); + }); + + it("does not leak regex state between calls", () => { + const tag = "<∞img;id=1;file=/media/a.png;w=10;h=20∞>"; + const first = parseInlineImagesFromString(tag); + const second = parseInlineImagesFromString(`prefix ${tag}`); + + expect(first).toHaveLength(1); + expect(second).toHaveLength(1); + expect(second[0].from).toBe("prefix ".length); + }); +}); + +describe("inlineImage image tag creation", () => { + it("creates tag with display dimensions", () => { + expect(createInlineImageTag({ + id: "1", + assetRef: "asset-id", + file: "/media/2026/04/test.png", + width: 100, + height: 200, + displayWidth: 50, + displayHeight: 60, + })).toBe("<∞img;id=1;asset=asset-id;file=/media/2026/04/test.png;w=100;h=200;dw=50;dh=60∞>"); + }); + + it("omits display dimensions when missing", () => { + expect(createInlineImageTag({ + id: "1", + file: "/media/2026/04/test.png", + width: 100, + height: 200, + })).toBe("<∞img;id=1;file=/media/2026/04/test.png;w=100;h=200∞>"); + }); +}); diff --git a/frontend/src/views/editor/extensions/inlineImage/inlineImageParsing.ts b/frontend/src/views/editor/extensions/inlineImage/inlineImageParsing.ts new file mode 100644 index 00000000..9a56cbce --- /dev/null +++ b/frontend/src/views/editor/extensions/inlineImage/inlineImageParsing.ts @@ -0,0 +1,174 @@ +import {EditorSelection, type EditorState} from '@codemirror/state'; +import type {EditorView} from '@codemirror/view'; +import {codeBlockEvent, CONTENT_EDIT} from '../codeblock/annotation'; +import type {InlineImageData, ParsedInlineImage} from './types'; + +export const INLINE_IMAGE_TAG_REGEX = /<∞img;([^∞>]*)∞>/g; +export const WIDGET_TAG_REGEX = /<∞.*?∞>/g; +export const WIDGET_TAG_REGEX_NON_GLOBAL = /<∞.*?∞>/; + +const REQUIRED_PARAMS = ['id', 'file', 'w', 'h'] as const; + +export function parseInlineImages(state: EditorState): ParsedInlineImage[] { + return parseInlineImagesFromString(state.doc.sliceString(0, state.doc.length)); +} + +/** + * 解析以下格式的图片标签: + * <∞img;id=uuid;file=/media/2026/04/foo.png;w=1200;h=630;dw=324;dh=170∞> + */ +export function parseInlineImagesFromString(content: string): ParsedInlineImage[] { + INLINE_IMAGE_TAG_REGEX.lastIndex = 0; + + let match: RegExpExecArray | null; + const images: ParsedInlineImage[] = []; + + while ((match = INLINE_IMAGE_TAG_REGEX.exec(content)) !== null) { + try { + const params: Record = {}; + + for (const part of match[1].split(';')) { + if (!part) { + continue; + } + + const eqIndex = part.indexOf('='); + if (eqIndex === -1) { + params[part] = true; + continue; + } + + params[part.slice(0, eqIndex)] = part.slice(eqIndex + 1); + } + + if (!REQUIRED_PARAMS.every(param => Object.hasOwn(params, param))) { + continue; + } + + images.push({ + from: match.index, + to: match.index + match[0].length, + id: String(params.id), + assetRef: params.asset ? String(params.asset) : undefined, + file: String(params.file), + width: Number(params.w), + height: Number(params.h), + displayWidth: params.dw ? Number(params.dw) : undefined, + displayHeight: params.dh ? Number(params.dh) : undefined, + }); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + console.error(`[inlineImage] Bad <∞img> tag at index ${match.index}: ${message}. Tag: ${match[0]}`); + } + } + + return images; +} + +export function createInlineImageTag(image: InlineImageData): string { + const params = [ + `id=${image.id}`, + ...(image.assetRef ? [`asset=${image.assetRef}`] : []), + `file=${image.file}`, + `w=${image.width}`, + `h=${image.height}`, + ]; + + if (image.displayWidth) { + params.push(`dw=${image.displayWidth}`); + } + if (image.displayHeight) { + params.push(`dh=${image.displayHeight}`); + } + + return `<∞img;${params.join(';')}∞>`; +} + +export function setInlineImageDisplayDimensions( + view: EditorView, + id: string, + width: number, + height: number, +): void { + const images = Object.fromEntries(parseInlineImages(view.state).map(image => [image.id, image])); + const image = images[id]; + + if (!image) { + return; + } + + image.displayWidth = width; + image.displayHeight = height; + + view.dispatch(view.state.update({ + changes: { + from: image.from, + to: image.to, + insert: createInlineImageTag(image), + }, + annotations: [codeBlockEvent.of(CONTENT_EDIT)], + })); +} + +export function setInlineImageFile(view: EditorView, id: string, file: string): void { + const images = Object.fromEntries(parseInlineImages(view.state).map(image => [image.id, image])); + const image = images[id]; + + if (!image) { + console.error(`[inlineImage] Image with id ${id} not found`); + return; + } + + image.file = file; + const nextTag = createInlineImageTag(image); + + view.dispatch(view.state.update({ + changes: { + from: image.from, + to: image.to, + insert: nextTag, + }, + selection: EditorSelection.cursor(image.from + nextTag.length, -1), + }, {scrollIntoView: true})); +} + +export function updateInlineImageData(view: EditorView, id: string, patch: Partial): void { + const images = Object.fromEntries(parseInlineImages(view.state).map(image => [image.id, image])); + const image = images[id]; + + if (!image) { + console.error(`[inlineImage] Image with id ${id} not found`); + return; + } + + Object.assign(image, patch); + const nextTag = createInlineImageTag(image); + + view.dispatch(view.state.update({ + changes: { + from: image.from, + to: image.to, + insert: nextTag, + }, + selection: EditorSelection.cursor(image.from + nextTag.length, -1), + }, {scrollIntoView: true})); +} + +export function removeInlineImage(view: EditorView, id: string): void { + const images = Object.fromEntries(parseInlineImages(view.state).map(image => [image.id, image])); + const image = images[id]; + + if (!image) { + console.error(`[inlineImage] Image with id ${id} not found`); + return; + } + + view.dispatch(view.state.update({ + changes: { + from: image.from, + to: image.to, + insert: '', + }, + annotations: [codeBlockEvent.of(CONTENT_EDIT)], + }, {scrollIntoView: true})); +} diff --git a/frontend/src/views/editor/extensions/inlineImage/inlineImageWidget.ts b/frontend/src/views/editor/extensions/inlineImage/inlineImageWidget.ts new file mode 100644 index 00000000..31a07710 --- /dev/null +++ b/frontend/src/views/editor/extensions/inlineImage/inlineImageWidget.ts @@ -0,0 +1,431 @@ +import {EditorSelection, type Compartment} from '@codemirror/state'; +import {EditorView, WidgetType} from '@codemirror/view'; +import i18n from '@/i18n'; +import {copyImage, deleteImageAsset} from './clipboard'; +import {inlineImageDrawManager} from './manager'; +import {parseInlineImages, removeInlineImage, setInlineImageDisplayDimensions} from './inlineImageParsing'; + +const FOLDED_HEIGHT = 16; + +function t(key: string): string { + return i18n.global.t(key); +} + +function withDialogCacheBust(url: string): string { + const cleanUrl = url.trim(); + if (!cleanUrl) { + return cleanUrl; + } + + const separator = cleanUrl.includes('?') ? '&' : '?'; + return `${cleanUrl}${separator}dialog_ts=${Date.now()}`; +} + +export interface InlineImageWidgetConfig { + id: string; + assetRef?: string; + path: string; + width: number; + height: number; + displayWidth?: number; + displayHeight?: number; + selected?: boolean; + isFolded?: boolean; + interactive?: boolean; + domEventCompartment?: Compartment; +} + +export class InlineImageWidget extends WidgetType { + readonly id: string; + readonly assetRef?: string; + readonly path: string; + readonly width: number; + readonly height: number; + readonly displayWidth?: number; + readonly displayHeight?: number; + readonly selected: boolean; + readonly isFolded: boolean; + readonly interactive: boolean; + readonly domEventCompartment?: Compartment; + readonly idealWidth: number; + readonly idealHeight: number; + + constructor(config: InlineImageWidgetConfig) { + super(); + this.id = config.id; + this.assetRef = config.assetRef; + this.path = config.path; + this.width = config.width; + this.height = config.height; + this.displayWidth = config.displayWidth; + this.displayHeight = config.displayHeight; + this.selected = Boolean(config.selected); + this.isFolded = Boolean(config.isFolded); + this.interactive = config.interactive ?? true; + this.domEventCompartment = config.domEventCompartment; + this.idealWidth = this.width / window.devicePixelRatio; + this.idealHeight = this.height / window.devicePixelRatio; + } + + override eq(other: InlineImageWidget): boolean { + return other.path === this.path + && other.width === this.width + && other.height === this.height + && other.displayWidth === this.displayWidth + && other.displayHeight === this.displayHeight + && other.selected === this.selected + && other.isFolded === this.isFolded + && other.id === this.id + && other.assetRef === this.assetRef + && other.interactive === this.interactive; + } + + override toDOM(view: EditorView): HTMLElement { + const wrap = document.createElement('div'); + this.syncWrapClassName(wrap); + wrap.dataset.id = this.id; + wrap.dataset.idealWidth = String(this.idealWidth); + wrap.dataset.idealHeight = String(this.idealHeight); + this.attachSelectionBehavior(view, wrap); + this.attachHoverBehavior(wrap); + + const inner = document.createElement('div'); + inner.className = 'inner'; + wrap.appendChild(inner); + + const highlightBorder = document.createElement('div'); + highlightBorder.className = 'highlight-border'; + inner.appendChild(highlightBorder); + + let copyButton: HTMLButtonElement | null = null; + let drawButton: HTMLButtonElement | null = null; + let deleteButton: HTMLButtonElement | null = null; + + if (this.interactive && !this.isFolded) { + const buttonsContainer = document.createElement('div'); + buttonsContainer.className = 'buttons-container'; + inner.appendChild(buttonsContainer); + + copyButton = document.createElement('button'); + copyButton.type = 'button'; + copyButton.title = t('inlineImage.copy'); + copyButton.innerHTML = `${t('inlineImage.copy')}`; + buttonsContainer.appendChild(copyButton); + + drawButton = document.createElement('button'); + drawButton.type = 'button'; + drawButton.className = 'draw'; + drawButton.title = t('inlineImage.draw'); + drawButton.innerHTML = `${t('inlineImage.draw')}`; + buttonsContainer.appendChild(drawButton); + + deleteButton = document.createElement('button'); + deleteButton.type = 'button'; + deleteButton.className = 'delete'; + deleteButton.title = t('inlineImage.delete'); + deleteButton.innerHTML = `${t('inlineImage.delete')}`; + buttonsContainer.appendChild(deleteButton); + + copyButton.addEventListener('mousedown', event => { + event.preventDefault(); + }); + drawButton.addEventListener('mousedown', event => { + event.preventDefault(); + }); + deleteButton.addEventListener('mousedown', event => { + event.preventDefault(); + }); + + requestAnimationFrame(() => { + this.syncControlScale(wrap, this.getNumericWidth(), buttonsContainer); + }); + } + + const image = document.createElement('img'); + image.src = this.path; + image.style.width = this.getWidth(); + image.style.height = this.getHeight(); + inner.appendChild(image); + + if (copyButton) { + copyButton.addEventListener('click', async event => { + event.preventDefault(); + try { + await copyImage(image.src); + copyButton!.innerText = t('inlineImage.copied'); + setTimeout(() => { + copyButton!.innerHTML = `${t('inlineImage.copy')}`; + }, 2000); + } catch (error) { + console.error('[inlineImage] Failed to copy image:', error); + } + }); + } + + if (drawButton) { + drawButton.addEventListener('click', event => { + event.preventDefault(); + const currentImage = parseInlineImages(view.state).find(image => image.id === this.id); + const imageUrl = currentImage?.file || this.path; + const assetRef = currentImage?.assetRef || this.assetRef || imageUrl; + inlineImageDrawManager.show(view, this.id, assetRef, withDialogCacheBust(imageUrl)); + }); + } + + if (deleteButton) { + deleteButton.addEventListener('click', async event => { + event.preventDefault(); + try { + await deleteImageAsset(this.assetRef || this.path); + removeInlineImage(view, this.id); + } catch (error) { + console.error('[inlineImage] Failed to delete image:', error); + } + }); + } + + if (this.interactive && !this.isFolded && this.domEventCompartment) { + const resizeHandle = document.createElement('div'); + resizeHandle.className = 'resize-handle'; + resizeHandle.innerHTML = '
'; + wrap.appendChild(resizeHandle); + this.attachResizeBehavior(view, wrap, image, resizeHandle); + } + + return wrap; + } + + override updateDOM(dom: HTMLElement): boolean { + this.syncWrapClassName(dom); + dom.dataset.id = this.id; + dom.dataset.idealWidth = String(this.idealWidth); + dom.dataset.idealHeight = String(this.idealHeight); + const image = dom.querySelector('img'); + if (!(image instanceof HTMLImageElement)) { + return false; + } + + image.src = this.path; + image.style.width = this.getWidth(); + image.style.height = this.getHeight(); + this.syncControlScale(dom, this.getNumericWidth(), dom.querySelector('.buttons-container')); + return true; + } + + override ignoreEvent(): boolean { + return false; + } + + private getClassName(): string { + return `inline-image${this.selected ? ' selected' : ''}${this.isFolded ? ' folded' : ''}`; + } + + private syncWrapClassName(dom: HTMLElement): void { + const isControlsVisible = dom.classList.contains('controls-visible'); + dom.className = this.getClassName(); + if (isControlsVisible) { + dom.classList.add('controls-visible'); + } + } + + private getWidth(): string { + const width = this.getNumericWidth(); + + return width ? `${width}px` : ''; + } + + private getNumericWidth(): number | undefined { + let width: number | undefined; + + if (this.isFolded) { + width = FOLDED_HEIGHT * (this.width / this.height); + } else if (this.displayWidth) { + width = this.displayWidth; + } else { + width = this.idealWidth; + } + + return width; + } + + private getHeight(): string { + let height: number; + + if (this.isFolded) { + height = FOLDED_HEIGHT; + } else if (this.displayHeight) { + height = this.displayHeight; + } else { + height = this.idealHeight; + } + + return `${height}px`; + } + + private attachHoverBehavior(wrap: HTMLDivElement): void { + wrap.addEventListener('mouseenter', () => { + wrap.classList.add('controls-visible'); + }); + + wrap.addEventListener('mouseleave', () => { + wrap.classList.remove('controls-visible'); + }); + } + + private attachSelectionBehavior(view: EditorView, wrap: HTMLDivElement): void { + wrap.addEventListener('mousedown', event => { + if (event.button !== 0) { + return; + } + + const target = event.target; + if (!(target instanceof Element)) { + return; + } + + if (target.closest('button, .resize-handle')) { + return; + } + + const currentImage = parseInlineImages(view.state).find(image => image.id === this.id); + if (!currentImage) { + return; + } + + event.preventDefault(); + view.focus(); + view.dispatch({ + selection: EditorSelection.cursor(currentImage.from, 1), + scrollIntoView: true, + }); + }); + } + + private attachResizeBehavior( + view: EditorView, + wrap: HTMLDivElement, + image: HTMLImageElement, + resizeHandle: HTMLDivElement, + ): void { + let initialWidth = 0; + let initialHeight = 0; + let initialX = 0; + let initialY = 0; + let shouldSnap = true; + + const onMouseMove = (event: MouseEvent) => { + const idealWidth = Number(wrap.dataset.idealWidth); + const idealHeight = Number(wrap.dataset.idealHeight); + const aspect = idealWidth / idealHeight; + + let width = initialWidth + (event.pageX - initialX); + let height = initialHeight + (event.pageY - initialY); + + const heightFromWidth = width / aspect; + const widthFromHeight = height * aspect; + + if (heightFromWidth <= height) { + height = heightFromWidth; + } else { + width = widthFromHeight; + } + + const snapTolerance = 10; + if (shouldSnap) { + if (Math.abs(width - idealWidth) <= snapTolerance || Math.abs(height - idealHeight) <= snapTolerance) { + width = idealWidth; + wrap.classList.add('snapped'); + } else { + wrap.classList.remove('snapped'); + } + } else if (Math.abs(width - idealWidth) > snapTolerance && Math.abs(height - idealHeight) > snapTolerance) { + shouldSnap = true; + } + + width = Math.max(width, 16); + height = width / aspect; + if (height < 17) { + height = 17; + width = height * aspect; + } + + image.style.width = `${width}px`; + image.style.height = `${height}px`; + this.syncControlScale(wrap, width, wrap.querySelector('.buttons-container')); + }; + + const endResize = () => { + view.dispatch({ + effects: [this.domEventCompartment!.reconfigure([])], + }); + setInlineImageDisplayDimensions(view, wrap.dataset.id || '', image.width, image.height); + setTimeout(() => { + wrap.classList.remove('resizing'); + }, 200); + }; + + resizeHandle.addEventListener('mousedown', event => { + event.preventDefault(); + initialWidth = image.getBoundingClientRect().width; + initialHeight = image.getBoundingClientRect().height; + initialX = event.pageX; + initialY = event.pageY; + shouldSnap = initialWidth !== this.idealWidth; + wrap.classList.add('resizing'); + + view.dispatch({ + effects: [this.domEventCompartment!.reconfigure([ + EditorView.domEventObservers({ + mousemove: moveEvent => { + onMouseMove(moveEvent as MouseEvent); + }, + mouseup: () => { + endResize(); + }, + mouseleave: () => { + endResize(); + }, + }), + EditorView.editorAttributes.of({class: 'resizing-image'}), + ])], + }); + }); + } + + private syncControlScale(dom: HTMLElement, width?: number, buttonsContainer?: Element | null): void { + if (!width || this.isFolded || !(buttonsContainer instanceof HTMLElement)) { + dom.style.removeProperty('--button-scale'); + return; + } + + const buttonsWidth = this.measureButtonsIntrinsicWidth(buttonsContainer); + if (!buttonsWidth) { + dom.style.removeProperty('--button-scale'); + return; + } + + const scale = Math.min(1, width / buttonsWidth); + dom.style.setProperty('--button-scale', scale.toFixed(3)); + } + + private measureButtonsIntrinsicWidth(buttonsContainer: HTMLElement): number { + const cached = Number(buttonsContainer.dataset.intrinsicWidth || '0'); + const previousWidth = buttonsContainer.style.width; + const previousMaxWidth = buttonsContainer.style.maxWidth; + + buttonsContainer.style.width = 'max-content'; + buttonsContainer.style.maxWidth = 'none'; + + const measured = buttonsContainer.scrollWidth; + + buttonsContainer.style.width = previousWidth; + buttonsContainer.style.maxWidth = previousMaxWidth; + + if (measured > 0) { + buttonsContainer.dataset.intrinsicWidth = String(measured); + return measured; + } + + return cached; + } +} diff --git a/frontend/src/views/editor/extensions/inlineImage/manager.ts b/frontend/src/views/editor/extensions/inlineImage/manager.ts new file mode 100644 index 00000000..86e895a7 --- /dev/null +++ b/frontend/src/views/editor/extensions/inlineImage/manager.ts @@ -0,0 +1,65 @@ +import type {EditorView} from '@codemirror/view'; +import {shallowReadonly, shallowRef, type ShallowRef} from 'vue'; + +interface InlineImageDrawState { + visible: boolean; + tagId: string; + assetRef: string; + imageUrl: string; + view: EditorView | null; +} + +class InlineImageDrawManager { + private state: ShallowRef = shallowRef({ + visible: false, + tagId: '', + assetRef: '', + imageUrl: '', + view: null, + }); + + useState() { + return shallowReadonly(this.state); + } + + show(view: EditorView, tagId: string, assetRef: string, imageUrl: string): void { + this.state.value = { + visible: true, + tagId, + assetRef, + imageUrl, + view, + }; + } + + hide(): void { + if (!this.state.value.visible) { + return; + } + + const view = this.state.value.view; + this.state.value = { + visible: false, + tagId: '', + assetRef: '', + imageUrl: '', + view: null, + }; + + if (view) { + view.focus(); + } + } + + destroy(): void { + this.state.value = { + visible: false, + tagId: '', + assetRef: '', + imageUrl: '', + view: null, + }; + } +} + +export const inlineImageDrawManager = new InlineImageDrawManager(); diff --git a/frontend/src/views/editor/extensions/inlineImage/types.ts b/frontend/src/views/editor/extensions/inlineImage/types.ts new file mode 100644 index 00000000..c99bbc89 --- /dev/null +++ b/frontend/src/views/editor/extensions/inlineImage/types.ts @@ -0,0 +1,18 @@ +export interface InlineImageOptions { + maxDisplayHeight: number; +} + +export interface InlineImageData { + id: string; + assetRef?: string; + file: string; + width: number; + height: number; + displayWidth?: number; + displayHeight?: number; +} + +export interface ParsedInlineImage extends InlineImageData { + from: number; + to: number; +} diff --git a/frontend/src/views/editor/extensions/translator/TranslatorDialog.vue b/frontend/src/views/editor/extensions/translator/TranslatorDialog.vue index a193cf2b..c487f2c2 100644 --- a/frontend/src/views/editor/extensions/translator/TranslatorDialog.vue +++ b/frontend/src/views/editor/extensions/translator/TranslatorDialog.vue @@ -2,6 +2,7 @@ import {computed, nextTick, onUnmounted, ref, watch} from 'vue'; import {translatorManager} from './manager'; import {useTranslationStore} from '@/stores/translationStore'; +import {useConfigStore} from '@/stores/configStore'; const props = defineProps<{ portalTarget?: HTMLElement | null; @@ -9,9 +10,10 @@ const props = defineProps<{ const state = translatorManager.useState(); const translationStore = useTranslationStore(); +const configStore = useConfigStore(); const dialogRef = ref(null); -const adjustedPosition = ref({ x: 0, y: 0 }); +const adjustedPosition = ref({x: 0, y: 0}); const isVisible = computed(() => state.value.visible); const sourceText = computed(() => state.value.sourceText); @@ -25,12 +27,12 @@ const translatedText = ref(''); const isLoading = ref(false); const isDragging = ref(false); -const dragStart = ref({ x: 0, y: 0 }); +const dragStart = ref({x: 0, y: 0}); // 监听可见性变化 watch(isVisible, async (visible) => { if (visible) { - adjustedPosition.value = { ...position.value }; + adjustedPosition.value = {...position.value}; await nextTick(); adjustDialogPosition(); await initializeTranslation(); @@ -49,7 +51,10 @@ onUnmounted(() => { const dialogStyle = computed(() => ({ left: `${adjustedPosition.value.x}px`, - top: `${adjustedPosition.value.y}px` + top: `${adjustedPosition.value.y}px`, + '--cm-translation-font-family': configStore.config.editing.fontFamily || 'var(--voidraft-font-mono, system-ui, -apple-system, sans-serif)', + '--cm-translation-font-weight': configStore.config.editing.fontWeight || '400', + '--cm-translation-line-height': String(configStore.config.editing.lineHeight || 1.5), })); const availableLanguages = computed(() => { @@ -70,7 +75,7 @@ function adjustDialogPosition() { const containerRect = container.getBoundingClientRect(); const dialogRect = dialogEl.getBoundingClientRect(); - + let x = adjustedPosition.value.x; let y = adjustedPosition.value.y; @@ -78,13 +83,13 @@ function adjustDialogPosition() { x = Math.max(containerRect.left, Math.min(x, containerRect.right - dialogRect.width - 8)); y = Math.max(containerRect.top, Math.min(y, containerRect.bottom - dialogRect.height - 8)); - adjustedPosition.value = { x, y }; + adjustedPosition.value = {x, y}; } function clampPosition(x: number, y: number) { const container = props.portalTarget; const dialogEl = dialogRef.value; - if (!container || !dialogEl) return { x, y }; + if (!container || !dialogEl) return {x, y}; const containerRect = container.getBoundingClientRect(); const dialogRect = dialogEl.getBoundingClientRect(); @@ -153,10 +158,10 @@ async function translate() { try { const result = await translationStore.translateText( - sourceText.value, - sourceLang, - targetLang, - translatorType + sourceText.value, + sourceLang, + targetLang, + translatorType ); translatedText.value = result.translatedText || result.error || ''; @@ -171,16 +176,16 @@ async function translate() { function startDrag(e: MouseEvent) { const target = e.target as HTMLElement; if (target.closest('select, button')) return; - + e.preventDefault(); e.stopPropagation(); - + const rect = dialogRef.value!.getBoundingClientRect(); dragStart.value = { x: e.clientX - rect.left, y: e.clientY - rect.top }; - + isDragging.value = true; document.addEventListener('mousemove', onDrag); document.addEventListener('mouseup', endDrag); @@ -219,50 +224,51 @@ function handleClickOutside(e: MouseEvent) {