Compare commits
15 Commits
markdown
...
1ab934cee9
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ab934cee9 | |||
| 6659ac6fad | |||
| 3a5ab1c614 | |||
| 1e07e1f833 | |||
| e1e91a3683 | |||
| c30d95a3e0 | |||
| 97f6fa843c | |||
| f43fc47539 | |||
|
|
c330de52fa | ||
| 67d35626cb | |||
| cc4c2189dc | |||
| d16905c0a3 | |||
| 4e611db349 | |||
| 7e9fc0ac3f | |||
| ff072d1a93 |
3
.github/workflows/build-release.yml
vendored
3
.github/workflows/build-release.yml
vendored
@@ -28,6 +28,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
# 准备构建配置
|
# 准备构建配置
|
||||||
prepare:
|
prepare:
|
||||||
|
permissions: {}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
@@ -84,6 +85,8 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
needs: prepare
|
needs: prepare
|
||||||
if: ${{ fromJson(needs.prepare.outputs.matrix).include[0] != null }}
|
if: ${{ fromJson(needs.prepare.outputs.matrix).include[0] != null }}
|
||||||
strategy:
|
strategy:
|
||||||
|
|||||||
109
.github/workflows/codeql.yml
vendored
Normal file
109
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL Advanced"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master" ]
|
||||||
|
schedule:
|
||||||
|
- cron: '29 8 * * 3'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze (${{ matrix.language }})
|
||||||
|
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||||
|
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||||
|
# - https://gh.io/supported-runners-and-hardware-resources
|
||||||
|
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||||
|
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||||
|
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||||
|
permissions:
|
||||||
|
# required for all workflows
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
# required to fetch internal or private CodeQL packs
|
||||||
|
packages: read
|
||||||
|
|
||||||
|
# only required for workflows in private repositories
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- language: actions
|
||||||
|
build-mode: none
|
||||||
|
- language: c-cpp
|
||||||
|
build-mode: none
|
||||||
|
- language: go
|
||||||
|
build-mode: autobuild
|
||||||
|
- language: javascript-typescript
|
||||||
|
build-mode: none
|
||||||
|
- language: python
|
||||||
|
build-mode: none
|
||||||
|
- language: rust
|
||||||
|
build-mode: none
|
||||||
|
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
|
||||||
|
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||||
|
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||||
|
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||||
|
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||||
|
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||||
|
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||||
|
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# Add any setup steps before running the `github/codeql-action/init` action.
|
||||||
|
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
||||||
|
# or others). This is typically only required for manual builds.
|
||||||
|
# - name: Setup runtime (example)
|
||||||
|
# uses: actions/setup-example@v1
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v4
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
build-mode: ${{ matrix.build-mode }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
|
||||||
|
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||||
|
# queries: security-extended,security-and-quality
|
||||||
|
|
||||||
|
# If the analyze step fails for one of the languages you are analyzing with
|
||||||
|
# "We were unable to automatically build your code", modify the matrix above
|
||||||
|
# to set the build mode to "manual" for that language. Then modify this step
|
||||||
|
# to build your code.
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
|
- name: Run manual build steps
|
||||||
|
if: matrix.build-mode == 'manual'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||||
|
'languages you are analyzing, replace this with the commands to build' \
|
||||||
|
'your code, for example:'
|
||||||
|
echo ' make bootstrap'
|
||||||
|
echo ' make release'
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v4
|
||||||
|
with:
|
||||||
|
category: "/language:${{matrix.language}}"
|
||||||
4
frontend/bindings/voidraft/internal/models/ent/index.ts
Normal file
4
frontend/bindings/voidraft/internal/models/ent/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export * from "./models.js";
|
||||||
334
frontend/bindings/voidraft/internal/models/ent/models.ts
Normal file
334
frontend/bindings/voidraft/internal/models/ent/models.ts
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import {Create as $Create} from "@wailsio/runtime";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import * as theme$0 from "./theme/models.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document is the model entity for the Document schema.
|
||||||
|
*/
|
||||||
|
export class Document {
|
||||||
|
/**
|
||||||
|
* ID of the ent.
|
||||||
|
*/
|
||||||
|
"id"?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UUID for cross-device sync (UUIDv7)
|
||||||
|
*/
|
||||||
|
"uuid": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creation time
|
||||||
|
*/
|
||||||
|
"created_at": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update time
|
||||||
|
*/
|
||||||
|
"updated_at": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deleted at
|
||||||
|
*/
|
||||||
|
"deleted_at"?: string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* document title
|
||||||
|
*/
|
||||||
|
"title": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* document content
|
||||||
|
*/
|
||||||
|
"content": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* document locked status
|
||||||
|
*/
|
||||||
|
"locked": boolean;
|
||||||
|
|
||||||
|
/** Creates a new Document instance. */
|
||||||
|
constructor($$source: Partial<Document> = {}) {
|
||||||
|
if (!("uuid" in $$source)) {
|
||||||
|
this["uuid"] = "";
|
||||||
|
}
|
||||||
|
if (!("created_at" in $$source)) {
|
||||||
|
this["created_at"] = "";
|
||||||
|
}
|
||||||
|
if (!("updated_at" in $$source)) {
|
||||||
|
this["updated_at"] = "";
|
||||||
|
}
|
||||||
|
if (!("title" in $$source)) {
|
||||||
|
this["title"] = "";
|
||||||
|
}
|
||||||
|
if (!("content" in $$source)) {
|
||||||
|
this["content"] = "";
|
||||||
|
}
|
||||||
|
if (!("locked" in $$source)) {
|
||||||
|
this["locked"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Document instance from a string or object.
|
||||||
|
*/
|
||||||
|
static createFrom($$source: any = {}): Document {
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
return new Document($$parsedSource as Partial<Document>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension is the model entity for the Extension schema.
|
||||||
|
*/
|
||||||
|
export class Extension {
|
||||||
|
/**
|
||||||
|
* ID of the ent.
|
||||||
|
*/
|
||||||
|
"id"?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UUID for cross-device sync (UUIDv7)
|
||||||
|
*/
|
||||||
|
"uuid": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creation time
|
||||||
|
*/
|
||||||
|
"created_at": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update time
|
||||||
|
*/
|
||||||
|
"updated_at": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deleted at
|
||||||
|
*/
|
||||||
|
"deleted_at"?: string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extension key
|
||||||
|
*/
|
||||||
|
"key": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extension enabled or not
|
||||||
|
*/
|
||||||
|
"enabled": boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extension config
|
||||||
|
*/
|
||||||
|
"config": { [_: string]: any };
|
||||||
|
|
||||||
|
/** Creates a new Extension instance. */
|
||||||
|
constructor($$source: Partial<Extension> = {}) {
|
||||||
|
if (!("uuid" in $$source)) {
|
||||||
|
this["uuid"] = "";
|
||||||
|
}
|
||||||
|
if (!("created_at" in $$source)) {
|
||||||
|
this["created_at"] = "";
|
||||||
|
}
|
||||||
|
if (!("updated_at" in $$source)) {
|
||||||
|
this["updated_at"] = "";
|
||||||
|
}
|
||||||
|
if (!("key" in $$source)) {
|
||||||
|
this["key"] = "";
|
||||||
|
}
|
||||||
|
if (!("enabled" in $$source)) {
|
||||||
|
this["enabled"] = false;
|
||||||
|
}
|
||||||
|
if (!("config" in $$source)) {
|
||||||
|
this["config"] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Extension instance from a string or object.
|
||||||
|
*/
|
||||||
|
static createFrom($$source: any = {}): Extension {
|
||||||
|
const $$createField7_0 = $$createType0;
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
if ("config" in $$parsedSource) {
|
||||||
|
$$parsedSource["config"] = $$createField7_0($$parsedSource["config"]);
|
||||||
|
}
|
||||||
|
return new Extension($$parsedSource as Partial<Extension>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KeyBinding is the model entity for the KeyBinding schema.
|
||||||
|
*/
|
||||||
|
export class KeyBinding {
|
||||||
|
/**
|
||||||
|
* ID of the ent.
|
||||||
|
*/
|
||||||
|
"id"?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UUID for cross-device sync (UUIDv7)
|
||||||
|
*/
|
||||||
|
"uuid": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creation time
|
||||||
|
*/
|
||||||
|
"created_at": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update time
|
||||||
|
*/
|
||||||
|
"updated_at": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deleted at
|
||||||
|
*/
|
||||||
|
"deleted_at"?: string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key binding key
|
||||||
|
*/
|
||||||
|
"key": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key binding command
|
||||||
|
*/
|
||||||
|
"command": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key binding extension
|
||||||
|
*/
|
||||||
|
"extension"?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key binding enabled
|
||||||
|
*/
|
||||||
|
"enabled": boolean;
|
||||||
|
|
||||||
|
/** Creates a new KeyBinding instance. */
|
||||||
|
constructor($$source: Partial<KeyBinding> = {}) {
|
||||||
|
if (!("uuid" in $$source)) {
|
||||||
|
this["uuid"] = "";
|
||||||
|
}
|
||||||
|
if (!("created_at" in $$source)) {
|
||||||
|
this["created_at"] = "";
|
||||||
|
}
|
||||||
|
if (!("updated_at" in $$source)) {
|
||||||
|
this["updated_at"] = "";
|
||||||
|
}
|
||||||
|
if (!("key" in $$source)) {
|
||||||
|
this["key"] = "";
|
||||||
|
}
|
||||||
|
if (!("command" in $$source)) {
|
||||||
|
this["command"] = "";
|
||||||
|
}
|
||||||
|
if (!("enabled" in $$source)) {
|
||||||
|
this["enabled"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new KeyBinding instance from a string or object.
|
||||||
|
*/
|
||||||
|
static createFrom($$source: any = {}): KeyBinding {
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
return new KeyBinding($$parsedSource as Partial<KeyBinding>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Theme is the model entity for the Theme schema.
|
||||||
|
*/
|
||||||
|
export class Theme {
|
||||||
|
/**
|
||||||
|
* ID of the ent.
|
||||||
|
*/
|
||||||
|
"id"?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UUID for cross-device sync (UUIDv7)
|
||||||
|
*/
|
||||||
|
"uuid": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creation time
|
||||||
|
*/
|
||||||
|
"created_at": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update time
|
||||||
|
*/
|
||||||
|
"updated_at": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deleted at
|
||||||
|
*/
|
||||||
|
"deleted_at"?: string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* theme key
|
||||||
|
*/
|
||||||
|
"key": string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* theme type
|
||||||
|
*/
|
||||||
|
"type": theme$0.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* theme colors
|
||||||
|
*/
|
||||||
|
"colors": { [_: string]: any };
|
||||||
|
|
||||||
|
/** Creates a new Theme instance. */
|
||||||
|
constructor($$source: Partial<Theme> = {}) {
|
||||||
|
if (!("uuid" in $$source)) {
|
||||||
|
this["uuid"] = "";
|
||||||
|
}
|
||||||
|
if (!("created_at" in $$source)) {
|
||||||
|
this["created_at"] = "";
|
||||||
|
}
|
||||||
|
if (!("updated_at" in $$source)) {
|
||||||
|
this["updated_at"] = "";
|
||||||
|
}
|
||||||
|
if (!("key" in $$source)) {
|
||||||
|
this["key"] = "";
|
||||||
|
}
|
||||||
|
if (!("type" in $$source)) {
|
||||||
|
this["type"] = ("" as theme$0.Type);
|
||||||
|
}
|
||||||
|
if (!("colors" in $$source)) {
|
||||||
|
this["colors"] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this, $$source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Theme instance from a string or object.
|
||||||
|
*/
|
||||||
|
static createFrom($$source: any = {}): Theme {
|
||||||
|
const $$createField7_0 = $$createType0;
|
||||||
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
|
if ("colors" in $$parsedSource) {
|
||||||
|
$$parsedSource["colors"] = $$createField7_0($$parsedSource["colors"]);
|
||||||
|
}
|
||||||
|
return new Theme($$parsedSource as Partial<Theme>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private type creation functions
|
||||||
|
const $$createType0 = $Create.Map($Create.Any, $Create.Any);
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export * from "./models.js";
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import {Create as $Create} from "@wailsio/runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type defines the type for the "type" enum field.
|
||||||
|
*/
|
||||||
|
export enum Type {
|
||||||
|
/**
|
||||||
|
* The Go zero value for the underlying type of the enum.
|
||||||
|
*/
|
||||||
|
$zero = "",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type values.
|
||||||
|
*/
|
||||||
|
TypeDark = "dark",
|
||||||
|
TypeLight = "light",
|
||||||
|
};
|
||||||
@@ -193,58 +193,6 @@ export class ConfigMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Document represents a document in the system
|
|
||||||
*/
|
|
||||||
export class Document {
|
|
||||||
"id": number;
|
|
||||||
"title": string;
|
|
||||||
"content": string;
|
|
||||||
"createdAt": string;
|
|
||||||
"updatedAt": string;
|
|
||||||
"is_deleted": boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 锁定标志,锁定的文档无法被删除
|
|
||||||
*/
|
|
||||||
"is_locked": boolean;
|
|
||||||
|
|
||||||
/** Creates a new Document instance. */
|
|
||||||
constructor($$source: Partial<Document> = {}) {
|
|
||||||
if (!("id" in $$source)) {
|
|
||||||
this["id"] = 0;
|
|
||||||
}
|
|
||||||
if (!("title" in $$source)) {
|
|
||||||
this["title"] = "";
|
|
||||||
}
|
|
||||||
if (!("content" in $$source)) {
|
|
||||||
this["content"] = "";
|
|
||||||
}
|
|
||||||
if (!("createdAt" in $$source)) {
|
|
||||||
this["createdAt"] = "";
|
|
||||||
}
|
|
||||||
if (!("updatedAt" in $$source)) {
|
|
||||||
this["updatedAt"] = "";
|
|
||||||
}
|
|
||||||
if (!("is_deleted" in $$source)) {
|
|
||||||
this["is_deleted"] = false;
|
|
||||||
}
|
|
||||||
if (!("is_locked" in $$source)) {
|
|
||||||
this["is_locked"] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(this, $$source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new Document instance from a string or object.
|
|
||||||
*/
|
|
||||||
static createFrom($$source: any = {}): Document {
|
|
||||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
|
||||||
return new Document($$parsedSource as Partial<Document>);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EditingConfig 编辑设置配置
|
* EditingConfig 编辑设置配置
|
||||||
*/
|
*/
|
||||||
@@ -332,40 +280,21 @@ export class EditingConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension 单个扩展配置
|
* Extension 扩展配置
|
||||||
*/
|
*/
|
||||||
export class Extension {
|
export class Extension {
|
||||||
/**
|
"key": ExtensionKey;
|
||||||
* 扩展唯一标识
|
|
||||||
*/
|
|
||||||
"id": ExtensionID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否启用
|
|
||||||
*/
|
|
||||||
"enabled": boolean;
|
"enabled": boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否为默认扩展
|
|
||||||
*/
|
|
||||||
"isDefault": boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 扩展配置项
|
|
||||||
*/
|
|
||||||
"config": ExtensionConfig;
|
"config": ExtensionConfig;
|
||||||
|
|
||||||
/** Creates a new Extension instance. */
|
/** Creates a new Extension instance. */
|
||||||
constructor($$source: Partial<Extension> = {}) {
|
constructor($$source: Partial<Extension> = {}) {
|
||||||
if (!("id" in $$source)) {
|
if (!("key" in $$source)) {
|
||||||
this["id"] = ("" as ExtensionID);
|
this["key"] = ("" as ExtensionKey);
|
||||||
}
|
}
|
||||||
if (!("enabled" in $$source)) {
|
if (!("enabled" in $$source)) {
|
||||||
this["enabled"] = false;
|
this["enabled"] = false;
|
||||||
}
|
}
|
||||||
if (!("isDefault" in $$source)) {
|
|
||||||
this["isDefault"] = false;
|
|
||||||
}
|
|
||||||
if (!("config" in $$source)) {
|
if (!("config" in $$source)) {
|
||||||
this["config"] = ({} as ExtensionConfig);
|
this["config"] = ({} as ExtensionConfig);
|
||||||
}
|
}
|
||||||
@@ -377,10 +306,10 @@ export class Extension {
|
|||||||
* Creates a new Extension instance from a string or object.
|
* Creates a new Extension instance from a string or object.
|
||||||
*/
|
*/
|
||||||
static createFrom($$source: any = {}): Extension {
|
static createFrom($$source: any = {}): Extension {
|
||||||
const $$createField3_0 = $$createType6;
|
const $$createField2_0 = $$createType6;
|
||||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
if ("config" in $$parsedSource) {
|
if ("config" in $$parsedSource) {
|
||||||
$$parsedSource["config"] = $$createField3_0($$parsedSource["config"]);
|
$$parsedSource["config"] = $$createField2_0($$parsedSource["config"]);
|
||||||
}
|
}
|
||||||
return new Extension($$parsedSource as Partial<Extension>);
|
return new Extension($$parsedSource as Partial<Extension>);
|
||||||
}
|
}
|
||||||
@@ -392,9 +321,9 @@ export class Extension {
|
|||||||
export type ExtensionConfig = { [_: string]: any };
|
export type ExtensionConfig = { [_: string]: any };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ExtensionID 扩展标识符
|
* ExtensionKey 扩展标识符
|
||||||
*/
|
*/
|
||||||
export enum ExtensionID {
|
export enum ExtensionKey {
|
||||||
/**
|
/**
|
||||||
* The Go zero value for the underlying type of the enum.
|
* The Go zero value for the underlying type of the enum.
|
||||||
*/
|
*/
|
||||||
@@ -442,7 +371,6 @@ export enum ExtensionID {
|
|||||||
ExtensionHighlightTrailingWhitespace = "highlightTrailingWhitespace",
|
ExtensionHighlightTrailingWhitespace = "highlightTrailingWhitespace",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UI增强扩展
|
|
||||||
* 小地图
|
* 小地图
|
||||||
*/
|
*/
|
||||||
ExtensionMinimap = "minimap",
|
ExtensionMinimap = "minimap",
|
||||||
@@ -458,16 +386,14 @@ export enum ExtensionID {
|
|||||||
ExtensionContextMenu = "contextMenu",
|
ExtensionContextMenu = "contextMenu",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工具扩展
|
|
||||||
* 搜索功能
|
* 搜索功能
|
||||||
*/
|
*/
|
||||||
ExtensionSearch = "search",
|
ExtensionSearch = "search",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 核心扩展
|
* HTTP 客户端
|
||||||
* 编辑器核心功能
|
|
||||||
*/
|
*/
|
||||||
ExtensionEditor = "editor",
|
ExtensionHttpClient = "httpClient",
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -758,48 +684,25 @@ export class HotkeyCombo {
|
|||||||
* KeyBinding 单个快捷键绑定
|
* KeyBinding 单个快捷键绑定
|
||||||
*/
|
*/
|
||||||
export class KeyBinding {
|
export class KeyBinding {
|
||||||
/**
|
"key": KeyBindingKey;
|
||||||
* 快捷键动作
|
"command": string;
|
||||||
*/
|
"extension": ExtensionKey;
|
||||||
"command": KeyBindingCommand;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 所属扩展
|
|
||||||
*/
|
|
||||||
"extension": ExtensionID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 快捷键组合(如 "Mod-f", "Ctrl-Shift-p")
|
|
||||||
*/
|
|
||||||
"key": string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否启用
|
|
||||||
*/
|
|
||||||
"enabled": boolean;
|
"enabled": boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否为默认快捷键
|
|
||||||
*/
|
|
||||||
"isDefault": boolean;
|
|
||||||
|
|
||||||
/** Creates a new KeyBinding instance. */
|
/** Creates a new KeyBinding instance. */
|
||||||
constructor($$source: Partial<KeyBinding> = {}) {
|
constructor($$source: Partial<KeyBinding> = {}) {
|
||||||
|
if (!("key" in $$source)) {
|
||||||
|
this["key"] = ("" as KeyBindingKey);
|
||||||
|
}
|
||||||
if (!("command" in $$source)) {
|
if (!("command" in $$source)) {
|
||||||
this["command"] = ("" as KeyBindingCommand);
|
this["command"] = "";
|
||||||
}
|
}
|
||||||
if (!("extension" in $$source)) {
|
if (!("extension" in $$source)) {
|
||||||
this["extension"] = ("" as ExtensionID);
|
this["extension"] = ("" as ExtensionKey);
|
||||||
}
|
|
||||||
if (!("key" in $$source)) {
|
|
||||||
this["key"] = "";
|
|
||||||
}
|
}
|
||||||
if (!("enabled" in $$source)) {
|
if (!("enabled" in $$source)) {
|
||||||
this["enabled"] = false;
|
this["enabled"] = false;
|
||||||
}
|
}
|
||||||
if (!("isDefault" in $$source)) {
|
|
||||||
this["isDefault"] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(this, $$source);
|
Object.assign(this, $$source);
|
||||||
}
|
}
|
||||||
@@ -814,263 +717,258 @@ export class KeyBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KeyBindingCommand 快捷键命令
|
* KeyBindingKey 快捷键命令
|
||||||
*/
|
*/
|
||||||
export enum KeyBindingCommand {
|
export enum KeyBindingKey {
|
||||||
/**
|
/**
|
||||||
* The Go zero value for the underlying type of the enum.
|
* The Go zero value for the underlying type of the enum.
|
||||||
*/
|
*/
|
||||||
$zero = "",
|
$zero = "",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 搜索扩展相关
|
|
||||||
* 显示搜索
|
* 显示搜索
|
||||||
*/
|
*/
|
||||||
ShowSearchCommand = "showSearch",
|
ShowSearchKeyBindingKey = "showSearch",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 隐藏搜索
|
* 隐藏搜索
|
||||||
*/
|
*/
|
||||||
HideSearchCommand = "hideSearch",
|
HideSearchKeyBindingKey = "hideSearch",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 代码块扩展相关
|
|
||||||
* 块内选择全部
|
* 块内选择全部
|
||||||
*/
|
*/
|
||||||
BlockSelectAllCommand = "blockSelectAll",
|
BlockSelectAllKeyBindingKey = "blockSelectAll",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在当前块后添加新块
|
* 在当前块后添加新块
|
||||||
*/
|
*/
|
||||||
BlockAddAfterCurrentCommand = "blockAddAfterCurrent",
|
BlockAddAfterCurrentKeyBindingKey = "blockAddAfterCurrent",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在最后添加新块
|
* 在最后添加新块
|
||||||
*/
|
*/
|
||||||
BlockAddAfterLastCommand = "blockAddAfterLast",
|
BlockAddAfterLastKeyBindingKey = "blockAddAfterLast",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在当前块前添加新块
|
* 在当前块前添加新块
|
||||||
*/
|
*/
|
||||||
BlockAddBeforeCurrentCommand = "blockAddBeforeCurrent",
|
BlockAddBeforeCurrentKeyBindingKey = "blockAddBeforeCurrent",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转到上一个块
|
* 跳转到上一个块
|
||||||
*/
|
*/
|
||||||
BlockGotoPreviousCommand = "blockGotoPrevious",
|
BlockGotoPreviousKeyBindingKey = "blockGotoPrevious",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转到下一个块
|
* 跳转到下一个块
|
||||||
*/
|
*/
|
||||||
BlockGotoNextCommand = "blockGotoNext",
|
BlockGotoNextKeyBindingKey = "blockGotoNext",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选择上一个块
|
* 选择上一个块
|
||||||
*/
|
*/
|
||||||
BlockSelectPreviousCommand = "blockSelectPrevious",
|
BlockSelectPreviousKeyBindingKey = "blockSelectPrevious",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选择下一个块
|
* 选择下一个块
|
||||||
*/
|
*/
|
||||||
BlockSelectNextCommand = "blockSelectNext",
|
BlockSelectNextKeyBindingKey = "blockSelectNext",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除当前块
|
* 删除当前块
|
||||||
*/
|
*/
|
||||||
BlockDeleteCommand = "blockDelete",
|
BlockDeleteKeyBindingKey = "blockDelete",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向上移动当前块
|
* 向上移动当前块
|
||||||
*/
|
*/
|
||||||
BlockMoveUpCommand = "blockMoveUp",
|
BlockMoveUpKeyBindingKey = "blockMoveUp",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向下移动当前块
|
* 向下移动当前块
|
||||||
*/
|
*/
|
||||||
BlockMoveDownCommand = "blockMoveDown",
|
BlockMoveDownKeyBindingKey = "blockMoveDown",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除行
|
* 删除行
|
||||||
*/
|
*/
|
||||||
BlockDeleteLineCommand = "blockDeleteLine",
|
BlockDeleteLineKeyBindingKey = "blockDeleteLine",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向上移动行
|
* 向上移动行
|
||||||
*/
|
*/
|
||||||
BlockMoveLineUpCommand = "blockMoveLineUp",
|
BlockMoveLineUpKeyBindingKey = "blockMoveLineUp",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向下移动行
|
* 向下移动行
|
||||||
*/
|
*/
|
||||||
BlockMoveLineDownCommand = "blockMoveLineDown",
|
BlockMoveLineDownKeyBindingKey = "blockMoveLineDown",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字符转置
|
* 字符转置
|
||||||
*/
|
*/
|
||||||
BlockTransposeCharsCommand = "blockTransposeChars",
|
BlockTransposeCharsKeyBindingKey = "blockTransposeChars",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化代码块
|
* 格式化代码块
|
||||||
*/
|
*/
|
||||||
BlockFormatCommand = "blockFormat",
|
BlockFormatKeyBindingKey = "blockFormat",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 复制
|
* 复制
|
||||||
*/
|
*/
|
||||||
BlockCopyCommand = "blockCopy",
|
BlockCopyKeyBindingKey = "blockCopy",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 剪切
|
* 剪切
|
||||||
*/
|
*/
|
||||||
BlockCutCommand = "blockCut",
|
BlockCutKeyBindingKey = "blockCut",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 粘贴
|
* 粘贴
|
||||||
*/
|
*/
|
||||||
BlockPasteCommand = "blockPaste",
|
BlockPasteKeyBindingKey = "blockPaste",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 代码折叠扩展相关
|
|
||||||
* 折叠代码
|
* 折叠代码
|
||||||
*/
|
*/
|
||||||
FoldCodeCommand = "foldCode",
|
FoldCodeKeyBindingKey = "foldCode",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 展开代码
|
* 展开代码
|
||||||
*/
|
*/
|
||||||
UnfoldCodeCommand = "unfoldCode",
|
UnfoldCodeKeyBindingKey = "unfoldCode",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 折叠全部
|
* 折叠全部
|
||||||
*/
|
*/
|
||||||
FoldAllCommand = "foldAll",
|
FoldAllKeyBindingKey = "foldAll",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 展开全部
|
* 展开全部
|
||||||
*/
|
*/
|
||||||
UnfoldAllCommand = "unfoldAll",
|
UnfoldAllKeyBindingKey = "unfoldAll",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用编辑扩展相关
|
|
||||||
* 光标按语法左移
|
* 光标按语法左移
|
||||||
*/
|
*/
|
||||||
CursorSyntaxLeftCommand = "cursorSyntaxLeft",
|
CursorSyntaxLeftKeyBindingKey = "cursorSyntaxLeft",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 光标按语法右移
|
* 光标按语法右移
|
||||||
*/
|
*/
|
||||||
CursorSyntaxRightCommand = "cursorSyntaxRight",
|
CursorSyntaxRightKeyBindingKey = "cursorSyntaxRight",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按语法选择左侧
|
* 按语法选择左侧
|
||||||
*/
|
*/
|
||||||
SelectSyntaxLeftCommand = "selectSyntaxLeft",
|
SelectSyntaxLeftKeyBindingKey = "selectSyntaxLeft",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按语法选择右侧
|
* 按语法选择右侧
|
||||||
*/
|
*/
|
||||||
SelectSyntaxRightCommand = "selectSyntaxRight",
|
SelectSyntaxRightKeyBindingKey = "selectSyntaxRight",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向上复制行
|
* 向上复制行
|
||||||
*/
|
*/
|
||||||
CopyLineUpCommand = "copyLineUp",
|
CopyLineUpKeyBindingKey = "copyLineUp",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向下复制行
|
* 向下复制行
|
||||||
*/
|
*/
|
||||||
CopyLineDownCommand = "copyLineDown",
|
CopyLineDownKeyBindingKey = "copyLineDown",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插入空行
|
* 插入空行
|
||||||
*/
|
*/
|
||||||
InsertBlankLineCommand = "insertBlankLine",
|
InsertBlankLineKeyBindingKey = "insertBlankLine",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选择行
|
* 选择行
|
||||||
*/
|
*/
|
||||||
SelectLineCommand = "selectLine",
|
SelectLineKeyBindingKey = "selectLine",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选择父级语法
|
* 选择父级语法
|
||||||
*/
|
*/
|
||||||
SelectParentSyntaxCommand = "selectParentSyntax",
|
SelectParentSyntaxKeyBindingKey = "selectParentSyntax",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 减少缩进
|
* 减少缩进
|
||||||
*/
|
*/
|
||||||
IndentLessCommand = "indentLess",
|
IndentLessKeyBindingKey = "indentLess",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 增加缩进
|
* 增加缩进
|
||||||
*/
|
*/
|
||||||
IndentMoreCommand = "indentMore",
|
IndentMoreKeyBindingKey = "indentMore",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缩进选择
|
* 缩进选择
|
||||||
*/
|
*/
|
||||||
IndentSelectionCommand = "indentSelection",
|
IndentSelectionKeyBindingKey = "indentSelection",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 光标到匹配括号
|
* 光标到匹配括号
|
||||||
*/
|
*/
|
||||||
CursorMatchingBracketCommand = "cursorMatchingBracket",
|
CursorMatchingBracketKeyBindingKey = "cursorMatchingBracket",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 切换注释
|
* 切换注释
|
||||||
*/
|
*/
|
||||||
ToggleCommentCommand = "toggleComment",
|
ToggleCommentKeyBindingKey = "toggleComment",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 切换块注释
|
* 切换块注释
|
||||||
*/
|
*/
|
||||||
ToggleBlockCommentCommand = "toggleBlockComment",
|
ToggleBlockCommentKeyBindingKey = "toggleBlockComment",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插入新行并缩进
|
* 插入新行并缩进
|
||||||
*/
|
*/
|
||||||
InsertNewlineAndIndentCommand = "insertNewlineAndIndent",
|
InsertNewlineAndIndentKeyBindingKey = "insertNewlineAndIndent",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向后删除字符
|
* 向后删除字符
|
||||||
*/
|
*/
|
||||||
DeleteCharBackwardCommand = "deleteCharBackward",
|
DeleteCharBackwardKeyBindingKey = "deleteCharBackward",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向前删除字符
|
* 向前删除字符
|
||||||
*/
|
*/
|
||||||
DeleteCharForwardCommand = "deleteCharForward",
|
DeleteCharForwardKeyBindingKey = "deleteCharForward",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向后删除组
|
* 向后删除组
|
||||||
*/
|
*/
|
||||||
DeleteGroupBackwardCommand = "deleteGroupBackward",
|
DeleteGroupBackwardKeyBindingKey = "deleteGroupBackward",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向前删除组
|
* 向前删除组
|
||||||
*/
|
*/
|
||||||
DeleteGroupForwardCommand = "deleteGroupForward",
|
DeleteGroupForwardKeyBindingKey = "deleteGroupForward",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 历史记录扩展相关
|
|
||||||
* 撤销
|
* 撤销
|
||||||
*/
|
*/
|
||||||
HistoryUndoCommand = "historyUndo",
|
HistoryUndoKeyBindingKey = "historyUndo",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重做
|
* 重做
|
||||||
*/
|
*/
|
||||||
HistoryRedoCommand = "historyRedo",
|
HistoryRedoKeyBindingKey = "historyRedo",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 撤销选择
|
* 撤销选择
|
||||||
*/
|
*/
|
||||||
HistoryUndoSelectionCommand = "historyUndoSelection",
|
HistoryUndoSelectionKeyBindingKey = "historyUndoSelection",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重做选择
|
* 重做选择
|
||||||
*/
|
*/
|
||||||
HistoryRedoSelectionCommand = "historyRedoSelection",
|
HistoryRedoSelectionKeyBindingKey = "historyRedoSelection",
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1138,76 +1036,6 @@ export enum TabType {
|
|||||||
TabTypeTab = "tab",
|
TabTypeTab = "tab",
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Theme 主题数据库模型
|
|
||||||
*/
|
|
||||||
export class Theme {
|
|
||||||
"id": number;
|
|
||||||
"name": string;
|
|
||||||
"type": ThemeType;
|
|
||||||
"colors": ThemeColorConfig;
|
|
||||||
"isDefault": boolean;
|
|
||||||
"createdAt": string;
|
|
||||||
"updatedAt": string;
|
|
||||||
|
|
||||||
/** Creates a new Theme instance. */
|
|
||||||
constructor($$source: Partial<Theme> = {}) {
|
|
||||||
if (!("id" in $$source)) {
|
|
||||||
this["id"] = 0;
|
|
||||||
}
|
|
||||||
if (!("name" in $$source)) {
|
|
||||||
this["name"] = "";
|
|
||||||
}
|
|
||||||
if (!("type" in $$source)) {
|
|
||||||
this["type"] = ("" as ThemeType);
|
|
||||||
}
|
|
||||||
if (!("colors" in $$source)) {
|
|
||||||
this["colors"] = ({} as ThemeColorConfig);
|
|
||||||
}
|
|
||||||
if (!("isDefault" in $$source)) {
|
|
||||||
this["isDefault"] = false;
|
|
||||||
}
|
|
||||||
if (!("createdAt" in $$source)) {
|
|
||||||
this["createdAt"] = "";
|
|
||||||
}
|
|
||||||
if (!("updatedAt" in $$source)) {
|
|
||||||
this["updatedAt"] = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(this, $$source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new Theme instance from a string or object.
|
|
||||||
*/
|
|
||||||
static createFrom($$source: any = {}): Theme {
|
|
||||||
const $$createField3_0 = $$createType9;
|
|
||||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
|
||||||
if ("colors" in $$parsedSource) {
|
|
||||||
$$parsedSource["colors"] = $$createField3_0($$parsedSource["colors"]);
|
|
||||||
}
|
|
||||||
return new Theme($$parsedSource as Partial<Theme>);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ThemeColorConfig 使用与前端 ThemeColors 相同的结构,存储任意主题键值
|
|
||||||
*/
|
|
||||||
export type ThemeColorConfig = { [_: string]: any };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ThemeType 主题类型枚举
|
|
||||||
*/
|
|
||||||
export enum ThemeType {
|
|
||||||
/**
|
|
||||||
* The Go zero value for the underlying type of the enum.
|
|
||||||
*/
|
|
||||||
$zero = "",
|
|
||||||
|
|
||||||
ThemeTypeDark = "dark",
|
|
||||||
ThemeTypeLight = "light",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateSourceType 更新源类型
|
* UpdateSourceType 更新源类型
|
||||||
*/
|
*/
|
||||||
@@ -1306,8 +1134,8 @@ export class UpdatesConfig {
|
|||||||
* Creates a new UpdatesConfig instance from a string or object.
|
* Creates a new UpdatesConfig instance from a string or object.
|
||||||
*/
|
*/
|
||||||
static createFrom($$source: any = {}): UpdatesConfig {
|
static createFrom($$source: any = {}): UpdatesConfig {
|
||||||
const $$createField6_0 = $$createType10;
|
const $$createField6_0 = $$createType9;
|
||||||
const $$createField7_0 = $$createType11;
|
const $$createField7_0 = $$createType10;
|
||||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||||
if ("github" in $$parsedSource) {
|
if ("github" in $$parsedSource) {
|
||||||
$$parsedSource["github"] = $$createField6_0($$parsedSource["github"]);
|
$$parsedSource["github"] = $$createField6_0($$parsedSource["github"]);
|
||||||
@@ -1334,11 +1162,5 @@ var $$createType6 = (function $$initCreateType6(...args): any {
|
|||||||
});
|
});
|
||||||
const $$createType7 = $Create.Map($Create.Any, $Create.Any);
|
const $$createType7 = $Create.Map($Create.Any, $Create.Any);
|
||||||
const $$createType8 = HotkeyCombo.createFrom;
|
const $$createType8 = HotkeyCombo.createFrom;
|
||||||
var $$createType9 = (function $$initCreateType9(...args): any {
|
const $$createType9 = GithubConfig.createFrom;
|
||||||
if ($$createType9 === $$initCreateType9) {
|
const $$createType10 = GiteaConfig.createFrom;
|
||||||
$$createType9 = $$createType7;
|
|
||||||
}
|
|
||||||
return $$createType9(...args);
|
|
||||||
});
|
|
||||||
const $$createType10 = GithubConfig.createFrom;
|
|
||||||
const $$createType11 = GiteaConfig.createFrom;
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BackupService 提供基于Git的备份功能
|
* BackupService 提供基于Git的备份同步功能
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/applic
|
|||||||
import * as models$0 from "../models/models.js";
|
import * as models$0 from "../models/models.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HandleConfigChange 处理备份配置变更
|
* HandleConfigChange 处理配置变更
|
||||||
*/
|
*/
|
||||||
export function HandleConfigChange(config: models$0.GitBackupConfig | null): Promise<void> & { cancel(): void } {
|
export function HandleConfigChange(config: models$0.GitBackupConfig | null): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(395287784, config) as any;
|
let $resultPromise = $Call.ByID(395287784, config) as any;
|
||||||
@@ -34,15 +34,7 @@ export function Initialize(): Promise<void> & { cancel(): void } {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PushToRemote 推送本地更改到远程仓库
|
* Reinitialize 重新初始化
|
||||||
*/
|
|
||||||
export function PushToRemote(): Promise<void> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(262644139) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reinitialize 重新初始化备份服务,用于响应配置变更
|
|
||||||
*/
|
*/
|
||||||
export function Reinitialize(): Promise<void> & { cancel(): void } {
|
export function Reinitialize(): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(301562543) as any;
|
let $resultPromise = $Call.ByID(301562543) as any;
|
||||||
@@ -50,7 +42,7 @@ export function Reinitialize(): Promise<void> & { cancel(): void } {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ServiceShutdown 服务关闭时的清理工作
|
* ServiceShutdown 服务关闭
|
||||||
*/
|
*/
|
||||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(422131801) as any;
|
let $resultPromise = $Call.ByID(422131801) as any;
|
||||||
@@ -63,7 +55,7 @@ export function ServiceStartup(options: application$0.ServiceOptions): Promise<v
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StartAutoBackup 启动自动备份定时器
|
* StartAutoBackup 启动自动备份
|
||||||
*/
|
*/
|
||||||
export function StartAutoBackup(): Promise<void> & { cancel(): void } {
|
export function StartAutoBackup(): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(3035755449) as any;
|
let $resultPromise = $Call.ByID(3035755449) as any;
|
||||||
@@ -77,3 +69,11 @@ export function StopAutoBackup(): Promise<void> & { cancel(): void } {
|
|||||||
let $resultPromise = $Call.ByID(2641894021) as any;
|
let $resultPromise = $Call.ByID(2641894021) as any;
|
||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync 执行完整的同步流程:导出 -> commit -> pull -> 解决冲突 -> push -> 导入
|
||||||
|
*/
|
||||||
|
export function Sync(): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(3420383211) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DatabaseService provides shared database functionality
|
* DatabaseService 数据库服务
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -15,15 +15,7 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|||||||
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RegisterModel 注册模型与表的映射关系
|
* ServiceShutdown 服务关闭
|
||||||
*/
|
|
||||||
export function RegisterModel(tableName: string, model: any): Promise<void> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(175397515, tableName, model) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ServiceShutdown shuts down the service when the application closes
|
|
||||||
*/
|
*/
|
||||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(3907893632) as any;
|
let $resultPromise = $Call.ByID(3907893632) as any;
|
||||||
@@ -31,7 +23,7 @@ export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ServiceStartup initializes the service when the application starts
|
* ServiceStartup 服务启动
|
||||||
*/
|
*/
|
||||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(2067840771, options) as any;
|
let $resultPromise = $Call.ByID(2067840771, options) as any;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DocumentService provides document management functionality
|
* DocumentService 文档服务
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -15,12 +15,12 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|||||||
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
import * as models$0 from "../models/models.js";
|
import * as ent$0 from "../models/ent/models.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CreateDocument creates a new document and returns the created document with ID
|
* CreateDocument 创建文档
|
||||||
*/
|
*/
|
||||||
export function CreateDocument(title: string): Promise<models$0.Document | null> & { cancel(): void } {
|
export function CreateDocument(title: string): Promise<ent$0.Document | null> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(3360680842, title) as any;
|
let $resultPromise = $Call.ByID(3360680842, title) as any;
|
||||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||||
return $$createType1($result);
|
return $$createType1($result);
|
||||||
@@ -30,7 +30,7 @@ export function CreateDocument(title: string): Promise<models$0.Document | null>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DeleteDocument marks a document as deleted (default document with ID=1 cannot be deleted)
|
* DeleteDocument 删除文档
|
||||||
*/
|
*/
|
||||||
export function DeleteDocument(id: number): Promise<void> & { cancel(): void } {
|
export function DeleteDocument(id: number): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(412287269, id) as any;
|
let $resultPromise = $Call.ByID(412287269, id) as any;
|
||||||
@@ -38,9 +38,9 @@ export function DeleteDocument(id: number): Promise<void> & { cancel(): void } {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetDocumentByID gets a document by ID
|
* GetDocumentByID 根据ID获取文档
|
||||||
*/
|
*/
|
||||||
export function GetDocumentByID(id: number): Promise<models$0.Document | null> & { cancel(): void } {
|
export function GetDocumentByID(id: number): Promise<ent$0.Document | null> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(3468193232, id) as any;
|
let $resultPromise = $Call.ByID(3468193232, id) as any;
|
||||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||||
return $$createType1($result);
|
return $$createType1($result);
|
||||||
@@ -50,9 +50,9 @@ export function GetDocumentByID(id: number): Promise<models$0.Document | null> &
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ListAllDocumentsMeta lists all active (non-deleted) document metadata
|
* ListAllDocumentsMeta 列出所有文档
|
||||||
*/
|
*/
|
||||||
export function ListAllDocumentsMeta(): Promise<(models$0.Document | null)[]> & { cancel(): void } {
|
export function ListAllDocumentsMeta(): Promise<(ent$0.Document | null)[]> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(3073950297) as any;
|
let $resultPromise = $Call.ByID(3073950297) as any;
|
||||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||||
return $$createType2($result);
|
return $$createType2($result);
|
||||||
@@ -62,19 +62,7 @@ export function ListAllDocumentsMeta(): Promise<(models$0.Document | null)[]> &
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ListDeletedDocumentsMeta lists all deleted document metadata
|
* LockDocument 锁定文档
|
||||||
*/
|
|
||||||
export function ListDeletedDocumentsMeta(): Promise<(models$0.Document | null)[]> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(490143787) as any;
|
|
||||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
|
||||||
return $$createType2($result);
|
|
||||||
}) as any;
|
|
||||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
|
||||||
return $typingPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LockDocument 锁定文档,防止删除
|
|
||||||
*/
|
*/
|
||||||
export function LockDocument(id: number): Promise<void> & { cancel(): void } {
|
export function LockDocument(id: number): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(1889494473, id) as any;
|
let $resultPromise = $Call.ByID(1889494473, id) as any;
|
||||||
@@ -82,15 +70,7 @@ export function LockDocument(id: number): Promise<void> & { cancel(): void } {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RestoreDocument restores a deleted document
|
* ServiceStartup 服务启动
|
||||||
*/
|
|
||||||
export function RestoreDocument(id: number): Promise<void> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(784200778, id) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ServiceStartup initializes the service when the application starts
|
|
||||||
*/
|
*/
|
||||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(1474135487, options) as any;
|
let $resultPromise = $Call.ByID(1474135487, options) as any;
|
||||||
@@ -106,7 +86,7 @@ export function UnlockDocument(id: number): Promise<void> & { cancel(): void } {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateDocumentContent updates the content of a document
|
* UpdateDocumentContent 更新文档内容
|
||||||
*/
|
*/
|
||||||
export function UpdateDocumentContent(id: number, content: string): Promise<void> & { cancel(): void } {
|
export function UpdateDocumentContent(id: number, content: string): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(3251897116, id, content) as any;
|
let $resultPromise = $Call.ByID(3251897116, id, content) as any;
|
||||||
@@ -114,7 +94,7 @@ export function UpdateDocumentContent(id: number, content: string): Promise<void
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateDocumentTitle updates the title of a document
|
* UpdateDocumentTitle 更新文档标题
|
||||||
*/
|
*/
|
||||||
export function UpdateDocumentTitle(id: number, title: string): Promise<void> & { cancel(): void } {
|
export function UpdateDocumentTitle(id: number, title: string): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(2045530459, id, title) as any;
|
let $resultPromise = $Call.ByID(2045530459, id, title) as any;
|
||||||
@@ -122,6 +102,6 @@ export function UpdateDocumentTitle(id: number, title: string): Promise<void> &
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Private type creation functions
|
// Private type creation functions
|
||||||
const $$createType0 = models$0.Document.createFrom;
|
const $$createType0 = ent$0.Document.createFrom;
|
||||||
const $$createType1 = $Create.Nullable($$createType0);
|
const $$createType1 = $Create.Nullable($$createType0);
|
||||||
const $$createType2 = $Create.Array($$createType1);
|
const $$createType2 = $Create.Array($$createType1);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ExtensionService 扩展管理服务
|
* ExtensionService 扩展服务
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -16,12 +16,39 @@ import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/applic
|
|||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
import * as models$0 from "../models/models.js";
|
import * as models$0 from "../models/models.js";
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import * as ent$0 from "../models/ent/models.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetAllExtensions 获取所有扩展配置
|
* GetAllExtensions 获取所有扩展
|
||||||
*/
|
*/
|
||||||
export function GetAllExtensions(): Promise<models$0.Extension[]> & { cancel(): void } {
|
export function GetAllExtensions(): Promise<(ent$0.Extension | null)[]> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(3094292124) as any;
|
let $resultPromise = $Call.ByID(3094292124) as any;
|
||||||
|
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||||
|
return $$createType2($result);
|
||||||
|
}) as any;
|
||||||
|
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||||
|
return $typingPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetDefaultExtensions 获取默认扩展配置(用于前端绑定生成)
|
||||||
|
*/
|
||||||
|
export function GetDefaultExtensions(): Promise<models$0.Extension[]> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(4036328166) as any;
|
||||||
|
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||||
|
return $$createType4($result);
|
||||||
|
}) as any;
|
||||||
|
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||||
|
return $typingPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetExtensionByKey 根据Key获取扩展
|
||||||
|
*/
|
||||||
|
export function GetExtensionByKey(key: string): Promise<ent$0.Extension | null> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(2551065776, key) as any;
|
||||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||||
return $$createType1($result);
|
return $$createType1($result);
|
||||||
}) as any;
|
}) as any;
|
||||||
@@ -30,23 +57,15 @@ export function GetAllExtensions(): Promise<models$0.Extension[]> & { cancel():
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ResetAllExtensionsToDefault 重置所有扩展到默认状态
|
* ResetExtensionConfig 重置单个扩展到默认状态
|
||||||
*/
|
*/
|
||||||
export function ResetAllExtensionsToDefault(): Promise<void> & { cancel(): void } {
|
export function ResetExtensionConfig(key: string): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(270611949) as any;
|
let $resultPromise = $Call.ByID(3990780299, key) as any;
|
||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ResetExtensionToDefault 重置扩展到默认状态
|
* ServiceStartup 服务启动
|
||||||
*/
|
|
||||||
export function ResetExtensionToDefault(id: models$0.ExtensionID): Promise<void> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(868308101, id) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ServiceStartup 启动时调用
|
|
||||||
*/
|
*/
|
||||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(40324057, options) as any;
|
let $resultPromise = $Call.ByID(40324057, options) as any;
|
||||||
@@ -54,21 +73,32 @@ export function ServiceStartup(options: application$0.ServiceOptions): Promise<v
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateExtensionEnabled 更新扩展启用状态
|
* SyncExtensions 同步扩展配置
|
||||||
*/
|
*/
|
||||||
export function UpdateExtensionEnabled(id: models$0.ExtensionID, enabled: boolean): Promise<void> & { cancel(): void } {
|
export function SyncExtensions(): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(1067300094, id, enabled) as any;
|
let $resultPromise = $Call.ByID(167560004) as any;
|
||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateExtensionState 更新扩展状态
|
* UpdateExtensionConfig 更新扩展配置
|
||||||
*/
|
*/
|
||||||
export function UpdateExtensionState(id: models$0.ExtensionID, enabled: boolean, config: models$0.ExtensionConfig): Promise<void> & { cancel(): void } {
|
export function UpdateExtensionConfig(key: string, config: { [_: string]: any }): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(2917946454, id, enabled, config) as any;
|
let $resultPromise = $Call.ByID(3184142503, key, config) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateExtensionEnabled 更新扩展启用状态
|
||||||
|
*/
|
||||||
|
export function UpdateExtensionEnabled(key: string, enabled: boolean): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(1067300094, key, enabled) as any;
|
||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private type creation functions
|
// Private type creation functions
|
||||||
const $$createType0 = models$0.Extension.createFrom;
|
const $$createType0 = ent$0.Extension.createFrom;
|
||||||
const $$createType1 = $Create.Array($$createType0);
|
const $$createType1 = $Create.Nullable($$createType0);
|
||||||
|
const $$createType2 = $Create.Array($$createType1);
|
||||||
|
const $$createType3 = models$0.Extension.createFrom;
|
||||||
|
const $$createType4 = $Create.Array($$createType3);
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import * as SystemService from "./systemservice.js";
|
|||||||
import * as TestService from "./testservice.js";
|
import * as TestService from "./testservice.js";
|
||||||
import * as ThemeService from "./themeservice.js";
|
import * as ThemeService from "./themeservice.js";
|
||||||
import * as TranslationService from "./translationservice.js";
|
import * as TranslationService from "./translationservice.js";
|
||||||
import * as TrayService from "./trayservice.js";
|
|
||||||
import * as WindowService from "./windowservice.js";
|
import * as WindowService from "./windowservice.js";
|
||||||
export {
|
export {
|
||||||
BackupService,
|
BackupService,
|
||||||
@@ -36,7 +35,6 @@ export {
|
|||||||
TestService,
|
TestService,
|
||||||
ThemeService,
|
ThemeService,
|
||||||
TranslationService,
|
TranslationService,
|
||||||
TrayService,
|
|
||||||
WindowService
|
WindowService
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KeyBindingService 快捷键管理服务
|
* KeyBindingService 快捷键服务
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -16,12 +16,39 @@ import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/applic
|
|||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
import * as models$0 from "../models/models.js";
|
import * as models$0 from "../models/models.js";
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import * as ent$0 from "../models/ent/models.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetAllKeyBindings 获取所有快捷键配置
|
* GetAllKeyBindings 获取所有快捷键
|
||||||
*/
|
*/
|
||||||
export function GetAllKeyBindings(): Promise<models$0.KeyBinding[]> & { cancel(): void } {
|
export function GetAllKeyBindings(): Promise<(ent$0.KeyBinding | null)[]> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(1633502882) as any;
|
let $resultPromise = $Call.ByID(1633502882) as any;
|
||||||
|
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||||
|
return $$createType2($result);
|
||||||
|
}) as any;
|
||||||
|
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||||
|
return $typingPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetDefaultKeyBindings 获取默认快捷键配置
|
||||||
|
*/
|
||||||
|
export function GetDefaultKeyBindings(): Promise<models$0.KeyBinding[]> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(3843471588) as any;
|
||||||
|
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||||
|
return $$createType4($result);
|
||||||
|
}) as any;
|
||||||
|
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||||
|
return $typingPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetKeyBindingByKey 根据Key获取快捷键
|
||||||
|
*/
|
||||||
|
export function GetKeyBindingByKey(key: string): Promise<ent$0.KeyBinding | null> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(852938650, key) as any;
|
||||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||||
return $$createType1($result);
|
return $$createType1($result);
|
||||||
}) as any;
|
}) as any;
|
||||||
@@ -30,13 +57,40 @@ export function GetAllKeyBindings(): Promise<models$0.KeyBinding[]> & { cancel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ServiceStartup 启动时调用
|
* ServiceStartup 服务启动
|
||||||
*/
|
*/
|
||||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(2057121990, options) as any;
|
let $resultPromise = $Call.ByID(2057121990, options) as any;
|
||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SyncKeyBindings 同步快捷键配置
|
||||||
|
*/
|
||||||
|
export function SyncKeyBindings(): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(1522202638) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateKeyBindingCommand 更新快捷键命令
|
||||||
|
*/
|
||||||
|
export function UpdateKeyBindingCommand(key: string, command: string): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(1293670628, key, command) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateKeyBindingEnabled 更新快捷键启用状态
|
||||||
|
*/
|
||||||
|
export function UpdateKeyBindingEnabled(key: string, enabled: boolean): Promise<void> & { cancel(): void } {
|
||||||
|
let $resultPromise = $Call.ByID(843626124, key, enabled) as any;
|
||||||
|
return $resultPromise;
|
||||||
|
}
|
||||||
|
|
||||||
// Private type creation functions
|
// Private type creation functions
|
||||||
const $$createType0 = models$0.KeyBinding.createFrom;
|
const $$createType0 = ent$0.KeyBinding.createFrom;
|
||||||
const $$createType1 = $Create.Array($$createType0);
|
const $$createType1 = $Create.Nullable($$createType0);
|
||||||
|
const $$createType2 = $Create.Array($$createType1);
|
||||||
|
const $$createType3 = models$0.KeyBinding.createFrom;
|
||||||
|
const $$createType4 = $Create.Array($$createType3);
|
||||||
|
|||||||
@@ -191,15 +191,14 @@ export class MemoryStats {
|
|||||||
* MigrationProgress 迁移进度信息
|
* MigrationProgress 迁移进度信息
|
||||||
*/
|
*/
|
||||||
export class MigrationProgress {
|
export class MigrationProgress {
|
||||||
"status": MigrationStatus;
|
/**
|
||||||
|
* 0-100
|
||||||
|
*/
|
||||||
"progress": number;
|
"progress": number;
|
||||||
"error"?: string;
|
"error"?: string;
|
||||||
|
|
||||||
/** Creates a new MigrationProgress instance. */
|
/** Creates a new MigrationProgress instance. */
|
||||||
constructor($$source: Partial<MigrationProgress> = {}) {
|
constructor($$source: Partial<MigrationProgress> = {}) {
|
||||||
if (!("status" in $$source)) {
|
|
||||||
this["status"] = ("" as MigrationStatus);
|
|
||||||
}
|
|
||||||
if (!("progress" in $$source)) {
|
if (!("progress" in $$source)) {
|
||||||
this["progress"] = 0;
|
this["progress"] = 0;
|
||||||
}
|
}
|
||||||
@@ -216,20 +215,6 @@ export class MigrationProgress {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* MigrationStatus 迁移状态
|
|
||||||
*/
|
|
||||||
export enum MigrationStatus {
|
|
||||||
/**
|
|
||||||
* The Go zero value for the underlying type of the enum.
|
|
||||||
*/
|
|
||||||
$zero = "",
|
|
||||||
|
|
||||||
MigrationStatusMigrating = "migrating",
|
|
||||||
MigrationStatusCompleted = "completed",
|
|
||||||
MigrationStatusFailed = "failed",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OSInfo 操作系统信息
|
* OSInfo 操作系统信息
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|||||||
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
import * as models$0 from "../models/models.js";
|
import * as ent$0 from "../models/ent/models.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetThemeByName 通过名称获取主题覆盖,若不存在则返回 nil
|
* GetThemeByKey 根据Key获取主题
|
||||||
*/
|
*/
|
||||||
export function GetThemeByName(name: string): Promise<models$0.Theme | null> & { cancel(): void } {
|
export function GetThemeByKey(key: string): Promise<ent$0.Theme | null> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(1938954770, name) as any;
|
let $resultPromise = $Call.ByID(808794256, key) as any;
|
||||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||||
return $$createType1($result);
|
return $$createType1($result);
|
||||||
}) as any;
|
}) as any;
|
||||||
@@ -30,18 +30,10 @@ export function GetThemeByName(name: string): Promise<models$0.Theme | null> & {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ResetTheme 删除指定主题的覆盖配置
|
* ResetTheme 删除主题
|
||||||
*/
|
*/
|
||||||
export function ResetTheme(name: string): Promise<void> & { cancel(): void } {
|
export function ResetTheme(key: string): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(1806334457, name) as any;
|
let $resultPromise = $Call.ByID(1806334457, key) as any;
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ServiceShutdown 服务关闭
|
|
||||||
*/
|
|
||||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(1676749034) as any;
|
|
||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,13 +46,13 @@ export function ServiceStartup(options: application$0.ServiceOptions): Promise<v
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateTheme 保存或更新主题覆盖
|
* UpdateTheme 保存或更新主题
|
||||||
*/
|
*/
|
||||||
export function UpdateTheme(name: string, colors: models$0.ThemeColorConfig): Promise<void> & { cancel(): void } {
|
export function UpdateTheme(key: string, colors: { [_: string]: any }): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(70189749, name, colors) as any;
|
let $resultPromise = $Call.ByID(70189749, key, colors) as any;
|
||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private type creation functions
|
// Private type creation functions
|
||||||
const $$createType0 = models$0.Theme.createFrom;
|
const $$createType0 = ent$0.Theme.createFrom;
|
||||||
const $$createType1 = $Create.Nullable($$createType0);
|
const $$createType1 = $Create.Nullable($$createType0);
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TrayService 系统托盘服务
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore: Unused imports
|
|
||||||
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AutoShowHide 自动显示/隐藏主窗口
|
|
||||||
*/
|
|
||||||
export function AutoShowHide(): Promise<void> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(4044219428) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HandleWindowClose 处理窗口关闭事件
|
|
||||||
*/
|
|
||||||
export function HandleWindowClose(): Promise<void> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(1824247204) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HandleWindowMinimize 处理窗口最小化事件
|
|
||||||
*/
|
|
||||||
export function HandleWindowMinimize(): Promise<void> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(178686624) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MinimizeButtonClicked 处理标题栏最小化按钮点击
|
|
||||||
*/
|
|
||||||
export function MinimizeButtonClicked(): Promise<void> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(2477618539) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ShouldMinimizeToTray 检查是否应该最小化到托盘
|
|
||||||
*/
|
|
||||||
export function ShouldMinimizeToTray(): Promise<boolean> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(3403884012) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ShowWindow 显示主窗口
|
|
||||||
*/
|
|
||||||
export function ShowWindow(): Promise<void> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(1315913255) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
126
frontend/package-lock.json
generated
126
frontend/package-lock.json
generated
@@ -71,7 +71,7 @@
|
|||||||
"@lezer/generator": "^1.8.0",
|
"@lezer/generator": "^1.8.0",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@vitejs/plugin-vue": "^6.0.2",
|
"@vitejs/plugin-vue": "^6.0.2",
|
||||||
"@wailsio/runtime": "latest",
|
"@wailsio/runtime": "*",
|
||||||
"cross-env": "^10.1.0",
|
"cross-env": "^10.1.0",
|
||||||
"eslint": "^9.39.1",
|
"eslint": "^9.39.1",
|
||||||
"eslint-plugin-vue": "^10.6.2",
|
"eslint-plugin-vue": "^10.6.2",
|
||||||
@@ -142,6 +142,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz",
|
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz",
|
||||||
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.28.5"
|
"@babel/types": "^7.28.5"
|
||||||
},
|
},
|
||||||
@@ -215,6 +216,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz",
|
||||||
"integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==",
|
"integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
@@ -263,6 +265,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
|
||||||
"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
|
"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
@@ -289,6 +292,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/lang-html/-/lang-html-6.4.11.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/lang-html/-/lang-html-6.4.11.tgz",
|
||||||
"integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==",
|
"integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
"@codemirror/lang-css": "^6.0.0",
|
"@codemirror/lang-css": "^6.0.0",
|
||||||
@@ -316,6 +320,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz",
|
||||||
"integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==",
|
"integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
"@codemirror/language": "^6.6.0",
|
"@codemirror/language": "^6.6.0",
|
||||||
@@ -528,6 +533,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/language/-/language-6.11.3.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/language/-/language-6.11.3.tgz",
|
||||||
"integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==",
|
"integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
"@codemirror/view": "^6.23.0",
|
"@codemirror/view": "^6.23.0",
|
||||||
@@ -604,6 +610,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.5.2.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.5.2.tgz",
|
||||||
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@marijn/find-cluster-break": "^1.0.0"
|
"@marijn/find-cluster-break": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -613,6 +620,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.38.8.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.38.8.tgz",
|
||||||
"integrity": "sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==",
|
"integrity": "sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
"crelt": "^1.0.6",
|
"crelt": "^1.0.6",
|
||||||
@@ -1436,7 +1444,8 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.3.0.tgz",
|
||||||
"integrity": "sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==",
|
"integrity": "sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/cpp": {
|
"node_modules/@lezer/cpp": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
@@ -1490,6 +1499,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.3.tgz",
|
"resolved": "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.3.tgz",
|
||||||
"integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==",
|
"integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.3.0"
|
"@lezer/common": "^1.3.0"
|
||||||
}
|
}
|
||||||
@@ -1521,6 +1531,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.5.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.5.1.tgz",
|
||||||
"integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
|
"integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.2.0",
|
"@lezer/common": "^1.2.0",
|
||||||
"@lezer/highlight": "^1.1.3",
|
"@lezer/highlight": "^1.1.3",
|
||||||
@@ -1553,6 +1564,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.4.tgz",
|
||||||
"integrity": "sha512-LHL17Mq0OcFXm1pGQssuGTQFPPdxARjKM8f7GA5+sGtHi0K3R84YaSbmche0+RKWHnCsx9asEe5OWOI4FHfe4A==",
|
"integrity": "sha512-LHL17Mq0OcFXm1pGQssuGTQFPPdxARjKM8f7GA5+sGtHi0K3R84YaSbmche0+RKWHnCsx9asEe5OWOI4FHfe4A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.0.0"
|
"@lezer/common": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -1648,52 +1660,6 @@
|
|||||||
"langium": "3.3.1"
|
"langium": "3.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nuxt/kit": {
|
|
||||||
"version": "3.17.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@nuxt/kit/-/kit-3.17.4.tgz",
|
|
||||||
"integrity": "sha512-l+hY8sy2XFfg3PigZj+PTu6+KIJzmbACTRimn1ew/gtCz+F38f6KTF4sMRTN5CUxiB8TRENgEonASmkAWfpO9Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"c12": "^3.0.4",
|
|
||||||
"consola": "^3.4.2",
|
|
||||||
"defu": "^6.1.4",
|
|
||||||
"destr": "^2.0.5",
|
|
||||||
"errx": "^0.1.0",
|
|
||||||
"exsolve": "^1.0.5",
|
|
||||||
"ignore": "^7.0.4",
|
|
||||||
"jiti": "^2.4.2",
|
|
||||||
"klona": "^2.0.6",
|
|
||||||
"knitwork": "^1.2.0",
|
|
||||||
"mlly": "^1.7.4",
|
|
||||||
"ohash": "^2.0.11",
|
|
||||||
"pathe": "^2.0.3",
|
|
||||||
"pkg-types": "^2.1.0",
|
|
||||||
"scule": "^1.3.0",
|
|
||||||
"semver": "^7.7.2",
|
|
||||||
"std-env": "^3.9.0",
|
|
||||||
"tinyglobby": "^0.2.13",
|
|
||||||
"ufo": "^1.6.1",
|
|
||||||
"unctx": "^2.4.1",
|
|
||||||
"unimport": "^5.0.1",
|
|
||||||
"untyped": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.12.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@nuxt/kit/node_modules/ignore": {
|
|
||||||
"version": "7.0.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.4.tgz",
|
|
||||||
"integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@parcel/watcher": {
|
"node_modules/@parcel/watcher": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz",
|
||||||
@@ -2880,6 +2846,7 @@
|
|||||||
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.16.0"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
@@ -2958,6 +2925,7 @@
|
|||||||
"integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==",
|
"integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.48.0",
|
"@typescript-eslint/scope-manager": "8.48.0",
|
||||||
"@typescript-eslint/types": "8.48.0",
|
"@typescript-eslint/types": "8.48.0",
|
||||||
@@ -3651,6 +3619,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -4045,7 +4014,6 @@
|
|||||||
"integrity": "sha512-t5FaZTYbbCtvxuZq9xxIruYydrAGsJ+8UdP0pZzMiK2xl/gNiSOy0OxhLzHUEEb0m1QXYqfzfvyIFEmz/g9lqg==",
|
"integrity": "sha512-t5FaZTYbbCtvxuZq9xxIruYydrAGsJ+8UdP0pZzMiK2xl/gNiSOy0OxhLzHUEEb0m1QXYqfzfvyIFEmz/g9lqg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.3",
|
"chokidar": "^4.0.3",
|
||||||
"confbox": "^0.2.2",
|
"confbox": "^0.2.2",
|
||||||
@@ -4194,6 +4162,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/chevrotain/-/chevrotain-11.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/chevrotain/-/chevrotain-11.0.3.tgz",
|
||||||
"integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
|
"integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chevrotain/cst-dts-gen": "11.0.3",
|
"@chevrotain/cst-dts-gen": "11.0.3",
|
||||||
"@chevrotain/gast": "11.0.3",
|
"@chevrotain/gast": "11.0.3",
|
||||||
@@ -4250,7 +4219,6 @@
|
|||||||
"integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==",
|
"integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"consola": "^3.2.3"
|
"consola": "^3.2.3"
|
||||||
}
|
}
|
||||||
@@ -4362,7 +4330,6 @@
|
|||||||
"integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
|
"integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.10.0"
|
"node": "^14.18.0 || >=16.10.0"
|
||||||
}
|
}
|
||||||
@@ -4555,6 +4522,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.33.1.tgz",
|
"resolved": "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.33.1.tgz",
|
||||||
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
|
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
@@ -4964,6 +4932,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -5165,8 +5134,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/destr/-/destr-2.0.5.tgz",
|
"resolved": "https://registry.npmmirror.com/destr/-/destr-2.0.5.tgz",
|
||||||
"integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
|
"integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
@@ -5242,7 +5210,6 @@
|
|||||||
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -5305,8 +5272,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/errx/-/errx-0.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/errx/-/errx-0.1.0.tgz",
|
||||||
"integrity": "sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==",
|
"integrity": "sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/es-define-property": {
|
"node_modules/es-define-property": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -5408,6 +5374,7 @@
|
|||||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -5722,6 +5689,7 @@
|
|||||||
"integrity": "sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==",
|
"integrity": "sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tabbable": "^6.3.0"
|
"tabbable": "^6.3.0"
|
||||||
}
|
}
|
||||||
@@ -5812,7 +5780,6 @@
|
|||||||
"integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==",
|
"integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"citty": "^0.1.6",
|
"citty": "^0.1.6",
|
||||||
"consola": "^3.4.0",
|
"consola": "^3.4.0",
|
||||||
@@ -6386,7 +6353,6 @@
|
|||||||
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
|
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"jiti": "lib/jiti-cli.mjs"
|
"jiti": "lib/jiti-cli.mjs"
|
||||||
}
|
}
|
||||||
@@ -6396,8 +6362,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz",
|
||||||
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
|
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
@@ -6470,7 +6435,6 @@
|
|||||||
"integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
|
"integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
@@ -6480,8 +6444,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/knitwork/-/knitwork-1.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/knitwork/-/knitwork-1.2.0.tgz",
|
||||||
"integrity": "sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==",
|
"integrity": "sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/kolorist": {
|
"node_modules/kolorist": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
@@ -6649,9 +6612,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mdast-util-to-hast": {
|
"node_modules/mdast-util-to-hast": {
|
||||||
"version": "13.2.0",
|
"version": "13.2.1",
|
||||||
"resolved": "https://registry.npmmirror.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
|
||||||
"integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
|
"integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6958,8 +6921,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/node-fetch-native/-/node-fetch-native-1.6.6.tgz",
|
"resolved": "https://registry.npmmirror.com/node-fetch-native/-/node-fetch-native-1.6.6.tgz",
|
||||||
"integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==",
|
"integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/node-stdlib-browser": {
|
"node_modules/node-stdlib-browser": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
@@ -7026,7 +6988,6 @@
|
|||||||
"integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==",
|
"integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"citty": "^0.1.6",
|
"citty": "^0.1.6",
|
||||||
"consola": "^3.4.0",
|
"consola": "^3.4.0",
|
||||||
@@ -7118,8 +7079,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz",
|
"resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz",
|
||||||
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
|
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/oniguruma-parser": {
|
"node_modules/oniguruma-parser": {
|
||||||
"version": "0.12.1",
|
"version": "0.12.1",
|
||||||
@@ -7375,6 +7335,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz",
|
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz",
|
||||||
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/devtools-api": "^7.7.7"
|
"@vue/devtools-api": "^7.7.7"
|
||||||
},
|
},
|
||||||
@@ -7523,6 +7484,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.7.2.tgz",
|
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.7.2.tgz",
|
||||||
"integrity": "sha512-n3HV2J6QhItCXndGa3oMWvWFAgN1ibnS7R9mt6iokScBOC0Ul9/iZORmU2IWUMcyAQaMPjTlY3uT34TqocUxMA==",
|
"integrity": "sha512-n3HV2J6QhItCXndGa3oMWvWFAgN1ibnS7R9mt6iokScBOC0Ul9/iZORmU2IWUMcyAQaMPjTlY3uT34TqocUxMA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
@@ -7661,7 +7623,6 @@
|
|||||||
"integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==",
|
"integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"destr": "^2.0.3"
|
"destr": "^2.0.3"
|
||||||
@@ -7788,6 +7749,7 @@
|
|||||||
"integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
|
"integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.8"
|
"@types/estree": "1.0.8"
|
||||||
},
|
},
|
||||||
@@ -7890,6 +7852,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.94.2.tgz",
|
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.94.2.tgz",
|
||||||
"integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==",
|
"integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.0",
|
"chokidar": "^4.0.0",
|
||||||
"immutable": "^5.0.2",
|
"immutable": "^5.0.2",
|
||||||
@@ -7910,8 +7873,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
|
||||||
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
|
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.2",
|
"version": "7.7.2",
|
||||||
@@ -8230,7 +8192,6 @@
|
|||||||
"integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
|
"integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-tokens": "^9.0.1"
|
"js-tokens": "^9.0.1"
|
||||||
},
|
},
|
||||||
@@ -8363,6 +8324,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -8482,6 +8444,7 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -8526,7 +8489,6 @@
|
|||||||
"integrity": "sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==",
|
"integrity": "sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^8.14.0",
|
"acorn": "^8.14.0",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
@@ -8540,7 +8502,6 @@
|
|||||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "^1.0.0"
|
"@types/estree": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -8558,7 +8519,6 @@
|
|||||||
"integrity": "sha512-1YWzPj6wYhtwHE+9LxRlyqP4DiRrhGfJxdtH475im8ktyZXO3jHj/3PZ97zDdvkYoovFdi0K4SKl3a7l92v3sQ==",
|
"integrity": "sha512-1YWzPj6wYhtwHE+9LxRlyqP4DiRrhGfJxdtH475im8ktyZXO3jHj/3PZ97zDdvkYoovFdi0K4SKl3a7l92v3sQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^8.14.1",
|
"acorn": "^8.14.1",
|
||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
@@ -8585,7 +8545,6 @@
|
|||||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
|
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -8599,7 +8558,6 @@
|
|||||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "^1.0.0"
|
"@types/estree": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -8610,7 +8568,6 @@
|
|||||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -8713,7 +8670,6 @@
|
|||||||
"integrity": "sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==",
|
"integrity": "sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pathe": "^2.0.2",
|
"pathe": "^2.0.2",
|
||||||
"picomatch": "^4.0.2"
|
"picomatch": "^4.0.2"
|
||||||
@@ -8731,7 +8687,6 @@
|
|||||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -8824,7 +8779,6 @@
|
|||||||
"integrity": "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==",
|
"integrity": "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"citty": "^0.1.6",
|
"citty": "^0.1.6",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
@@ -8937,6 +8891,7 @@
|
|||||||
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -9047,6 +9002,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -9296,6 +9252,7 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz",
|
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz",
|
||||||
"integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
|
"integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.25",
|
"@vue/compiler-dom": "3.5.25",
|
||||||
"@vue/compiler-sfc": "3.5.25",
|
"@vue/compiler-sfc": "3.5.25",
|
||||||
@@ -9318,6 +9275,7 @@
|
|||||||
"integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==",
|
"integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
"eslint-scope": "^8.2.0",
|
"eslint-scope": "^8.2.0",
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ onBeforeMount(async () => {
|
|||||||
// 并行初始化配置、系统信息和快捷键配置
|
// 并行初始化配置、系统信息和快捷键配置
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
configStore.initConfig(),
|
configStore.initConfig(),
|
||||||
systemStore.initializeSystemInfo(),
|
systemStore.initSystemInfo(),
|
||||||
keybindingStore.loadKeyBindings(),
|
keybindingStore.loadKeyBindings(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 初始化语言和主题
|
// 初始化语言和主题
|
||||||
await configStore.initializeLanguage();
|
await configStore.initLanguage();
|
||||||
await themeStore.initializeTheme();
|
await themeStore.initTheme();
|
||||||
await translationStore.loadTranslators();
|
await translationStore.loadTranslators();
|
||||||
|
|
||||||
// 启动时检查更新
|
// 启动时检查更新
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
/**
|
|
||||||
* 翻译图标SVG
|
|
||||||
*/
|
|
||||||
export const TRANSLATION_ICON_SVG = `
|
|
||||||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
||||||
<path d="M599.68 485.056h-8l30.592 164.672c20.352-7.04 38.72-17.344 54.912-31.104a271.36 271.36 0 0 1-40.704-64.64l32.256-4.032c8.896 17.664 19.072 33.28 30.592 46.72 23.872-27.968 42.24-65.152 55.04-111.744l-154.688 0.128z m121.92 133.76c18.368 15.36 39.36 26.56 62.848 33.472l14.784 4.416-8.64 30.336-14.72-4.352a205.696 205.696 0 0 1-76.48-41.728c-20.672 17.92-44.928 31.552-71.232 40.064l20.736 110.912H519.424l-9.984 72.512h385.152c18.112 0 32.704-14.144 32.704-31.616V295.424a32.128 32.128 0 0 0-32.704-31.552H550.528l35.2 189.696h79.424v-31.552h61.44v31.552h102.4v31.616h-42.688c-14.272 55.488-35.712 100.096-64.64 133.568zM479.36 791.68H193.472c-36.224 0-65.472-28.288-65.472-63.168V191.168C128 156.16 157.312 128 193.472 128h327.68l20.544 104.32h352.832c36.224 0 65.472 28.224 65.472 63.104v537.408c0 34.944-29.312 63.168-65.472 63.168H468.608l10.688-104.32zM337.472 548.352v-33.28H272.768v-48.896h60.16V433.28h-60.16v-41.728h64.704v-32.896h-102.4v189.632h102.4z m158.272 0V453.76c0-17.216-4.032-30.272-12.16-39.488-8.192-9.152-20.288-13.696-36.032-13.696a55.04 55.04 0 0 0-24.768 5.376 39.04 39.04 0 0 0-17.088 15.936h-1.984l-5.056-18.56h-28.352V548.48h37.12V480c0-17.088 2.304-29.376 6.912-36.736 4.608-7.424 12.16-11.072 22.528-11.072 7.616 0 13.248 2.56 16.64 7.872 3.52 5.248 5.312 13.056 5.312 23.488v84.736h36.928z" fill="currentColor"></path>
|
|
||||||
</svg>`;
|
|
||||||
65
frontend/src/common/utils/formatter.ts
Normal file
65
frontend/src/common/utils/formatter.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* Formatter utility functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface DateTimeFormatOptions {
|
||||||
|
locale?: string;
|
||||||
|
includeTime?: boolean;
|
||||||
|
hour12?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format date time string to localized format
|
||||||
|
* @param dateString - ISO date string or null
|
||||||
|
* @param options - Formatting options
|
||||||
|
* @returns Formatted date string or error message
|
||||||
|
*/
|
||||||
|
export const formatDateTime = (
|
||||||
|
dateString: string | null,
|
||||||
|
options: DateTimeFormatOptions = {}
|
||||||
|
): string => {
|
||||||
|
const {
|
||||||
|
locale = 'en-US',
|
||||||
|
includeTime = true,
|
||||||
|
hour12 = false
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
if (!dateString) {
|
||||||
|
return 'Unknown time';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
return 'Invalid date';
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatOptions: Intl.DateTimeFormatOptions = {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (includeTime) {
|
||||||
|
formatOptions.hour = '2-digit';
|
||||||
|
formatOptions.minute = '2-digit';
|
||||||
|
formatOptions.hour12 = hour12;
|
||||||
|
}
|
||||||
|
|
||||||
|
return date.toLocaleString(locale, formatOptions);
|
||||||
|
} catch {
|
||||||
|
return 'Time error';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncate string with ellipsis
|
||||||
|
* @param str - String to truncate
|
||||||
|
* @param maxLength - Maximum length before truncation
|
||||||
|
* @returns Truncated string with ellipsis if needed
|
||||||
|
*/
|
||||||
|
export const truncateString = (str: string, maxLength: number): string => {
|
||||||
|
if (!str) return '';
|
||||||
|
return str.length > maxLength ? str.substring(0, maxLength) + '...' : str;
|
||||||
|
};
|
||||||
42
frontend/src/common/utils/validation.ts
Normal file
42
frontend/src/common/utils/validation.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Validation utility functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate document title
|
||||||
|
* @param title - The title to validate
|
||||||
|
* @param maxLength - Maximum allowed length (default: 50)
|
||||||
|
* @returns Error message if invalid, null if valid
|
||||||
|
*/
|
||||||
|
export const validateDocumentTitle = (title: string, maxLength: number = 50): string | null => {
|
||||||
|
const trimmed = title.trim();
|
||||||
|
|
||||||
|
if (!trimmed) {
|
||||||
|
return 'Document name cannot be empty';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed.length > maxLength) {
|
||||||
|
return `Document name cannot exceed ${maxLength} characters`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string is empty or whitespace only
|
||||||
|
* @param value - The string to check
|
||||||
|
* @returns true if empty or whitespace only
|
||||||
|
*/
|
||||||
|
export const isEmpty = (value: string | null | undefined): boolean => {
|
||||||
|
return !value || value.trim().length === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string exceeds max length
|
||||||
|
* @param value - The string to check
|
||||||
|
* @param maxLength - Maximum allowed length
|
||||||
|
* @returns true if exceeds max length
|
||||||
|
*/
|
||||||
|
export const exceedsMaxLength = (value: string, maxLength: number): boolean => {
|
||||||
|
return value.trim().length > maxLength;
|
||||||
|
};
|
||||||
@@ -142,7 +142,7 @@ onBeforeUnmount(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-family: var(--voidraft-font-mono),serif;
|
font-family: Menlo, monospace,serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-word {
|
.loading-word {
|
||||||
|
|||||||
@@ -1,35 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="visible && canClose"
|
v-if="visible && canClose"
|
||||||
class="tab-context-menu"
|
v-click-outside="handleClose"
|
||||||
:style="{
|
class="tab-context-menu"
|
||||||
left: position.x + 'px',
|
:style="{
|
||||||
top: position.y + 'px'
|
left: position.x + 'px',
|
||||||
}"
|
top: position.y + 'px'
|
||||||
@click.stop
|
}"
|
||||||
|
@click.stop
|
||||||
>
|
>
|
||||||
<div v-if="canClose" class="menu-item" @click="handleMenuClick('close')">
|
<div v-if="canClose" class="menu-item" @click="handleMenuClick('close')">
|
||||||
<svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M18 6L6 18M6 6l12 12"/>
|
<path d="M18 6L6 18M6 6l12 12"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="menu-text">{{ t('tabs.contextMenu.closeTab') }}</span>
|
<span class="menu-text">{{ t('tabs.contextMenu.closeTab') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasOtherTabs" class="menu-item" @click="handleMenuClick('closeOthers')">
|
<div v-if="hasOtherTabs" class="menu-item" @click="handleMenuClick('closeOthers')">
|
||||||
<svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
||||||
<path d="M9 9l6 6M15 9l-6 6"/>
|
<path d="M9 9l6 6M15 9l-6 6"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="menu-text">{{ t('tabs.contextMenu.closeOthers') }}</span>
|
<span class="menu-text">{{ t('tabs.contextMenu.closeOthers') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasTabsToLeft" class="menu-item" @click="handleMenuClick('closeLeft')">
|
<div v-if="hasTabsToLeft" class="menu-item" @click="handleMenuClick('closeLeft')">
|
||||||
<svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M15 18l-6-6 6-6"/>
|
<path d="M15 18l-6-6 6-6"/>
|
||||||
<path d="M9 18l-6-6 6-6"/>
|
<path d="M9 18l-6-6 6-6"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="menu-text">{{ t('tabs.contextMenu.closeLeft') }}</span>
|
<span class="menu-text">{{ t('tabs.contextMenu.closeLeft') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasTabsToRight" class="menu-item" @click="handleMenuClick('closeRight')">
|
<div v-if="hasTabsToRight" class="menu-item" @click="handleMenuClick('closeRight')">
|
||||||
<svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M9 18l6-6-6-6"/>
|
<path d="M9 18l6-6-6-6"/>
|
||||||
<path d="M15 18l6-6-6-6"/>
|
<path d="M15 18l6-6-6-6"/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -39,9 +44,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, onUnmounted } from 'vue';
|
import {computed, onMounted, onUnmounted} from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import {useI18n} from 'vue-i18n';
|
||||||
import { useTabStore } from '@/stores/tabStore';
|
import {useTabStore} from '@/stores/tabStore';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
@@ -54,7 +59,7 @@ const emit = defineEmits<{
|
|||||||
close: [];
|
close: [];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const {t} = useI18n();
|
||||||
const tabStore = useTabStore();
|
const tabStore = useTabStore();
|
||||||
|
|
||||||
// 计算属性
|
// 计算属性
|
||||||
@@ -79,6 +84,9 @@ const hasTabsToLeft = computed(() => {
|
|||||||
return index > 0;
|
return index > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
emit('close');
|
||||||
|
};
|
||||||
// 处理菜单项点击
|
// 处理菜单项点击
|
||||||
const handleMenuClick = (action: string) => {
|
const handleMenuClick = (action: string) => {
|
||||||
if (!props.targetDocumentId) return;
|
if (!props.targetDocumentId) return;
|
||||||
@@ -97,34 +105,9 @@ const handleMenuClick = (action: string) => {
|
|||||||
tabStore.closeTabsToRight(props.targetDocumentId);
|
tabStore.closeTabsToRight(props.targetDocumentId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit('close');
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理外部点击
|
|
||||||
const handleClickOutside = (_event: MouseEvent) => {
|
|
||||||
if (props.visible) {
|
|
||||||
emit('close');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理ESC键
|
|
||||||
const handleEscapeKey = (event: KeyboardEvent) => {
|
|
||||||
if (event.key === 'Escape' && props.visible) {
|
|
||||||
emit('close');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 生命周期
|
|
||||||
onMounted(() => {
|
|
||||||
document.addEventListener('click', handleClickOutside);
|
|
||||||
document.addEventListener('keydown', handleEscapeKey);
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
document.removeEventListener('click', handleClickOutside);
|
|
||||||
document.removeEventListener('keydown', handleEscapeKey);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -150,12 +133,12 @@ onUnmounted(() => {
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
transition: all 0.15s ease;
|
transition: all 0.15s ease;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--toolbar-button-hover);
|
background-color: var(--toolbar-button-hover);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
background-color: var(--border-color);
|
background-color: var(--border-color);
|
||||||
}
|
}
|
||||||
@@ -167,7 +150,7 @@ onUnmounted(() => {
|
|||||||
height: 12px;
|
height: 12px;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
transition: color 0.15s ease;
|
transition: color 0.15s ease;
|
||||||
|
|
||||||
.menu-item:hover & {
|
.menu-item:hover & {
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
@@ -178,4 +161,4 @@ onUnmounted(() => {
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="linux-titlebar" style="--wails-draggable:drag" @contextmenu.prevent>
|
<div class="linux-titlebar" style="--wails-draggable:drag" @contextmenu.prevent>
|
||||||
<div class="titlebar-content" @dblclick="toggleMaximize" @contextmenu.prevent>
|
<div class="titlebar-content" @dblclick="handleToggleMaximize" @contextmenu.prevent>
|
||||||
<div class="titlebar-icon">
|
<div class="titlebar-icon">
|
||||||
<img src="/appicon.png" alt="voidraft"/>
|
<img src="/appicon.png" alt="voidraft"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!tabStore.isTabsEnabled && !isInSettings" class="titlebar-title" :title="fullTitleText">{{ titleText }}</div>
|
<div v-if="!tabStore.isTabsEnabled && !isInSettings" class="titlebar-title" :title="fullTitleText">
|
||||||
|
{{ titleText }}
|
||||||
|
</div>
|
||||||
<!-- 标签页容器区域 -->
|
<!-- 标签页容器区域 -->
|
||||||
<div class="titlebar-tabs" v-if="tabStore.isTabsEnabled && !isInSettings" style="--wails-draggable:drag">
|
<div class="titlebar-tabs" v-if="tabStore.isTabsEnabled && !isInSettings" style="--wails-draggable:drag">
|
||||||
<TabContainer />
|
<TabContainer/>
|
||||||
</div>
|
</div>
|
||||||
<!-- 设置页面标题 -->
|
<!-- 设置页面标题 -->
|
||||||
<div v-if="isInSettings" class="titlebar-title" :title="fullTitleText">{{ titleText }}</div>
|
<div v-if="isInSettings" class="titlebar-title" :title="fullTitleText">{{ titleText }}</div>
|
||||||
@@ -26,7 +28,7 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
class="titlebar-button maximize-button"
|
class="titlebar-button maximize-button"
|
||||||
@click="toggleMaximize"
|
@click="handleToggleMaximize"
|
||||||
:title="isMaximized ? t('titlebar.restore') : t('titlebar.maximize')"
|
:title="isMaximized ? t('titlebar.restore') : t('titlebar.maximize')"
|
||||||
>
|
>
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" v-if="!isMaximized">
|
<svg width="16" height="16" viewBox="0 0 16 16" v-if="!isMaximized">
|
||||||
@@ -55,81 +57,43 @@
|
|||||||
import {computed, onMounted, ref} from 'vue';
|
import {computed, onMounted, ref} from 'vue';
|
||||||
import {useI18n} from 'vue-i18n';
|
import {useI18n} from 'vue-i18n';
|
||||||
import {useRoute} from 'vue-router';
|
import {useRoute} from 'vue-router';
|
||||||
import * as runtime from '@wailsio/runtime';
|
|
||||||
import {useDocumentStore} from '@/stores/documentStore';
|
import {useDocumentStore} from '@/stores/documentStore';
|
||||||
|
import {useTabStore} from '@/stores/tabStore';
|
||||||
import TabContainer from '@/components/tabs/TabContainer.vue';
|
import TabContainer from '@/components/tabs/TabContainer.vue';
|
||||||
import {useTabStore} from "@/stores/tabStore";
|
import {
|
||||||
|
minimizeWindow,
|
||||||
|
toggleMaximize,
|
||||||
|
closeWindow,
|
||||||
|
getMaximizedState,
|
||||||
|
generateTitleText,
|
||||||
|
generateFullTitleText
|
||||||
|
} from './index';
|
||||||
|
|
||||||
const tabStore = useTabStore();
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const isMaximized = ref(false);
|
const tabStore = useTabStore();
|
||||||
const documentStore = useDocumentStore();
|
const documentStore = useDocumentStore();
|
||||||
|
|
||||||
// 判断是否在设置页面
|
const isMaximized = ref(false);
|
||||||
const isInSettings = computed(() => route.path.startsWith('/settings'));
|
const isInSettings = computed(() => route.path.startsWith('/settings'));
|
||||||
|
|
||||||
// 计算标题文本
|
|
||||||
const titleText = computed(() => {
|
const titleText = computed(() => {
|
||||||
if (isInSettings.value) {
|
if (isInSettings.value) return `voidraft - ${t('settings.title')}`;
|
||||||
return `voidraft - ` + t('settings.title');
|
return generateTitleText(documentStore.currentDocument?.title);
|
||||||
}
|
|
||||||
const currentDoc = documentStore.currentDocument;
|
|
||||||
if (currentDoc) {
|
|
||||||
// 限制文档标题长度,避免标题栏换行
|
|
||||||
const maxTitleLength = 30;
|
|
||||||
const truncatedTitle = currentDoc.title.length > maxTitleLength
|
|
||||||
? currentDoc.title.substring(0, maxTitleLength) + '...'
|
|
||||||
: currentDoc.title;
|
|
||||||
return `voidraft - ${truncatedTitle}`;
|
|
||||||
}
|
|
||||||
return 'voidraft';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算完整标题文本(用于tooltip)
|
|
||||||
const fullTitleText = computed(() => {
|
const fullTitleText = computed(() => {
|
||||||
if (isInSettings.value) {
|
if (isInSettings.value) return `voidraft - ${t('settings.title')}`;
|
||||||
return `voidraft - ` + t('settings.title');
|
return generateFullTitleText(documentStore.currentDocument?.title);
|
||||||
}
|
|
||||||
const currentDoc = documentStore.currentDocument;
|
|
||||||
return currentDoc ? `voidraft - ${currentDoc.title}` : 'voidraft';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const minimizeWindow = async () => {
|
const handleToggleMaximize = async () => {
|
||||||
try {
|
await toggleMaximize();
|
||||||
await runtime.Window.Minimise();
|
isMaximized.value = await getMaximizedState();
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleMaximize = async () => {
|
|
||||||
try {
|
|
||||||
await runtime.Window.ToggleMaximise();
|
|
||||||
await checkMaximizedState();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeWindow = async () => {
|
|
||||||
try {
|
|
||||||
await runtime.Window.Close();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkMaximizedState = async () => {
|
|
||||||
try {
|
|
||||||
isMaximized.value = await runtime.Window.IsMaximised();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await checkMaximizedState();
|
isMaximized.value = await getMaximizedState();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -160,7 +124,7 @@ onMounted(async () => {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
min-width: 0; /* 允许内容收缩 */
|
min-width: 0;
|
||||||
|
|
||||||
-webkit-context-menu: none;
|
-webkit-context-menu: none;
|
||||||
-moz-context-menu: none;
|
-moz-context-menu: none;
|
||||||
@@ -310,4 +274,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="macos-titlebar" style="--wails-draggable:drag" @contextmenu.prevent>
|
<div class="macos-titlebar" style="--wails-draggable:drag" @contextmenu.prevent>
|
||||||
<div class="titlebar-controls" style="--wails-draggable:no-drag" @contextmenu.prevent>
|
<div class="titlebar-controls" style="--wails-draggable:no-drag" @contextmenu.prevent>
|
||||||
<button
|
<button
|
||||||
class="titlebar-button close-button"
|
class="titlebar-button close-button"
|
||||||
@click="closeWindow"
|
@click="closeWindow"
|
||||||
:title="t('titlebar.close')"
|
:title="t('titlebar.close')"
|
||||||
>
|
>
|
||||||
<div class="button-icon">
|
<div class="button-icon">
|
||||||
<svg width="6" height="6" viewBox="0 0 6 6" v-show="showControlIcons">
|
<svg width="6" height="6" viewBox="0 0 6 6" v-show="showControlIcons">
|
||||||
@@ -12,11 +12,11 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="titlebar-button minimize-button"
|
class="titlebar-button minimize-button"
|
||||||
@click="minimizeWindow"
|
@click="minimizeWindow"
|
||||||
:title="t('titlebar.minimize')"
|
:title="t('titlebar.minimize')"
|
||||||
>
|
>
|
||||||
<div class="button-icon">
|
<div class="button-icon">
|
||||||
<svg width="8" height="1" viewBox="0 0 8 1" v-show="showControlIcons">
|
<svg width="8" height="1" viewBox="0 0 8 1" v-show="showControlIcons">
|
||||||
@@ -24,11 +24,11 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="titlebar-button maximize-button"
|
class="titlebar-button maximize-button"
|
||||||
@click="toggleMaximize"
|
@click="handleToggleMaximize"
|
||||||
:title="isMaximized ? t('titlebar.restore') : t('titlebar.maximize')"
|
:title="isMaximized ? t('titlebar.restore') : t('titlebar.maximize')"
|
||||||
>
|
>
|
||||||
<div class="button-icon">
|
<div class="button-icon">
|
||||||
<svg width="6" height="6" viewBox="0 0 6 6" v-show="showControlIcons && !isMaximized">
|
<svg width="6" height="6" viewBox="0 0 6 6" v-show="showControlIcons && !isMaximized">
|
||||||
@@ -42,98 +42,61 @@
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 标签页容器区域 -->
|
<!-- 标签页容器区域 -->
|
||||||
<div class="titlebar-tabs" v-if="tabStore.isTabsEnabled && !isInSettings" style="--wails-draggable:drag">
|
<div class="titlebar-tabs" v-if="tabStore.isTabsEnabled && !isInSettings" style="--wails-draggable:drag">
|
||||||
<TabContainer />
|
<TabContainer/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="titlebar-content" @dblclick="toggleMaximize" @contextmenu.prevent v-if="!tabStore.isTabsEnabled || isInSettings">
|
<div class="titlebar-content" @dblclick="handleToggleMaximize" @contextmenu.prevent
|
||||||
|
v-if="!tabStore.isTabsEnabled || isInSettings">
|
||||||
<div class="titlebar-title" :title="fullTitleText">{{ titleText }}</div>
|
<div class="titlebar-title" :title="fullTitleText">{{ titleText }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, computed } from 'vue';
|
import {computed, onMounted, ref} from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import {useI18n} from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import {useRoute} from 'vue-router';
|
||||||
import * as runtime from '@wailsio/runtime';
|
import {useDocumentStore} from '@/stores/documentStore';
|
||||||
import { useDocumentStore } from '@/stores/documentStore';
|
import {useTabStore} from '@/stores/tabStore';
|
||||||
import TabContainer from '@/components/tabs/TabContainer.vue';
|
import TabContainer from '@/components/tabs/TabContainer.vue';
|
||||||
import { useTabStore } from "@/stores/tabStore";
|
import {
|
||||||
|
minimizeWindow,
|
||||||
|
toggleMaximize,
|
||||||
|
closeWindow,
|
||||||
|
getMaximizedState,
|
||||||
|
generateTitleText,
|
||||||
|
generateFullTitleText
|
||||||
|
} from './index';
|
||||||
|
|
||||||
const tabStore = useTabStore();
|
const {t} = useI18n();
|
||||||
const { t } = useI18n();
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const isMaximized = ref(false);
|
const tabStore = useTabStore();
|
||||||
const showControlIcons = ref(false);
|
|
||||||
const documentStore = useDocumentStore();
|
const documentStore = useDocumentStore();
|
||||||
|
|
||||||
// 判断是否在设置页面
|
const isMaximized = ref(false);
|
||||||
|
const showControlIcons = ref(false);
|
||||||
const isInSettings = computed(() => route.path.startsWith('/settings'));
|
const isInSettings = computed(() => route.path.startsWith('/settings'));
|
||||||
|
|
||||||
const minimizeWindow = async () => {
|
|
||||||
try {
|
|
||||||
await runtime.Window.Minimise();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleMaximize = async () => {
|
|
||||||
try {
|
|
||||||
await runtime.Window.ToggleMaximise();
|
|
||||||
await checkMaximizedState();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeWindow = async () => {
|
|
||||||
try {
|
|
||||||
await runtime.Window.Close();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkMaximizedState = async () => {
|
|
||||||
try {
|
|
||||||
isMaximized.value = await runtime.Window.IsMaximised();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 计算标题文本
|
|
||||||
const titleText = computed(() => {
|
const titleText = computed(() => {
|
||||||
if (isInSettings.value) {
|
if (isInSettings.value) return `voidraft - ${t('settings.title')}`;
|
||||||
return `voidraft - ` + t('settings.title');
|
return generateTitleText(documentStore.currentDocument?.title);
|
||||||
}
|
|
||||||
const currentDoc = documentStore.currentDocument;
|
|
||||||
if (currentDoc) {
|
|
||||||
// 限制文档标题长度,避免标题栏换行
|
|
||||||
const maxTitleLength = 30;
|
|
||||||
const truncatedTitle = currentDoc.title.length > maxTitleLength
|
|
||||||
? currentDoc.title.substring(0, maxTitleLength) + '...'
|
|
||||||
: currentDoc.title;
|
|
||||||
return `voidraft - ${truncatedTitle}`;
|
|
||||||
}
|
|
||||||
return 'voidraft';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算完整标题文本(用于tooltip)
|
|
||||||
const fullTitleText = computed(() => {
|
const fullTitleText = computed(() => {
|
||||||
if (isInSettings.value) {
|
if (isInSettings.value) return `voidraft - ${t('settings.title')}`;
|
||||||
return `voidraft - ` + t('settings.title');
|
return generateFullTitleText(documentStore.currentDocument?.title);
|
||||||
}
|
|
||||||
const currentDoc = documentStore.currentDocument;
|
|
||||||
return currentDoc ? `voidraft - ${currentDoc.title}` : 'voidraft';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleToggleMaximize = async () => {
|
||||||
|
await toggleMaximize();
|
||||||
|
isMaximized.value = await getMaximizedState();
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await checkMaximizedState();
|
isMaximized.value = await getMaximizedState();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -147,11 +110,11 @@ onMounted(async () => {
|
|||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif;
|
||||||
|
|
||||||
-webkit-context-menu: none;
|
-webkit-context-menu: none;
|
||||||
-moz-context-menu: none;
|
-moz-context-menu: none;
|
||||||
context-menu: none;
|
context-menu: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.titlebar-button {
|
.titlebar-button {
|
||||||
.button-icon {
|
.button-icon {
|
||||||
@@ -168,7 +131,7 @@ onMounted(async () => {
|
|||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
-webkit-context-menu: none;
|
-webkit-context-menu: none;
|
||||||
-moz-context-menu: none;
|
-moz-context-menu: none;
|
||||||
context-menu: none;
|
context-menu: none;
|
||||||
@@ -187,7 +150,7 @@ onMounted(async () => {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.button-icon {
|
.button-icon {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s ease;
|
transition: opacity 0.2s ease;
|
||||||
@@ -198,7 +161,7 @@ onMounted(async () => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
color: rgba(0, 0, 0, 0.7);
|
color: rgba(0, 0, 0, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover .button-icon {
|
&:hover .button-icon {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
@@ -206,11 +169,11 @@ onMounted(async () => {
|
|||||||
|
|
||||||
.close-button {
|
.close-button {
|
||||||
background: #ff5f57;
|
background: #ff5f57;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #ff453a;
|
background: #ff453a;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
background: #d7463f;
|
background: #d7463f;
|
||||||
}
|
}
|
||||||
@@ -218,11 +181,11 @@ onMounted(async () => {
|
|||||||
|
|
||||||
.minimize-button {
|
.minimize-button {
|
||||||
background: #ffbd2e;
|
background: #ffbd2e;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #ffb524;
|
background: #ffb524;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
background: #e6a220;
|
background: #e6a220;
|
||||||
}
|
}
|
||||||
@@ -230,11 +193,11 @@ onMounted(async () => {
|
|||||||
|
|
||||||
.maximize-button {
|
.maximize-button {
|
||||||
background: #28ca42;
|
background: #28ca42;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #1ebe36;
|
background: #1ebe36;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
background: #1ba932;
|
background: #1ba932;
|
||||||
}
|
}
|
||||||
@@ -247,7 +210,7 @@ onMounted(async () => {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
-webkit-context-menu: none;
|
-webkit-context-menu: none;
|
||||||
-moz-context-menu: none;
|
-moz-context-menu: none;
|
||||||
context-menu: none;
|
context-menu: none;
|
||||||
@@ -261,34 +224,32 @@ onMounted(async () => {
|
|||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
overflow: visible; /* 允许TabContainer内部处理滚动 */
|
overflow: visible;
|
||||||
|
|
||||||
/* 确保TabContainer能够正确处理滚动 */
|
|
||||||
:deep(.tab-container) {
|
:deep(.tab-container) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.tab-bar) {
|
:deep(.tab-bar) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.tab-scroll-wrapper) {
|
:deep(.tab-scroll-wrapper) {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 确保底部线条能够正确显示 */
|
|
||||||
:deep(.tab-item) {
|
:deep(.tab-item) {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -319,13 +280,13 @@ onMounted(async () => {
|
|||||||
background: var(--toolbar-bg, #2d2d2d);
|
background: var(--toolbar-bg, #2d2d2d);
|
||||||
border-bottom-color: var(--toolbar-border, rgba(255, 255, 255, 0.1));
|
border-bottom-color: var(--toolbar-border, rgba(255, 255, 255, 0.1));
|
||||||
}
|
}
|
||||||
|
|
||||||
.titlebar-title {
|
.titlebar-title {
|
||||||
color: var(--toolbar-text, #fff);
|
color: var(--toolbar-text, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
.titlebar-button .button-icon {
|
.titlebar-button .button-icon {
|
||||||
color: rgba(255, 255, 255, 0.8);
|
color: rgba(255, 255, 255, 0.8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="windows-titlebar" style="--wails-draggable:drag">
|
<div class="windows-titlebar" style="--wails-draggable:drag">
|
||||||
<div class="titlebar-content" @dblclick="toggleMaximize" @contextmenu.prevent>
|
<div class="titlebar-content" @dblclick="handleToggleMaximize" @contextmenu.prevent>
|
||||||
<div class="titlebar-icon">
|
<div class="titlebar-icon">
|
||||||
<img src="/appicon.png" alt="voidraft"/>
|
<img src="/appicon.png" alt="voidraft"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!tabStore.isTabsEnabled && !isInSettings" class="titlebar-title" :title="fullTitleText">{{ titleText }}</div>
|
<div v-if="!tabStore.isTabsEnabled && !isInSettings" class="titlebar-title" :title="fullTitleText">
|
||||||
|
{{ titleText }}
|
||||||
|
</div>
|
||||||
<!-- 标签页容器区域 -->
|
<!-- 标签页容器区域 -->
|
||||||
<div class="titlebar-tabs" v-if="tabStore.isTabsEnabled && !isInSettings" style="--wails-draggable:drag">
|
<div class="titlebar-tabs" v-if="tabStore.isTabsEnabled && !isInSettings" style="--wails-draggable:drag">
|
||||||
<TabContainer />
|
<TabContainer/>
|
||||||
</div>
|
</div>
|
||||||
<!-- 设置页面标题 -->
|
<!-- 设置页面标题 -->
|
||||||
<div v-if="isInSettings" class="titlebar-title" :title="fullTitleText">{{ titleText }}</div>
|
<div v-if="isInSettings" class="titlebar-title" :title="fullTitleText">{{ titleText }}</div>
|
||||||
@@ -24,7 +26,7 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
class="titlebar-button maximize-button"
|
class="titlebar-button maximize-button"
|
||||||
@click="toggleMaximize"
|
@click="handleToggleMaximize"
|
||||||
:title="isMaximized ? t('titlebar.restore') : t('titlebar.maximize')"
|
:title="isMaximized ? t('titlebar.restore') : t('titlebar.maximize')"
|
||||||
>
|
>
|
||||||
<span class="titlebar-icon" v-html="maximizeIcon"></span>
|
<span class="titlebar-icon" v-html="maximizeIcon"></span>
|
||||||
@@ -45,84 +47,44 @@
|
|||||||
import {computed, onMounted, ref} from 'vue';
|
import {computed, onMounted, ref} from 'vue';
|
||||||
import {useI18n} from 'vue-i18n';
|
import {useI18n} from 'vue-i18n';
|
||||||
import {useRoute} from 'vue-router';
|
import {useRoute} from 'vue-router';
|
||||||
import * as runtime from '@wailsio/runtime';
|
|
||||||
import {useDocumentStore} from '@/stores/documentStore';
|
import {useDocumentStore} from '@/stores/documentStore';
|
||||||
|
import {useTabStore} from '@/stores/tabStore';
|
||||||
import TabContainer from '@/components/tabs/TabContainer.vue';
|
import TabContainer from '@/components/tabs/TabContainer.vue';
|
||||||
import {useTabStore} from "@/stores/tabStore";
|
import {
|
||||||
|
minimizeWindow,
|
||||||
|
toggleMaximize,
|
||||||
|
closeWindow,
|
||||||
|
getMaximizedState,
|
||||||
|
generateTitleText,
|
||||||
|
generateFullTitleText
|
||||||
|
} from './index';
|
||||||
|
|
||||||
const tabStore = useTabStore();
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const isMaximized = ref(false);
|
const tabStore = useTabStore();
|
||||||
const documentStore = useDocumentStore();
|
const documentStore = useDocumentStore();
|
||||||
|
|
||||||
// 计算属性用于图标,减少重复渲染
|
const isMaximized = ref(false);
|
||||||
const maximizeIcon = computed(() => isMaximized.value ? '' : '');
|
const maximizeIcon = computed(() => isMaximized.value ? '' : '');
|
||||||
|
|
||||||
// 判断是否在设置页面
|
|
||||||
const isInSettings = computed(() => route.path.startsWith('/settings'));
|
const isInSettings = computed(() => route.path.startsWith('/settings'));
|
||||||
|
|
||||||
// 计算标题文本
|
|
||||||
const titleText = computed(() => {
|
const titleText = computed(() => {
|
||||||
if (isInSettings.value) {
|
if (isInSettings.value) return `voidraft - ${t('settings.title')}`;
|
||||||
return `voidraft - ` + t('settings.title');
|
return generateTitleText(documentStore.currentDocument?.title);
|
||||||
}
|
|
||||||
const currentDoc = documentStore.currentDocument;
|
|
||||||
if (currentDoc) {
|
|
||||||
// 限制文档标题长度,避免标题栏换行
|
|
||||||
const maxTitleLength = 30;
|
|
||||||
const truncatedTitle = currentDoc.title.length > maxTitleLength
|
|
||||||
? currentDoc.title.substring(0, maxTitleLength) + '...'
|
|
||||||
: currentDoc.title;
|
|
||||||
return `voidraft - ${truncatedTitle}`;
|
|
||||||
}
|
|
||||||
return 'voidraft';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算完整标题文本(用于tooltip)
|
|
||||||
const fullTitleText = computed(() => {
|
const fullTitleText = computed(() => {
|
||||||
if (isInSettings.value) {
|
if (isInSettings.value) return `voidraft - ${t('settings.title')}`;
|
||||||
return `voidraft - ` + t('settings.title');
|
return generateFullTitleText(documentStore.currentDocument?.title);
|
||||||
}
|
|
||||||
const currentDoc = documentStore.currentDocument;
|
|
||||||
return currentDoc ? `voidraft - ${currentDoc.title}` : 'voidraft';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const minimizeWindow = async () => {
|
const handleToggleMaximize = async () => {
|
||||||
try {
|
await toggleMaximize();
|
||||||
await runtime.Window.Minimise();
|
isMaximized.value = await getMaximizedState();
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleMaximize = async () => {
|
|
||||||
try {
|
|
||||||
await runtime.Window.ToggleMaximise();
|
|
||||||
await checkMaximizedState();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeWindow = async () => {
|
|
||||||
try {
|
|
||||||
await runtime.Window.Close();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkMaximizedState = async () => {
|
|
||||||
try {
|
|
||||||
isMaximized.value = await runtime.Window.IsMaximised();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await checkMaximizedState();
|
isMaximized.value = await getMaximizedState();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -152,7 +114,7 @@ onMounted(async () => {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
min-width: 0; /* 允许内容收缩 */
|
min-width: 0;
|
||||||
|
|
||||||
-webkit-context-menu: none;
|
-webkit-context-menu: none;
|
||||||
-moz-context-menu: none;
|
-moz-context-menu: none;
|
||||||
@@ -178,7 +140,6 @@ onMounted(async () => {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
//margin-right: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.titlebar-controls {
|
.titlebar-controls {
|
||||||
@@ -254,4 +215,4 @@ onMounted(async () => {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
60
frontend/src/components/titlebar/index.ts
Normal file
60
frontend/src/components/titlebar/index.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import * as runtime from '@wailsio/runtime';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Titlebar utility functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Window control functions
|
||||||
|
export const minimizeWindow = async () => {
|
||||||
|
try {
|
||||||
|
await runtime.Window.Minimise();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to minimize window:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toggleMaximize = async () => {
|
||||||
|
try {
|
||||||
|
await runtime.Window.ToggleMaximise();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to toggle maximize:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const closeWindow = async () => {
|
||||||
|
try {
|
||||||
|
await runtime.Window.Close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to close window:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMaximizedState = async (): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
return await runtime.Window.IsMaximised();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to check maximized state:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate title text with optional truncation
|
||||||
|
*/
|
||||||
|
export const generateTitleText = (
|
||||||
|
title: string | undefined,
|
||||||
|
maxLength: number = 30
|
||||||
|
): string => {
|
||||||
|
if (!title) return 'voidraft';
|
||||||
|
const truncated = title.length > maxLength
|
||||||
|
? title.substring(0, maxLength) + '...'
|
||||||
|
: title;
|
||||||
|
return `voidraft - ${truncated}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate full title text (no truncation)
|
||||||
|
*/
|
||||||
|
export const generateFullTitleText = (title: string | undefined): string => {
|
||||||
|
return title ? `voidraft - ${title}` : 'voidraft';
|
||||||
|
};
|
||||||
@@ -1,49 +1,63 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
import {computed, nextTick, reactive, ref, watch} from 'vue';
|
||||||
import { useDocumentStore } from '@/stores/documentStore';
|
import {useDocumentStore} from '@/stores/documentStore';
|
||||||
import { useTabStore } from '@/stores/tabStore';
|
import {useTabStore} from '@/stores/tabStore';
|
||||||
import { useWindowStore } from '@/stores/windowStore';
|
import {useWindowStore} from '@/stores/windowStore';
|
||||||
import { useI18n } from 'vue-i18n';
|
import {useI18n} from 'vue-i18n';
|
||||||
import type { Document } from '@/../bindings/voidraft/internal/models/models';
|
import {useConfirm} from '@/composables';
|
||||||
|
import {validateDocumentTitle} from '@/common/utils/validation';
|
||||||
|
import {formatDateTime, truncateString} from '@/common/utils/formatter';
|
||||||
|
import type {Document} from '@/../bindings/voidraft/internal/models/ent/models';
|
||||||
|
|
||||||
|
// 类型定义
|
||||||
|
interface DocumentItem extends Document {
|
||||||
|
isCreateOption?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const documentStore = useDocumentStore();
|
const documentStore = useDocumentStore();
|
||||||
const tabStore = useTabStore();
|
const tabStore = useTabStore();
|
||||||
const windowStore = useWindowStore();
|
const windowStore = useWindowStore();
|
||||||
const { t } = useI18n();
|
const {t} = useI18n();
|
||||||
|
|
||||||
|
// DOM 引用
|
||||||
|
const inputRef = ref<HTMLInputElement>();
|
||||||
|
const editInputRef = ref<HTMLInputElement>();
|
||||||
|
|
||||||
// 组件状态
|
// 组件状态
|
||||||
const inputValue = ref('');
|
const state = reactive({
|
||||||
const inputRef = ref<HTMLInputElement>();
|
isLoaded: false,
|
||||||
const editingId = ref<number | null>(null);
|
searchQuery: '',
|
||||||
const editingTitle = ref('');
|
editing: {
|
||||||
const editInputRef = ref<HTMLInputElement>();
|
id: null as number | null,
|
||||||
const deleteConfirmId = ref<number | null>(null);
|
title: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 常量
|
// 常量
|
||||||
const MAX_TITLE_LENGTH = 50;
|
const MAX_TITLE_LENGTH = 50;
|
||||||
|
const DELETE_CONFIRM_TIMEOUT = 3000;
|
||||||
|
|
||||||
// 计算属性
|
// 计算属性
|
||||||
const currentDocName = computed(() => {
|
const currentDocName = computed(() => {
|
||||||
if (!documentStore.currentDocument) return t('toolbar.selectDocument');
|
if (!documentStore.currentDocument) return t('toolbar.selectDocument');
|
||||||
const title = documentStore.currentDocument.title;
|
return truncateString(documentStore.currentDocument.title || '', 12);
|
||||||
return title.length > 12 ? title.substring(0, 12) + '...' : title;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredItems = computed(() => {
|
const filteredItems = computed<DocumentItem[]>(() => {
|
||||||
const docs = documentStore.documentList;
|
const docs = documentStore.documentList;
|
||||||
const query = inputValue.value.trim();
|
const query = state.searchQuery.trim();
|
||||||
|
|
||||||
if (!query) return docs;
|
if (!query) return docs;
|
||||||
|
|
||||||
const filtered = docs.filter(doc =>
|
const filtered = docs.filter(doc =>
|
||||||
doc.title.toLowerCase().includes(query.toLowerCase())
|
(doc.title || '').toLowerCase().includes(query.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
// 如果输入的不是已存在文档的完整标题,添加创建选项
|
// 如果输入的不是已存在文档的完整标题,添加创建选项
|
||||||
const exactMatch = docs.some(doc => doc.title.toLowerCase() === query.toLowerCase());
|
const exactMatch = docs.some(doc => (doc.title || '').toLowerCase() === query.toLowerCase());
|
||||||
if (!exactMatch && query.length > 0) {
|
if (!exactMatch && query.length > 0) {
|
||||||
return [
|
return [
|
||||||
{ id: -1, title: t('toolbar.createDocument') + ` "${query}"`, isCreateOption: true } as any,
|
{id: -1, title: t('toolbar.createDocument') + ` "${query}"`, isCreateOption: true} as DocumentItem,
|
||||||
...filtered
|
...filtered
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -51,53 +65,32 @@ const filteredItems = computed(() => {
|
|||||||
return filtered;
|
return filtered;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 工具函数
|
|
||||||
const validateTitle = (title: string): string | null => {
|
|
||||||
if (!title.trim()) return t('toolbar.documentNameRequired');
|
|
||||||
if (title.trim().length > MAX_TITLE_LENGTH) {
|
|
||||||
return t('toolbar.documentNameTooLong', { max: MAX_TITLE_LENGTH });
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatTime = (dateString: string | null) => {
|
|
||||||
if (!dateString) return t('toolbar.unknownTime');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const date = new Date(dateString);
|
|
||||||
if (isNaN(date.getTime())) return t('toolbar.invalidDate');
|
|
||||||
|
|
||||||
const locale = t('locale') === 'zh-CN' ? 'zh-CN' : 'en-US';
|
|
||||||
return date.toLocaleString(locale, {
|
|
||||||
year: 'numeric',
|
|
||||||
month: '2-digit',
|
|
||||||
day: '2-digit',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit',
|
|
||||||
hour12: false
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
return t('toolbar.timeError');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 核心操作
|
// 核心操作
|
||||||
const openMenu = async () => {
|
const openMenu = async () => {
|
||||||
documentStore.openDocumentSelector();
|
|
||||||
await documentStore.getDocumentMetaList();
|
await documentStore.getDocumentMetaList();
|
||||||
|
documentStore.openDocumentSelector();
|
||||||
|
state.isLoaded = true;
|
||||||
await nextTick();
|
await nextTick();
|
||||||
inputRef.value?.focus();
|
inputRef.value?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 删除确认
|
||||||
|
const {isConfirming: isDeleting, startConfirm: startDeleteConfirm, reset: resetDeleteConfirm} = useConfirm({
|
||||||
|
timeout: DELETE_CONFIRM_TIMEOUT
|
||||||
|
});
|
||||||
|
|
||||||
const closeMenu = () => {
|
const closeMenu = () => {
|
||||||
|
state.isLoaded = false;
|
||||||
documentStore.closeDocumentSelector();
|
documentStore.closeDocumentSelector();
|
||||||
inputValue.value = '';
|
state.searchQuery = '';
|
||||||
editingId.value = null;
|
state.editing.id = null;
|
||||||
editingTitle.value = '';
|
state.editing.title = '';
|
||||||
deleteConfirmId.value = null;
|
resetDeleteConfirm();
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectDoc = async (doc: Document) => {
|
const selectDoc = async (doc: Document) => {
|
||||||
|
if (doc.id === undefined) return;
|
||||||
|
|
||||||
// 如果选择的就是当前文档,直接关闭菜单
|
// 如果选择的就是当前文档,直接关闭菜单
|
||||||
if (documentStore.currentDocument?.id === doc.id) {
|
if (documentStore.currentDocument?.id === doc.id) {
|
||||||
closeMenu();
|
closeMenu();
|
||||||
@@ -121,7 +114,7 @@ const selectDoc = async (doc: Document) => {
|
|||||||
|
|
||||||
const createDoc = async (title: string) => {
|
const createDoc = async (title: string) => {
|
||||||
const trimmedTitle = title.trim();
|
const trimmedTitle = title.trim();
|
||||||
const error = validateTitle(trimmedTitle);
|
const error = validateDocumentTitle(trimmedTitle, MAX_TITLE_LENGTH);
|
||||||
if (error) return;
|
if (error) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -132,20 +125,28 @@ const createDoc = async (title: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectItem = async (item: any) => {
|
const selectDocItem = async (item: any) => {
|
||||||
if (item.isCreateOption) {
|
if (item.isCreateOption) {
|
||||||
await createDoc(inputValue.value.trim());
|
await createDoc(state.searchQuery.trim());
|
||||||
} else {
|
} else {
|
||||||
await selectDoc(item);
|
await selectDoc(item);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 搜索框回车处理
|
||||||
|
const handleSearchEnter = () => {
|
||||||
|
const query = state.searchQuery.trim();
|
||||||
|
if (query && filteredItems.value.length > 0) {
|
||||||
|
selectDocItem(filteredItems.value[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 编辑操作
|
// 编辑操作
|
||||||
const startRename = (doc: Document, event: Event) => {
|
const renameDoc = (doc: Document, event: Event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
editingId.value = doc.id;
|
state.editing.id = doc.id ?? null;
|
||||||
editingTitle.value = doc.title;
|
state.editing.title = doc.title || '';
|
||||||
deleteConfirmId.value = null;
|
resetDeleteConfirm();
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
editInputRef.value?.focus();
|
editInputRef.value?.focus();
|
||||||
editInputRef.value?.select();
|
editInputRef.value?.select();
|
||||||
@@ -153,35 +154,41 @@ const startRename = (doc: Document, event: Event) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const saveEdit = async () => {
|
const saveEdit = async () => {
|
||||||
if (!editingId.value || !editingTitle.value.trim()) {
|
if (!state.editing.id || !state.editing.title.trim()) {
|
||||||
editingId.value = null;
|
state.editing.id = null;
|
||||||
editingTitle.value = '';
|
state.editing.title = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const trimmedTitle = editingTitle.value.trim();
|
const trimmedTitle = state.editing.title.trim();
|
||||||
const error = validateTitle(trimmedTitle);
|
const error = validateDocumentTitle(trimmedTitle, MAX_TITLE_LENGTH);
|
||||||
if (error) return;
|
if (error) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await documentStore.updateDocumentMetadata(editingId.value, trimmedTitle);
|
await documentStore.updateDocumentMetadata(state.editing.id, trimmedTitle);
|
||||||
await documentStore.getDocumentMetaList();
|
await documentStore.getDocumentMetaList();
|
||||||
|
|
||||||
// 如果tabs功能开启且该文档有标签页,更新标签页标题
|
// 如果tabs功能开启且该文档有标签页,更新标签页标题
|
||||||
if (tabStore.isTabsEnabled && tabStore.hasTab(editingId.value)) {
|
if (tabStore.isTabsEnabled && tabStore.hasTab(state.editing.id)) {
|
||||||
tabStore.updateTabTitle(editingId.value, trimmedTitle);
|
tabStore.updateTabTitle(state.editing.id, trimmedTitle);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update document:', error);
|
console.error('Failed to update document:', error);
|
||||||
} finally {
|
} finally {
|
||||||
editingId.value = null;
|
state.editing.id = null;
|
||||||
editingTitle.value = '';
|
state.editing.title = '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cancelEdit = () => {
|
||||||
|
state.editing.id = null;
|
||||||
|
state.editing.title = '';
|
||||||
|
};
|
||||||
|
|
||||||
// 其他操作
|
// 其他操作
|
||||||
const openInNewWindow = async (doc: Document, event: Event) => {
|
const openInNewWindow = async (doc: Document, event: Event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
if (doc.id === undefined) return;
|
||||||
try {
|
try {
|
||||||
await documentStore.openDocumentInNewWindow(doc.id);
|
await documentStore.openDocumentInNewWindow(doc.id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -191,13 +198,14 @@ const openInNewWindow = async (doc: Document, event: Event) => {
|
|||||||
|
|
||||||
const handleDelete = async (doc: Document, event: Event) => {
|
const handleDelete = async (doc: Document, event: Event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
if (doc.id === undefined) return;
|
||||||
|
|
||||||
if (deleteConfirmId.value === doc.id) {
|
if (isDeleting(doc.id)) {
|
||||||
// 确认删除前检查文档是否在其他窗口打开
|
// 确认删除前检查文档是否在其他窗口打开
|
||||||
const hasOpen = await windowStore.isDocumentWindowOpen(doc.id);
|
const hasOpen = await windowStore.isDocumentWindowOpen(doc.id);
|
||||||
if (hasOpen) {
|
if (hasOpen) {
|
||||||
documentStore.setError(doc.id, t('toolbar.alreadyOpenInNewWindow'));
|
documentStore.setError(doc.id, t('toolbar.alreadyOpenInNewWindow'));
|
||||||
deleteConfirmId.value = null;
|
resetDeleteConfirm();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,228 +218,181 @@ const handleDelete = async (doc: Document, event: Event) => {
|
|||||||
if (firstDoc) await selectDoc(firstDoc);
|
if (firstDoc) await selectDoc(firstDoc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deleteConfirmId.value = null;
|
resetDeleteConfirm();
|
||||||
} else {
|
} else {
|
||||||
// 进入确认状态
|
// 进入确认状态
|
||||||
deleteConfirmId.value = doc.id;
|
startDeleteConfirm(doc.id);
|
||||||
editingId.value = null;
|
state.editing.id = null;
|
||||||
|
|
||||||
// 3秒后自动取消确认状态
|
|
||||||
setTimeout(() => {
|
|
||||||
if (deleteConfirmId.value === doc.id) {
|
|
||||||
deleteConfirmId.value = null;
|
|
||||||
}
|
|
||||||
}, 3000);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 键盘事件处理
|
// 切换菜单
|
||||||
const createKeyHandler = (handlers: Record<string, () => void>) => (event: KeyboardEvent) => {
|
const toggleMenu = () => {
|
||||||
const handler = handlers[event.key];
|
if (documentStore.showDocumentSelector) {
|
||||||
if (handler) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
handler();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleGlobalKeydown = createKeyHandler({
|
|
||||||
Escape: () => {
|
|
||||||
if (editingId.value) {
|
|
||||||
editingId.value = null;
|
|
||||||
editingTitle.value = '';
|
|
||||||
} else if (deleteConfirmId.value) {
|
|
||||||
deleteConfirmId.value = null;
|
|
||||||
} else {
|
|
||||||
closeMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleInputKeydown = createKeyHandler({
|
|
||||||
Enter: () => {
|
|
||||||
const query = inputValue.value.trim();
|
|
||||||
if (query && filteredItems.value.length > 0) {
|
|
||||||
selectItem(filteredItems.value[0]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Escape: closeMenu
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleEditKeydown = createKeyHandler({
|
|
||||||
Enter: saveEdit,
|
|
||||||
Escape: () => {
|
|
||||||
editingId.value = null;
|
|
||||||
editingTitle.value = '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 点击外部关闭
|
|
||||||
const handleClickOutside = (event: Event) => {
|
|
||||||
const target = event.target as HTMLElement;
|
|
||||||
if (!target.closest('.document-selector')) {
|
|
||||||
closeMenu();
|
closeMenu();
|
||||||
|
} else {
|
||||||
|
openMenu();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 生命周期
|
|
||||||
onMounted(() => {
|
|
||||||
document.addEventListener('click', handleClickOutside);
|
|
||||||
document.addEventListener('keydown', handleGlobalKeydown);
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
document.removeEventListener('click', handleClickOutside);
|
|
||||||
document.removeEventListener('keydown', handleGlobalKeydown);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听菜单状态变化
|
// 监听菜单状态变化
|
||||||
watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||||
if (isOpen) {
|
if (isOpen && !state.isLoaded) {
|
||||||
openMenu();
|
openMenu();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="document-selector">
|
<div class="document-selector" v-click-outside="closeMenu">
|
||||||
<!-- 选择器按钮 -->
|
<!-- 选择器按钮 -->
|
||||||
<button class="doc-btn" @click="documentStore.toggleDocumentSelector">
|
<button class="doc-btn" @click="toggleMenu">
|
||||||
<span class="doc-icon">
|
<span class="doc-icon">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
|
<path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
|
||||||
<polyline points="14,2 14,8 20,8"></polyline>
|
<polyline points="14,2 14,8 20,8"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<span class="doc-name">{{ currentDocName }}</span>
|
<span class="doc-name">{{ currentDocName }}</span>
|
||||||
<span class="arrow" :class="{ open: documentStore.showDocumentSelector }">▲</span>
|
<span class="arrow" :class="{ open: state.isLoaded }">▲</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- 菜单 -->
|
<!-- 菜单 -->
|
||||||
<div v-if="documentStore.showDocumentSelector" class="doc-menu">
|
<Transition name="slide-up">
|
||||||
<!-- 输入框 -->
|
<div v-if="state.isLoaded" class="doc-menu">
|
||||||
<div class="input-box">
|
<!-- 输入框 -->
|
||||||
<input
|
<div class="input-box">
|
||||||
ref="inputRef"
|
<input
|
||||||
v-model="inputValue"
|
ref="inputRef"
|
||||||
type="text"
|
v-model="state.searchQuery"
|
||||||
class="main-input"
|
type="text"
|
||||||
:placeholder="t('toolbar.searchOrCreateDocument')"
|
class="main-input"
|
||||||
:maxlength="MAX_TITLE_LENGTH"
|
:placeholder="t('toolbar.searchOrCreateDocument')"
|
||||||
@keydown="handleInputKeydown"
|
:maxlength="MAX_TITLE_LENGTH"
|
||||||
/>
|
@keydown.enter="handleSearchEnter"
|
||||||
<svg class="input-icon" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"
|
@keydown.esc="closeMenu"
|
||||||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
/>
|
||||||
<circle cx="11" cy="11" r="8"></circle>
|
<svg class="input-icon" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"
|
||||||
<path d="m21 21-4.35-4.35"></path>
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
</svg>
|
<circle cx="11" cy="11" r="8"></circle>
|
||||||
</div>
|
<path d="m21 21-4.35-4.35"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 项目列表 -->
|
<!-- 项目列表 -->
|
||||||
<div class="item-list">
|
<div class="item-list">
|
||||||
<div
|
<div
|
||||||
v-for="item in filteredItems"
|
v-for="item in filteredItems"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
class="list-item"
|
class="list-item"
|
||||||
:class="{
|
:class="{
|
||||||
'active': !item.isCreateOption && documentStore.currentDocument?.id === item.id,
|
'active': !item.isCreateOption && documentStore.currentDocument?.id === item.id,
|
||||||
'create-item': item.isCreateOption
|
'create-item': item.isCreateOption
|
||||||
}"
|
}"
|
||||||
@click="selectItem(item)"
|
@click="selectDocItem(item)"
|
||||||
>
|
>
|
||||||
<!-- 创建选项 -->
|
<!-- 创建选项 -->
|
||||||
<div v-if="item.isCreateOption" class="create-option">
|
<div v-if="item.isCreateOption" class="create-option">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none"
|
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none"
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M5 12h14"></path>
|
<path d="M5 12h14"></path>
|
||||||
<path d="M12 5v14"></path>
|
<path d="M12 5v14"></path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>{{ item.title }}</span>
|
<span>{{ item.title }}</span>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 文档项 -->
|
|
||||||
<div v-else class="doc-item-content">
|
|
||||||
<!-- 普通显示 -->
|
|
||||||
<div v-if="editingId !== item.id" class="doc-info">
|
|
||||||
<div class="doc-title">{{ item.title }}</div>
|
|
||||||
<!-- 根据状态显示错误信息或时间 -->
|
|
||||||
<div v-if="documentStore.selectorError?.docId === item.id" class="doc-error">
|
|
||||||
{{ documentStore.selectorError?.message }}
|
|
||||||
</div>
|
|
||||||
<div v-else class="doc-date">{{ formatTime(item.updatedAt) }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑状态 -->
|
<!-- 文档项 -->
|
||||||
<div v-else class="doc-edit">
|
<div v-else class="doc-item-content">
|
||||||
<input
|
<!-- 普通显示 -->
|
||||||
:ref="el => editInputRef = el as HTMLInputElement"
|
<div v-if="state.editing.id !== item.id" class="doc-info">
|
||||||
v-model="editingTitle"
|
<div class="doc-title">{{ item.title }}</div>
|
||||||
type="text"
|
<!-- 根据状态显示错误信息或时间 -->
|
||||||
class="edit-input"
|
<div v-if="documentStore.selectorError?.docId === item.id" class="doc-error">
|
||||||
:maxlength="MAX_TITLE_LENGTH"
|
{{ documentStore.selectorError?.message }}
|
||||||
@keydown="handleEditKeydown"
|
</div>
|
||||||
@blur="saveEdit"
|
<div v-else class="doc-date">{{ formatDateTime(item.updated_at) }}</div>
|
||||||
@click.stop
|
</div>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 编辑状态 -->
|
||||||
<div v-if="editingId !== item.id" class="doc-actions">
|
<div v-else class="doc-edit">
|
||||||
<!-- 只有非当前文档才显示在新窗口打开按钮 -->
|
<input
|
||||||
<button
|
:ref="el => editInputRef = el as HTMLInputElement"
|
||||||
v-if="documentStore.currentDocument?.id !== item.id"
|
v-model="state.editing.title"
|
||||||
class="action-btn"
|
type="text"
|
||||||
@click="openInNewWindow(item, $event)"
|
class="edit-input"
|
||||||
:title="t('toolbar.openInNewWindow')"
|
:maxlength="MAX_TITLE_LENGTH"
|
||||||
>
|
@keydown.enter="saveEdit"
|
||||||
<svg width="12" height="12" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
@keydown.esc="cancelEdit"
|
||||||
fill="currentColor">
|
@blur="saveEdit"
|
||||||
<path
|
@click.stop
|
||||||
d="M172.8 1017.6c-89.6 0-166.4-70.4-166.4-166.4V441.6c0-89.6 70.4-166.4 166.4-166.4h416c89.6 0 166.4 70.4 166.4 166.4v416c0 89.6-70.4 166.4-166.4 166.4l-416-6.4z m0-659.2c-51.2 0-89.6 38.4-89.6 89.6v416c0 51.2 38.4 89.6 89.6 89.6h416c51.2 0 89.6-38.4 89.6-89.6V441.6c0-51.2-38.4-89.6-89.6-89.6H172.8z"></path>
|
/>
|
||||||
<path
|
</div>
|
||||||
d="M851.2 19.2H435.2C339.2 19.2 268.8 96 268.8 185.6v25.6h70.4v-25.6c0-51.2 38.4-89.6 89.6-89.6h409.6c51.2 0 89.6 38.4 89.6 89.6v409.6c0 51.2-38.4 89.6-89.6 89.6h-38.4V768h51.2c96 0 166.4-76.8 166.4-166.4V185.6c0-96-76.8-166.4-166.4-166.4z"></path>
|
|
||||||
</svg>
|
<!-- 操作按钮 -->
|
||||||
</button>
|
<div v-if="state.editing.id !== item.id" class="doc-actions">
|
||||||
<button class="action-btn" @click="startRename(item, $event)" :title="t('toolbar.rename')">
|
<!-- 只有非当前文档才显示在新窗口打开按钮 -->
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none"
|
<button
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
v-if="documentStore.currentDocument?.id !== item.id"
|
||||||
<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"></path>
|
class="action-btn"
|
||||||
</svg>
|
@click="openInNewWindow(item, $event)"
|
||||||
</button>
|
:title="t('toolbar.openInNewWindow')"
|
||||||
<button
|
>
|
||||||
v-if="documentStore.documentList.length > 1 && item.id !== 1"
|
<svg width="12" height="12" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
class="action-btn delete-btn"
|
fill="currentColor">
|
||||||
:class="{ 'delete-confirm': deleteConfirmId === item.id }"
|
<path
|
||||||
@click="handleDelete(item, $event)"
|
d="M172.8 1017.6c-89.6 0-166.4-70.4-166.4-166.4V441.6c0-89.6 70.4-166.4 166.4-166.4h416c89.6 0 166.4 70.4 166.4 166.4v416c0 89.6-70.4 166.4-166.4 166.4l-416-6.4z m0-659.2c-51.2 0-89.6 38.4-89.6 89.6v416c0 51.2 38.4 89.6 89.6 89.6h416c51.2 0 89.6-38.4 89.6-89.6V441.6c0-51.2-38.4-89.6-89.6-89.6H172.8z"></path>
|
||||||
:title="deleteConfirmId === item.id ? t('toolbar.confirmDelete') : t('toolbar.delete')"
|
<path
|
||||||
>
|
d="M851.2 19.2H435.2C339.2 19.2 268.8 96 268.8 185.6v25.6h70.4v-25.6c0-51.2 38.4-89.6 89.6-89.6h409.6c51.2 0 89.6 38.4 89.6 89.6v409.6c0 51.2-38.4 89.6-89.6 89.6h-38.4V768h51.2c96 0 166.4-76.8 166.4-166.4V185.6c0-96-76.8-166.4-166.4-166.4z"></path>
|
||||||
<svg v-if="deleteConfirmId !== item.id" xmlns="http://www.w3.org/2000/svg" width="12" height="12"
|
</svg>
|
||||||
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
</button>
|
||||||
stroke-linejoin="round">
|
<button class="action-btn" @click="renameDoc(item, $event)" :title="t('toolbar.rename')">
|
||||||
<polyline points="3,6 5,6 21,6"></polyline>
|
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none"
|
||||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
</svg>
|
<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"></path>
|
||||||
<span v-else class="confirm-text">{{ t('toolbar.confirm') }}</span>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="documentStore.documentList.length > 1 && item.id !== 1"
|
||||||
|
class="action-btn delete-btn"
|
||||||
|
:class="{ 'delete-confirm': isDeleting(item.id!) }"
|
||||||
|
@click="handleDelete(item, $event)"
|
||||||
|
:title="isDeleting(item.id!) ? t('toolbar.confirmDelete') : t('toolbar.delete')"
|
||||||
|
>
|
||||||
|
<svg v-if="!isDeleting(item.id!)" xmlns="http://www.w3.org/2000/svg" width="12" height="12"
|
||||||
|
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round">
|
||||||
|
<polyline points="3,6 5,6 21,6"></polyline>
|
||||||
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||||
|
</svg>
|
||||||
|
<span v-else class="confirm-text">{{ t('toolbar.confirm') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 空状态 -->
|
||||||
<div v-if="filteredItems.length === 0" class="empty">
|
<div v-if="filteredItems.length === 0" class="empty">
|
||||||
{{ t('toolbar.noDocumentFound') }}
|
{{ t('toolbar.noDocumentFound') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 加载状态 -->
|
|
||||||
<div v-if="documentStore.isLoading" class="loading">
|
|
||||||
{{ t('toolbar.loading') }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.slide-up-enter-active,
|
||||||
|
.slide-up-leave-active {
|
||||||
|
transition: opacity 0.15s ease, transform 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-from,
|
||||||
|
.slide-up-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(8px);
|
||||||
|
}
|
||||||
|
|
||||||
.document-selector {
|
.document-selector {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
@@ -483,8 +444,8 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
|||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
width: 260px;
|
width: 300px;
|
||||||
max-height: calc(100vh - 40px); // 限制最大高度,留出titlebar空间(32px)和一些边距
|
max-height: calc(100vh - 40px);
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -527,7 +488,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.item-list {
|
.item-list {
|
||||||
max-height: calc(100vh - 100px); // 为输入框和边距预留空间
|
max-height: calc(100vh - 100px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
@@ -594,7 +555,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.doc-error {
|
.doc-error {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
color: var(--text-danger);
|
color: var(--text-danger);
|
||||||
@@ -669,7 +630,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty, .loading {
|
.empty {
|
||||||
padding: 16px 8px;
|
padding: 16px 8px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
@@ -680,9 +641,17 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fadeInOut {
|
@keyframes fadeInOut {
|
||||||
0% { opacity: 0; }
|
0% {
|
||||||
10% { opacity: 1; }
|
opacity: 0;
|
||||||
90% { opacity: 1; }
|
}
|
||||||
100% { opacity: 0; }
|
10% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
5
frontend/src/composables/index.ts
Normal file
5
frontend/src/composables/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export { useConfirm } from './useConfirm';
|
||||||
|
export type { UseConfirmOptions } from './useConfirm';
|
||||||
|
|
||||||
|
export { usePolling } from './usePolling';
|
||||||
|
export type { UsePollingOptions, UsePollingReturn } from './usePolling';
|
||||||
174
frontend/src/composables/useConfirm.ts
Normal file
174
frontend/src/composables/useConfirm.ts
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import { ref, readonly, onUnmounted, type Ref, type DeepReadonly } from 'vue';
|
||||||
|
|
||||||
|
export interface UseConfirmOptions<T extends string | number = string | number> {
|
||||||
|
/** Auto cancel timeout in ms (default: 3000, set 0 to disable) */
|
||||||
|
timeout?: number;
|
||||||
|
/** Callback when confirmed */
|
||||||
|
onConfirm?: (id: T) => void | Promise<void>;
|
||||||
|
/** Callback when cancelled (timeout or manual) */
|
||||||
|
onCancel?: (id: T) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UseConfirmReturn<T extends string | number = string | number> {
|
||||||
|
/** Current confirming id (readonly) */
|
||||||
|
confirmId: DeepReadonly<Ref<T | null>>;
|
||||||
|
/** Whether confirm action is executing */
|
||||||
|
isPending: DeepReadonly<Ref<boolean>>;
|
||||||
|
/** Check if a specific id is in confirming state */
|
||||||
|
isConfirming: (id: T) => boolean;
|
||||||
|
/** Start confirming state (with auto timeout) */
|
||||||
|
startConfirm: (id: T) => void;
|
||||||
|
/** Request confirmation (toggle between request and execute) */
|
||||||
|
requestConfirm: (id: T) => Promise<boolean>;
|
||||||
|
/** Manually confirm current id */
|
||||||
|
confirm: () => Promise<void>;
|
||||||
|
/** Cancel confirmation */
|
||||||
|
cancel: () => void;
|
||||||
|
/** Reset without triggering callbacks */
|
||||||
|
reset: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable for handling confirm actions (e.g., delete confirmation)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* // Basic usage
|
||||||
|
* const { isConfirming, requestConfirm } = useConfirm({
|
||||||
|
* timeout: 3000,
|
||||||
|
* onConfirm: async (id) => { await deleteItem(id) }
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* // In template
|
||||||
|
* <button @click="requestConfirm('delete')">
|
||||||
|
* {{ isConfirming('delete') ? 'Confirm?' : 'Delete' }}
|
||||||
|
* </button>
|
||||||
|
*
|
||||||
|
* // With loading state
|
||||||
|
* const { isPending, requestConfirm } = useConfirm({ ... })
|
||||||
|
* <button :disabled="isPending" @click="requestConfirm('id')">
|
||||||
|
* {{ isPending ? 'Processing...' : 'Delete' }}
|
||||||
|
* </button>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function useConfirm<T extends string | number = string | number>(
|
||||||
|
options: UseConfirmOptions<T> = {}
|
||||||
|
): UseConfirmReturn<T> {
|
||||||
|
const { timeout = 3000, onConfirm, onCancel } = options;
|
||||||
|
|
||||||
|
const confirmId = ref<T | null>(null) as Ref<T | null>;
|
||||||
|
const isPending = ref(false);
|
||||||
|
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
|
const clearTimer = (): void => {
|
||||||
|
if (timeoutId) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
timeoutId = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a specific id is in confirming state
|
||||||
|
*/
|
||||||
|
const isConfirming = (id: T): boolean => {
|
||||||
|
return confirmId.value === id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start confirming state for an id (with auto timeout)
|
||||||
|
*/
|
||||||
|
const startConfirm = (id: T): void => {
|
||||||
|
clearTimer();
|
||||||
|
confirmId.value = id;
|
||||||
|
|
||||||
|
// Auto cancel after timeout (0 = disabled)
|
||||||
|
if (timeout > 0) {
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
if (confirmId.value === id) {
|
||||||
|
confirmId.value = null;
|
||||||
|
onCancel?.(id);
|
||||||
|
}
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request confirmation for an id
|
||||||
|
* - First click: enter confirming state
|
||||||
|
* - Second click: execute confirm action
|
||||||
|
* @returns true if confirmed, false if entered confirming state
|
||||||
|
*/
|
||||||
|
const requestConfirm = async (id: T): Promise<boolean> => {
|
||||||
|
// Prevent action while pending
|
||||||
|
if (isPending.value) return false;
|
||||||
|
|
||||||
|
if (confirmId.value === id) {
|
||||||
|
// Already confirming, execute action
|
||||||
|
clearTimer();
|
||||||
|
isPending.value = true;
|
||||||
|
try {
|
||||||
|
await onConfirm?.(id);
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
confirmId.value = null;
|
||||||
|
isPending.value = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Enter confirming state
|
||||||
|
startConfirm(id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually confirm the current id
|
||||||
|
*/
|
||||||
|
const confirm = async (): Promise<void> => {
|
||||||
|
if (confirmId.value === null || isPending.value) return;
|
||||||
|
|
||||||
|
clearTimer();
|
||||||
|
const id = confirmId.value;
|
||||||
|
isPending.value = true;
|
||||||
|
try {
|
||||||
|
await onConfirm?.(id);
|
||||||
|
} finally {
|
||||||
|
confirmId.value = null;
|
||||||
|
isPending.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the confirming state
|
||||||
|
*/
|
||||||
|
const cancel = (): void => {
|
||||||
|
if (confirmId.value === null) return;
|
||||||
|
|
||||||
|
const id = confirmId.value;
|
||||||
|
clearTimer();
|
||||||
|
confirmId.value = null;
|
||||||
|
onCancel?.(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset state without triggering callbacks
|
||||||
|
*/
|
||||||
|
const reset = (): void => {
|
||||||
|
clearTimer();
|
||||||
|
confirmId.value = null;
|
||||||
|
isPending.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cleanup on unmount
|
||||||
|
onUnmounted(clearTimer);
|
||||||
|
|
||||||
|
return {
|
||||||
|
confirmId: readonly(confirmId),
|
||||||
|
isPending: readonly(isPending),
|
||||||
|
isConfirming,
|
||||||
|
startConfirm,
|
||||||
|
requestConfirm,
|
||||||
|
confirm,
|
||||||
|
cancel,
|
||||||
|
reset
|
||||||
|
};
|
||||||
|
}
|
||||||
147
frontend/src/composables/usePolling.ts
Normal file
147
frontend/src/composables/usePolling.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import { ref, readonly, onUnmounted, type Ref, type DeepReadonly } from 'vue';
|
||||||
|
|
||||||
|
export interface UsePollingOptions<T> {
|
||||||
|
/** Polling interval in ms (default: 500) */
|
||||||
|
interval?: number;
|
||||||
|
/** Execute immediately when started (default: true) */
|
||||||
|
immediate?: boolean;
|
||||||
|
/** Auto-stop condition, return true to stop polling */
|
||||||
|
shouldStop?: (data: T) => boolean;
|
||||||
|
/** Callback on each successful poll */
|
||||||
|
onSuccess?: (data: T) => void;
|
||||||
|
/** Callback when error occurs */
|
||||||
|
onError?: (error: unknown) => void;
|
||||||
|
/** Callback when polling stops (either manual or auto) */
|
||||||
|
onStop?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UsePollingReturn<T> {
|
||||||
|
/** Latest fetched data (readonly) */
|
||||||
|
data: DeepReadonly<Ref<T | null>>;
|
||||||
|
/** Error message if any (readonly) */
|
||||||
|
error: DeepReadonly<Ref<string>>;
|
||||||
|
/** Whether polling is active (readonly) */
|
||||||
|
isActive: DeepReadonly<Ref<boolean>>;
|
||||||
|
/** Start polling */
|
||||||
|
start: () => void;
|
||||||
|
/** Stop polling */
|
||||||
|
stop: () => void;
|
||||||
|
/** Reset all state (also stops polling) */
|
||||||
|
reset: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable for polling async operations with auto-stop support
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* // Basic usage
|
||||||
|
* const { data, isActive, start, stop } = usePolling(
|
||||||
|
* () => api.getProgress(),
|
||||||
|
* {
|
||||||
|
* interval: 200,
|
||||||
|
* shouldStop: (d) => d.progress >= 100 || !!d.error,
|
||||||
|
* onSuccess: (d) => console.log('Progress:', d.progress)
|
||||||
|
* }
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* // Start polling
|
||||||
|
* start()
|
||||||
|
*
|
||||||
|
* // With reactive data binding
|
||||||
|
* <div>{{ isActive ? `${data?.progress}%` : 'Idle' }}</div>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function usePolling<T>(
|
||||||
|
fetcher: () => Promise<T>,
|
||||||
|
options: UsePollingOptions<T> = {}
|
||||||
|
): UsePollingReturn<T> {
|
||||||
|
const {
|
||||||
|
interval = 500,
|
||||||
|
immediate = true,
|
||||||
|
shouldStop,
|
||||||
|
onSuccess,
|
||||||
|
onError,
|
||||||
|
onStop
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const data = ref<T | null>(null) as Ref<T | null>;
|
||||||
|
const error = ref('');
|
||||||
|
const isActive = ref(false);
|
||||||
|
let timerId = 0;
|
||||||
|
|
||||||
|
const clearTimer = (): void => {
|
||||||
|
if (timerId) {
|
||||||
|
clearInterval(timerId);
|
||||||
|
timerId = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const poll = async (): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const result = await fetcher();
|
||||||
|
data.value = result;
|
||||||
|
error.value = '';
|
||||||
|
onSuccess?.(result);
|
||||||
|
|
||||||
|
// Check auto-stop condition
|
||||||
|
if (shouldStop?.(result)) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
error.value = e instanceof Error ? e.message : String(e);
|
||||||
|
onError?.(e);
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start polling
|
||||||
|
*/
|
||||||
|
const start = (): void => {
|
||||||
|
if (isActive.value) return;
|
||||||
|
|
||||||
|
isActive.value = true;
|
||||||
|
error.value = '';
|
||||||
|
|
||||||
|
// Execute immediately if configured
|
||||||
|
if (immediate) {
|
||||||
|
poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
timerId = window.setInterval(poll, interval);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop polling
|
||||||
|
*/
|
||||||
|
const stop = (): void => {
|
||||||
|
if (!isActive.value) return;
|
||||||
|
|
||||||
|
clearTimer();
|
||||||
|
isActive.value = false;
|
||||||
|
onStop?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all state to initial values
|
||||||
|
*/
|
||||||
|
const reset = (): void => {
|
||||||
|
clearTimer();
|
||||||
|
data.value = null;
|
||||||
|
error.value = '';
|
||||||
|
isActive.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cleanup on unmount
|
||||||
|
onUnmounted(clearTimer);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: readonly(data),
|
||||||
|
error: readonly(error),
|
||||||
|
isActive: readonly(isActive),
|
||||||
|
start,
|
||||||
|
stop,
|
||||||
|
reset
|
||||||
|
};
|
||||||
|
}
|
||||||
37
frontend/src/directives/clickOutside.ts
Normal file
37
frontend/src/directives/clickOutside.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import type { Directive, DirectiveBinding } from 'vue';
|
||||||
|
|
||||||
|
type ClickOutsideHandler = (event: MouseEvent) => void;
|
||||||
|
|
||||||
|
interface ClickOutsideElement extends HTMLElement {
|
||||||
|
_clickOutsideHandler?: (event: MouseEvent) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* v-click-outside directive
|
||||||
|
* Triggers a callback when clicking outside the element
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* <div v-click-outside="handleClickOutside">...</div>
|
||||||
|
*/
|
||||||
|
export const clickOutside: Directive<ClickOutsideElement, ClickOutsideHandler> = {
|
||||||
|
mounted(el: ClickOutsideElement, binding: DirectiveBinding<ClickOutsideHandler>) {
|
||||||
|
const handler = (event: MouseEvent) => {
|
||||||
|
const target = event.target as Node;
|
||||||
|
if (el && !el.contains(target)) {
|
||||||
|
binding.value(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
el._clickOutsideHandler = handler;
|
||||||
|
document.addEventListener('click', handler);
|
||||||
|
},
|
||||||
|
|
||||||
|
unmounted(el: ClickOutsideElement) {
|
||||||
|
if (el._clickOutsideHandler) {
|
||||||
|
document.removeEventListener('click', el._clickOutsideHandler);
|
||||||
|
delete el._clickOutsideHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default clickOutside;
|
||||||
13
frontend/src/directives/index.ts
Normal file
13
frontend/src/directives/index.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
import { clickOutside } from './clickOutside';
|
||||||
|
|
||||||
|
export { clickOutside };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register all custom directives
|
||||||
|
*/
|
||||||
|
export function registerDirectives(app: App) {
|
||||||
|
app.directive('click-outside', clickOutside);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default registerDirectives;
|
||||||
@@ -33,13 +33,6 @@ export default {
|
|||||||
confirmDelete: 'Click again to confirm delete',
|
confirmDelete: 'Click again to confirm delete',
|
||||||
openInNewWindow: 'Open in New Window',
|
openInNewWindow: 'Open in New Window',
|
||||||
alreadyOpenInNewWindow: 'Already open in another window',
|
alreadyOpenInNewWindow: 'Already open in another window',
|
||||||
documentNameTooLong: 'Document name cannot exceed {max} characters',
|
|
||||||
documentNameRequired: 'Document name cannot be empty',
|
|
||||||
cannotDeleteLastDocument: 'Cannot delete the last document',
|
|
||||||
cannotDeleteDefaultDocument: 'Cannot delete the default document',
|
|
||||||
unknownTime: 'Unknown time',
|
|
||||||
invalidDate: 'Invalid date',
|
|
||||||
timeError: 'Time error',
|
|
||||||
},
|
},
|
||||||
languages: {
|
languages: {
|
||||||
'zh-CN': 'Chinese',
|
'zh-CN': 'Chinese',
|
||||||
@@ -53,7 +46,7 @@ export default {
|
|||||||
keybindings: {
|
keybindings: {
|
||||||
headers: {
|
headers: {
|
||||||
shortcut: 'Shortcut',
|
shortcut: 'Shortcut',
|
||||||
category: 'Category',
|
extension: 'Extension',
|
||||||
description: 'Description'
|
description: 'Description'
|
||||||
},
|
},
|
||||||
commands: {
|
commands: {
|
||||||
@@ -234,14 +227,10 @@ export default {
|
|||||||
sshKeyPassphrase: 'SSH Key Passphrase',
|
sshKeyPassphrase: 'SSH Key Passphrase',
|
||||||
sshKeyPassphrasePlaceholder: 'Enter SSH key passphrase',
|
sshKeyPassphrasePlaceholder: 'Enter SSH key passphrase',
|
||||||
backupOperations: 'Backup Operations',
|
backupOperations: 'Backup Operations',
|
||||||
pushToRemote: 'Push to Remote',
|
syncToRemote: 'Sync to Remote',
|
||||||
pushing: 'Pushing...',
|
syncing: 'Syncing...',
|
||||||
actions: {
|
actions: {
|
||||||
push: 'Push',
|
sync: 'Sync',
|
||||||
},
|
|
||||||
status: {
|
|
||||||
success: 'Success',
|
|
||||||
failed: 'Failed'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -297,6 +286,10 @@ export default {
|
|||||||
highlightTrailingWhitespace: {
|
highlightTrailingWhitespace: {
|
||||||
name: 'Highlight Trailing Whitespace',
|
name: 'Highlight Trailing Whitespace',
|
||||||
description: 'Highlight trailing whitespace at the end of lines'
|
description: 'Highlight trailing whitespace at the end of lines'
|
||||||
|
},
|
||||||
|
httpClient: {
|
||||||
|
name: 'HTTP Client',
|
||||||
|
description: 'Send HTTP requests directly in the editor and view responses'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
|
|||||||
@@ -33,13 +33,6 @@ export default {
|
|||||||
confirmDelete: '再次点击确认删除',
|
confirmDelete: '再次点击确认删除',
|
||||||
openInNewWindow: '在新窗口中打开',
|
openInNewWindow: '在新窗口中打开',
|
||||||
alreadyOpenInNewWindow: '已在新窗口中打开',
|
alreadyOpenInNewWindow: '已在新窗口中打开',
|
||||||
documentNameTooLong: '文档名称不能超过{max}个字符',
|
|
||||||
documentNameRequired: '文档名称不能为空',
|
|
||||||
cannotDeleteLastDocument: '无法删除最后一个文档',
|
|
||||||
cannotDeleteDefaultDocument: '无法删除默认文档',
|
|
||||||
unknownTime: '未知时间',
|
|
||||||
invalidDate: '无效日期',
|
|
||||||
timeError: '时间错误',
|
|
||||||
},
|
},
|
||||||
languages: {
|
languages: {
|
||||||
'zh-CN': '简体中文',
|
'zh-CN': '简体中文',
|
||||||
@@ -53,7 +46,7 @@ export default {
|
|||||||
keybindings: {
|
keybindings: {
|
||||||
headers: {
|
headers: {
|
||||||
shortcut: '快捷键',
|
shortcut: '快捷键',
|
||||||
category: '分类',
|
extension: '扩展',
|
||||||
description: '描述'
|
description: '描述'
|
||||||
},
|
},
|
||||||
commands: {
|
commands: {
|
||||||
@@ -236,14 +229,10 @@ export default {
|
|||||||
sshKeyPassphrase: 'SSH密钥密码',
|
sshKeyPassphrase: 'SSH密钥密码',
|
||||||
sshKeyPassphrasePlaceholder: '请输入SSH密钥密码',
|
sshKeyPassphrasePlaceholder: '请输入SSH密钥密码',
|
||||||
backupOperations: '备份操作',
|
backupOperations: '备份操作',
|
||||||
pushToRemote: '推送到远程',
|
syncToRemote: '同步到远程',
|
||||||
pushing: '推送中...',
|
syncing: '同步中...',
|
||||||
actions: {
|
actions: {
|
||||||
push: '推送',
|
sync: '同步',
|
||||||
},
|
|
||||||
status: {
|
|
||||||
success: '成功',
|
|
||||||
failed: '失败'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -299,6 +288,10 @@ export default {
|
|||||||
highlightTrailingWhitespace: {
|
highlightTrailingWhitespace: {
|
||||||
name: '高亮行尾空白',
|
name: '高亮行尾空白',
|
||||||
description: '高亮显示行尾的多余空白字符'
|
description: '高亮显示行尾的多余空白字符'
|
||||||
|
},
|
||||||
|
httpClient: {
|
||||||
|
name: 'HTTP 客户端',
|
||||||
|
description: '在编辑器中直接发送 HTTP 请求并查看响应'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
import {createApp} from 'vue';
|
import { createApp } from 'vue';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import '@/assets/styles/index.css';
|
import '@/assets/styles/index.css';
|
||||||
import {createPinia} from 'pinia';
|
import { createPinia } from 'pinia';
|
||||||
import i18n from './i18n';
|
import i18n from './i18n';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
|
||||||
|
import { registerDirectives } from './directives';
|
||||||
|
|
||||||
|
const pinia = createPinia();
|
||||||
|
pinia.use(piniaPluginPersistedstate);
|
||||||
|
|
||||||
const pinia = createPinia()
|
|
||||||
pinia.use(piniaPluginPersistedstate)
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
app.use(pinia)
|
app.use(pinia);
|
||||||
app.use(i18n);
|
app.use(i18n);
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
registerDirectives(app);
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
@@ -1,49 +1,31 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { ref, onScopeDispose } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { BackupService } from '@/../bindings/voidraft/internal/services';
|
import { BackupService } from '@/../bindings/voidraft/internal/services';
|
||||||
import { useConfigStore } from '@/stores/configStore';
|
|
||||||
import { createTimerManager } from '@/common/utils/timerUtils';
|
|
||||||
|
|
||||||
export const useBackupStore = defineStore('backup', () => {
|
export const useBackupStore = defineStore('backup', () => {
|
||||||
const isPushing = ref(false);
|
const isSyncing = ref(false);
|
||||||
const message = ref<string | null>(null);
|
const error = ref<string | null>(null);
|
||||||
const isError = ref(false);
|
|
||||||
|
|
||||||
const timer = createTimerManager();
|
|
||||||
const configStore = useConfigStore();
|
|
||||||
|
|
||||||
onScopeDispose(() => timer.clear());
|
const sync = async (): Promise<void> => {
|
||||||
|
if (isSyncing.value) {
|
||||||
const pushToRemote = async () => {
|
|
||||||
const isConfigured = Boolean(configStore.config.backup.repo_url?.trim());
|
|
||||||
|
|
||||||
if (isPushing.value || !isConfigured) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSyncing.value = true;
|
||||||
|
error.value = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isPushing.value = true;
|
await BackupService.Sync();
|
||||||
message.value = null;
|
} catch (e) {
|
||||||
timer.clear();
|
error.value = e instanceof Error ? e.message : String(e);
|
||||||
|
|
||||||
await BackupService.PushToRemote();
|
|
||||||
|
|
||||||
isError.value = false;
|
|
||||||
message.value = 'push successful';
|
|
||||||
timer.set(() => { message.value = null; }, 3000);
|
|
||||||
} catch (error) {
|
|
||||||
isError.value = true;
|
|
||||||
message.value = error instanceof Error ? error.message : 'backup operation failed';
|
|
||||||
timer.set(() => { message.value = null; }, 5000);
|
|
||||||
} finally {
|
} finally {
|
||||||
isPushing.value = false;
|
isSyncing.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isPushing,
|
isSyncing,
|
||||||
message,
|
error,
|
||||||
isError,
|
sync
|
||||||
pushToRemote
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -161,7 +161,7 @@ export const useConfigStore = defineStore('config', () => {
|
|||||||
|
|
||||||
|
|
||||||
// 初始化语言设置
|
// 初始化语言设置
|
||||||
const initializeLanguage = async (): Promise<void> => {
|
const initLanguage = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
// 如果配置未加载,先加载配置
|
// 如果配置未加载,先加载配置
|
||||||
if (!state.configLoaded) {
|
if (!state.configLoaded) {
|
||||||
@@ -210,7 +210,7 @@ export const useConfigStore = defineStore('config', () => {
|
|||||||
|
|
||||||
// 语言相关方法
|
// 语言相关方法
|
||||||
setLanguage,
|
setLanguage,
|
||||||
initializeLanguage,
|
initLanguage,
|
||||||
|
|
||||||
// 主题相关方法
|
// 主题相关方法
|
||||||
setSystemTheme,
|
setSystemTheme,
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ import {defineStore} from 'pinia';
|
|||||||
import {computed, ref} from 'vue';
|
import {computed, ref} from 'vue';
|
||||||
import {DocumentService} from '@/../bindings/voidraft/internal/services';
|
import {DocumentService} from '@/../bindings/voidraft/internal/services';
|
||||||
import {OpenDocumentWindow} from '@/../bindings/voidraft/internal/services/windowservice';
|
import {OpenDocumentWindow} from '@/../bindings/voidraft/internal/services/windowservice';
|
||||||
import {Document} from '@/../bindings/voidraft/internal/models/models';
|
import {Document} from '@/../bindings/voidraft/internal/models/ent/models';
|
||||||
import {useTabStore} from "@/stores/tabStore";
|
import {useTabStore} from "@/stores/tabStore";
|
||||||
import type {EditorViewState} from '@/stores/editorStore';
|
import type {EditorViewState} from '@/stores/editorStore';
|
||||||
|
|
||||||
export const useDocumentStore = defineStore('document', () => {
|
export const useDocumentStore = defineStore('document', () => {
|
||||||
const DEFAULT_DOCUMENT_ID = ref<number>(1); // 默认草稿文档ID
|
|
||||||
|
|
||||||
// === 核心状态 ===
|
// === 核心状态 ===
|
||||||
const documents = ref<Record<number, Document>>({});
|
const documents = ref<Record<number, Document>>({});
|
||||||
@@ -15,7 +14,6 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
const currentDocument = ref<Document | null>(null);
|
const currentDocument = ref<Document | null>(null);
|
||||||
|
|
||||||
// === 编辑器状态持久化 ===
|
// === 编辑器状态持久化 ===
|
||||||
// 修复:使用统一的 EditorViewState 类型定义
|
|
||||||
const documentStates = ref<Record<number, EditorViewState>>({});
|
const documentStates = ref<Record<number, EditorViewState>>({});
|
||||||
|
|
||||||
// === UI状态 ===
|
// === UI状态 ===
|
||||||
@@ -26,15 +24,18 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
// === 计算属性 ===
|
// === 计算属性 ===
|
||||||
const documentList = computed(() =>
|
const documentList = computed(() =>
|
||||||
Object.values(documents.value).sort((a, b) => {
|
Object.values(documents.value).sort((a, b) => {
|
||||||
return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();
|
const timeA = a.updated_at ? new Date(a.updated_at).getTime() : 0;
|
||||||
|
const timeB = b.updated_at ? new Date(b.updated_at).getTime() : 0;
|
||||||
|
return timeB - timeA;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// === 私有方法 ===
|
|
||||||
const setDocuments = (docs: Document[]) => {
|
const setDocuments = (docs: Document[]) => {
|
||||||
documents.value = {};
|
documents.value = {};
|
||||||
docs.forEach(doc => {
|
docs.forEach(doc => {
|
||||||
documents.value[doc.id] = doc;
|
if (doc.id !== undefined) {
|
||||||
|
documents.value[doc.id] = doc;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,15 +65,7 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
clearError();
|
clearError();
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDocumentSelector = () => {
|
|
||||||
if (showDocumentSelector.value) {
|
|
||||||
closeDocumentSelector();
|
|
||||||
} else {
|
|
||||||
openDocumentSelector();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// === 文档操作方法 ===
|
|
||||||
|
|
||||||
// 在新窗口中打开文档
|
// 在新窗口中打开文档
|
||||||
const openDocumentInNewWindow = async (docId: number): Promise<boolean> => {
|
const openDocumentInNewWindow = async (docId: number): Promise<boolean> => {
|
||||||
@@ -94,7 +87,7 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
const createNewDocument = async (title: string): Promise<Document | null> => {
|
const createNewDocument = async (title: string): Promise<Document | null> => {
|
||||||
try {
|
try {
|
||||||
const doc = await DocumentService.CreateDocument(title);
|
const doc = await DocumentService.CreateDocument(title);
|
||||||
if (doc) {
|
if (doc && doc.id !== undefined) {
|
||||||
documents.value[doc.id] = doc;
|
documents.value[doc.id] = doc;
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
@@ -123,8 +116,6 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
// 打开文档
|
// 打开文档
|
||||||
const openDocument = async (docId: number): Promise<boolean> => {
|
const openDocument = async (docId: number): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
closeDocumentSelector();
|
|
||||||
|
|
||||||
// 获取完整文档数据
|
// 获取完整文档数据
|
||||||
const doc = await DocumentService.GetDocumentByID(docId);
|
const doc = await DocumentService.GetDocumentByID(docId);
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
@@ -150,14 +141,18 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
const doc = documents.value[docId];
|
const doc = documents.value[docId];
|
||||||
if (doc) {
|
if (doc) {
|
||||||
doc.title = title;
|
doc.title = title;
|
||||||
doc.updatedAt = new Date().toISOString();
|
doc.updated_at = new Date().toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentDocument.value?.id === docId) {
|
if (currentDocument.value?.id === docId) {
|
||||||
currentDocument.value.title = title;
|
currentDocument.value.title = title;
|
||||||
currentDocument.value.updatedAt = new Date().toISOString();
|
currentDocument.value.updated_at = new Date().toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 同步更新标签页标题
|
||||||
|
const tabStore = useTabStore();
|
||||||
|
tabStore.updateTabTitle(docId, title);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update document metadata:', error);
|
console.error('Failed to update document metadata:', error);
|
||||||
@@ -168,20 +163,21 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
// 删除文档
|
// 删除文档
|
||||||
const deleteDocument = async (docId: number): Promise<boolean> => {
|
const deleteDocument = async (docId: number): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
// 检查是否是默认文档(使用ID判断)
|
|
||||||
if (docId === DEFAULT_DOCUMENT_ID.value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await DocumentService.DeleteDocument(docId);
|
await DocumentService.DeleteDocument(docId);
|
||||||
|
|
||||||
// 更新本地状态
|
// 更新本地状态
|
||||||
delete documents.value[docId];
|
delete documents.value[docId];
|
||||||
|
|
||||||
|
// 同步清理标签页
|
||||||
|
const tabStore = useTabStore();
|
||||||
|
if (tabStore.hasTab(docId)) {
|
||||||
|
tabStore.closeTab(docId);
|
||||||
|
}
|
||||||
|
|
||||||
// 如果删除的是当前文档,切换到第一个可用文档
|
// 如果删除的是当前文档,切换到第一个可用文档
|
||||||
if (currentDocumentId.value === docId) {
|
if (currentDocumentId.value === docId) {
|
||||||
const availableDocs = Object.values(documents.value);
|
const availableDocs = Object.values(documents.value);
|
||||||
if (availableDocs.length > 0) {
|
if (availableDocs.length > 0 && availableDocs[0].id !== undefined) {
|
||||||
await openDocument(availableDocs[0].id);
|
await openDocument(availableDocs[0].id);
|
||||||
} else {
|
} else {
|
||||||
currentDocumentId.value = null;
|
currentDocumentId.value = null;
|
||||||
@@ -208,8 +204,10 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
// 如果URL中没有指定文档ID,则使用持久化的文档ID
|
// 如果URL中没有指定文档ID,则使用持久化的文档ID
|
||||||
await openDocument(currentDocumentId.value);
|
await openDocument(currentDocumentId.value);
|
||||||
} else {
|
} else {
|
||||||
// 否则打开默认文档
|
// 否则打开第一个文档
|
||||||
await openDocument(DEFAULT_DOCUMENT_ID.value);
|
if (documents.value[0].id) {
|
||||||
|
await openDocument(documents.value[0].id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to initialize document store:', error);
|
console.error('Failed to initialize document store:', error);
|
||||||
@@ -217,7 +215,6 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
DEFAULT_DOCUMENT_ID,
|
|
||||||
// 状态
|
// 状态
|
||||||
documents,
|
documents,
|
||||||
documentList,
|
documentList,
|
||||||
@@ -237,7 +234,6 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
deleteDocument,
|
deleteDocument,
|
||||||
openDocumentSelector,
|
openDocumentSelector,
|
||||||
closeDocumentSelector,
|
closeDocumentSelector,
|
||||||
toggleDocumentSelector,
|
|
||||||
setError,
|
setError,
|
||||||
clearError,
|
clearError,
|
||||||
initialize,
|
initialize,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {EditorView} from '@codemirror/view';
|
|||||||
import {EditorState, Extension} from '@codemirror/state';
|
import {EditorState, Extension} from '@codemirror/state';
|
||||||
import {useConfigStore} from './configStore';
|
import {useConfigStore} from './configStore';
|
||||||
import {useDocumentStore} from './documentStore';
|
import {useDocumentStore} from './documentStore';
|
||||||
import {ExtensionID} from '@/../bindings/voidraft/internal/models/models';
|
|
||||||
import {DocumentService, ExtensionService} from '@/../bindings/voidraft/internal/services';
|
import {DocumentService, ExtensionService} from '@/../bindings/voidraft/internal/services';
|
||||||
import {ensureSyntaxTree} from "@codemirror/language";
|
import {ensureSyntaxTree} from "@codemirror/language";
|
||||||
import {createBasicSetup} from '@/views/editor/basic/basicSetup';
|
import {createBasicSetup} from '@/views/editor/basic/basicSetup';
|
||||||
@@ -28,7 +27,6 @@ import {AsyncManager} from '@/common/utils/asyncManager';
|
|||||||
import {generateContentHash} from "@/common/utils/hashUtils";
|
import {generateContentHash} from "@/common/utils/hashUtils";
|
||||||
import {createTimerManager, type TimerManager} from '@/common/utils/timerUtils';
|
import {createTimerManager, type TimerManager} from '@/common/utils/timerUtils';
|
||||||
import {EDITOR_CONFIG} from '@/common/constant/editor';
|
import {EDITOR_CONFIG} from '@/common/constant/editor';
|
||||||
import {createHttpClientExtension} from "@/views/editor/extensions/httpclient";
|
|
||||||
import {createDebounce} from '@/common/utils/debounce';
|
import {createDebounce} from '@/common/utils/debounce';
|
||||||
|
|
||||||
export interface DocumentStats {
|
export interface DocumentStats {
|
||||||
@@ -93,84 +91,6 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
}
|
}
|
||||||
}, { delay: 500 }); // 500ms 内的多次输入只清理一次
|
}, { delay: 500 }); // 500ms 内的多次输入只清理一次
|
||||||
|
|
||||||
// === 私有方法 ===
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查位置是否在代码块分隔符区域内
|
|
||||||
*/
|
|
||||||
const isPositionInDelimiter = (view: EditorView, pos: number): boolean => {
|
|
||||||
try {
|
|
||||||
const blocks = view.state.field(blockState, false);
|
|
||||||
if (!blocks) return false;
|
|
||||||
|
|
||||||
for (const block of blocks) {
|
|
||||||
if (pos >= block.delimiter.from && pos < block.delimiter.to) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 调整光标位置到有效的内容区域
|
|
||||||
* 如果位置在分隔符内,移动到该块的内容开始位置
|
|
||||||
*/
|
|
||||||
const adjustCursorPosition = (view: EditorView, pos: number): number => {
|
|
||||||
try {
|
|
||||||
const blocks = view.state.field(blockState, false);
|
|
||||||
if (!blocks || blocks.length === 0) return pos;
|
|
||||||
|
|
||||||
// 如果位置在分隔符内,移动到该块的内容开始位置
|
|
||||||
for (const block of blocks) {
|
|
||||||
if (pos >= block.delimiter.from && pos < block.delimiter.to) {
|
|
||||||
return block.content.from;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pos;
|
|
||||||
} catch {
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 恢复编辑器的光标位置(自动滚动到光标处)
|
|
||||||
*/
|
|
||||||
const restoreEditorState = (instance: EditorInstance, documentId: number): void => {
|
|
||||||
const savedState = instance.editorState;
|
|
||||||
|
|
||||||
if (savedState) {
|
|
||||||
// 有保存的状态,恢复光标位置
|
|
||||||
let pos = Math.min(savedState.cursorPos, instance.view.state.doc.length);
|
|
||||||
|
|
||||||
// 确保位置不在分隔符上
|
|
||||||
if (isPositionInDelimiter(instance.view, pos)) {
|
|
||||||
pos = adjustCursorPosition(instance.view, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 修复:设置光标位置并居中滚动(更好的用户体验)
|
|
||||||
instance.view.dispatch({
|
|
||||||
selection: {anchor: pos, head: pos},
|
|
||||||
effects: EditorView.scrollIntoView(pos, {
|
|
||||||
y: "center", // 垂直居中显示
|
|
||||||
yMargin: 100 // 上下留一些边距
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 首次打开或没有记录,光标在文档末尾
|
|
||||||
const docLength = instance.view.state.doc.length;
|
|
||||||
instance.view.dispatch({
|
|
||||||
selection: {anchor: docLength, head: docLength},
|
|
||||||
effects: EditorView.scrollIntoView(docLength, {
|
|
||||||
y: "center",
|
|
||||||
yMargin: 100
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 缓存化的语法树确保方法
|
// 缓存化的语法树确保方法
|
||||||
const ensureSyntaxTreeCached = (view: EditorView, documentId: number): void => {
|
const ensureSyntaxTreeCached = (view: EditorView, documentId: number): void => {
|
||||||
@@ -245,7 +165,7 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
increaseFontSize: () => configStore.increaseFontSizeLocal(),
|
increaseFontSize: () => configStore.increaseFontSizeLocal(),
|
||||||
decreaseFontSize: () => configStore.decreaseFontSizeLocal(),
|
decreaseFontSize: () => configStore.decreaseFontSizeLocal(),
|
||||||
onSave: () => configStore.saveFontSize(),
|
onSave: () => configStore.saveFontSize(),
|
||||||
saveDelay: 500
|
saveDelay: 1000
|
||||||
});
|
});
|
||||||
|
|
||||||
// 统计扩展
|
// 统计扩展
|
||||||
@@ -260,9 +180,6 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
enableAutoDetection: true
|
enableAutoDetection: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const httpExtension = createHttpClientExtension();
|
|
||||||
|
|
||||||
|
|
||||||
// 再次检查操作有效性
|
// 再次检查操作有效性
|
||||||
if (!operationManager.isOperationValid(operationId, documentId)) {
|
if (!operationManager.isOperationValid(operationId, documentId)) {
|
||||||
throw new Error('Operation cancelled');
|
throw new Error('Operation cancelled');
|
||||||
@@ -277,7 +194,7 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 动态扩展,传递文档ID以便扩展管理器可以预初始化
|
// 动态扩展,传递文档ID以便扩展管理器可以预初始化
|
||||||
const dynamicExtensions = await createDynamicExtensions(documentId);
|
const dynamicExtensions = await createDynamicExtensions();
|
||||||
|
|
||||||
// 最终检查操作有效性
|
// 最终检查操作有效性
|
||||||
if (!operationManager.isOperationValid(operationId, documentId)) {
|
if (!operationManager.isOperationValid(operationId, documentId)) {
|
||||||
@@ -296,7 +213,6 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
contentChangeExtension,
|
contentChangeExtension,
|
||||||
codeBlockExtension,
|
codeBlockExtension,
|
||||||
...dynamicExtensions,
|
...dynamicExtensions,
|
||||||
...httpExtension,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// 创建编辑器状态
|
// 创建编辑器状态
|
||||||
@@ -320,7 +236,6 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
lastModified: new Date(),
|
lastModified: new Date(),
|
||||||
autoSaveTimer: createTimerManager(),
|
autoSaveTimer: createTimerManager(),
|
||||||
syntaxTreeCache: null,
|
syntaxTreeCache: null,
|
||||||
// 修复:创建实例时从 documentStore 读取持久化的编辑器状态
|
|
||||||
editorState: documentStore.documentStates[documentId]
|
editorState: documentStore.documentStates[documentId]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -401,9 +316,6 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
//使用 nextTick + requestAnimationFrame 确保 DOM 完全渲染
|
//使用 nextTick + requestAnimationFrame 确保 DOM 完全渲染
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
// 恢复编辑器状态(光标位置和滚动位置)
|
|
||||||
restoreEditorState(instance, documentId);
|
|
||||||
|
|
||||||
// 聚焦编辑器
|
// 聚焦编辑器
|
||||||
instance.view.focus();
|
instance.view.focus();
|
||||||
|
|
||||||
@@ -433,7 +345,6 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
instance.isDirty = false;
|
instance.isDirty = false;
|
||||||
instance.lastModified = new Date();
|
instance.lastModified = new Date();
|
||||||
}
|
}
|
||||||
// 如果内容在保存期间被修改了,保持 isDirty 状态
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -462,15 +373,14 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
}, getAutoSaveDelay());
|
}, getAutoSaveDelay());
|
||||||
};
|
};
|
||||||
|
|
||||||
// === 公共API ===
|
|
||||||
|
|
||||||
// 设置编辑器容器
|
// 设置编辑器容器
|
||||||
const setEditorContainer = (container: HTMLElement | null) => {
|
const setEditorContainer = (container: HTMLElement | null) => {
|
||||||
containerElement.value = container;
|
containerElement.value = container;
|
||||||
|
|
||||||
// 如果设置容器时已有当前文档,立即加载编辑器
|
// 如果设置容器时已有当前文档,立即加载编辑器
|
||||||
if (container && documentStore.currentDocument) {
|
if (container && documentStore.currentDocument && documentStore.currentDocument.id !== undefined) {
|
||||||
loadEditor(documentStore.currentDocument.id, documentStore.currentDocument.content);
|
loadEditor(documentStore.currentDocument.id, documentStore.currentDocument.content || '');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -696,20 +606,20 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 更新扩展
|
// 更新扩展
|
||||||
const updateExtension = async (id: ExtensionID, enabled: boolean, config?: any) => {
|
const updateExtension = async (key: string, enabled: boolean, config?: any) => {
|
||||||
// 如果只是更新启用状态
|
// 更新启用状态
|
||||||
if (config === undefined) {
|
await ExtensionService.UpdateExtensionEnabled(key, enabled);
|
||||||
await ExtensionService.UpdateExtensionEnabled(id, enabled);
|
|
||||||
} else {
|
// 如果需要更新配置
|
||||||
// 如果需要更新配置
|
if (config !== undefined) {
|
||||||
await ExtensionService.UpdateExtensionState(id, enabled, config);
|
await ExtensionService.UpdateExtensionConfig(key, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新前端编辑器扩展 - 应用于所有实例
|
// 更新前端编辑器扩展 - 应用于所有实例
|
||||||
const manager = getExtensionManager();
|
const manager = getExtensionManager();
|
||||||
if (manager) {
|
if (manager) {
|
||||||
// 直接更新前端扩展至所有视图
|
// 直接更新前端扩展至所有视图
|
||||||
manager.updateExtension(id, enabled, config);
|
manager.updateExtension(key, enabled, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重新加载扩展配置
|
// 重新加载扩展配置
|
||||||
@@ -723,23 +633,10 @@ export const useEditorStore = defineStore('editor', () => {
|
|||||||
|
|
||||||
// 监听文档切换
|
// 监听文档切换
|
||||||
watch(() => documentStore.currentDocument, async (newDoc, oldDoc) => {
|
watch(() => documentStore.currentDocument, async (newDoc, oldDoc) => {
|
||||||
if (newDoc && containerElement.value) {
|
if (newDoc && newDoc.id !== undefined && containerElement.value) {
|
||||||
// 修复:在切换到新文档前,只保存旧文档的光标位置
|
|
||||||
if (oldDoc && oldDoc.id !== newDoc.id && currentEditor.value) {
|
|
||||||
const oldInstance = editorCache.get(oldDoc.id);
|
|
||||||
if (oldInstance) {
|
|
||||||
const currentState: EditorViewState = {
|
|
||||||
cursorPos: currentEditor.value.state.selection.main.head
|
|
||||||
};
|
|
||||||
// 同时保存到实例和 documentStore
|
|
||||||
oldInstance.editorState = currentState;
|
|
||||||
documentStore.documentStates[oldDoc.id] = currentState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 等待 DOM 更新完成,再加载新文档的编辑器
|
// 等待 DOM 更新完成,再加载新文档的编辑器
|
||||||
await nextTick();
|
await nextTick();
|
||||||
loadEditor(newDoc.id, newDoc.content);
|
loadEditor(newDoc.id, newDoc.content || '');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { Extension, ExtensionID } from '@/../bindings/voidraft/internal/models/models';
|
import { Extension } from '@/../bindings/voidraft/internal/models/ent/models';
|
||||||
import { ExtensionService } from '@/../bindings/voidraft/internal/services';
|
import { ExtensionService } from '@/../bindings/voidraft/internal/services';
|
||||||
|
|
||||||
export const useExtensionStore = defineStore('extension', () => {
|
export const useExtensionStore = defineStore('extension', () => {
|
||||||
@@ -12,9 +12,9 @@ export const useExtensionStore = defineStore('extension', () => {
|
|||||||
extensions.value.filter(ext => ext.enabled)
|
extensions.value.filter(ext => ext.enabled)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 获取启用的扩展ID列表
|
// 获取启用的扩展ID列表 (key)
|
||||||
const enabledExtensionIds = computed(() =>
|
const enabledExtensionIds = computed(() =>
|
||||||
enabledExtensions.value.map(ext => ext.id)
|
enabledExtensions.value.map(ext => ext.key).filter((k): k is string => k !== undefined)
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,7 +22,8 @@ export const useExtensionStore = defineStore('extension', () => {
|
|||||||
*/
|
*/
|
||||||
const loadExtensions = async (): Promise<void> => {
|
const loadExtensions = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
extensions.value = await ExtensionService.GetAllExtensions();
|
const result = await ExtensionService.GetAllExtensions();
|
||||||
|
extensions.value = result.filter((ext): ext is Extension => ext !== null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[ExtensionStore] Failed to load extensions:', err);
|
console.error('[ExtensionStore] Failed to load extensions:', err);
|
||||||
}
|
}
|
||||||
@@ -31,8 +32,8 @@ export const useExtensionStore = defineStore('extension', () => {
|
|||||||
/**
|
/**
|
||||||
* 获取扩展配置
|
* 获取扩展配置
|
||||||
*/
|
*/
|
||||||
const getExtensionConfig = (id: ExtensionID): any => {
|
const getExtensionConfig = (key: string): any => {
|
||||||
const extension = extensions.value.find(ext => ext.id === id);
|
const extension = extensions.value.find(ext => ext.key === key);
|
||||||
return extension?.config ?? {};
|
return extension?.config ?? {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {defineStore} from 'pinia';
|
import {defineStore} from 'pinia';
|
||||||
import {computed, ref} from 'vue';
|
import {computed, ref} from 'vue';
|
||||||
import {ExtensionID, KeyBinding, KeyBindingCommand} from '@/../bindings/voidraft/internal/models/models';
|
import {KeyBinding} from '@/../bindings/voidraft/internal/models/ent/models';
|
||||||
import {GetAllKeyBindings} from '@/../bindings/voidraft/internal/services/keybindingservice';
|
import {GetAllKeyBindings} from '@/../bindings/voidraft/internal/services/keybindingservice';
|
||||||
|
|
||||||
export const useKeybindingStore = defineStore('keybinding', () => {
|
export const useKeybindingStore = defineStore('keybinding', () => {
|
||||||
@@ -14,13 +14,14 @@ export const useKeybindingStore = defineStore('keybinding', () => {
|
|||||||
|
|
||||||
// 按扩展分组的快捷键
|
// 按扩展分组的快捷键
|
||||||
const keyBindingsByExtension = computed(() => {
|
const keyBindingsByExtension = computed(() => {
|
||||||
const groups = new Map<ExtensionID, KeyBinding[]>();
|
const groups = new Map<string, KeyBinding[]>();
|
||||||
|
|
||||||
for (const binding of keyBindings.value) {
|
for (const binding of keyBindings.value) {
|
||||||
if (!groups.has(binding.extension)) {
|
const ext = binding.extension || '';
|
||||||
groups.set(binding.extension, []);
|
if (!groups.has(ext)) {
|
||||||
|
groups.set(ext, []);
|
||||||
}
|
}
|
||||||
groups.get(binding.extension)!.push(binding);
|
groups.get(ext)!.push(binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
return groups;
|
return groups;
|
||||||
@@ -28,13 +29,13 @@ export const useKeybindingStore = defineStore('keybinding', () => {
|
|||||||
|
|
||||||
// 获取指定扩展的快捷键
|
// 获取指定扩展的快捷键
|
||||||
const getKeyBindingsByExtension = computed(() =>
|
const getKeyBindingsByExtension = computed(() =>
|
||||||
(extension: ExtensionID) =>
|
(extension: string) =>
|
||||||
keyBindings.value.filter(kb => kb.extension === extension)
|
keyBindings.value.filter(kb => kb.extension === extension)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 按命令获取快捷键
|
// 按命令获取快捷键
|
||||||
const getKeyBindingByCommand = computed(() =>
|
const getKeyBindingByCommand = computed(() =>
|
||||||
(command: KeyBindingCommand) =>
|
(command: string) =>
|
||||||
keyBindings.value.find(kb => kb.command === command)
|
keyBindings.value.find(kb => kb.command === command)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -42,13 +43,14 @@ export const useKeybindingStore = defineStore('keybinding', () => {
|
|||||||
* 从后端加载快捷键配置
|
* 从后端加载快捷键配置
|
||||||
*/
|
*/
|
||||||
const loadKeyBindings = async (): Promise<void> => {
|
const loadKeyBindings = async (): Promise<void> => {
|
||||||
keyBindings.value = await GetAllKeyBindings();
|
const result = await GetAllKeyBindings();
|
||||||
|
keyBindings.value = result.filter((kb): kb is KeyBinding => kb !== null);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查是否存在指定命令的快捷键
|
* 检查是否存在指定命令的快捷键
|
||||||
*/
|
*/
|
||||||
const hasCommand = (command: KeyBindingCommand): boolean => {
|
const hasCommand = (command: string): boolean => {
|
||||||
return keyBindings.value.some(kb => kb.command === command && kb.enabled);
|
return keyBindings.value.some(kb => kb.command === command && kb.enabled);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,9 +59,11 @@ export const useKeybindingStore = defineStore('keybinding', () => {
|
|||||||
* 获取扩展相关的所有扩展ID
|
* 获取扩展相关的所有扩展ID
|
||||||
*/
|
*/
|
||||||
const getAllExtensionIds = computed(() => {
|
const getAllExtensionIds = computed(() => {
|
||||||
const extensionIds = new Set<ExtensionID>();
|
const extensionIds = new Set<string>();
|
||||||
for (const binding of keyBindings.value) {
|
for (const binding of keyBindings.value) {
|
||||||
extensionIds.add(binding.extension);
|
if (binding.extension) {
|
||||||
|
extensionIds.add(binding.extension);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Array.from(extensionIds);
|
return Array.from(extensionIds);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export const useSystemStore = defineStore('system', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 初始化系统信息
|
// 初始化系统信息
|
||||||
const initializeSystemInfo = async (): Promise<void> => {
|
const initSystemInfo = async (): Promise<void> => {
|
||||||
if (isLoading.value) return;
|
if (isLoading.value) return;
|
||||||
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
@@ -102,7 +102,7 @@ export const useSystemStore = defineStore('system', () => {
|
|||||||
titleBarHeight,
|
titleBarHeight,
|
||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
initializeSystemInfo,
|
initSystemInfo,
|
||||||
setWindowOnTop,
|
setWindowOnTop,
|
||||||
toggleWindowOnTop,
|
toggleWindowOnTop,
|
||||||
resetWindowOnTop,
|
resetWindowOnTop,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {defineStore} from 'pinia';
|
|||||||
import {computed, readonly, ref} from 'vue';
|
import {computed, readonly, ref} from 'vue';
|
||||||
import {useConfigStore} from './configStore';
|
import {useConfigStore} from './configStore';
|
||||||
import {useDocumentStore} from './documentStore';
|
import {useDocumentStore} from './documentStore';
|
||||||
import type {Document} from '@/../bindings/voidraft/internal/models/models';
|
import type {Document} from '@/../bindings/voidraft/internal/models/ent/models';
|
||||||
|
|
||||||
export interface Tab {
|
export interface Tab {
|
||||||
documentId: number; // 直接使用文档ID作为唯一标识
|
documentId: number; // 直接使用文档ID作为唯一标识
|
||||||
@@ -15,7 +15,7 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
const documentStore = useDocumentStore();
|
const documentStore = useDocumentStore();
|
||||||
|
|
||||||
// === 核心状态 ===
|
// === 核心状态 ===
|
||||||
const tabsMap = ref<Map<number, Tab>>(new Map());
|
const tabsMap = ref<Record<number, Tab>>({});
|
||||||
const tabOrder = ref<number[]>([]); // 维护标签页顺序
|
const tabOrder = ref<number[]>([]); // 维护标签页顺序
|
||||||
const draggedTabId = ref<number | null>(null);
|
const draggedTabId = ref<number | null>(null);
|
||||||
|
|
||||||
@@ -28,21 +28,21 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
// 按顺序返回标签页数组(用于UI渲染)
|
// 按顺序返回标签页数组(用于UI渲染)
|
||||||
const tabs = computed(() => {
|
const tabs = computed(() => {
|
||||||
return tabOrder.value
|
return tabOrder.value
|
||||||
.map(documentId => tabsMap.value.get(documentId))
|
.map(documentId => tabsMap.value[documentId])
|
||||||
.filter(tab => tab !== undefined) as Tab[];
|
.filter(tab => tab !== undefined) as Tab[];
|
||||||
});
|
});
|
||||||
|
|
||||||
// === 私有方法 ===
|
// === 私有方法 ===
|
||||||
const hasTab = (documentId: number): boolean => {
|
const hasTab = (documentId: number): boolean => {
|
||||||
return tabsMap.value.has(documentId);
|
return documentId in tabsMap.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTab = (documentId: number): Tab | undefined => {
|
const getTab = (documentId: number): Tab | undefined => {
|
||||||
return tabsMap.value.get(documentId);
|
return tabsMap.value[documentId];
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateTabTitle = (documentId: number, title: string) => {
|
const updateTabTitle = (documentId: number, title: string) => {
|
||||||
const tab = tabsMap.value.get(documentId);
|
const tab = tabsMap.value[documentId];
|
||||||
if (tab) {
|
if (tab) {
|
||||||
tab.title = title;
|
tab.title = title;
|
||||||
}
|
}
|
||||||
@@ -55,6 +55,7 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
*/
|
*/
|
||||||
const addOrActivateTab = (document: Document) => {
|
const addOrActivateTab = (document: Document) => {
|
||||||
const documentId = document.id;
|
const documentId = document.id;
|
||||||
|
if (documentId === undefined) return;
|
||||||
|
|
||||||
if (hasTab(documentId)) {
|
if (hasTab(documentId)) {
|
||||||
// 标签页已存在,无需重复添加
|
// 标签页已存在,无需重复添加
|
||||||
@@ -64,10 +65,10 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
// 创建新标签页
|
// 创建新标签页
|
||||||
const newTab: Tab = {
|
const newTab: Tab = {
|
||||||
documentId,
|
documentId,
|
||||||
title: document.title
|
title: document.title || ''
|
||||||
};
|
};
|
||||||
|
|
||||||
tabsMap.value.set(documentId, newTab);
|
tabsMap.value[documentId] = newTab;
|
||||||
tabOrder.value.push(documentId);
|
tabOrder.value.push(documentId);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,7 +82,7 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
if (tabIndex === -1) return;
|
if (tabIndex === -1) return;
|
||||||
|
|
||||||
// 从映射和顺序数组中移除
|
// 从映射和顺序数组中移除
|
||||||
tabsMap.value.delete(documentId);
|
delete tabsMap.value[documentId];
|
||||||
tabOrder.value.splice(tabIndex, 1);
|
tabOrder.value.splice(tabIndex, 1);
|
||||||
|
|
||||||
// 如果关闭的是当前文档,需要切换到其他文档
|
// 如果关闭的是当前文档,需要切换到其他文档
|
||||||
@@ -111,7 +112,7 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
if (tabIndex === -1) return;
|
if (tabIndex === -1) return;
|
||||||
|
|
||||||
// 从映射和顺序数组中移除
|
// 从映射和顺序数组中移除
|
||||||
tabsMap.value.delete(documentId);
|
delete tabsMap.value[documentId];
|
||||||
tabOrder.value.splice(tabIndex, 1);
|
tabOrder.value.splice(tabIndex, 1);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -121,12 +122,12 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
*/
|
*/
|
||||||
const switchToTabAndDocument = (documentId: number) => {
|
const switchToTabAndDocument = (documentId: number) => {
|
||||||
if (!hasTab(documentId)) return;
|
if (!hasTab(documentId)) return;
|
||||||
|
|
||||||
// 如果点击的是当前已激活的文档,不需要重复请求
|
// 如果点击的是当前已激活的文档,不需要重复请求
|
||||||
if (documentStore.currentDocumentId === documentId) {
|
if (documentStore.currentDocumentId === documentId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
documentStore.openDocument(documentId);
|
documentStore.openDocument(documentId);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -150,10 +151,31 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
return tabOrder.value.indexOf(documentId);
|
return tabOrder.value.indexOf(documentId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证并清理无效的标签页
|
||||||
|
*/
|
||||||
|
const validateTabs = () => {
|
||||||
|
const validDocIds = Object.keys(documentStore.documents).map(Number);
|
||||||
|
|
||||||
|
// 找出无效的标签页(文档已被删除)
|
||||||
|
const invalidTabIds = tabOrder.value.filter(docId => !validDocIds.includes(docId));
|
||||||
|
|
||||||
|
if (invalidTabIds.length > 0) {
|
||||||
|
// 批量清理无效标签页
|
||||||
|
invalidTabIds.forEach(docId => {
|
||||||
|
delete tabsMap.value[docId];
|
||||||
|
});
|
||||||
|
tabOrder.value = tabOrder.value.filter(docId => validDocIds.includes(docId));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化标签页(当前文档)
|
* 初始化标签页(当前文档)
|
||||||
*/
|
*/
|
||||||
const initializeTab = () => {
|
const initializeTab = () => {
|
||||||
|
// 先验证并清理无效的标签页(处理持久化的脏数据)
|
||||||
|
validateTabs();
|
||||||
|
|
||||||
if (isTabsEnabled.value) {
|
if (isTabsEnabled.value) {
|
||||||
const currentDoc = documentStore.currentDocument;
|
const currentDoc = documentStore.currentDocument;
|
||||||
if (currentDoc) {
|
if (currentDoc) {
|
||||||
@@ -169,13 +191,13 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
*/
|
*/
|
||||||
const closeOtherTabs = (keepDocumentId: number) => {
|
const closeOtherTabs = (keepDocumentId: number) => {
|
||||||
if (!hasTab(keepDocumentId)) return;
|
if (!hasTab(keepDocumentId)) return;
|
||||||
|
|
||||||
// 获取所有其他标签页的ID
|
// 获取所有其他标签页的ID
|
||||||
const otherTabIds = tabOrder.value.filter(id => id !== keepDocumentId);
|
const otherTabIds = tabOrder.value.filter(id => id !== keepDocumentId);
|
||||||
|
|
||||||
// 批量关闭其他标签页
|
// 批量关闭其他标签页
|
||||||
closeTabs(otherTabIds);
|
closeTabs(otherTabIds);
|
||||||
|
|
||||||
// 如果当前打开的文档在被关闭的标签中,需要切换到保留的文档
|
// 如果当前打开的文档在被关闭的标签中,需要切换到保留的文档
|
||||||
if (otherTabIds.includes(documentStore.currentDocumentId!)) {
|
if (otherTabIds.includes(documentStore.currentDocumentId!)) {
|
||||||
switchToTabAndDocument(keepDocumentId);
|
switchToTabAndDocument(keepDocumentId);
|
||||||
@@ -188,13 +210,13 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
const closeTabsToRight = (documentId: number) => {
|
const closeTabsToRight = (documentId: number) => {
|
||||||
const index = getTabIndex(documentId);
|
const index = getTabIndex(documentId);
|
||||||
if (index === -1) return;
|
if (index === -1) return;
|
||||||
|
|
||||||
// 获取右侧所有标签页的ID
|
// 获取右侧所有标签页的ID
|
||||||
const rightTabIds = tabOrder.value.slice(index + 1);
|
const rightTabIds = tabOrder.value.slice(index + 1);
|
||||||
|
|
||||||
// 批量关闭右侧标签页
|
// 批量关闭右侧标签页
|
||||||
closeTabs(rightTabIds);
|
closeTabs(rightTabIds);
|
||||||
|
|
||||||
// 如果当前打开的文档在被关闭的右侧标签中,需要切换到指定的文档
|
// 如果当前打开的文档在被关闭的右侧标签中,需要切换到指定的文档
|
||||||
if (rightTabIds.includes(documentStore.currentDocumentId!)) {
|
if (rightTabIds.includes(documentStore.currentDocumentId!)) {
|
||||||
switchToTabAndDocument(documentId);
|
switchToTabAndDocument(documentId);
|
||||||
@@ -207,13 +229,13 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
const closeTabsToLeft = (documentId: number) => {
|
const closeTabsToLeft = (documentId: number) => {
|
||||||
const index = getTabIndex(documentId);
|
const index = getTabIndex(documentId);
|
||||||
if (index <= 0) return;
|
if (index <= 0) return;
|
||||||
|
|
||||||
// 获取左侧所有标签页的ID
|
// 获取左侧所有标签页的ID
|
||||||
const leftTabIds = tabOrder.value.slice(0, index);
|
const leftTabIds = tabOrder.value.slice(0, index);
|
||||||
|
|
||||||
// 批量关闭左侧标签页
|
// 批量关闭左侧标签页
|
||||||
closeTabs(leftTabIds);
|
closeTabs(leftTabIds);
|
||||||
|
|
||||||
// 如果当前打开的文档在被关闭的左侧标签中,需要切换到指定的文档
|
// 如果当前打开的文档在被关闭的左侧标签中,需要切换到指定的文档
|
||||||
if (leftTabIds.includes(documentStore.currentDocumentId!)) {
|
if (leftTabIds.includes(documentStore.currentDocumentId!)) {
|
||||||
switchToTabAndDocument(documentId);
|
switchToTabAndDocument(documentId);
|
||||||
@@ -224,12 +246,15 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
* 清空所有标签页
|
* 清空所有标签页
|
||||||
*/
|
*/
|
||||||
const clearAllTabs = () => {
|
const clearAllTabs = () => {
|
||||||
tabsMap.value.clear();
|
tabsMap.value = {};
|
||||||
tabOrder.value = [];
|
tabOrder.value = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
// === 公共API ===
|
// === 公共API ===
|
||||||
return {
|
return {
|
||||||
|
tabsMap,
|
||||||
|
tabOrder,
|
||||||
|
|
||||||
// 状态
|
// 状态
|
||||||
tabs: readonly(tabs),
|
tabs: readonly(tabs),
|
||||||
draggedTabId,
|
draggedTabId,
|
||||||
@@ -251,9 +276,16 @@ export const useTabStore = defineStore('tab', () => {
|
|||||||
initializeTab,
|
initializeTab,
|
||||||
clearAllTabs,
|
clearAllTabs,
|
||||||
updateTabTitle,
|
updateTabTitle,
|
||||||
|
validateTabs,
|
||||||
|
|
||||||
// 工具方法
|
// 工具方法
|
||||||
hasTab,
|
hasTab,
|
||||||
getTab
|
getTab
|
||||||
};
|
};
|
||||||
});
|
}, {
|
||||||
|
persist: {
|
||||||
|
key: 'voidraft-tabs',
|
||||||
|
storage: localStorage,
|
||||||
|
pick: ['tabsMap', 'tabOrder'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,159 +1,161 @@
|
|||||||
import { defineStore } from 'pinia';
|
import {defineStore} from 'pinia';
|
||||||
import { computed, ref } from 'vue';
|
import {computed, ref} from 'vue';
|
||||||
import { SystemThemeType, ThemeType, ThemeColorConfig } from '@/../bindings/voidraft/internal/models/models';
|
import {SystemThemeType} from '@/../bindings/voidraft/internal/models/models';
|
||||||
import { ThemeService } from '@/../bindings/voidraft/internal/services';
|
import {Type as ThemeType} from '@/../bindings/voidraft/internal/models/ent/theme/models';
|
||||||
import { useConfigStore } from './configStore';
|
import {ThemeService} from '@/../bindings/voidraft/internal/services';
|
||||||
import { useEditorStore } from './editorStore';
|
import {useConfigStore} from './configStore';
|
||||||
import type { ThemeColors } from '@/views/editor/theme/types';
|
import {useEditorStore} from './editorStore';
|
||||||
import { cloneThemeColors, FALLBACK_THEME_NAME, themePresetList, themePresetMap } from '@/views/editor/theme/presets';
|
import type {ThemeColors} from '@/views/editor/theme/types';
|
||||||
|
import {cloneThemeColors, FALLBACK_THEME_NAME, themePresetList, themePresetMap} from '@/views/editor/theme/presets';
|
||||||
|
|
||||||
|
type ThemeColorConfig = { [_: string]: any };
|
||||||
type ThemeOption = { name: string; type: ThemeType };
|
type ThemeOption = { name: string; type: ThemeType };
|
||||||
|
|
||||||
const resolveThemeName = (name?: string) =>
|
const resolveThemeName = (name?: string) =>
|
||||||
name && themePresetMap[name] ? name : FALLBACK_THEME_NAME;
|
name && themePresetMap[name] ? name : FALLBACK_THEME_NAME;
|
||||||
|
|
||||||
const createThemeOptions = (type: ThemeType): ThemeOption[] =>
|
const createThemeOptions = (type: ThemeType): ThemeOption[] =>
|
||||||
themePresetList
|
themePresetList
|
||||||
.filter(preset => preset.type === type)
|
.filter(preset => preset.type === type)
|
||||||
.map(preset => ({ name: preset.name, type: preset.type }));
|
.map(preset => ({name: preset.name, type: preset.type}));
|
||||||
|
|
||||||
const darkThemeOptions = createThemeOptions(ThemeType.ThemeTypeDark);
|
const darkThemeOptions = createThemeOptions(ThemeType.TypeDark);
|
||||||
const lightThemeOptions = createThemeOptions(ThemeType.ThemeTypeLight);
|
const lightThemeOptions = createThemeOptions(ThemeType.TypeLight);
|
||||||
|
|
||||||
const cloneColors = (colors: ThemeColorConfig): ThemeColors =>
|
const cloneColors = (colors: ThemeColorConfig): ThemeColors =>
|
||||||
JSON.parse(JSON.stringify(colors)) as ThemeColors;
|
JSON.parse(JSON.stringify(colors)) as ThemeColors;
|
||||||
|
|
||||||
const getPresetColors = (name: string): ThemeColors => {
|
const getPresetColors = (name: string): ThemeColors => {
|
||||||
const preset = themePresetMap[name] ?? themePresetMap[FALLBACK_THEME_NAME];
|
const preset = themePresetMap[name] ?? themePresetMap[FALLBACK_THEME_NAME];
|
||||||
const colors = cloneThemeColors(preset.colors);
|
const colors = cloneThemeColors(preset.colors);
|
||||||
colors.themeName = name;
|
colors.themeName = name;
|
||||||
return colors;
|
return colors;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchThemeColors = async (themeName: string): Promise<ThemeColors> => {
|
const fetchThemeColors = async (themeName: string): Promise<ThemeColors> => {
|
||||||
const safeName = resolveThemeName(themeName);
|
const safeName = resolveThemeName(themeName);
|
||||||
try {
|
try {
|
||||||
const theme = await ThemeService.GetThemeByName(safeName);
|
const theme = await ThemeService.GetThemeByKey(safeName);
|
||||||
if (theme?.colors) {
|
if (theme?.colors) {
|
||||||
const colors = cloneColors(theme.colors);
|
const colors = cloneColors(theme.colors);
|
||||||
colors.themeName = safeName;
|
colors.themeName = safeName;
|
||||||
return colors;
|
return colors;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load theme override:', error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
return getPresetColors(safeName);
|
||||||
console.error('Failed to load theme override:', error);
|
|
||||||
}
|
|
||||||
return getPresetColors(safeName);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useThemeStore = defineStore('theme', () => {
|
export const useThemeStore = defineStore('theme', () => {
|
||||||
const configStore = useConfigStore();
|
const configStore = useConfigStore();
|
||||||
const currentColors = ref<ThemeColors | null>(null);
|
const currentColors = ref<ThemeColors | null>(null);
|
||||||
|
|
||||||
const currentTheme = computed(
|
const currentTheme = computed(
|
||||||
() => configStore.config?.appearance?.systemTheme || SystemThemeType.SystemThemeAuto
|
() => configStore.config?.appearance?.systemTheme || SystemThemeType.SystemThemeAuto
|
||||||
);
|
|
||||||
|
|
||||||
const isDarkMode = computed(
|
|
||||||
() =>
|
|
||||||
currentTheme.value === SystemThemeType.SystemThemeDark ||
|
|
||||||
(currentTheme.value === SystemThemeType.SystemThemeAuto &&
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
||||||
);
|
|
||||||
|
|
||||||
const availableThemes = computed<ThemeOption[]>(() =>
|
|
||||||
isDarkMode.value ? darkThemeOptions : lightThemeOptions
|
|
||||||
);
|
|
||||||
|
|
||||||
const applyThemeToDOM = (theme: SystemThemeType) => {
|
|
||||||
const themeMap = {
|
|
||||||
[SystemThemeType.SystemThemeAuto]: 'auto',
|
|
||||||
[SystemThemeType.SystemThemeDark]: 'dark',
|
|
||||||
[SystemThemeType.SystemThemeLight]: 'light',
|
|
||||||
};
|
|
||||||
document.documentElement.setAttribute('data-theme', themeMap[theme]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadThemeColors = async (themeName?: string) => {
|
|
||||||
const targetName = resolveThemeName(
|
|
||||||
themeName || configStore.config?.appearance?.currentTheme
|
|
||||||
);
|
);
|
||||||
currentColors.value = await fetchThemeColors(targetName);
|
|
||||||
};
|
|
||||||
|
|
||||||
const initializeTheme = async () => {
|
const isDarkMode = computed(
|
||||||
applyThemeToDOM(currentTheme.value);
|
() =>
|
||||||
await loadThemeColors();
|
currentTheme.value === SystemThemeType.SystemThemeDark ||
|
||||||
};
|
(currentTheme.value === SystemThemeType.SystemThemeAuto &&
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||||
|
);
|
||||||
|
|
||||||
const setTheme = async (theme: SystemThemeType) => {
|
const availableThemes = computed<ThemeOption[]>(() =>
|
||||||
await configStore.setSystemTheme(theme);
|
isDarkMode.value ? darkThemeOptions : lightThemeOptions
|
||||||
applyThemeToDOM(theme);
|
);
|
||||||
refreshEditorTheme();
|
|
||||||
};
|
|
||||||
|
|
||||||
const switchToTheme = async (themeName: string) => {
|
const applyThemeToDOM = (theme: SystemThemeType) => {
|
||||||
if (!themePresetMap[themeName]) {
|
const themeMap = {
|
||||||
console.error('Theme not found:', themeName);
|
[SystemThemeType.SystemThemeAuto]: 'auto',
|
||||||
return false;
|
[SystemThemeType.SystemThemeDark]: 'dark',
|
||||||
}
|
[SystemThemeType.SystemThemeLight]: 'light',
|
||||||
|
};
|
||||||
|
document.documentElement.setAttribute('data-theme', themeMap[theme]);
|
||||||
|
};
|
||||||
|
|
||||||
await loadThemeColors(themeName);
|
const loadThemeColors = async (themeName?: string) => {
|
||||||
await configStore.setCurrentTheme(themeName);
|
const targetName = resolveThemeName(
|
||||||
refreshEditorTheme();
|
themeName || configStore.config?.appearance?.currentTheme
|
||||||
return true;
|
);
|
||||||
};
|
currentColors.value = await fetchThemeColors(targetName);
|
||||||
|
};
|
||||||
|
|
||||||
const updateCurrentColors = (colors: Partial<ThemeColors>) => {
|
const initTheme = async () => {
|
||||||
if (!currentColors.value) return;
|
applyThemeToDOM(currentTheme.value);
|
||||||
Object.assign(currentColors.value, colors);
|
await loadThemeColors();
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveCurrentTheme = async () => {
|
const setTheme = async (theme: SystemThemeType) => {
|
||||||
if (!currentColors.value) {
|
await configStore.setSystemTheme(theme);
|
||||||
throw new Error('No theme selected');
|
applyThemeToDOM(theme);
|
||||||
}
|
refreshEditorTheme();
|
||||||
|
};
|
||||||
|
|
||||||
const themeName = resolveThemeName(currentColors.value.themeName);
|
const switchToTheme = async (themeName: string) => {
|
||||||
currentColors.value.themeName = themeName;
|
if (!themePresetMap[themeName]) {
|
||||||
|
console.error('Theme not found:', themeName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
await ThemeService.UpdateTheme(themeName, currentColors.value as unknown as ThemeColorConfig);
|
await loadThemeColors(themeName);
|
||||||
|
await configStore.setCurrentTheme(themeName);
|
||||||
|
refreshEditorTheme();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
await loadThemeColors(themeName);
|
const updateCurrentColors = (colors: Partial<ThemeColors>) => {
|
||||||
refreshEditorTheme();
|
if (!currentColors.value) return;
|
||||||
return true;
|
Object.assign(currentColors.value, colors);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetCurrentTheme = async () => {
|
const saveCurrentTheme = async () => {
|
||||||
if (!currentColors.value) {
|
if (!currentColors.value) {
|
||||||
throw new Error('No theme selected');
|
throw new Error('No theme selected');
|
||||||
}
|
}
|
||||||
|
|
||||||
const themeName = resolveThemeName(currentColors.value.themeName);
|
const themeName = resolveThemeName(currentColors.value.themeName);
|
||||||
await ThemeService.ResetTheme(themeName);
|
currentColors.value.themeName = themeName;
|
||||||
|
|
||||||
await loadThemeColors(themeName);
|
await ThemeService.UpdateTheme(themeName, currentColors.value as ThemeColorConfig);
|
||||||
refreshEditorTheme();
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const refreshEditorTheme = () => {
|
await loadThemeColors(themeName);
|
||||||
applyThemeToDOM(currentTheme.value);
|
refreshEditorTheme();
|
||||||
const editorStore = useEditorStore();
|
return true;
|
||||||
editorStore?.applyThemeSettings();
|
};
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
const resetCurrentTheme = async () => {
|
||||||
availableThemes,
|
if (!currentColors.value) {
|
||||||
currentTheme,
|
throw new Error('No theme selected');
|
||||||
currentColors,
|
}
|
||||||
isDarkMode,
|
|
||||||
setTheme,
|
const themeName = resolveThemeName(currentColors.value.themeName);
|
||||||
switchToTheme,
|
await ThemeService.ResetTheme(themeName);
|
||||||
initializeTheme,
|
|
||||||
updateCurrentColors,
|
await loadThemeColors(themeName);
|
||||||
saveCurrentTheme,
|
refreshEditorTheme();
|
||||||
resetCurrentTheme,
|
return true;
|
||||||
refreshEditorTheme,
|
};
|
||||||
applyThemeToDOM,
|
|
||||||
};
|
const refreshEditorTheme = () => {
|
||||||
|
applyThemeToDOM(currentTheme.value);
|
||||||
|
const editorStore = useEditorStore();
|
||||||
|
editorStore?.applyThemeSettings();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
availableThemes,
|
||||||
|
currentTheme,
|
||||||
|
currentColors,
|
||||||
|
isDarkMode,
|
||||||
|
setTheme,
|
||||||
|
switchToTheme,
|
||||||
|
initTheme,
|
||||||
|
updateCurrentColors,
|
||||||
|
saveCurrentTheme,
|
||||||
|
resetCurrentTheme,
|
||||||
|
refreshEditorTheme,
|
||||||
|
applyThemeToDOM,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -150,15 +150,19 @@ const blockLayer = layer({
|
|||||||
// 转换为视口坐标进行后续计算
|
// 转换为视口坐标进行后续计算
|
||||||
const fromCoordsTop = fromLineBlock.top + view.documentTop;
|
const fromCoordsTop = fromLineBlock.top + view.documentTop;
|
||||||
let toCoordsBottom = toLineBlock.bottom + view.documentTop;
|
let toCoordsBottom = toLineBlock.bottom + view.documentTop;
|
||||||
|
|
||||||
// 对最后一个块进行特殊处理,让它直接延伸到底部
|
|
||||||
if (idx === blocks.length - 1) {
|
if (idx === blocks.length - 1) {
|
||||||
const editorHeight = view.dom.clientHeight;
|
// 计算需要添加到最后一个块的额外高度,以覆盖 scrollPastEnd 添加的额外滚动空间
|
||||||
const contentBottom = toCoordsBottom - view.documentTop + view.documentPadding.top;
|
// scrollPastEnd 会在文档底部添加相当于 scrollDOM.clientHeight 的额外空间
|
||||||
|
// 当滚动到最底部时,顶部仍会显示一行(defaultLineHeight),需要减去这部分
|
||||||
|
const editorHeight = view.scrollDOM.clientHeight;
|
||||||
|
const extraHeight = editorHeight - (
|
||||||
|
view.defaultLineHeight + // 当滚动到最底部时,顶部仍显示一行
|
||||||
|
view.documentPadding.top +
|
||||||
|
8 // 额外的边距调整
|
||||||
|
);
|
||||||
|
|
||||||
// 让最后一个块直接延伸到编辑器底部
|
if (extraHeight > 0) {
|
||||||
if (contentBottom < editorHeight) {
|
|
||||||
const extraHeight = editorHeight - contentBottom - 10;
|
|
||||||
toCoordsBottom += extraHeight;
|
toCoordsBottom += extraHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { EditorView } from '@codemirror/view';
|
import { EditorView } from '@codemirror/view';
|
||||||
import { Extension } from '@codemirror/state';
|
import { Extension } from '@codemirror/state';
|
||||||
import { copyCommand, cutCommand, pasteCommand } from '../codeblock/copyPaste';
|
import { copyCommand, cutCommand, pasteCommand } from '../codeblock/copyPaste';
|
||||||
import { KeyBindingCommand } from '../../../../../bindings/voidraft/internal/models/models';
|
import { KeyBindingKey } from '@/../bindings/voidraft/internal/models/models';
|
||||||
import { useKeybindingStore } from '@/stores/keybindingStore';
|
import { useKeybindingStore } from '@/stores/keybindingStore';
|
||||||
import { undo, redo } from '@codemirror/commands';
|
import { undo, redo } from '@codemirror/commands';
|
||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
@@ -26,59 +26,58 @@ function formatKeyBinding(keyBinding: string): string {
|
|||||||
|
|
||||||
return keyBinding
|
return keyBinding
|
||||||
.replace("Mod", isMac ? "Cmd" : "Ctrl")
|
.replace("Mod", isMac ? "Cmd" : "Ctrl")
|
||||||
.replace("Shift", "Shift")
|
|
||||||
.replace("Alt", isMac ? "Option" : "Alt")
|
.replace("Alt", isMac ? "Option" : "Alt")
|
||||||
.replace("Ctrl", isMac ? "Ctrl" : "Ctrl")
|
|
||||||
.replace(/-/g, " + ");
|
.replace(/-/g, " + ");
|
||||||
}
|
}
|
||||||
|
|
||||||
const shortcutCache = new Map<KeyBindingCommand, string>();
|
const shortcutCache = new Map<KeyBindingKey, string>();
|
||||||
|
|
||||||
|
|
||||||
function getShortcutText(command?: KeyBindingCommand): string {
|
function getShortcutText(keyBindingKey?: KeyBindingKey): string {
|
||||||
if (command === undefined) {
|
if (keyBindingKey === undefined) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const cached = shortcutCache.get(command);
|
const cached = shortcutCache.get(keyBindingKey);
|
||||||
if (cached !== undefined) {
|
if (cached !== undefined) {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const keybindingStore = useKeybindingStore();
|
const keybindingStore = useKeybindingStore();
|
||||||
|
// binding.key 是命令标识符,binding.command 是快捷键组合
|
||||||
const binding = keybindingStore.keyBindings.find(
|
const binding = keybindingStore.keyBindings.find(
|
||||||
(kb) => kb.command === command && kb.enabled
|
(kb) => kb.key === keyBindingKey && kb.enabled
|
||||||
);
|
);
|
||||||
|
|
||||||
if (binding?.key) {
|
if (binding?.command) {
|
||||||
const formatted = formatKeyBinding(binding.key);
|
const formatted = formatKeyBinding(binding.command);
|
||||||
shortcutCache.set(command, formatted);
|
shortcutCache.set(keyBindingKey, formatted);
|
||||||
return formatted;
|
return formatted;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("An error occurred while getting the shortcut:", error);
|
console.warn("An error occurred while getting the shortcut:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
shortcutCache.set(command, "");
|
shortcutCache.set(keyBindingKey, "");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getBuiltinMenuNodes(): MenuSchemaNode[] {
|
function builtinMenuNodes(): MenuSchemaNode[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: "copy",
|
id: "copy",
|
||||||
labelKey: "keybindings.commands.blockCopy",
|
labelKey: "keybindings.commands.blockCopy",
|
||||||
command: copyCommand,
|
command: copyCommand,
|
||||||
shortcutCommand: KeyBindingCommand.BlockCopyCommand,
|
keyBindingKey: KeyBindingKey.BlockCopyKeyBindingKey,
|
||||||
enabled: (context) => context.hasSelection
|
enabled: (context) => context.hasSelection
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "cut",
|
id: "cut",
|
||||||
labelKey: "keybindings.commands.blockCut",
|
labelKey: "keybindings.commands.blockCut",
|
||||||
command: cutCommand,
|
command: cutCommand,
|
||||||
shortcutCommand: KeyBindingCommand.BlockCutCommand,
|
keyBindingKey: KeyBindingKey.BlockCutKeyBindingKey,
|
||||||
visible: (context) => context.isEditable,
|
visible: (context) => context.isEditable,
|
||||||
enabled: (context) => context.hasSelection && context.isEditable
|
enabled: (context) => context.hasSelection && context.isEditable
|
||||||
},
|
},
|
||||||
@@ -86,21 +85,21 @@ function getBuiltinMenuNodes(): MenuSchemaNode[] {
|
|||||||
id: "paste",
|
id: "paste",
|
||||||
labelKey: "keybindings.commands.blockPaste",
|
labelKey: "keybindings.commands.blockPaste",
|
||||||
command: pasteCommand,
|
command: pasteCommand,
|
||||||
shortcutCommand: KeyBindingCommand.BlockPasteCommand,
|
keyBindingKey: KeyBindingKey.BlockPasteKeyBindingKey,
|
||||||
visible: (context) => context.isEditable
|
visible: (context) => context.isEditable
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "undo",
|
id: "undo",
|
||||||
labelKey: "keybindings.commands.historyUndo",
|
labelKey: "keybindings.commands.historyUndo",
|
||||||
command: undo,
|
command: undo,
|
||||||
shortcutCommand: KeyBindingCommand.HistoryUndoCommand,
|
keyBindingKey: KeyBindingKey.HistoryUndoKeyBindingKey,
|
||||||
visible: (context) => context.isEditable
|
visible: (context) => context.isEditable
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "redo",
|
id: "redo",
|
||||||
labelKey: "keybindings.commands.historyRedo",
|
labelKey: "keybindings.commands.historyRedo",
|
||||||
command: redo,
|
command: redo,
|
||||||
shortcutCommand: KeyBindingCommand.HistoryRedoCommand,
|
keyBindingKey: KeyBindingKey.HistoryRedoKeyBindingKey,
|
||||||
visible: (context) => context.isEditable
|
visible: (context) => context.isEditable
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -110,7 +109,7 @@ let builtinMenuRegistered = false;
|
|||||||
|
|
||||||
function ensureBuiltinMenuRegistered(): void {
|
function ensureBuiltinMenuRegistered(): void {
|
||||||
if (builtinMenuRegistered) return;
|
if (builtinMenuRegistered) return;
|
||||||
registerMenuNodes(getBuiltinMenuNodes());
|
registerMenuNodes(builtinMenuNodes());
|
||||||
builtinMenuRegistered = true;
|
builtinMenuRegistered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EditorView } from '@codemirror/view';
|
import type { EditorView } from '@codemirror/view';
|
||||||
import { EditorState } from '@codemirror/state';
|
import { EditorState } from '@codemirror/state';
|
||||||
import type { KeyBindingCommand } from '../../../../../bindings/voidraft/internal/models/models';
|
import { KeyBindingKey } from '@/../bindings/voidraft/internal/models/models';
|
||||||
|
|
||||||
export interface MenuContext {
|
export interface MenuContext {
|
||||||
view: EditorView;
|
view: EditorView;
|
||||||
@@ -16,7 +16,7 @@ export type MenuSchemaNode =
|
|||||||
type?: "action";
|
type?: "action";
|
||||||
labelKey: string;
|
labelKey: string;
|
||||||
command?: (view: EditorView) => boolean;
|
command?: (view: EditorView) => boolean;
|
||||||
shortcutCommand?: KeyBindingCommand;
|
keyBindingKey?: KeyBindingKey;
|
||||||
visible?: (context: MenuContext) => boolean;
|
visible?: (context: MenuContext) => boolean;
|
||||||
enabled?: (context: MenuContext) => boolean;
|
enabled?: (context: MenuContext) => boolean;
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ export interface RenderMenuItem {
|
|||||||
|
|
||||||
interface MenuBuildOptions {
|
interface MenuBuildOptions {
|
||||||
translate: (key: string) => string;
|
translate: (key: string) => string;
|
||||||
formatShortcut: (command?: KeyBindingCommand) => string;
|
formatShortcut: (keyBindingKey?: KeyBindingKey) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const menuRegistry: MenuSchemaNode[] = [];
|
const menuRegistry: MenuSchemaNode[] = [];
|
||||||
@@ -89,7 +89,7 @@ function convertNode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const disabled = node.enabled ? !node.enabled(context) : false;
|
const disabled = node.enabled ? !node.enabled(context) : false;
|
||||||
const shortcut = options.formatShortcut(node.shortcutCommand);
|
const shortcut = options.formatShortcut(node.keyBindingKey);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: node.id,
|
id: node.id,
|
||||||
|
|||||||
572
frontend/src/views/editor/extensions/minimap/blockManager.ts
Normal file
572
frontend/src/views/editor/extensions/minimap/blockManager.ts
Normal file
@@ -0,0 +1,572 @@
|
|||||||
|
import { EditorView } from '@codemirror/view';
|
||||||
|
import { EditorState, ChangeSet } from '@codemirror/state';
|
||||||
|
import { syntaxTree, highlightingFor } from '@codemirror/language';
|
||||||
|
import { Highlighter, highlightTree } from '@lezer/highlight';
|
||||||
|
import { Scale } from './config';
|
||||||
|
import { LinesState, getLinesSnapshot } from './linesState';
|
||||||
|
import {
|
||||||
|
ToWorkerMessage,
|
||||||
|
ToMainMessage,
|
||||||
|
BlockRequest,
|
||||||
|
Highlight,
|
||||||
|
LineSpan,
|
||||||
|
FontInfo,
|
||||||
|
UpdateFontInfoRequest,
|
||||||
|
} from './worker/protocol';
|
||||||
|
import crelt from 'crelt';
|
||||||
|
|
||||||
|
const BLOCK_LINES = 50;
|
||||||
|
const MAX_BLOCKS = 20;
|
||||||
|
|
||||||
|
interface Block {
|
||||||
|
index: number;
|
||||||
|
startLine: number;
|
||||||
|
endLine: number;
|
||||||
|
bitmap: ImageBitmap | null;
|
||||||
|
dirty: boolean;
|
||||||
|
rendering: boolean;
|
||||||
|
requestId: number;
|
||||||
|
lastUsed: number; // LRU 时间戳
|
||||||
|
// 高亮缓存
|
||||||
|
cachedHighlights: Highlight[] | null;
|
||||||
|
cachedLines: LineSpan[][] | null;
|
||||||
|
cachedTextSlice: string | null;
|
||||||
|
cachedTextOffset: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BlockManager {
|
||||||
|
private worker: Worker | null = null;
|
||||||
|
private blocks = new Map<number, Block>();
|
||||||
|
private fontInfoMap = new Map<string, FontInfo>();
|
||||||
|
private fontDirty = true;
|
||||||
|
private fontVersion = 0;
|
||||||
|
private sentFontTags = new Set<string>(); // 已发送给 Worker 的字体标签
|
||||||
|
private measureCache: { charWidth: number; lineHeight: number; version: number } | null = null;
|
||||||
|
private displayText: 'blocks' | 'characters' = 'characters';
|
||||||
|
private themeClasses: Set<string>;
|
||||||
|
private requestId = 0;
|
||||||
|
private onBlockReady: (() => void) | null = null;
|
||||||
|
|
||||||
|
// 批量处理块完成事件
|
||||||
|
private pendingBlockReadyHandle: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
private renderingCount = 0;
|
||||||
|
|
||||||
|
constructor(private view: EditorView) {
|
||||||
|
this.themeClasses = new Set(Array.from(view.dom.classList));
|
||||||
|
this.initWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initWorker(): void {
|
||||||
|
this.worker = new Worker(
|
||||||
|
new URL('./worker/block.worker.ts', import.meta.url),
|
||||||
|
{ type: 'module' }
|
||||||
|
);
|
||||||
|
|
||||||
|
this.worker.onmessage = (e: MessageEvent<ToMainMessage>) => {
|
||||||
|
this.handleWorkerMessage(e.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.worker.onerror = (e) => {
|
||||||
|
console.error('[BlockManager] Worker error:', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.worker.postMessage({ type: 'init' } as ToWorkerMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleWorkerMessage(msg: ToMainMessage): void {
|
||||||
|
switch (msg.type) {
|
||||||
|
case 'ready':
|
||||||
|
break;
|
||||||
|
case 'blockComplete': {
|
||||||
|
const block = this.blocks.get(msg.blockIndex);
|
||||||
|
if (block && block.requestId === msg.blockId) {
|
||||||
|
block.bitmap = msg.bitmap;
|
||||||
|
block.dirty = false;
|
||||||
|
block.rendering = false;
|
||||||
|
this.renderingCount = Math.max(0, this.renderingCount - 1);
|
||||||
|
this.scheduleBlockReady();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'error':
|
||||||
|
console.error('[BlockManager] Worker error:', msg.message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量触发块就绪回调
|
||||||
|
// 策略:等 30ms,让多个块完成事件合并
|
||||||
|
private scheduleBlockReady(): void {
|
||||||
|
if (this.pendingBlockReadyHandle !== null) {
|
||||||
|
clearTimeout(this.pendingBlockReadyHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pendingBlockReadyHandle = setTimeout(() => {
|
||||||
|
this.pendingBlockReadyHandle = null;
|
||||||
|
if (this.renderingCount === 0) {
|
||||||
|
this.onBlockReady?.();
|
||||||
|
}
|
||||||
|
}, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnBlockReady(callback: () => void): void {
|
||||||
|
this.onBlockReady = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisplayText(mode: 'blocks' | 'characters'): void {
|
||||||
|
if (this.displayText !== mode) {
|
||||||
|
this.displayText = mode;
|
||||||
|
this.markAllDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkThemeChange(): boolean {
|
||||||
|
const nowClasses = Array.from(this.view.dom.classList);
|
||||||
|
const now = new Set(nowClasses);
|
||||||
|
const prev = this.themeClasses;
|
||||||
|
this.themeClasses = now;
|
||||||
|
|
||||||
|
if (!prev) {
|
||||||
|
this.fontDirty = true;
|
||||||
|
this.markAllDirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevSet = new Set(prev);
|
||||||
|
const nowSet = new Set(now);
|
||||||
|
prevSet.delete('cm-focused');
|
||||||
|
nowSet.delete('cm-focused');
|
||||||
|
|
||||||
|
if (prevSet.size !== nowSet.size) {
|
||||||
|
this.fontDirty = true;
|
||||||
|
this.markAllDirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const cls of prevSet) {
|
||||||
|
if (!nowSet.has(cls)) {
|
||||||
|
this.fontDirty = true;
|
||||||
|
this.markAllDirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
markAllDirty(): void {
|
||||||
|
for (const block of this.blocks.values()) {
|
||||||
|
block.dirty = true;
|
||||||
|
// 清除缓存,强制重新收集数据
|
||||||
|
block.cachedHighlights = null;
|
||||||
|
block.cachedLines = null;
|
||||||
|
block.cachedTextSlice = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDocChange(state: EditorState, changes: ChangeSet, oldLineCount: number): void {
|
||||||
|
const totalLines = state.doc.lines;
|
||||||
|
const maxIndex = Math.ceil(totalLines / BLOCK_LINES) - 1;
|
||||||
|
|
||||||
|
// 找出变化影响的块
|
||||||
|
const affectedBlocks = new Set<number>();
|
||||||
|
|
||||||
|
// 正确检测行数变化:比较新旧文档总行数
|
||||||
|
const hasLineCountChange = totalLines !== oldLineCount;
|
||||||
|
|
||||||
|
changes.iterChanges((fromA, toA, fromB, toB) => {
|
||||||
|
// 找出新文档中受影响的行范围
|
||||||
|
const startLine = state.doc.lineAt(fromB).number;
|
||||||
|
const endLine = state.doc.lineAt(Math.min(toB, state.doc.length)).number;
|
||||||
|
|
||||||
|
const startBlock = Math.floor((startLine - 1) / BLOCK_LINES);
|
||||||
|
const endBlock = Math.floor((endLine - 1) / BLOCK_LINES);
|
||||||
|
|
||||||
|
for (let i = startBlock; i <= endBlock; i++) {
|
||||||
|
affectedBlocks.add(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果行数变化,后续所有块都需要标记为 dirty
|
||||||
|
let markRest = false;
|
||||||
|
|
||||||
|
for (const [index, block] of this.blocks) {
|
||||||
|
if (index > maxIndex) {
|
||||||
|
block.bitmap?.close();
|
||||||
|
this.blocks.delete(index);
|
||||||
|
} else if (affectedBlocks.has(index)) {
|
||||||
|
block.dirty = true;
|
||||||
|
// 清除缓存
|
||||||
|
block.cachedHighlights = null;
|
||||||
|
block.cachedLines = null;
|
||||||
|
block.cachedTextSlice = null;
|
||||||
|
if (hasLineCountChange) {
|
||||||
|
markRest = true; // 从这个块开始,后续块都需要更新
|
||||||
|
}
|
||||||
|
} else if (markRest) {
|
||||||
|
block.dirty = true;
|
||||||
|
// 清除缓存
|
||||||
|
block.cachedHighlights = null;
|
||||||
|
block.cachedLines = null;
|
||||||
|
block.cachedTextSlice = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算可见范围(提取公共计算逻辑)
|
||||||
|
*/
|
||||||
|
private getVisibleRange(
|
||||||
|
canvasHeight: number,
|
||||||
|
lineHeight: number,
|
||||||
|
scrollInfo: { scrollTop: number; clientHeight: number; scrollHeight: number }
|
||||||
|
): {
|
||||||
|
valid: boolean;
|
||||||
|
totalLines: number;
|
||||||
|
scaledPTop: number;
|
||||||
|
canvasTop: number;
|
||||||
|
startBlock: number;
|
||||||
|
endBlock: number;
|
||||||
|
} | null {
|
||||||
|
const totalLines = this.view.state.field(LinesState).length;
|
||||||
|
if (totalLines === 0 || canvasHeight <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { top: pTop, bottom: pBottom } = this.view.documentPadding;
|
||||||
|
const scaledPTop = pTop / Scale.SizeRatio;
|
||||||
|
const scaledPBottom = pBottom / Scale.SizeRatio;
|
||||||
|
const totalHeight = scaledPTop + scaledPBottom + totalLines * lineHeight;
|
||||||
|
|
||||||
|
const { scrollTop, clientHeight, scrollHeight } = scrollInfo;
|
||||||
|
const scrollPercent = Math.max(0, Math.min(1, scrollTop / (scrollHeight - clientHeight))) || 0;
|
||||||
|
const canvasTop = Math.max(0, scrollPercent * (totalHeight - canvasHeight));
|
||||||
|
|
||||||
|
const visibleStart = Math.max(1, Math.floor((canvasTop - scaledPTop) / lineHeight) + 1);
|
||||||
|
const visibleEnd = Math.min(totalLines, Math.ceil((canvasTop + canvasHeight - scaledPTop) / lineHeight));
|
||||||
|
|
||||||
|
if (visibleEnd < visibleStart) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
totalLines,
|
||||||
|
scaledPTop,
|
||||||
|
canvasTop,
|
||||||
|
startBlock: Math.floor((visibleStart - 1) / BLOCK_LINES),
|
||||||
|
endBlock: Math.floor((visibleEnd - 1) / BLOCK_LINES),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render(
|
||||||
|
canvas: HTMLCanvasElement,
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
scrollInfo: { scrollTop: number; clientHeight: number; scrollHeight: number }
|
||||||
|
): void {
|
||||||
|
if (this.fontDirty) {
|
||||||
|
this.refreshFontCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { charWidth, lineHeight } = this.measure(ctx);
|
||||||
|
const range = this.getVisibleRange(canvas.height, lineHeight, scrollInfo);
|
||||||
|
if (!range) return;
|
||||||
|
|
||||||
|
const { totalLines, scaledPTop, canvasTop, startBlock, endBlock } = range;
|
||||||
|
const state = this.view.state;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
for (let i = startBlock; i <= endBlock; i++) {
|
||||||
|
const block = this.getOrCreateBlock(i, state, totalLines);
|
||||||
|
if (!block) continue;
|
||||||
|
|
||||||
|
if (block.dirty && !block.rendering) {
|
||||||
|
this.requestBlockRender(block, state, charWidth, lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block.bitmap) {
|
||||||
|
const blockY = scaledPTop + (block.startLine - 1) * lineHeight - canvasTop;
|
||||||
|
ctx.drawImage(block.bitmap, 0, blockY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fontDirty = false;
|
||||||
|
this.evictOldBlocks();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只绘制缓存的块,不请求新渲染(用于 overlay-only 更新)
|
||||||
|
*/
|
||||||
|
drawCachedBlocks(
|
||||||
|
canvas: HTMLCanvasElement,
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
scrollInfo: { scrollTop: number; clientHeight: number; scrollHeight: number }
|
||||||
|
): void {
|
||||||
|
const { lineHeight } = this.measure(ctx);
|
||||||
|
const range = this.getVisibleRange(canvas.height, lineHeight, scrollInfo);
|
||||||
|
if (!range) return;
|
||||||
|
|
||||||
|
const { scaledPTop, canvasTop, startBlock, endBlock } = range;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
for (let i = startBlock; i <= endBlock; i++) {
|
||||||
|
const block = this.blocks.get(i);
|
||||||
|
if (block?.bitmap) {
|
||||||
|
const blockY = scaledPTop + (block.startLine - 1) * lineHeight - canvasTop;
|
||||||
|
ctx.drawImage(block.bitmap, 0, blockY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getOrCreateBlock(index: number, state: EditorState, totalLines: number): Block | null {
|
||||||
|
const startLine = index * BLOCK_LINES + 1;
|
||||||
|
if (startLine > totalLines) return null;
|
||||||
|
|
||||||
|
const endLine = Math.min((index + 1) * BLOCK_LINES, totalLines);
|
||||||
|
const now = performance.now();
|
||||||
|
|
||||||
|
let block = this.blocks.get(index);
|
||||||
|
if (!block) {
|
||||||
|
block = {
|
||||||
|
index,
|
||||||
|
startLine,
|
||||||
|
endLine,
|
||||||
|
bitmap: null,
|
||||||
|
dirty: true,
|
||||||
|
rendering: false,
|
||||||
|
requestId: 0,
|
||||||
|
lastUsed: now,
|
||||||
|
cachedHighlights: null,
|
||||||
|
cachedLines: null,
|
||||||
|
cachedTextSlice: null,
|
||||||
|
cachedTextOffset: 0,
|
||||||
|
};
|
||||||
|
this.blocks.set(index, block);
|
||||||
|
} else {
|
||||||
|
block.startLine = startLine;
|
||||||
|
block.endLine = endLine;
|
||||||
|
block.lastUsed = now; // 更新 LRU 时间戳
|
||||||
|
}
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
private requestBlockRender(
|
||||||
|
block: Block,
|
||||||
|
state: EditorState,
|
||||||
|
charWidth: number,
|
||||||
|
lineHeight: number
|
||||||
|
): void {
|
||||||
|
if (!this.worker) return;
|
||||||
|
|
||||||
|
block.rendering = true;
|
||||||
|
block.requestId = ++this.requestId;
|
||||||
|
this.renderingCount++;
|
||||||
|
|
||||||
|
const { startLine, endLine } = block;
|
||||||
|
|
||||||
|
let highlights: Highlight[];
|
||||||
|
let lines: LineSpan[][];
|
||||||
|
let textSlice: string;
|
||||||
|
let textOffset: number;
|
||||||
|
|
||||||
|
// 只有当块是 dirty 时才重新收集数据,否则使用缓存
|
||||||
|
if (block.dirty || !block.cachedHighlights) {
|
||||||
|
const linesSnapshot = getLinesSnapshot(state);
|
||||||
|
const tree = syntaxTree(state);
|
||||||
|
|
||||||
|
// Collect highlights
|
||||||
|
highlights = [];
|
||||||
|
if (tree.length > 0 && startLine <= state.doc.lines) {
|
||||||
|
const highlighter: Highlighter = {
|
||||||
|
style: (tags) => highlightingFor(state, tags),
|
||||||
|
};
|
||||||
|
const startPos = state.doc.line(startLine).from;
|
||||||
|
const endPos = state.doc.line(Math.min(endLine, state.doc.lines)).to;
|
||||||
|
|
||||||
|
highlightTree(tree, highlighter, (from, to, tags) => {
|
||||||
|
highlights.push({ from, to, tags });
|
||||||
|
}, startPos, endPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract relevant lines
|
||||||
|
const startIdx = startLine - 1;
|
||||||
|
const endIdx = Math.min(endLine, linesSnapshot.length);
|
||||||
|
lines = linesSnapshot.slice(startIdx, endIdx).map(line =>
|
||||||
|
line.map(span => ({ from: span.from, to: span.to, folded: span.folded }))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get text slice
|
||||||
|
textOffset = 0;
|
||||||
|
let textEnd = 0;
|
||||||
|
if (lines.length > 0 && lines[0].length > 0) {
|
||||||
|
textOffset = lines[0][0].from;
|
||||||
|
const lastLine = lines[lines.length - 1];
|
||||||
|
if (lastLine.length > 0) {
|
||||||
|
textEnd = lastLine[lastLine.length - 1].to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textSlice = state.doc.sliceString(textOffset, textEnd);
|
||||||
|
|
||||||
|
// 缓存数据
|
||||||
|
block.cachedHighlights = highlights;
|
||||||
|
block.cachedLines = lines;
|
||||||
|
block.cachedTextSlice = textSlice;
|
||||||
|
block.cachedTextOffset = textOffset;
|
||||||
|
} else {
|
||||||
|
// 使用缓存的数据
|
||||||
|
highlights = block.cachedHighlights;
|
||||||
|
lines = block.cachedLines!;
|
||||||
|
textSlice = block.cachedTextSlice!;
|
||||||
|
textOffset = block.cachedTextOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保字体信息已发送给 Worker
|
||||||
|
this.ensureFontInfoSent(highlights);
|
||||||
|
|
||||||
|
const blockLines = endLine - startLine + 1;
|
||||||
|
const request: BlockRequest = {
|
||||||
|
type: 'renderBlock',
|
||||||
|
blockId: block.requestId,
|
||||||
|
blockIndex: block.index,
|
||||||
|
startLine,
|
||||||
|
endLine,
|
||||||
|
width: Math.ceil(Scale.MaxWidth * Scale.PixelMultiplier),
|
||||||
|
height: Math.ceil(blockLines * lineHeight),
|
||||||
|
highlights,
|
||||||
|
lines,
|
||||||
|
textSlice,
|
||||||
|
textOffset,
|
||||||
|
displayText: this.displayText,
|
||||||
|
charWidth,
|
||||||
|
lineHeight,
|
||||||
|
gutterOffset: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.worker.postMessage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保字体信息已发送给 Worker
|
||||||
|
* 增量发送:只发送新的标签
|
||||||
|
*/
|
||||||
|
private ensureFontInfoSent(highlights: Highlight[]): void {
|
||||||
|
if (!this.worker) return;
|
||||||
|
|
||||||
|
// 收集新的标签
|
||||||
|
const newTags: string[] = [];
|
||||||
|
for (const hl of highlights) {
|
||||||
|
if (!this.sentFontTags.has(hl.tags)) {
|
||||||
|
newTags.push(hl.tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 默认字体标签
|
||||||
|
if (!this.sentFontTags.has('')) {
|
||||||
|
newTags.push('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有新标签,不需要发送
|
||||||
|
if (newTags.length === 0) return;
|
||||||
|
|
||||||
|
// 构建新标签的字体信息
|
||||||
|
const fontInfoMap: Record<string, FontInfo> = {};
|
||||||
|
for (const tag of newTags) {
|
||||||
|
fontInfoMap[tag] = this.getFontInfo(tag);
|
||||||
|
this.sentFontTags.add(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateRequest: UpdateFontInfoRequest = {
|
||||||
|
type: 'updateFontInfo',
|
||||||
|
fontInfoMap,
|
||||||
|
defaultFont: this.getFontInfo(''),
|
||||||
|
};
|
||||||
|
this.worker.postMessage(updateRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private evictOldBlocks(): void {
|
||||||
|
if (this.blocks.size <= MAX_BLOCKS) return;
|
||||||
|
|
||||||
|
// LRU 驱逐:按 lastUsed 升序排序,驱逐最久未使用的块
|
||||||
|
const sorted = Array.from(this.blocks.entries())
|
||||||
|
.filter(([, b]) => !b.rendering)
|
||||||
|
.sort((a, b) => a[1].lastUsed - b[1].lastUsed);
|
||||||
|
|
||||||
|
const toRemove = sorted.slice(0, this.blocks.size - MAX_BLOCKS);
|
||||||
|
for (const [index, block] of toRemove) {
|
||||||
|
block.bitmap?.close();
|
||||||
|
this.blocks.delete(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private refreshFontCache(): void {
|
||||||
|
this.fontInfoMap.clear();
|
||||||
|
this.measureCache = null;
|
||||||
|
this.sentFontTags.clear(); // 需要重新发送字体信息给 Worker
|
||||||
|
// 注意:fontDirty 在成功渲染块后才设为 false
|
||||||
|
this.fontVersion++;
|
||||||
|
this.markAllDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
measure(ctx: CanvasRenderingContext2D): { charWidth: number; lineHeight: number } {
|
||||||
|
const info = this.getFontInfo('');
|
||||||
|
ctx.textBaseline = 'ideographic';
|
||||||
|
ctx.fillStyle = info.color;
|
||||||
|
ctx.font = info.font;
|
||||||
|
|
||||||
|
if (this.measureCache?.version === this.fontVersion) {
|
||||||
|
return { charWidth: this.measureCache.charWidth, lineHeight: this.measureCache.lineHeight };
|
||||||
|
}
|
||||||
|
|
||||||
|
const charWidth = ctx.measureText('_').width;
|
||||||
|
this.measureCache = { charWidth, lineHeight: info.lineHeight, version: this.fontVersion };
|
||||||
|
return { charWidth, lineHeight: info.lineHeight };
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFontInfo(tags: string): FontInfo {
|
||||||
|
const cached = this.fontInfoMap.get(tags);
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
|
const mockToken = crelt('span', { class: tags });
|
||||||
|
const mockLine = crelt('div', { class: 'cm-line', style: 'display: none' }, mockToken);
|
||||||
|
this.view.contentDOM.appendChild(mockLine);
|
||||||
|
|
||||||
|
const style = window.getComputedStyle(mockToken);
|
||||||
|
|
||||||
|
// 获取字体大小(用于渲染字符)
|
||||||
|
const fontSize = parseFloat(style.fontSize) || this.view.defaultLineHeight;
|
||||||
|
const scaledFontSize = Math.max(1, fontSize / Scale.SizeRatio);
|
||||||
|
|
||||||
|
// 获取行高(用于行间距)
|
||||||
|
const rawLineHeight = parseFloat(style.lineHeight);
|
||||||
|
const resolvedLineHeight = Number.isFinite(rawLineHeight) && rawLineHeight > 0 ? rawLineHeight : fontSize;
|
||||||
|
const lineHeight = Math.max(1, resolvedLineHeight / Scale.SizeRatio);
|
||||||
|
|
||||||
|
const result: FontInfo = {
|
||||||
|
color: style.color,
|
||||||
|
font: `${style.fontStyle} ${style.fontWeight} ${scaledFontSize}px ${style.fontFamily}`,
|
||||||
|
lineHeight,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.view.contentDOM.removeChild(mockLine);
|
||||||
|
this.fontInfoMap.set(tags, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
if (this.pendingBlockReadyHandle !== null) {
|
||||||
|
clearTimeout(this.pendingBlockReadyHandle);
|
||||||
|
}
|
||||||
|
for (const block of this.blocks.values()) {
|
||||||
|
block.bitmap?.close();
|
||||||
|
}
|
||||||
|
this.blocks.clear();
|
||||||
|
this.worker?.postMessage({ type: 'destroy' } as ToWorkerMessage);
|
||||||
|
this.worker?.terminate();
|
||||||
|
this.worker = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -8,12 +8,21 @@ import {
|
|||||||
|
|
||||||
import { LineBasedState } from "./linebasedstate";
|
import { LineBasedState } from "./linebasedstate";
|
||||||
import { DrawContext } from "./types";
|
import { DrawContext } from "./types";
|
||||||
import { Lines, LinesState, foldsChanged } from "./linesState";
|
import { LinesState, foldsChanged } from "./linesState";
|
||||||
import { Config } from "./config";
|
import { Config, Scale } from "./config";
|
||||||
|
import { lineLength, lineNumberAt, offsetWithinLine } from "./lineGeometry";
|
||||||
|
|
||||||
type Severity = Diagnostic["severity"];
|
type Severity = Diagnostic["severity"];
|
||||||
|
type DiagnosticRange = { from: number; to: number };
|
||||||
|
type LineDiagnostics = {
|
||||||
|
severity: Severity;
|
||||||
|
ranges: Array<DiagnosticRange>;
|
||||||
|
};
|
||||||
|
const MIN_PIXEL_WIDTH = 1 / Scale.PixelMultiplier;
|
||||||
|
const snapToDevice = (value: number) =>
|
||||||
|
Math.round(value * Scale.PixelMultiplier) / Scale.PixelMultiplier;
|
||||||
|
|
||||||
export class DiagnosticState extends LineBasedState<Severity> {
|
export class DiagnosticState extends LineBasedState<LineDiagnostics> {
|
||||||
private count: number | undefined = undefined;
|
private count: number | undefined = undefined;
|
||||||
|
|
||||||
public constructor(view: EditorView) {
|
public constructor(view: EditorView) {
|
||||||
@@ -63,70 +72,74 @@ export class DiagnosticState extends LineBasedState<Severity> {
|
|||||||
this.count = diagnosticCount(update.state);
|
this.count = diagnosticCount(update.state);
|
||||||
|
|
||||||
forEachDiagnostic(update.state, (diagnostic, from, to) => {
|
forEachDiagnostic(update.state, (diagnostic, from, to) => {
|
||||||
// Find the start and end lines for the diagnostic
|
const lineStart = lineNumberAt(lines, from);
|
||||||
const lineStart = this.findLine(from, lines);
|
const lineEnd = lineNumberAt(lines, to);
|
||||||
const lineEnd = this.findLine(to, lines);
|
if (lineStart <= 0 || lineEnd <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Populate each line in the range with the highest severity diagnostic
|
for (let lineNumber = lineStart; lineNumber <= lineEnd; lineNumber++) {
|
||||||
let severity = diagnostic.severity;
|
const spans = lines[lineNumber - 1];
|
||||||
for (let i = lineStart; i <= lineEnd; i++) {
|
if (!spans || spans.length === 0) {
|
||||||
const previous = this.get(i);
|
continue;
|
||||||
if (previous) {
|
|
||||||
severity = [severity, previous]
|
|
||||||
.sort(this.sort.bind(this))
|
|
||||||
.slice(0, 1)[0];
|
|
||||||
}
|
}
|
||||||
this.set(i, severity);
|
|
||||||
|
const length = lineLength(spans);
|
||||||
|
|
||||||
|
const startOffset =
|
||||||
|
lineNumber === lineStart
|
||||||
|
? offsetWithinLine(from, spans)
|
||||||
|
: 0;
|
||||||
|
const endOffset =
|
||||||
|
lineNumber === lineEnd ? offsetWithinLine(to, spans) : length;
|
||||||
|
|
||||||
|
const fromOffset = Math.max(0, Math.min(length, startOffset));
|
||||||
|
let toOffset = Math.max(fromOffset, Math.min(length, endOffset));
|
||||||
|
if (toOffset === fromOffset) {
|
||||||
|
toOffset = Math.min(length, fromOffset + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pushRange(lineNumber, diagnostic.severity, {
|
||||||
|
from: fromOffset,
|
||||||
|
to: toOffset,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.mergeRanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawLine(ctx: DrawContext, lineNumber: number) {
|
public drawLine(ctx: DrawContext, lineNumber: number) {
|
||||||
const { context, lineHeight, offsetX, offsetY } = ctx;
|
const diagnostics = this.get(lineNumber);
|
||||||
const severity = this.get(lineNumber);
|
if (!diagnostics) {
|
||||||
if (!severity) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the full line width rectangle in the background
|
const { context, lineHeight, charWidth, offsetX, offsetY } = ctx;
|
||||||
context.globalAlpha = 0.65;
|
const color = this.color(diagnostics.severity);
|
||||||
context.beginPath();
|
const snappedY = snapToDevice(offsetY);
|
||||||
context.rect(
|
const snappedHeight =
|
||||||
offsetX,
|
Math.max(MIN_PIXEL_WIDTH, snapToDevice(offsetY + lineHeight) - snappedY) ||
|
||||||
offsetY /* TODO Scaling causes anti-aliasing in rectangles */,
|
MIN_PIXEL_WIDTH;
|
||||||
context.canvas.width - offsetX,
|
|
||||||
lineHeight
|
|
||||||
);
|
|
||||||
context.fillStyle = this.color(severity);
|
|
||||||
context.fill();
|
|
||||||
|
|
||||||
// Draw diagnostic range rectangle in the foreground
|
context.fillStyle = color;
|
||||||
// TODO: We need to update the state to have specific ranges
|
for (const range of diagnostics.ranges) {
|
||||||
// context.globalAlpha = 1;
|
const startX = offsetX + range.from * charWidth;
|
||||||
// context.beginPath();
|
const width = Math.max(
|
||||||
// context.rect(offsetX, offsetY, textWidth, lineHeight);
|
MIN_PIXEL_WIDTH,
|
||||||
// context.fillStyle = this.color(severity);
|
(range.to - range.from) * charWidth
|
||||||
// context.fill();
|
);
|
||||||
}
|
const snappedX = snapToDevice(startX);
|
||||||
|
const snappedWidth =
|
||||||
|
Math.max(MIN_PIXEL_WIDTH, snapToDevice(startX + width) - snappedX) ||
|
||||||
|
MIN_PIXEL_WIDTH;
|
||||||
|
|
||||||
/**
|
context.globalAlpha = 0.65;
|
||||||
* Given a position and a set of line ranges, return
|
context.beginPath();
|
||||||
* the line number the position falls within
|
context.rect(snappedX, snappedY, snappedWidth, snappedHeight);
|
||||||
*/
|
context.fill();
|
||||||
private findLine(pos: number, lines: Lines) {
|
}
|
||||||
const index = lines.findIndex((spans) => {
|
context.globalAlpha = 1;
|
||||||
const start = spans.slice(0, 1)[0];
|
|
||||||
const end = spans.slice(-1)[0];
|
|
||||||
|
|
||||||
if (!start || !end) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return start.from <= pos && pos <= end.to;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Line numbers begin at 1
|
|
||||||
return index + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,12 +154,6 @@ export class DiagnosticState extends LineBasedState<Severity> {
|
|||||||
: "#999";
|
: "#999";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sorts severity from most to least severe */
|
|
||||||
private sort(a: Severity, b: Severity) {
|
|
||||||
return this.score(b) - this.score(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Assigns a score to severity, with most severe being the highest */
|
|
||||||
private score(s: Severity) {
|
private score(s: Severity) {
|
||||||
switch (s) {
|
switch (s) {
|
||||||
case "error": {
|
case "error": {
|
||||||
@@ -160,6 +167,47 @@ export class DiagnosticState extends LineBasedState<Severity> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private pushRange(
|
||||||
|
lineNumber: number,
|
||||||
|
severity: Severity,
|
||||||
|
range: DiagnosticRange
|
||||||
|
) {
|
||||||
|
let entry = this.get(lineNumber);
|
||||||
|
if (!entry) {
|
||||||
|
entry = { severity, ranges: [range] };
|
||||||
|
this.set(lineNumber, entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.score(severity) > this.score(entry.severity)) {
|
||||||
|
entry.severity = severity;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.ranges.push(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
private mergeRanges() {
|
||||||
|
for (const entry of this.map.values()) {
|
||||||
|
if (entry.ranges.length <= 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.ranges.sort((a, b) => a.from - b.from);
|
||||||
|
const merged: Array<DiagnosticRange> = [];
|
||||||
|
|
||||||
|
for (const range of entry.ranges) {
|
||||||
|
const last = merged[merged.length - 1];
|
||||||
|
if (last && range.from <= last.to) {
|
||||||
|
last.to = Math.max(last.to, range.to);
|
||||||
|
} else {
|
||||||
|
merged.push({ ...range });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.ranges = merged;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function diagnostics(view: EditorView): DiagnosticState {
|
export function diagnostics(view: EditorView): DiagnosticState {
|
||||||
|
|||||||
@@ -1,27 +1,37 @@
|
|||||||
import { Facet } from "@codemirror/state";
|
/**
|
||||||
import { EditorView, ViewPlugin, ViewUpdate } from "@codemirror/view";
|
* Minimap Extension Entry
|
||||||
import { Overlay } from "./overlay";
|
* Uses block rendering for visible area only
|
||||||
import { Config, Options, Scale } from "./config";
|
*/
|
||||||
import { DiagnosticState, diagnostics } from "./diagnostics";
|
|
||||||
import { SelectionState, selections } from "./selections";
|
import { Facet } from '@codemirror/state';
|
||||||
import { TextState, text } from "./text";
|
import { EditorView, ViewPlugin, ViewUpdate } from '@codemirror/view';
|
||||||
import { LinesState } from "./linesState";
|
import { Overlay } from './overlay';
|
||||||
import crelt from "crelt";
|
import { Config, Options, Scale } from './config';
|
||||||
import { GUTTER_WIDTH, drawLineGutter } from "./gutters";
|
import { DiagnosticState, diagnostics } from './diagnostics';
|
||||||
|
import { SelectionState, selections } from './selections';
|
||||||
|
import { LinesState, foldsChanged } from './linesState';
|
||||||
|
import { BlockManager } from './blockManager';
|
||||||
|
import crelt from 'crelt';
|
||||||
|
import { GUTTER_WIDTH, drawLineGutter } from './gutters';
|
||||||
|
import { createDebounce } from '@/common/utils/debounce';
|
||||||
|
|
||||||
const Theme = EditorView.theme({
|
const Theme = EditorView.theme({
|
||||||
"&": {
|
'&': {
|
||||||
height: "100%",
|
height: '100%',
|
||||||
overflowY: "auto",
|
overflowY: 'auto',
|
||||||
},
|
},
|
||||||
"& .cm-minimap-gutter": {
|
'& .cm-minimap-gutter': {
|
||||||
borderRight: 0,
|
borderRight: 0,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
left: "unset",
|
left: 'unset',
|
||||||
position: "sticky",
|
position: 'sticky',
|
||||||
right: 0,
|
right: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
},
|
},
|
||||||
|
// 初始化时隐藏,避免宽度未设置时闪烁
|
||||||
|
'& .cm-minimap-initializing': {
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
'& .cm-minimap-autohide': {
|
'& .cm-minimap-autohide': {
|
||||||
opacity: 0.0,
|
opacity: 0.0,
|
||||||
transition: 'opacity 0.3s',
|
transition: 'opacity 0.3s',
|
||||||
@@ -29,62 +39,130 @@ const Theme = EditorView.theme({
|
|||||||
'& .cm-minimap-autohide:hover': {
|
'& .cm-minimap-autohide:hover': {
|
||||||
opacity: 1.0,
|
opacity: 1.0,
|
||||||
},
|
},
|
||||||
"& .cm-minimap-inner": {
|
'& .cm-minimap-inner': {
|
||||||
height: "100%",
|
height: '100%',
|
||||||
position: "absolute",
|
position: 'absolute',
|
||||||
right: 0,
|
right: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
overflowY: "hidden",
|
overflowY: 'hidden',
|
||||||
"& canvas": {
|
'& canvas': {
|
||||||
display: "block",
|
display: 'block',
|
||||||
|
willChange: 'transform, opacity',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"& .cm-minimap-box-shadow": {
|
'& .cm-minimap-box-shadow': {
|
||||||
boxShadow: "12px 0px 20px 5px #6c6c6c",
|
boxShadow: '12px 0px 20px 5px #6c6c6c',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const WIDTH_RATIO = 6;
|
const WIDTH_RATIO = 6;
|
||||||
|
type RenderReason = 'scroll' | 'data';
|
||||||
|
|
||||||
|
// 渲染类型:blocks=块内容变化需要重渲染, overlays=只需重绘选区等覆盖层
|
||||||
|
type RenderType = 'blocks' | 'overlays';
|
||||||
|
|
||||||
const minimapClass = ViewPlugin.fromClass(
|
const minimapClass = ViewPlugin.fromClass(
|
||||||
class {
|
class {
|
||||||
private dom: HTMLElement | undefined;
|
private dom: HTMLElement | undefined;
|
||||||
private inner: HTMLElement | undefined;
|
private inner: HTMLElement | undefined;
|
||||||
private canvas: HTMLCanvasElement | undefined;
|
private canvas: HTMLCanvasElement | undefined;
|
||||||
|
private renderHandle: number | ReturnType<typeof setTimeout> | null = null;
|
||||||
|
private cancelRender: (() => void) | null = null;
|
||||||
|
private pendingScrollTop: number | null = null;
|
||||||
|
private lastRenderedScrollTop: number = -1;
|
||||||
|
private pendingRenderReason: RenderReason | null = null;
|
||||||
|
private pendingRenderType: RenderType | null = null;
|
||||||
|
|
||||||
public text: TextState;
|
// 块管理器(Worker 渲染)
|
||||||
|
private blockManager: BlockManager;
|
||||||
public selection: SelectionState;
|
public selection: SelectionState;
|
||||||
public diagnostic: DiagnosticState;
|
public diagnostic: DiagnosticState;
|
||||||
|
|
||||||
|
// 等待滚动位置稳定
|
||||||
|
private initialRenderDelay: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
private isInitialized = false;
|
||||||
|
private hasRenderedOnce = false; // 是否已首次渲染
|
||||||
|
private lastScrollTop = -1;
|
||||||
|
private scrollStableCount = 0;
|
||||||
|
|
||||||
|
// 块渲染防抖(500ms)
|
||||||
|
private debouncedBlockRender: ReturnType<typeof createDebounce>['debouncedFn'];
|
||||||
|
private cancelDebounce: () => void;
|
||||||
|
|
||||||
public constructor(private view: EditorView) {
|
public constructor(private view: EditorView) {
|
||||||
this.text = text(view);
|
this.blockManager = new BlockManager(view);
|
||||||
this.selection = selections(view);
|
this.selection = selections(view);
|
||||||
this.diagnostic = diagnostics(view);
|
this.diagnostic = diagnostics(view);
|
||||||
|
|
||||||
|
// 创建防抖的块渲染函数
|
||||||
|
const { debouncedFn, cancel } = createDebounce(() => {
|
||||||
|
this.requestRender('data', 'blocks');
|
||||||
|
}, { delay: 1000 });
|
||||||
|
this.debouncedBlockRender = debouncedFn;
|
||||||
|
this.cancelDebounce = cancel;
|
||||||
|
|
||||||
|
// 当块渲染完成时,请求重新渲染(只渲染 overlays)
|
||||||
|
this.blockManager.setOnBlockReady(() => {
|
||||||
|
if (this.isInitialized) {
|
||||||
|
this.requestRender('data', 'overlays');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (view.state.facet(showMinimapFacet)) {
|
if (view.state.facet(showMinimapFacet)) {
|
||||||
this.create(view);
|
this.create(view);
|
||||||
|
this.waitForScrollStable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 等待滚动位置稳定后再渲染
|
||||||
|
private waitForScrollStable(): void {
|
||||||
|
const check = () => {
|
||||||
|
const scrollTop = this.view.scrollDOM.scrollTop;
|
||||||
|
|
||||||
|
if (scrollTop === this.lastScrollTop) {
|
||||||
|
this.scrollStableCount++;
|
||||||
|
// 连续 3 次检测位置不变,认为稳定
|
||||||
|
if (this.scrollStableCount >= 3) {
|
||||||
|
this.isInitialized = true;
|
||||||
|
this.initialRenderDelay = null;
|
||||||
|
this.requestRender('data', 'blocks');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.scrollStableCount = 0;
|
||||||
|
this.lastScrollTop = scrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每 20ms 检测一次,最多等待 200ms
|
||||||
|
if (this.scrollStableCount < 10) {
|
||||||
|
this.initialRenderDelay = setTimeout(check, 20);
|
||||||
|
} else {
|
||||||
|
this.isInitialized = true;
|
||||||
|
this.initialRenderDelay = null;
|
||||||
|
this.requestRender('data', 'blocks');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.initialRenderDelay = setTimeout(check, 20);
|
||||||
|
}
|
||||||
|
|
||||||
private create(view: EditorView) {
|
private create(view: EditorView) {
|
||||||
const config = view.state.facet(showMinimapFacet);
|
const config = view.state.facet(showMinimapFacet);
|
||||||
if (!config) {
|
if (!config) {
|
||||||
throw Error("Expected nonnull");
|
throw Error('Expected nonnull');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inner = crelt("div", { class: "cm-minimap-inner" });
|
this.inner = crelt('div', { class: 'cm-minimap-inner' });
|
||||||
this.canvas = crelt("canvas") as HTMLCanvasElement;
|
this.canvas = crelt('canvas') as HTMLCanvasElement;
|
||||||
|
|
||||||
this.dom = config.create(view).dom;
|
this.dom = config.create(view).dom;
|
||||||
this.dom.classList.add("cm-gutters");
|
this.dom.classList.add('cm-gutters');
|
||||||
this.dom.classList.add("cm-minimap-gutter");
|
this.dom.classList.add('cm-minimap-gutter');
|
||||||
|
this.dom.classList.add('cm-minimap-initializing'); // 初始隐藏
|
||||||
|
|
||||||
this.inner.appendChild(this.canvas);
|
this.inner.appendChild(this.canvas);
|
||||||
this.dom.appendChild(this.inner);
|
this.dom.appendChild(this.inner);
|
||||||
|
|
||||||
// For now let's keep this same behavior. We might want to change
|
|
||||||
// this in the future and have the extension figure out how to mount.
|
|
||||||
// Or expose some more generic right gutter api and use that
|
|
||||||
this.view.scrollDOM.insertBefore(
|
this.view.scrollDOM.insertBefore(
|
||||||
this.dom,
|
this.dom,
|
||||||
this.view.contentDOM.nextSibling
|
this.view.contentDOM.nextSibling
|
||||||
@@ -97,35 +175,23 @@ const minimapClass = ViewPlugin.fromClass(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 阻止小地图上的右键菜单
|
|
||||||
this.dom.addEventListener('contextmenu', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 阻止小地图内部元素和画布上的右键菜单
|
|
||||||
this.inner.addEventListener('contextmenu', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.canvas.addEventListener('contextmenu', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (config.autohide) {
|
if (config.autohide) {
|
||||||
this.dom.classList.add('cm-minimap-autohide');
|
this.dom.classList.add('cm-minimap-autohide');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置显示模式
|
||||||
|
this.blockManager.setDisplayText(view.state.facet(Config).displayText);
|
||||||
}
|
}
|
||||||
|
|
||||||
private remove() {
|
private remove() {
|
||||||
|
this.cancelRenderRequest();
|
||||||
if (this.dom) {
|
if (this.dom) {
|
||||||
this.dom.remove();
|
this.dom.remove();
|
||||||
}
|
}
|
||||||
|
this.dom = undefined;
|
||||||
|
this.inner = undefined;
|
||||||
|
this.canvas = undefined;
|
||||||
|
this.hasRenderedOnce = false; // 重置首次渲染标记
|
||||||
}
|
}
|
||||||
|
|
||||||
update(update: ViewUpdate) {
|
update(update: ViewUpdate) {
|
||||||
@@ -142,6 +208,9 @@ const minimapClass = ViewPlugin.fromClass(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (now) {
|
if (now) {
|
||||||
|
let needBlockRender = false;
|
||||||
|
let needOverlayRender = false;
|
||||||
|
|
||||||
if (prev && this.dom && prev.autohide !== now.autohide) {
|
if (prev && this.dom && prev.autohide !== now.autohide) {
|
||||||
if (now.autohide) {
|
if (now.autohide) {
|
||||||
this.dom.classList.add('cm-minimap-autohide');
|
this.dom.classList.add('cm-minimap-autohide');
|
||||||
@@ -150,10 +219,45 @@ const minimapClass = ViewPlugin.fromClass(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.text.update(update);
|
// Check theme change
|
||||||
|
if (this.blockManager.checkThemeChange()) {
|
||||||
|
needBlockRender = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check config change
|
||||||
|
const prevConfig = update.startState.facet(Config);
|
||||||
|
const nowConfig = update.state.facet(Config);
|
||||||
|
if (prevConfig.displayText !== nowConfig.displayText) {
|
||||||
|
this.blockManager.setDisplayText(nowConfig.displayText);
|
||||||
|
needBlockRender = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check doc change
|
||||||
|
if (update.docChanged) {
|
||||||
|
const oldLineCount = update.startState.doc.lines;
|
||||||
|
this.blockManager.handleDocChange(update.state, update.changes, oldLineCount);
|
||||||
|
needBlockRender = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check fold change
|
||||||
|
if (foldsChanged(update.transactions)) {
|
||||||
|
this.blockManager.markAllDirty();
|
||||||
|
needBlockRender = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update selection and diagnostics
|
||||||
this.selection.update(update);
|
this.selection.update(update);
|
||||||
this.diagnostic.update(update);
|
this.diagnostic.update(update);
|
||||||
this.render();
|
if (update.selectionSet) {
|
||||||
|
needOverlayRender = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据变化类型决定渲染方式
|
||||||
|
if (needBlockRender) {
|
||||||
|
this.debouncedBlockRender();
|
||||||
|
} else if (needOverlayRender) {
|
||||||
|
this.requestRender('data', 'overlays');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,85 +271,182 @@ const minimapClass = ViewPlugin.fromClass(
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// If we don't have elements to draw to exit early
|
|
||||||
if (!this.dom || !this.canvas || !this.inner) {
|
if (!this.dom || !this.canvas || !this.inner) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.text.beforeDraw();
|
const effectiveScrollTop = this.pendingScrollTop ?? this.view.scrollDOM.scrollTop;
|
||||||
|
const renderType = this.pendingRenderType ?? 'blocks';
|
||||||
|
this.pendingScrollTop = null;
|
||||||
|
this.pendingRenderReason = null;
|
||||||
|
this.pendingRenderType = null;
|
||||||
|
this.lastRenderedScrollTop = effectiveScrollTop;
|
||||||
|
|
||||||
this.updateBoxShadow();
|
this.updateBoxShadow();
|
||||||
|
|
||||||
this.dom.style.width = this.getWidth() + "px";
|
// Set canvas size
|
||||||
this.canvas.style.maxWidth = this.getWidth() + "px";
|
const width = this.getWidth();
|
||||||
this.canvas.width = this.getWidth() * Scale.PixelMultiplier;
|
this.dom.style.width = width + 'px';
|
||||||
|
this.canvas.style.maxWidth = width + 'px';
|
||||||
|
this.canvas.width = width * Scale.PixelMultiplier;
|
||||||
|
|
||||||
const domHeight = this.view.dom.getBoundingClientRect().height;
|
const domHeight = this.view.dom.getBoundingClientRect().height;
|
||||||
this.inner.style.minHeight = domHeight + "px";
|
this.inner.style.minHeight = domHeight + 'px';
|
||||||
this.canvas.height = domHeight * Scale.PixelMultiplier;
|
this.canvas.height = domHeight * Scale.PixelMultiplier;
|
||||||
this.canvas.style.height = domHeight + "px";
|
this.canvas.style.height = domHeight + 'px';
|
||||||
|
|
||||||
const context = this.canvas.getContext("2d");
|
const context = this.canvas.getContext('2d');
|
||||||
if (!context) {
|
if (!context) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
// Get scroll info
|
||||||
|
const scrollInfo = {
|
||||||
|
scrollTop: effectiveScrollTop,
|
||||||
|
clientHeight: this.view.scrollDOM.clientHeight,
|
||||||
|
scrollHeight: this.view.scrollDOM.scrollHeight,
|
||||||
|
};
|
||||||
|
|
||||||
/* We need to get the correct font dimensions before this to measure characters */
|
// 渲染块
|
||||||
const { charWidth, lineHeight } = this.text.measure(context);
|
if (renderType === 'blocks') {
|
||||||
|
this.blockManager.render(this.canvas, context, scrollInfo);
|
||||||
|
} else {
|
||||||
|
this.blockManager.drawCachedBlocks(this.canvas, context, scrollInfo);
|
||||||
|
}
|
||||||
|
|
||||||
let { startIndex, endIndex, offsetY } = this.canvasStartAndEndIndex(
|
// Render overlays (gutters, selections, diagnostics)
|
||||||
|
const gutters = this.view.state.facet(Config).gutters;
|
||||||
|
this.renderOverlays(context, effectiveScrollTop, gutters);
|
||||||
|
|
||||||
|
// 首次渲染完成后显示 minimap
|
||||||
|
if (!this.hasRenderedOnce && this.dom) {
|
||||||
|
this.hasRenderedOnce = true;
|
||||||
|
this.dom.classList.remove('cm-minimap-initializing');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染覆盖层(gutters、选区、诊断)
|
||||||
|
*/
|
||||||
|
private renderOverlays(
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
scrollTop: number,
|
||||||
|
gutters: Required<Options>['gutters']
|
||||||
|
) {
|
||||||
|
const { charWidth, lineHeight } = this.blockManager.measure(context);
|
||||||
|
const { startIndex, endIndex, offsetY: initialOffsetY } = this.canvasStartAndEndIndex(
|
||||||
context,
|
context,
|
||||||
lineHeight
|
lineHeight,
|
||||||
|
scrollTop
|
||||||
);
|
);
|
||||||
|
|
||||||
const gutters = this.view.state.facet(Config).gutters;
|
const lines = this.view.state.field(LinesState);
|
||||||
|
let offsetY = initialOffsetY;
|
||||||
|
|
||||||
for (let i = startIndex; i < endIndex; i++) {
|
for (let i = startIndex; i < endIndex; i++) {
|
||||||
const lines = this.view.state.field(LinesState);
|
|
||||||
if (i >= lines.length) break;
|
if (i >= lines.length) break;
|
||||||
|
|
||||||
const drawContext = {
|
let offsetX = 0;
|
||||||
offsetX: 0,
|
const lineNumber = i + 1;
|
||||||
offsetY,
|
|
||||||
context,
|
|
||||||
lineHeight,
|
|
||||||
charWidth,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// 渲染 gutters
|
||||||
if (gutters.length) {
|
if (gutters.length) {
|
||||||
/* Small leading buffer */
|
offsetX += 2;
|
||||||
drawContext.offsetX += 2;
|
|
||||||
|
|
||||||
for (const gutter of gutters) {
|
for (const gutter of gutters) {
|
||||||
drawLineGutter(gutter, drawContext, i + 1);
|
drawLineGutter(gutter, { offsetX, offsetY, context, lineHeight, charWidth }, lineNumber);
|
||||||
drawContext.offsetX += GUTTER_WIDTH;
|
offsetX += GUTTER_WIDTH;
|
||||||
}
|
}
|
||||||
|
offsetX += 2;
|
||||||
/* Small trailing buffer */
|
|
||||||
drawContext.offsetX += 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.text.drawLine(drawContext, i + 1);
|
// 渲染选区
|
||||||
this.selection.drawLine(drawContext, i + 1);
|
this.selection.drawLine({ offsetX, offsetY, context, lineHeight, charWidth }, lineNumber);
|
||||||
this.diagnostic.drawLine(drawContext, i + 1);
|
|
||||||
|
// 渲染诊断
|
||||||
|
if (this.diagnostic.has(lineNumber)) {
|
||||||
|
this.diagnostic.drawLine({ offsetX, offsetY, context, lineHeight, charWidth }, lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
offsetY += lineHeight;
|
offsetY += lineHeight;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.restore();
|
requestRender(reason: RenderReason = 'data', type: RenderType = 'blocks') {
|
||||||
|
if (!this.isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reason === 'scroll') {
|
||||||
|
const scrollTop = this.view.scrollDOM.scrollTop;
|
||||||
|
if (this.lastRenderedScrollTop === scrollTop && !this.pendingRenderReason) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.pendingRenderReason === 'scroll' &&
|
||||||
|
this.pendingScrollTop === scrollTop
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.pendingScrollTop = scrollTop;
|
||||||
|
} else {
|
||||||
|
this.pendingScrollTop = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reason === 'data' || this.pendingRenderReason === null) {
|
||||||
|
this.pendingRenderReason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并渲染类型:blocks > overlays
|
||||||
|
if (this.pendingRenderType === null || type === 'blocks') {
|
||||||
|
this.pendingRenderType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.renderHandle !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof requestAnimationFrame === 'function') {
|
||||||
|
const handle = requestAnimationFrame(() => {
|
||||||
|
this.renderHandle = null;
|
||||||
|
this.cancelRender = null;
|
||||||
|
this.render();
|
||||||
|
});
|
||||||
|
this.renderHandle = handle;
|
||||||
|
this.cancelRender = () => cancelAnimationFrame(handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handle = setTimeout(() => {
|
||||||
|
this.renderHandle = null;
|
||||||
|
this.cancelRender = null;
|
||||||
|
this.render();
|
||||||
|
}, 16);
|
||||||
|
this.renderHandle = handle;
|
||||||
|
this.cancelRender = () => clearTimeout(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelRenderRequest() {
|
||||||
|
if (!this.cancelRender) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.cancelRender();
|
||||||
|
this.renderHandle = null;
|
||||||
|
this.cancelRender = null;
|
||||||
|
this.pendingScrollTop = null;
|
||||||
|
this.pendingRenderReason = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private canvasStartAndEndIndex(
|
private canvasStartAndEndIndex(
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
lineHeight: number
|
lineHeight: number,
|
||||||
|
scrollTopOverride?: number
|
||||||
) {
|
) {
|
||||||
let { top: pTop, bottom: pBottom } = this.view.documentPadding;
|
let { top: pTop, bottom: pBottom } = this.view.documentPadding;
|
||||||
(pTop /= Scale.SizeRatio), (pBottom /= Scale.SizeRatio);
|
(pTop /= Scale.SizeRatio), (pBottom /= Scale.SizeRatio);
|
||||||
|
|
||||||
const canvasHeight = context.canvas.height;
|
const canvasHeight = context.canvas.height;
|
||||||
const { clientHeight, scrollHeight, scrollTop } = this.view.scrollDOM;
|
const { clientHeight, scrollHeight } = this.view.scrollDOM;
|
||||||
|
const scrollTop = scrollTopOverride ?? this.view.scrollDOM.scrollTop;
|
||||||
let scrollPercent = scrollTop / (scrollHeight - clientHeight);
|
let scrollPercent = scrollTop / (scrollHeight - clientHeight);
|
||||||
if (isNaN(scrollPercent)) {
|
if (isNaN(scrollPercent)) {
|
||||||
scrollPercent = 0;
|
scrollPercent = 0;
|
||||||
@@ -278,20 +479,26 @@ const minimapClass = ViewPlugin.fromClass(
|
|||||||
const { clientWidth, scrollWidth, scrollLeft } = this.view.scrollDOM;
|
const { clientWidth, scrollWidth, scrollLeft } = this.view.scrollDOM;
|
||||||
|
|
||||||
if (clientWidth + scrollLeft < scrollWidth) {
|
if (clientWidth + scrollLeft < scrollWidth) {
|
||||||
this.canvas.classList.add("cm-minimap-box-shadow");
|
this.canvas.classList.add('cm-minimap-box-shadow');
|
||||||
} else {
|
} else {
|
||||||
this.canvas.classList.remove("cm-minimap-box-shadow");
|
this.canvas.classList.remove('cm-minimap-box-shadow');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
if (this.initialRenderDelay) {
|
||||||
|
clearTimeout(this.initialRenderDelay);
|
||||||
|
this.initialRenderDelay = null;
|
||||||
|
}
|
||||||
|
this.cancelDebounce();
|
||||||
|
this.blockManager.destroy();
|
||||||
this.remove();
|
this.remove();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
eventHandlers: {
|
eventHandlers: {
|
||||||
scroll() {
|
scroll() {
|
||||||
requestAnimationFrame(() => this.render());
|
this.requestRender('scroll', 'blocks');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
provide: (plugin) => {
|
provide: (plugin) => {
|
||||||
@@ -307,8 +514,7 @@ const minimapClass = ViewPlugin.fromClass(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 使用type定义
|
export type MinimapConfig = Omit<Options, 'enabled'> & {
|
||||||
export type MinimapConfig = Omit<Options, "enabled"> & {
|
|
||||||
/**
|
/**
|
||||||
* A function that creates the element that contains the minimap
|
* A function that creates the element that contains the minimap
|
||||||
*/
|
*/
|
||||||
@@ -318,43 +524,35 @@ export type MinimapConfig = Omit<Options, "enabled"> & {
|
|||||||
/**
|
/**
|
||||||
* Facet used to show a minimap in the right gutter of the editor using the
|
* Facet used to show a minimap in the right gutter of the editor using the
|
||||||
* provided configuration.
|
* provided configuration.
|
||||||
*
|
|
||||||
* If you return `null`, a minimap will not be shown.
|
|
||||||
*/
|
*/
|
||||||
const showMinimapFacet = Facet.define<MinimapConfig | null, MinimapConfig | null>({
|
const showMinimapFacet = Facet.define<MinimapConfig | null, MinimapConfig | null>({
|
||||||
combine: (c) => c.find((o) => o !== null) ?? null,
|
combine: (c) => c.find((o) => o !== null) ?? null,
|
||||||
enables: (f) => {
|
|
||||||
return [
|
|
||||||
[
|
|
||||||
Config.compute([f], (s) => s.facet(f)),
|
|
||||||
Theme,
|
|
||||||
LinesState,
|
|
||||||
minimapClass, // TODO, codemirror-ify this one better
|
|
||||||
Overlay,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建默认的minimap DOM元素
|
* 创建默认的 minimap DOM 元素
|
||||||
*/
|
*/
|
||||||
const defaultCreateFn = (view: EditorView) => {
|
const defaultCreateFn = (_view: EditorView) => {
|
||||||
const dom = document.createElement('div');
|
const dom = document.createElement('div');
|
||||||
return { dom };
|
return { dom };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加minimap到编辑器
|
* 添加 minimap 到编辑器
|
||||||
* @param options Minimap配置项
|
* @param options Minimap 配置项
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
export function minimap(options: Partial<Omit<MinimapConfig, 'create'>> = {}) {
|
export function minimap(options: Partial<Omit<MinimapConfig, 'create'>> = {}) {
|
||||||
return showMinimapFacet.of({
|
const config: MinimapConfig = {
|
||||||
create: defaultCreateFn,
|
create: defaultCreateFn,
|
||||||
...options
|
...options,
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// 保持原始接口兼容性
|
return [
|
||||||
export { showMinimapFacet as showMinimap };
|
showMinimapFacet.of(config),
|
||||||
|
Config.compute([showMinimapFacet], (s) => s.facet(showMinimapFacet)),
|
||||||
|
Theme,
|
||||||
|
LinesState,
|
||||||
|
minimapClass,
|
||||||
|
Overlay,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|||||||
85
frontend/src/views/editor/extensions/minimap/lineGeometry.ts
Normal file
85
frontend/src/views/editor/extensions/minimap/lineGeometry.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { Lines } from "./linesState";
|
||||||
|
|
||||||
|
const DEFAULT_LINE_NUMBER = 0;
|
||||||
|
|
||||||
|
function lineBoundary(spans: Lines[number]) {
|
||||||
|
if (!spans || spans.length === 0) {
|
||||||
|
return { start: 0, end: 0 };
|
||||||
|
}
|
||||||
|
const start = spans[0].from;
|
||||||
|
const end = spans[spans.length - 1].to;
|
||||||
|
return { start, end };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function lineNumberAt(lines: Lines, position: number): number {
|
||||||
|
if (!lines.length) {
|
||||||
|
return DEFAULT_LINE_NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
const first = lineBoundary(lines[0]);
|
||||||
|
const last = lineBoundary(lines[lines.length - 1]);
|
||||||
|
|
||||||
|
let target = position;
|
||||||
|
if (target < first.start) {
|
||||||
|
target = first.start;
|
||||||
|
} else if (target > last.end) {
|
||||||
|
target = last.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
let low = 0;
|
||||||
|
let high = lines.length - 1;
|
||||||
|
|
||||||
|
while (low <= high) {
|
||||||
|
const mid = (low + high) >> 1;
|
||||||
|
const spans = lines[mid];
|
||||||
|
const { start, end } = lineBoundary(spans);
|
||||||
|
|
||||||
|
if (target < start) {
|
||||||
|
high = mid - 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target > end) {
|
||||||
|
low = mid + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mid + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(1, Math.min(lines.length, low + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function lineLength(spans: Lines[number]) {
|
||||||
|
if (!spans) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let length = 0;
|
||||||
|
for (const span of spans) {
|
||||||
|
length += span.folded ? 1 : Math.max(0, span.to - span.from);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(1, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function offsetWithinLine(pos: number, spans: Lines[number]) {
|
||||||
|
if (!spans) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
for (const span of spans) {
|
||||||
|
const spanLength = span.folded ? 1 : Math.max(0, span.to - span.from);
|
||||||
|
if (!span.folded && pos < span.to) {
|
||||||
|
return offset + Math.max(0, pos - span.from);
|
||||||
|
}
|
||||||
|
if (span.folded && pos <= span.to) {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
offset += spanLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
@@ -14,6 +14,10 @@ export abstract class LineBasedState<TValue> {
|
|||||||
return this.map.get(lineNumber);
|
return this.map.get(lineNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public has(lineNumber: number): boolean {
|
||||||
|
return this.map.has(lineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
protected set(lineNumber: number, value: TValue) {
|
protected set(lineNumber: number, value: TValue) {
|
||||||
this.map.set(lineNumber, value);
|
this.map.set(lineNumber, value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ function computeLinesState(state: EditorState): Lines {
|
|||||||
const LinesState = StateField.define<Lines>({
|
const LinesState = StateField.define<Lines>({
|
||||||
create: (state) => computeLinesState(state),
|
create: (state) => computeLinesState(state),
|
||||||
update: (current, tr) => {
|
update: (current, tr) => {
|
||||||
|
const prevEnabled = tr.startState.facet(Config).enabled;
|
||||||
|
const nextEnabled = tr.state.facet(Config).enabled;
|
||||||
|
if (prevEnabled !== nextEnabled) {
|
||||||
|
return computeLinesState(tr.state);
|
||||||
|
}
|
||||||
|
|
||||||
if (foldsChanged([tr]) || tr.docChanged) {
|
if (foldsChanged([tr]) || tr.docChanged) {
|
||||||
return computeLinesState(tr.state);
|
return computeLinesState(tr.state);
|
||||||
}
|
}
|
||||||
@@ -93,3 +99,11 @@ function foldsChanged(transactions: readonly Transaction[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export { foldsChanged, LinesState };
|
export { foldsChanged, LinesState };
|
||||||
|
|
||||||
|
export function getLinesSnapshot(state: EditorState): Lines {
|
||||||
|
const lines = state.field(LinesState);
|
||||||
|
if (lines.length || !state.facet(Config).enabled) {
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
return computeLinesState(state);
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ const OverlayView = ViewPlugin.fromClass(
|
|||||||
|
|
||||||
private _isDragging: boolean = false;
|
private _isDragging: boolean = false;
|
||||||
private _dragStartY: number | undefined;
|
private _dragStartY: number | undefined;
|
||||||
|
private abortController: AbortController | undefined;
|
||||||
|
private readonly _boundMouseDown = (event: MouseEvent) => this.onMouseDown(event);
|
||||||
|
private readonly _boundMouseUp = (event: MouseEvent) => this.onMouseUp(event);
|
||||||
|
private readonly _boundMouseMove = (event: MouseEvent) => this.onMouseMove(event);
|
||||||
|
|
||||||
public constructor(private view: EditorView) {
|
public constructor(private view: EditorView) {
|
||||||
if (view.state.facet(Config).enabled) {
|
if (view.state.facet(Config).enabled) {
|
||||||
@@ -59,14 +63,19 @@ const OverlayView = ViewPlugin.fromClass(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private create(view: EditorView) {
|
private create(view: EditorView) {
|
||||||
|
this.remove();
|
||||||
|
|
||||||
|
this.abortController = new AbortController();
|
||||||
|
const signal = this.abortController.signal;
|
||||||
|
|
||||||
this.container = crelt("div", { class: "cm-minimap-overlay-container" });
|
this.container = crelt("div", { class: "cm-minimap-overlay-container" });
|
||||||
this.dom = crelt("div", { class: "cm-minimap-overlay" });
|
this.dom = crelt("div", { class: "cm-minimap-overlay" });
|
||||||
this.container.appendChild(this.dom);
|
this.container.appendChild(this.dom);
|
||||||
|
|
||||||
// Attach event listeners for overlay
|
// Attach event listeners for overlay
|
||||||
this.container.addEventListener("mousedown", this.onMouseDown.bind(this));
|
this.container.addEventListener("mousedown", this._boundMouseDown, { signal });
|
||||||
window.addEventListener("mouseup", this.onMouseUp.bind(this));
|
window.addEventListener("mouseup", this._boundMouseUp, { signal });
|
||||||
window.addEventListener("mousemove", this.onMouseMove.bind(this));
|
window.addEventListener("mousemove", this._boundMouseMove, { signal });
|
||||||
|
|
||||||
// Attach the overlay elements to the minimap
|
// Attach the overlay elements to the minimap
|
||||||
const inner = view.dom.querySelector(".cm-minimap-inner");
|
const inner = view.dom.querySelector(".cm-minimap-inner");
|
||||||
@@ -81,11 +90,15 @@ const OverlayView = ViewPlugin.fromClass(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private remove() {
|
private remove() {
|
||||||
|
if (this.abortController) {
|
||||||
|
this.abortController.abort();
|
||||||
|
this.abortController = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.container) {
|
if (this.container) {
|
||||||
this.container.removeEventListener("mousedown", this.onMouseDown);
|
|
||||||
window.removeEventListener("mouseup", this.onMouseUp);
|
|
||||||
window.removeEventListener("mousemove", this.onMouseMove);
|
|
||||||
this.container.remove();
|
this.container.remove();
|
||||||
|
this.container = undefined;
|
||||||
|
this.dom = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,27 @@
|
|||||||
import { LineBasedState } from "./linebasedstate";
|
import { LineBasedState } from "./linebasedstate";
|
||||||
import { EditorView, ViewUpdate } from "@codemirror/view";
|
import { EditorView, ViewUpdate } from "@codemirror/view";
|
||||||
import { LinesState, foldsChanged } from "./linesState";
|
import { EditorState } from "@codemirror/state";
|
||||||
|
import { Lines, foldsChanged, getLinesSnapshot } from "./linesState";
|
||||||
import { DrawContext } from "./types";
|
import { DrawContext } from "./types";
|
||||||
import { Config } from "./config";
|
import { Config } from "./config";
|
||||||
|
import { lineLength, lineNumberAt, offsetWithinLine } from "./lineGeometry";
|
||||||
|
|
||||||
type Selection = { from: number; to: number; extends: boolean };
|
type Selection = { from: number; to: number; extends: boolean };
|
||||||
type DrawInfo = { backgroundColor: string };
|
type DrawInfo = { backgroundColor: string };
|
||||||
|
type RangeInfo = {
|
||||||
|
from: number;
|
||||||
|
to: number;
|
||||||
|
lineFrom: number;
|
||||||
|
lineTo: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_CACHED_LINES = 800;
|
||||||
|
|
||||||
export class SelectionState extends LineBasedState<Array<Selection>> {
|
export class SelectionState extends LineBasedState<Array<Selection>> {
|
||||||
private _drawInfo: DrawInfo | undefined;
|
private _drawInfo: DrawInfo | undefined;
|
||||||
private _themeClasses: string;
|
private _themeClasses: string;
|
||||||
|
private _rangeInfo: Array<RangeInfo> = [];
|
||||||
|
private _linesSnapshot: Lines = [];
|
||||||
|
|
||||||
public constructor(view: EditorView) {
|
public constructor(view: EditorView) {
|
||||||
super(view);
|
super(view);
|
||||||
@@ -52,95 +64,7 @@ export class SelectionState extends LineBasedState<Array<Selection>> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.map.clear();
|
this.rebuild(update.state);
|
||||||
|
|
||||||
/* If class list has changed, clear and recalculate the selection style */
|
|
||||||
if (this._themeClasses !== this.view.dom.classList.value) {
|
|
||||||
this._drawInfo = undefined;
|
|
||||||
this._themeClasses = this.view.dom.classList.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { ranges } = update.state.selection;
|
|
||||||
|
|
||||||
let selectionIndex = 0;
|
|
||||||
for (const [index, line] of update.state.field(LinesState).entries()) {
|
|
||||||
const selections: Array<Selection> = [];
|
|
||||||
|
|
||||||
let offset = 0;
|
|
||||||
for (const span of line) {
|
|
||||||
do {
|
|
||||||
// We've already processed all selections
|
|
||||||
if (selectionIndex >= ranges.length) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The next selection begins after this span
|
|
||||||
if (span.to < ranges[selectionIndex].from) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore 0-length selections
|
|
||||||
if (ranges[selectionIndex].from === ranges[selectionIndex].to) {
|
|
||||||
selectionIndex++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the selection for the current span
|
|
||||||
const range = ranges[selectionIndex];
|
|
||||||
const selection = {
|
|
||||||
from: offset + Math.max(span.from, range.from) - span.from,
|
|
||||||
to: offset + Math.min(span.to, range.to) - span.from,
|
|
||||||
extends: range.to > span.to,
|
|
||||||
};
|
|
||||||
|
|
||||||
const lastSelection = selections.slice(-1)[0];
|
|
||||||
if (lastSelection && lastSelection.to === selection.from) {
|
|
||||||
// The selection in this span may just be a continuation of the
|
|
||||||
// selection in the previous span
|
|
||||||
|
|
||||||
// Adjust `to` depending on if we're in a folded span
|
|
||||||
let { to } = selection;
|
|
||||||
if (span.folded && selection.extends) {
|
|
||||||
to = selection.from + 1;
|
|
||||||
} else if (span.folded && !selection.extends) {
|
|
||||||
to = lastSelection.to;
|
|
||||||
}
|
|
||||||
|
|
||||||
selections[selections.length - 1] = {
|
|
||||||
...lastSelection,
|
|
||||||
to,
|
|
||||||
extends: selection.extends,
|
|
||||||
};
|
|
||||||
} else if (!span.folded) {
|
|
||||||
// It's a new selection; if we're not in a folded span we
|
|
||||||
// should push it onto the stack
|
|
||||||
selections.push(selection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the selection doesn't end in this span, break out of the loop
|
|
||||||
if (selection.extends) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, move to the next selection
|
|
||||||
selectionIndex++;
|
|
||||||
} while (
|
|
||||||
selectionIndex < ranges.length &&
|
|
||||||
span.to >= ranges[selectionIndex].from
|
|
||||||
);
|
|
||||||
|
|
||||||
offset += span.folded ? 1 : span.to - span.from;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't have any selections on this line, we don't need to store anything
|
|
||||||
if (selections.length === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lines are indexed beginning at 1 instead of 0
|
|
||||||
const lineNumber = index + 1;
|
|
||||||
this.map.set(lineNumber, selections);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawLine(ctx: DrawContext, lineNumber: number) {
|
public drawLine(ctx: DrawContext, lineNumber: number) {
|
||||||
@@ -151,7 +75,7 @@ export class SelectionState extends LineBasedState<Array<Selection>> {
|
|||||||
offsetX: startOffsetX,
|
offsetX: startOffsetX,
|
||||||
offsetY,
|
offsetY,
|
||||||
} = ctx;
|
} = ctx;
|
||||||
const selections = this.get(lineNumber);
|
const selections = this.ensureSelections(lineNumber);
|
||||||
if (!selections) {
|
if (!selections) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -199,6 +123,123 @@ export class SelectionState extends LineBasedState<Array<Selection>> {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private appendSelection(target: Array<Selection>, entry: Selection) {
|
||||||
|
const last = target[target.length - 1];
|
||||||
|
if (last && entry.from <= last.to) {
|
||||||
|
target[target.length - 1] = {
|
||||||
|
from: Math.min(last.from, entry.from),
|
||||||
|
to: Math.max(last.to, entry.to),
|
||||||
|
extends: entry.extends || last.extends,
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target.push(entry);
|
||||||
|
}
|
||||||
|
private rebuild(state: EditorState) {
|
||||||
|
if (this._themeClasses !== this.view.dom.classList.value) {
|
||||||
|
this._drawInfo = undefined;
|
||||||
|
this._themeClasses = this.view.dom.classList.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._linesSnapshot = getLinesSnapshot(state);
|
||||||
|
this._rangeInfo = this.buildRangeInfo(state, this._linesSnapshot);
|
||||||
|
this.map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildRangeInfo(state: EditorState, lines: Lines) {
|
||||||
|
const info: Array<RangeInfo> = [];
|
||||||
|
for (const range of state.selection.ranges) {
|
||||||
|
if (range.empty) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startLine = lineNumberAt(lines, range.from);
|
||||||
|
const endLine = lineNumberAt(lines, Math.max(range.from, range.to - 1));
|
||||||
|
if (startLine <= 0 || endLine <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.push({
|
||||||
|
from: range.from,
|
||||||
|
to: range.to,
|
||||||
|
lineFrom: startLine,
|
||||||
|
lineTo: endLine,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ensureSelections(lineNumber: number) {
|
||||||
|
const cached = this.get(lineNumber);
|
||||||
|
if (cached) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
const computed = this.buildSelectionsForLine(lineNumber);
|
||||||
|
if (!computed || computed.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.map.has(lineNumber)) {
|
||||||
|
this.map.delete(lineNumber);
|
||||||
|
}
|
||||||
|
this.map.set(lineNumber, computed);
|
||||||
|
while (this.map.size > MAX_CACHED_LINES) {
|
||||||
|
const oldest = this.map.keys().next();
|
||||||
|
if (oldest.done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.map.delete(oldest.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return computed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildSelectionsForLine(lineNumber: number) {
|
||||||
|
const spans = this._linesSnapshot[lineNumber - 1];
|
||||||
|
if (!spans || spans.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const relevant = this._rangeInfo.filter(
|
||||||
|
(info) => lineNumber >= info.lineFrom && lineNumber <= info.lineTo
|
||||||
|
);
|
||||||
|
if (!relevant.length) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selections: Array<Selection> = [];
|
||||||
|
for (const range of relevant) {
|
||||||
|
const length = lineLength(spans);
|
||||||
|
const fromOffset =
|
||||||
|
lineNumber === range.lineFrom
|
||||||
|
? offsetWithinLine(range.from, spans)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
let toOffset =
|
||||||
|
lineNumber === range.lineTo
|
||||||
|
? offsetWithinLine(range.to, spans)
|
||||||
|
: length;
|
||||||
|
if (toOffset === fromOffset) {
|
||||||
|
toOffset = Math.min(length, fromOffset + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastSpan = spans[spans.length - 1];
|
||||||
|
const spanEnd = lastSpan ? lastSpan.to : range.to;
|
||||||
|
const extendsLine =
|
||||||
|
lineNumber < range.lineTo ||
|
||||||
|
(lineNumber === range.lineTo && range.to > spanEnd);
|
||||||
|
|
||||||
|
this.appendSelection(selections, {
|
||||||
|
from: fromOffset,
|
||||||
|
to: toOffset,
|
||||||
|
extends: extendsLine,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return selections;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selections(view: EditorView): SelectionState {
|
export function selections(view: EditorView): SelectionState {
|
||||||
|
|||||||
@@ -1,415 +0,0 @@
|
|||||||
import { LineBasedState } from "./linebasedstate";
|
|
||||||
import { Highlighter, highlightTree } from "@lezer/highlight";
|
|
||||||
import { ChangedRange, Tree, TreeFragment } from "@lezer/common";
|
|
||||||
import { highlightingFor, language } from "@codemirror/language";
|
|
||||||
import { EditorView, ViewUpdate } from "@codemirror/view";
|
|
||||||
import { DrawContext } from "./types";
|
|
||||||
import { Config, Options, Scale } from "./config";
|
|
||||||
import { LinesState, foldsChanged } from "./linesState";
|
|
||||||
import crelt from "crelt";
|
|
||||||
import { ChangeSet, EditorState } from "@codemirror/state";
|
|
||||||
|
|
||||||
type TagSpan = { text: string; tags: string };
|
|
||||||
type FontInfo = { color: string; font: string; lineHeight: number };
|
|
||||||
|
|
||||||
export class TextState extends LineBasedState<Array<TagSpan>> {
|
|
||||||
private _previousTree: Tree | undefined;
|
|
||||||
private _displayText: Required<Options>["displayText"] | undefined;
|
|
||||||
private _fontInfoMap: Map<string, FontInfo> = new Map();
|
|
||||||
private _themeClasses: Set<string> | undefined;
|
|
||||||
private _highlightingCallbackId: number | NodeJS.Timeout | undefined;
|
|
||||||
|
|
||||||
public constructor(view: EditorView) {
|
|
||||||
super(view);
|
|
||||||
|
|
||||||
this._themeClasses = new Set(Array.from(view.dom.classList));
|
|
||||||
|
|
||||||
if (view.state.facet(Config).enabled) {
|
|
||||||
this.updateImpl(view.state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private shouldUpdate(update: ViewUpdate) {
|
|
||||||
// If the doc changed
|
|
||||||
if (update.docChanged) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If configuration settings changed
|
|
||||||
if (update.state.facet(Config) !== update.startState.facet(Config)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the theme changed
|
|
||||||
if (this.themeChanged()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the folds changed
|
|
||||||
if (foldsChanged(update.transactions)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public update(update: ViewUpdate) {
|
|
||||||
if (!this.shouldUpdate(update)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._highlightingCallbackId) {
|
|
||||||
typeof window.requestIdleCallback !== "undefined"
|
|
||||||
? cancelIdleCallback(this._highlightingCallbackId as number)
|
|
||||||
: clearTimeout(this._highlightingCallbackId);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateImpl(update.state, update.changes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateImpl(state: EditorState, changes?: ChangeSet) {
|
|
||||||
this.map.clear();
|
|
||||||
|
|
||||||
/* Store display text setting for rendering */
|
|
||||||
this._displayText = state.facet(Config).displayText;
|
|
||||||
|
|
||||||
/* If class list has changed, clear and recalculate the font info map */
|
|
||||||
if (this.themeChanged()) {
|
|
||||||
this._fontInfoMap.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Incrementally parse the tree based on previous tree + changes */
|
|
||||||
let treeFragments: ReadonlyArray<TreeFragment> | undefined = undefined;
|
|
||||||
if (this._previousTree && changes) {
|
|
||||||
const previousFragments = TreeFragment.addTree(this._previousTree);
|
|
||||||
|
|
||||||
const changedRanges: Array<ChangedRange> = [];
|
|
||||||
changes.iterChangedRanges((fromA, toA, fromB, toB) =>
|
|
||||||
changedRanges.push({ fromA, toA, fromB, toB })
|
|
||||||
);
|
|
||||||
|
|
||||||
treeFragments = TreeFragment.applyChanges(
|
|
||||||
previousFragments,
|
|
||||||
changedRanges
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse the document into a lezer tree */
|
|
||||||
const docToString = state.doc.toString();
|
|
||||||
const parser = state.facet(language)?.parser;
|
|
||||||
const tree = parser ? parser.parse(docToString, treeFragments) : undefined;
|
|
||||||
this._previousTree = tree;
|
|
||||||
|
|
||||||
/* Highlight the document, and store the text and tags for each line */
|
|
||||||
const highlighter: Highlighter = {
|
|
||||||
style: (tags) => highlightingFor(state, tags),
|
|
||||||
};
|
|
||||||
|
|
||||||
let highlights: Array<{ from: number; to: number; tags: string }> = [];
|
|
||||||
|
|
||||||
if (tree) {
|
|
||||||
/**
|
|
||||||
* The viewport renders a few extra lines above and below the editor view. To approximate
|
|
||||||
* the lines visible in the minimap, we multiply the lines in the viewport by the scale multipliers.
|
|
||||||
*
|
|
||||||
* Based on the current scroll position, the minimap may show a larger portion of lines above or
|
|
||||||
* below the lines currently in the editor view. On a long document, when the scroll position is
|
|
||||||
* near the top of the document, the minimap will show a small number of lines above the lines
|
|
||||||
* in the editor view, and a large number of lines below the lines in the editor view.
|
|
||||||
*
|
|
||||||
* To approximate this ratio, we can use the viewport scroll percentage
|
|
||||||
*
|
|
||||||
* ┌─────────────────────┐
|
|
||||||
* │ │
|
|
||||||
* │ Extra viewport │
|
|
||||||
* │ buffer │
|
|
||||||
* ├─────────────────────┼───────┐
|
|
||||||
* │ │Minimap│
|
|
||||||
* │ │Gutter │
|
|
||||||
* │ ├───────┤
|
|
||||||
* │ Editor View │Scaled │
|
|
||||||
* │ │View │
|
|
||||||
* │ │Overlay│
|
|
||||||
* │ ├───────┤
|
|
||||||
* │ │ │
|
|
||||||
* │ │ │
|
|
||||||
* ├─────────────────────┼───────┘
|
|
||||||
* │ │
|
|
||||||
* │ Extra viewport │
|
|
||||||
* │ buffer │
|
|
||||||
* └─────────────────────┘
|
|
||||||
*
|
|
||||||
**/
|
|
||||||
|
|
||||||
const vpLineTop = state.doc.lineAt(this.view.viewport.from).number;
|
|
||||||
const vpLineBottom = state.doc.lineAt(this.view.viewport.to).number;
|
|
||||||
const vpLineCount = vpLineBottom - vpLineTop;
|
|
||||||
const vpScroll = vpLineTop / (state.doc.lines - vpLineCount);
|
|
||||||
|
|
||||||
const { SizeRatio, PixelMultiplier } = Scale;
|
|
||||||
const mmLineCount = vpLineCount * SizeRatio * PixelMultiplier;
|
|
||||||
const mmLineRatio = vpScroll * mmLineCount;
|
|
||||||
|
|
||||||
const mmLineTop = Math.max(1, Math.floor(vpLineTop - mmLineRatio));
|
|
||||||
const mmLineBottom = Math.min(
|
|
||||||
vpLineBottom + Math.floor(mmLineCount - mmLineRatio),
|
|
||||||
state.doc.lines
|
|
||||||
);
|
|
||||||
|
|
||||||
// Highlight the in-view lines synchronously
|
|
||||||
highlightTree(
|
|
||||||
tree,
|
|
||||||
highlighter,
|
|
||||||
(from, to, tags) => {
|
|
||||||
highlights.push({ from, to, tags });
|
|
||||||
},
|
|
||||||
state.doc.line(mmLineTop).from,
|
|
||||||
state.doc.line(mmLineBottom).to
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the map
|
|
||||||
this.updateMapImpl(state, highlights);
|
|
||||||
|
|
||||||
// Highlight the entire tree in an idle callback
|
|
||||||
highlights = [];
|
|
||||||
const highlightingCallback = () => {
|
|
||||||
if (tree) {
|
|
||||||
highlightTree(tree, highlighter, (from, to, tags) => {
|
|
||||||
highlights.push({ from, to, tags });
|
|
||||||
});
|
|
||||||
this.updateMapImpl(state, highlights);
|
|
||||||
this._highlightingCallbackId = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._highlightingCallbackId =
|
|
||||||
typeof window.requestIdleCallback !== "undefined"
|
|
||||||
? requestIdleCallback(highlightingCallback)
|
|
||||||
: setTimeout(highlightingCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateMapImpl(
|
|
||||||
state: EditorState,
|
|
||||||
highlights: Array<{ from: number; to: number; tags: string }>
|
|
||||||
) {
|
|
||||||
this.map.clear();
|
|
||||||
|
|
||||||
const docToString = state.doc.toString();
|
|
||||||
const highlightsIterator = highlights.values();
|
|
||||||
let highlightPtr = highlightsIterator.next();
|
|
||||||
|
|
||||||
for (const [index, line] of state.field(LinesState).entries()) {
|
|
||||||
const spans: Array<TagSpan> = [];
|
|
||||||
|
|
||||||
for (const span of line) {
|
|
||||||
// Skip if it's a 0-length span
|
|
||||||
if (span.from === span.to) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append a placeholder for a folded span
|
|
||||||
if (span.folded) {
|
|
||||||
spans.push({ text: "…", tags: "" });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let position = span.from;
|
|
||||||
while (!highlightPtr.done && highlightPtr.value.from < span.to) {
|
|
||||||
const { from, to, tags } = highlightPtr.value;
|
|
||||||
|
|
||||||
// Iterate until our highlight is over the current span
|
|
||||||
if (to < position) {
|
|
||||||
highlightPtr = highlightsIterator.next();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append unstyled text before the highlight begins
|
|
||||||
if (from > position) {
|
|
||||||
spans.push({ text: docToString.slice(position, from), tags: "" });
|
|
||||||
}
|
|
||||||
|
|
||||||
// A highlight may start before and extend beyond the current span
|
|
||||||
const start = Math.max(from, span.from);
|
|
||||||
const end = Math.min(to, span.to);
|
|
||||||
|
|
||||||
// Append the highlighted text
|
|
||||||
spans.push({ text: docToString.slice(start, end), tags });
|
|
||||||
position = end;
|
|
||||||
|
|
||||||
// If the highlight continues beyond this span, break from this loop
|
|
||||||
if (to > end) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, move to the next highlight
|
|
||||||
highlightPtr = highlightsIterator.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are remaining spans that did not get highlighted, append them unstyled
|
|
||||||
if (position !== span.to) {
|
|
||||||
spans.push({
|
|
||||||
text: docToString.slice(position, span.to),
|
|
||||||
tags: "",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lines are indexed beginning at 1 instead of 0
|
|
||||||
const lineNumber = index + 1;
|
|
||||||
this.map.set(lineNumber, spans);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public measure(context: CanvasRenderingContext2D): {
|
|
||||||
charWidth: number;
|
|
||||||
lineHeight: number;
|
|
||||||
} {
|
|
||||||
const { color, font, lineHeight } = this.getFontInfo("");
|
|
||||||
|
|
||||||
context.textBaseline = "ideographic";
|
|
||||||
context.fillStyle = color;
|
|
||||||
context.font = font;
|
|
||||||
|
|
||||||
return {
|
|
||||||
charWidth: context.measureText("_").width,
|
|
||||||
lineHeight: lineHeight,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public beforeDraw() {
|
|
||||||
this._fontInfoMap.clear(); // Confirm this worked for theme changes or get rid of it because it's slow
|
|
||||||
}
|
|
||||||
|
|
||||||
public drawLine(ctx: DrawContext, lineNumber: number) {
|
|
||||||
const line = this.get(lineNumber);
|
|
||||||
if (!line) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { context, charWidth, lineHeight, offsetX, offsetY } = ctx;
|
|
||||||
|
|
||||||
let prevInfo: FontInfo | undefined;
|
|
||||||
context.textBaseline = "ideographic";
|
|
||||||
|
|
||||||
for (const span of line) {
|
|
||||||
const info = this.getFontInfo(span.tags);
|
|
||||||
|
|
||||||
if (!prevInfo || prevInfo.color !== info.color) {
|
|
||||||
context.fillStyle = info.color;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!prevInfo || prevInfo.font !== info.font) {
|
|
||||||
context.font = info.font;
|
|
||||||
}
|
|
||||||
|
|
||||||
prevInfo = info;
|
|
||||||
|
|
||||||
lineHeight = Math.max(lineHeight, info.lineHeight);
|
|
||||||
|
|
||||||
switch (this._displayText) {
|
|
||||||
case "characters": {
|
|
||||||
// TODO: `fillText` takes up the majority of profiling time in `render`
|
|
||||||
// Try speeding it up with `drawImage`
|
|
||||||
// https://stackoverflow.com/questions/8237030/html5-canvas-faster-filltext-vs-drawimage/8237081
|
|
||||||
|
|
||||||
context.fillText(span.text, offsetX, offsetY + lineHeight);
|
|
||||||
offsetX += span.text.length * charWidth;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "blocks": {
|
|
||||||
const nonWhitespace = /\S+/g;
|
|
||||||
let start: RegExpExecArray | null;
|
|
||||||
while ((start = nonWhitespace.exec(span.text)) !== null) {
|
|
||||||
const startX = offsetX + start.index * charWidth;
|
|
||||||
let width = (nonWhitespace.lastIndex - start.index) * charWidth;
|
|
||||||
|
|
||||||
// Reached the edge of the minimap
|
|
||||||
if (startX > context.canvas.width) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limit width to edge of minimap
|
|
||||||
if (startX + width > context.canvas.width) {
|
|
||||||
width = context.canvas.width - startX;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scaled 2px buffer between lines
|
|
||||||
const yBuffer = 2 / Scale.SizeRatio;
|
|
||||||
const height = lineHeight - yBuffer;
|
|
||||||
|
|
||||||
context.fillStyle = info.color;
|
|
||||||
context.globalAlpha = 0.65; // Make the blocks a bit faded
|
|
||||||
context.beginPath();
|
|
||||||
context.rect(startX, offsetY, width, height);
|
|
||||||
context.fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
offsetX += span.text.length * charWidth;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getFontInfo(tags: string): FontInfo {
|
|
||||||
const cached = this._fontInfoMap.get(tags);
|
|
||||||
if (cached) {
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a mock token wrapped in a cm-line
|
|
||||||
const mockToken = crelt("span", { class: tags });
|
|
||||||
const mockLine = crelt(
|
|
||||||
"div",
|
|
||||||
{ class: "cm-line", style: "display: none" },
|
|
||||||
mockToken
|
|
||||||
);
|
|
||||||
this.view.contentDOM.appendChild(mockLine);
|
|
||||||
|
|
||||||
// Get style information and store it
|
|
||||||
const style = window.getComputedStyle(mockToken);
|
|
||||||
const lineHeight = parseFloat(style.lineHeight) / Scale.SizeRatio;
|
|
||||||
const result = {
|
|
||||||
color: style.color,
|
|
||||||
font: `${style.fontStyle} ${style.fontWeight} ${lineHeight}px ${style.fontFamily}`,
|
|
||||||
lineHeight,
|
|
||||||
};
|
|
||||||
this._fontInfoMap.set(tags, result);
|
|
||||||
|
|
||||||
// Clean up and return
|
|
||||||
this.view.contentDOM.removeChild(mockLine);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private themeChanged(): boolean {
|
|
||||||
const previous = this._themeClasses;
|
|
||||||
const now = new Set<string>(Array.from(this.view.dom.classList));
|
|
||||||
this._themeClasses = now;
|
|
||||||
|
|
||||||
if (!previous) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore certain classes being added/removed
|
|
||||||
previous.delete("cm-focused");
|
|
||||||
now.delete("cm-focused");
|
|
||||||
|
|
||||||
if (previous.size !== now.size) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let containsAll = true;
|
|
||||||
previous.forEach((theme) => {
|
|
||||||
if (!now.has(theme)) {
|
|
||||||
containsAll = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return !containsAll;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function text(view: EditorView): TextState {
|
|
||||||
return new TextState(view);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
import {
|
||||||
|
ToWorkerMessage,
|
||||||
|
ToMainMessage,
|
||||||
|
BlockRequest,
|
||||||
|
TagSpan,
|
||||||
|
FontInfo,
|
||||||
|
} from './protocol';
|
||||||
|
|
||||||
|
// 缓存字体信息,只在主题变化时更新
|
||||||
|
let cachedFontInfoMap: Record<string, FontInfo> = {};
|
||||||
|
let cachedDefaultFont: FontInfo = { color: '#000', font: '12px monospace', lineHeight: 14 };
|
||||||
|
|
||||||
|
function post(msg: ToMainMessage, transfer?: Transferable[]): void {
|
||||||
|
self.postMessage(msg, { transfer });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二分查找:找到第一个 to > pos 的高亮索引
|
||||||
|
*/
|
||||||
|
function findHighlightStart(highlights: BlockRequest['highlights'], pos: number): number {
|
||||||
|
let left = 0;
|
||||||
|
let right = highlights.length;
|
||||||
|
while (left < right) {
|
||||||
|
const mid = (left + right) >>> 1;
|
||||||
|
if (highlights[mid].to <= pos) {
|
||||||
|
left = mid + 1;
|
||||||
|
} else {
|
||||||
|
right = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLineSpans(
|
||||||
|
request: BlockRequest
|
||||||
|
): Map<number, TagSpan[]> {
|
||||||
|
const { highlights, lines, textSlice, textOffset, startLine, endLine } = request;
|
||||||
|
const result = new Map<number, TagSpan[]>();
|
||||||
|
|
||||||
|
const slice = (from: number, to: number): string => {
|
||||||
|
return textSlice.slice(from - textOffset, to - textOffset);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用二分查找找到起始高亮索引
|
||||||
|
const firstSpanPos = lines[0]?.[0]?.from ?? 0;
|
||||||
|
let hlIndex = findHighlightStart(highlights, firstSpanPos);
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length && i < endLine - startLine + 1; i++) {
|
||||||
|
const lineNumber = startLine + i;
|
||||||
|
const line = lines[i];
|
||||||
|
if (!line) continue;
|
||||||
|
|
||||||
|
const spans: TagSpan[] = [];
|
||||||
|
|
||||||
|
for (const span of line) {
|
||||||
|
if (span.from === span.to) continue;
|
||||||
|
|
||||||
|
if (span.folded) {
|
||||||
|
spans.push({ text: '…', tags: '' });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pos = span.from;
|
||||||
|
|
||||||
|
// 从当前索引继续查找
|
||||||
|
while (hlIndex < highlights.length && highlights[hlIndex].from < span.to) {
|
||||||
|
const hl = highlights[hlIndex];
|
||||||
|
|
||||||
|
if (hl.to <= pos) {
|
||||||
|
hlIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hl.from > pos) {
|
||||||
|
spans.push({ text: slice(pos, Math.min(hl.from, span.to)), tags: '' });
|
||||||
|
pos = Math.min(hl.from, span.to);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hl.from < span.to) {
|
||||||
|
const start = Math.max(hl.from, span.from);
|
||||||
|
const end = Math.min(hl.to, span.to);
|
||||||
|
if (start < end) {
|
||||||
|
spans.push({ text: slice(start, end), tags: hl.tags });
|
||||||
|
pos = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hl.to <= span.to) {
|
||||||
|
hlIndex++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos < span.to) {
|
||||||
|
spans.push({ text: slice(pos, span.to), tags: '' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.set(lineNumber, spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBlock(request: BlockRequest): void {
|
||||||
|
const {
|
||||||
|
blockId,
|
||||||
|
blockIndex,
|
||||||
|
startLine,
|
||||||
|
endLine,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
displayText,
|
||||||
|
charWidth,
|
||||||
|
lineHeight,
|
||||||
|
gutterOffset,
|
||||||
|
} = request;
|
||||||
|
|
||||||
|
// 使用缓存的字体信息
|
||||||
|
const fontInfoMap = cachedFontInfoMap;
|
||||||
|
const defaultFont = cachedDefaultFont;
|
||||||
|
|
||||||
|
// Create OffscreenCanvas for this block
|
||||||
|
const canvas = new OffscreenCanvas(width, height);
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (!ctx) {
|
||||||
|
post({ type: 'error', message: 'Failed to get 2d context' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build line data
|
||||||
|
const lineSpans = buildLineSpans(request);
|
||||||
|
|
||||||
|
// Render each line
|
||||||
|
ctx.textBaseline = 'ideographic';
|
||||||
|
let offsetY = 0;
|
||||||
|
|
||||||
|
for (let lineNum = startLine; lineNum <= endLine; lineNum++) {
|
||||||
|
const spans = lineSpans.get(lineNum);
|
||||||
|
if (spans && spans.length > 0) {
|
||||||
|
drawLine(ctx, spans, fontInfoMap, defaultFont, displayText, gutterOffset, offsetY, charWidth, lineHeight, width);
|
||||||
|
}
|
||||||
|
offsetY += lineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to ImageBitmap and send back
|
||||||
|
const bitmap = canvas.transferToImageBitmap();
|
||||||
|
post({ type: 'blockComplete', blockId, blockIndex, bitmap }, [bitmap]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawLine(
|
||||||
|
ctx: OffscreenCanvasRenderingContext2D,
|
||||||
|
spans: TagSpan[],
|
||||||
|
fontInfoMap: Record<string, FontInfo>,
|
||||||
|
defaultFont: FontInfo,
|
||||||
|
displayText: 'blocks' | 'characters',
|
||||||
|
offsetX: number,
|
||||||
|
offsetY: number,
|
||||||
|
charWidth: number,
|
||||||
|
lineHeight: number,
|
||||||
|
availableWidth: number
|
||||||
|
): void {
|
||||||
|
let cursorX = offsetX;
|
||||||
|
let prevFont: FontInfo | null = null;
|
||||||
|
|
||||||
|
for (const span of spans) {
|
||||||
|
const info = fontInfoMap[span.tags] || defaultFont;
|
||||||
|
|
||||||
|
if (!prevFont || prevFont.color !== info.color) {
|
||||||
|
ctx.fillStyle = info.color;
|
||||||
|
}
|
||||||
|
if (!prevFont || prevFont.font !== info.font) {
|
||||||
|
ctx.font = info.font;
|
||||||
|
}
|
||||||
|
prevFont = info;
|
||||||
|
|
||||||
|
if (displayText === 'characters') {
|
||||||
|
cursorX = drawCharacters(ctx, span.text, cursorX, offsetY, charWidth, lineHeight);
|
||||||
|
} else {
|
||||||
|
cursorX = drawTextBlocks(ctx, span.text, cursorX, offsetY, charWidth, lineHeight, availableWidth - offsetX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCharacters(
|
||||||
|
ctx: OffscreenCanvasRenderingContext2D,
|
||||||
|
text: string,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
charWidth: number,
|
||||||
|
lineHeight: number
|
||||||
|
): number {
|
||||||
|
const yPos = y + lineHeight;
|
||||||
|
let batchStart = -1;
|
||||||
|
let batchX = x;
|
||||||
|
|
||||||
|
for (let i = 0; i <= text.length; i++) {
|
||||||
|
const char = text[i];
|
||||||
|
const isWhitespace = !char || char === ' ' || char === '\t';
|
||||||
|
|
||||||
|
if (isWhitespace) {
|
||||||
|
// 输出之前积累的批次
|
||||||
|
if (batchStart !== -1) {
|
||||||
|
ctx.fillText(text.slice(batchStart, i), batchX, yPos);
|
||||||
|
batchStart = -1;
|
||||||
|
}
|
||||||
|
if (char) x += charWidth;
|
||||||
|
} else {
|
||||||
|
// 开始新批次
|
||||||
|
if (batchStart === -1) {
|
||||||
|
batchStart = i;
|
||||||
|
batchX = x;
|
||||||
|
}
|
||||||
|
x += charWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawTextBlocks(
|
||||||
|
ctx: OffscreenCanvasRenderingContext2D,
|
||||||
|
text: string,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
charWidth: number,
|
||||||
|
lineHeight: number,
|
||||||
|
availableWidth: number
|
||||||
|
): number {
|
||||||
|
const nonWhitespace = /\S+/g;
|
||||||
|
let match: RegExpExecArray | null;
|
||||||
|
|
||||||
|
while ((match = nonWhitespace.exec(text)) !== null) {
|
||||||
|
const startX = x + match.index * charWidth;
|
||||||
|
let width = (nonWhitespace.lastIndex - match.index) * charWidth;
|
||||||
|
|
||||||
|
if (startX > availableWidth) break;
|
||||||
|
if (startX + width > availableWidth) {
|
||||||
|
width = availableWidth - startX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width > 0) {
|
||||||
|
ctx.fillRect(startX, y, width, lineHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return x + text.length * charWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMessage(msg: ToWorkerMessage): void {
|
||||||
|
switch (msg.type) {
|
||||||
|
case 'init':
|
||||||
|
// 重置字体缓存
|
||||||
|
cachedFontInfoMap = {};
|
||||||
|
cachedDefaultFont = { color: '#000', font: '12px monospace', lineHeight: 14 };
|
||||||
|
post({ type: 'ready' });
|
||||||
|
break;
|
||||||
|
case 'updateFontInfo':
|
||||||
|
// 增量合并字体信息
|
||||||
|
Object.assign(cachedFontInfoMap, msg.fontInfoMap);
|
||||||
|
cachedDefaultFont = msg.defaultFont;
|
||||||
|
break;
|
||||||
|
case 'renderBlock':
|
||||||
|
renderBlock(msg);
|
||||||
|
break;
|
||||||
|
case 'destroy':
|
||||||
|
// 清理缓存
|
||||||
|
cachedFontInfoMap = {};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onmessage = (e: MessageEvent<ToWorkerMessage>) => {
|
||||||
|
try {
|
||||||
|
handleMessage(e.data);
|
||||||
|
} catch (err) {
|
||||||
|
post({ type: 'error', message: String(err) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* Worker Communication Protocol
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface TagSpan {
|
||||||
|
text: string;
|
||||||
|
tags: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Highlight {
|
||||||
|
from: number;
|
||||||
|
to: number;
|
||||||
|
tags: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LineSpan {
|
||||||
|
from: number;
|
||||||
|
to: number;
|
||||||
|
folded: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FontInfo {
|
||||||
|
color: string;
|
||||||
|
font: string;
|
||||||
|
lineHeight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新字体信息(主题变化时发送一次)
|
||||||
|
*/
|
||||||
|
export interface UpdateFontInfoRequest {
|
||||||
|
type: 'updateFontInfo';
|
||||||
|
fontInfoMap: Record<string, FontInfo>;
|
||||||
|
defaultFont: FontInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BlockRequest {
|
||||||
|
type: 'renderBlock';
|
||||||
|
blockId: number;
|
||||||
|
blockIndex: number;
|
||||||
|
startLine: number;
|
||||||
|
endLine: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
highlights: Highlight[];
|
||||||
|
lines: LineSpan[][];
|
||||||
|
textSlice: string;
|
||||||
|
textOffset: number;
|
||||||
|
displayText: 'blocks' | 'characters';
|
||||||
|
charWidth: number;
|
||||||
|
lineHeight: number;
|
||||||
|
gutterOffset: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InitRequest {
|
||||||
|
type: 'init';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DestroyRequest {
|
||||||
|
type: 'destroy';
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ToWorkerMessage = BlockRequest | InitRequest | DestroyRequest | UpdateFontInfoRequest;
|
||||||
|
|
||||||
|
export interface BlockComplete {
|
||||||
|
type: 'blockComplete';
|
||||||
|
blockId: number;
|
||||||
|
blockIndex: number;
|
||||||
|
bitmap: ImageBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkerReady {
|
||||||
|
type: 'ready';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkerError {
|
||||||
|
type: 'error';
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ToMainMessage = BlockComplete | WorkerReady | WorkerError;
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
import { Extension, StateField } from '@codemirror/state';
|
import { Extension, StateField } from '@codemirror/state';
|
||||||
import { EditorView, showTooltip, Tooltip } from '@codemirror/view';
|
import { EditorView, showTooltip, Tooltip } from '@codemirror/view';
|
||||||
import { translatorManager } from './manager';
|
import { translatorManager } from './manager';
|
||||||
import { TRANSLATION_ICON_SVG } from '@/common/constant/translation';
|
|
||||||
|
const TRANSLATION_ICON_SVG = `
|
||||||
|
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
||||||
|
<path d="M599.68 485.056h-8l30.592 164.672c20.352-7.04 38.72-17.344 54.912-31.104a271.36 271.36 0 0 1-40.704-64.64l32.256-4.032c8.896 17.664 19.072 33.28 30.592 46.72 23.872-27.968 42.24-65.152 55.04-111.744l-154.688 0.128z m121.92 133.76c18.368 15.36 39.36 26.56 62.848 33.472l14.784 4.416-8.64 30.336-14.72-4.352a205.696 205.696 0 0 1-76.48-41.728c-20.672 17.92-44.928 31.552-71.232 40.064l20.736 110.912H519.424l-9.984 72.512h385.152c18.112 0 32.704-14.144 32.704-31.616V295.424a32.128 32.128 0 0 0-32.704-31.552H550.528l35.2 189.696h79.424v-31.552h61.44v31.552h102.4v31.616h-42.688c-14.272 55.488-35.712 100.096-64.64 133.568zM479.36 791.68H193.472c-36.224 0-65.472-28.288-65.472-63.168V191.168C128 156.16 157.312 128 193.472 128h327.68l20.544 104.32h352.832c36.224 0 65.472 28.224 65.472 63.104v537.408c0 34.944-29.312 63.168-65.472 63.168H468.608l10.688-104.32zM337.472 548.352v-33.28H272.768v-48.896h60.16V433.28h-60.16v-41.728h64.704v-32.896h-102.4v189.632h102.4z m158.272 0V453.76c0-17.216-4.032-30.272-12.16-39.488-8.192-9.152-20.288-13.696-36.032-13.696a55.04 55.04 0 0 0-24.768 5.376 39.04 39.04 0 0 0-17.088 15.936h-1.984l-5.056-18.56h-28.352V548.48h37.12V480c0-17.088 2.304-29.376 6.912-36.736 4.608-7.424 12.16-11.072 22.528-11.072 7.616 0 13.248 2.56 16.64 7.872 3.52 5.248 5.312 13.056 5.312 23.488v84.736h36.928z" fill="currentColor"></path>
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
function TranslationTooltips(state: any): readonly Tooltip[] {
|
function TranslationTooltips(state: any): readonly Tooltip[] {
|
||||||
const selection = state.selection.main;
|
const selection = state.selection.main;
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
import {KeyBindingCommand} from '@/../bindings/voidraft/internal/models/models';
|
import {closeSearchPanel, openSearchPanel,} from '@codemirror/search';
|
||||||
import {
|
|
||||||
openSearchPanel,
|
|
||||||
closeSearchPanel,
|
|
||||||
} from '@codemirror/search';
|
|
||||||
import {
|
import {
|
||||||
addNewBlockAfterCurrent,
|
addNewBlockAfterCurrent,
|
||||||
addNewBlockAfterLast,
|
addNewBlockAfterLast,
|
||||||
@@ -49,249 +45,249 @@ import {
|
|||||||
} from '@codemirror/commands';
|
} from '@codemirror/commands';
|
||||||
import {foldAll, foldCode, unfoldAll, unfoldCode} from '@codemirror/language';
|
import {foldAll, foldCode, unfoldAll, unfoldCode} from '@codemirror/language';
|
||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
|
import {KeyBindingKey} from '@/../bindings/voidraft/internal/models/models';
|
||||||
|
|
||||||
// 默认编辑器选项
|
// 默认代码块扩展选项
|
||||||
const defaultEditorOptions = {
|
const defaultBlockExtensionOptions = {
|
||||||
defaultBlockToken: 'text',
|
defaultBlockToken: 'text',
|
||||||
defaultBlockAutoDetect: true,
|
defaultBlockAutoDetect: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 前端命令注册表
|
* 前端命令注册表
|
||||||
* 将后端定义的command字段映射到具体的前端方法和翻译键
|
* 将后端定义的key字段映射到具体的前端方法和翻译键
|
||||||
*/
|
*/
|
||||||
export const commands = {
|
export const commands: Record<string, { handler: any; descriptionKey: string }> = {
|
||||||
[KeyBindingCommand.ShowSearchCommand]: {
|
[KeyBindingKey.ShowSearchKeyBindingKey]: {
|
||||||
handler: openSearchPanel,
|
handler: openSearchPanel,
|
||||||
descriptionKey: 'keybindings.commands.showSearch'
|
descriptionKey: 'keybindings.commands.showSearch'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.HideSearchCommand]: {
|
[KeyBindingKey.HideSearchKeyBindingKey]: {
|
||||||
handler: closeSearchPanel,
|
handler: closeSearchPanel,
|
||||||
descriptionKey: 'keybindings.commands.hideSearch'
|
descriptionKey: 'keybindings.commands.hideSearch'
|
||||||
},
|
},
|
||||||
// 代码块操作命令
|
[KeyBindingKey.BlockSelectAllKeyBindingKey]: {
|
||||||
[KeyBindingCommand.BlockSelectAllCommand]: {
|
|
||||||
handler: selectAll,
|
handler: selectAll,
|
||||||
descriptionKey: 'keybindings.commands.blockSelectAll'
|
descriptionKey: 'keybindings.commands.blockSelectAll'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockAddAfterCurrentCommand]: {
|
[KeyBindingKey.BlockAddAfterCurrentKeyBindingKey]: {
|
||||||
handler: addNewBlockAfterCurrent(defaultEditorOptions),
|
handler: addNewBlockAfterCurrent(defaultBlockExtensionOptions),
|
||||||
descriptionKey: 'keybindings.commands.blockAddAfterCurrent'
|
descriptionKey: 'keybindings.commands.blockAddAfterCurrent'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockAddAfterLastCommand]: {
|
[KeyBindingKey.BlockAddAfterLastKeyBindingKey]: {
|
||||||
handler: addNewBlockAfterLast(defaultEditorOptions),
|
handler: addNewBlockAfterLast(defaultBlockExtensionOptions),
|
||||||
descriptionKey: 'keybindings.commands.blockAddAfterLast'
|
descriptionKey: 'keybindings.commands.blockAddAfterLast'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockAddBeforeCurrentCommand]: {
|
[KeyBindingKey.BlockAddBeforeCurrentKeyBindingKey]: {
|
||||||
handler: addNewBlockBeforeCurrent(defaultEditorOptions),
|
handler: addNewBlockBeforeCurrent(defaultBlockExtensionOptions),
|
||||||
descriptionKey: 'keybindings.commands.blockAddBeforeCurrent'
|
descriptionKey: 'keybindings.commands.blockAddBeforeCurrent'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockGotoPreviousCommand]: {
|
[KeyBindingKey.BlockGotoPreviousKeyBindingKey]: {
|
||||||
handler: gotoPreviousBlock,
|
handler: gotoPreviousBlock,
|
||||||
descriptionKey: 'keybindings.commands.blockGotoPrevious'
|
descriptionKey: 'keybindings.commands.blockGotoPrevious'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockGotoNextCommand]: {
|
[KeyBindingKey.BlockGotoNextKeyBindingKey]: {
|
||||||
handler: gotoNextBlock,
|
handler: gotoNextBlock,
|
||||||
descriptionKey: 'keybindings.commands.blockGotoNext'
|
descriptionKey: 'keybindings.commands.blockGotoNext'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockSelectPreviousCommand]: {
|
[KeyBindingKey.BlockSelectPreviousKeyBindingKey]: {
|
||||||
handler: selectPreviousBlock,
|
handler: selectPreviousBlock,
|
||||||
descriptionKey: 'keybindings.commands.blockSelectPrevious'
|
descriptionKey: 'keybindings.commands.blockSelectPrevious'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockSelectNextCommand]: {
|
[KeyBindingKey.BlockSelectNextKeyBindingKey]: {
|
||||||
handler: selectNextBlock,
|
handler: selectNextBlock,
|
||||||
descriptionKey: 'keybindings.commands.blockSelectNext'
|
descriptionKey: 'keybindings.commands.blockSelectNext'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockDeleteCommand]: {
|
[KeyBindingKey.BlockDeleteKeyBindingKey]: {
|
||||||
handler: deleteBlock(defaultEditorOptions),
|
handler: deleteBlock(defaultBlockExtensionOptions),
|
||||||
descriptionKey: 'keybindings.commands.blockDelete'
|
descriptionKey: 'keybindings.commands.blockDelete'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockMoveUpCommand]: {
|
[KeyBindingKey.BlockMoveUpKeyBindingKey]: {
|
||||||
handler: moveCurrentBlockUp,
|
handler: moveCurrentBlockUp,
|
||||||
descriptionKey: 'keybindings.commands.blockMoveUp'
|
descriptionKey: 'keybindings.commands.blockMoveUp'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockMoveDownCommand]: {
|
[KeyBindingKey.BlockMoveDownKeyBindingKey]: {
|
||||||
handler: moveCurrentBlockDown,
|
handler: moveCurrentBlockDown,
|
||||||
descriptionKey: 'keybindings.commands.blockMoveDown'
|
descriptionKey: 'keybindings.commands.blockMoveDown'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockDeleteLineCommand]: {
|
[KeyBindingKey.BlockDeleteLineKeyBindingKey]: {
|
||||||
handler: deleteLineCommand,
|
handler: deleteLineCommand,
|
||||||
descriptionKey: 'keybindings.commands.blockDeleteLine'
|
descriptionKey: 'keybindings.commands.blockDeleteLine'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockMoveLineUpCommand]: {
|
[KeyBindingKey.BlockMoveLineUpKeyBindingKey]: {
|
||||||
handler: moveLineUp,
|
handler: moveLineUp,
|
||||||
descriptionKey: 'keybindings.commands.blockMoveLineUp'
|
descriptionKey: 'keybindings.commands.blockMoveLineUp'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockMoveLineDownCommand]: {
|
[KeyBindingKey.BlockMoveLineDownKeyBindingKey]: {
|
||||||
handler: moveLineDown,
|
handler: moveLineDown,
|
||||||
descriptionKey: 'keybindings.commands.blockMoveLineDown'
|
descriptionKey: 'keybindings.commands.blockMoveLineDown'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockTransposeCharsCommand]: {
|
[KeyBindingKey.BlockTransposeCharsKeyBindingKey]: {
|
||||||
handler: transposeChars,
|
handler: transposeChars,
|
||||||
descriptionKey: 'keybindings.commands.blockTransposeChars'
|
descriptionKey: 'keybindings.commands.blockTransposeChars'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockFormatCommand]: {
|
[KeyBindingKey.BlockFormatKeyBindingKey]: {
|
||||||
handler: formatCurrentBlock,
|
handler: formatCurrentBlock,
|
||||||
descriptionKey: 'keybindings.commands.blockFormat'
|
descriptionKey: 'keybindings.commands.blockFormat'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockCopyCommand]: {
|
[KeyBindingKey.BlockCopyKeyBindingKey]: {
|
||||||
handler: copyCommand,
|
handler: copyCommand,
|
||||||
descriptionKey: 'keybindings.commands.blockCopy'
|
descriptionKey: 'keybindings.commands.blockCopy'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockCutCommand]: {
|
[KeyBindingKey.BlockCutKeyBindingKey]: {
|
||||||
handler: cutCommand,
|
handler: cutCommand,
|
||||||
descriptionKey: 'keybindings.commands.blockCut'
|
descriptionKey: 'keybindings.commands.blockCut'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.BlockPasteCommand]: {
|
[KeyBindingKey.BlockPasteKeyBindingKey]: {
|
||||||
handler: pasteCommand,
|
handler: pasteCommand,
|
||||||
descriptionKey: 'keybindings.commands.blockPaste'
|
descriptionKey: 'keybindings.commands.blockPaste'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.HistoryUndoCommand]: {
|
[KeyBindingKey.HistoryUndoKeyBindingKey]: {
|
||||||
handler: undo,
|
handler: undo,
|
||||||
descriptionKey: 'keybindings.commands.historyUndo'
|
descriptionKey: 'keybindings.commands.historyUndo'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.HistoryRedoCommand]: {
|
[KeyBindingKey.HistoryRedoKeyBindingKey]: {
|
||||||
handler: redo,
|
handler: redo,
|
||||||
descriptionKey: 'keybindings.commands.historyRedo'
|
descriptionKey: 'keybindings.commands.historyRedo'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.HistoryUndoSelectionCommand]: {
|
[KeyBindingKey.HistoryUndoSelectionKeyBindingKey]: {
|
||||||
handler: undoSelection,
|
handler: undoSelection,
|
||||||
descriptionKey: 'keybindings.commands.historyUndoSelection'
|
descriptionKey: 'keybindings.commands.historyUndoSelection'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.HistoryRedoSelectionCommand]: {
|
[KeyBindingKey.HistoryRedoSelectionKeyBindingKey]: {
|
||||||
handler: redoSelection,
|
handler: redoSelection,
|
||||||
descriptionKey: 'keybindings.commands.historyRedoSelection'
|
descriptionKey: 'keybindings.commands.historyRedoSelection'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.FoldCodeCommand]: {
|
[KeyBindingKey.FoldCodeKeyBindingKey]: {
|
||||||
handler: foldCode,
|
handler: foldCode,
|
||||||
descriptionKey: 'keybindings.commands.foldCode'
|
descriptionKey: 'keybindings.commands.foldCode'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.UnfoldCodeCommand]: {
|
[KeyBindingKey.UnfoldCodeKeyBindingKey]: {
|
||||||
handler: unfoldCode,
|
handler: unfoldCode,
|
||||||
descriptionKey: 'keybindings.commands.unfoldCode'
|
descriptionKey: 'keybindings.commands.unfoldCode'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.FoldAllCommand]: {
|
[KeyBindingKey.FoldAllKeyBindingKey]: {
|
||||||
handler: foldAll,
|
handler: foldAll,
|
||||||
descriptionKey: 'keybindings.commands.foldAll'
|
descriptionKey: 'keybindings.commands.foldAll'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.UnfoldAllCommand]: {
|
[KeyBindingKey.UnfoldAllKeyBindingKey]: {
|
||||||
handler: unfoldAll,
|
handler: unfoldAll,
|
||||||
descriptionKey: 'keybindings.commands.unfoldAll'
|
descriptionKey: 'keybindings.commands.unfoldAll'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.CursorSyntaxLeftCommand]: {
|
[KeyBindingKey.CursorSyntaxLeftKeyBindingKey]: {
|
||||||
handler: cursorSyntaxLeft,
|
handler: cursorSyntaxLeft,
|
||||||
descriptionKey: 'keybindings.commands.cursorSyntaxLeft'
|
descriptionKey: 'keybindings.commands.cursorSyntaxLeft'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.CursorSyntaxRightCommand]: {
|
[KeyBindingKey.CursorSyntaxRightKeyBindingKey]: {
|
||||||
handler: cursorSyntaxRight,
|
handler: cursorSyntaxRight,
|
||||||
descriptionKey: 'keybindings.commands.cursorSyntaxRight'
|
descriptionKey: 'keybindings.commands.cursorSyntaxRight'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.SelectSyntaxLeftCommand]: {
|
[KeyBindingKey.SelectSyntaxLeftKeyBindingKey]: {
|
||||||
handler: selectSyntaxLeft,
|
handler: selectSyntaxLeft,
|
||||||
descriptionKey: 'keybindings.commands.selectSyntaxLeft'
|
descriptionKey: 'keybindings.commands.selectSyntaxLeft'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.SelectSyntaxRightCommand]: {
|
[KeyBindingKey.SelectSyntaxRightKeyBindingKey]: {
|
||||||
handler: selectSyntaxRight,
|
handler: selectSyntaxRight,
|
||||||
descriptionKey: 'keybindings.commands.selectSyntaxRight'
|
descriptionKey: 'keybindings.commands.selectSyntaxRight'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.CopyLineUpCommand]: {
|
[KeyBindingKey.CopyLineUpKeyBindingKey]: {
|
||||||
handler: copyLineUp,
|
handler: copyLineUp,
|
||||||
descriptionKey: 'keybindings.commands.copyLineUp'
|
descriptionKey: 'keybindings.commands.copyLineUp'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.CopyLineDownCommand]: {
|
[KeyBindingKey.CopyLineDownKeyBindingKey]: {
|
||||||
handler: copyLineDown,
|
handler: copyLineDown,
|
||||||
descriptionKey: 'keybindings.commands.copyLineDown'
|
descriptionKey: 'keybindings.commands.copyLineDown'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.InsertBlankLineCommand]: {
|
[KeyBindingKey.InsertBlankLineKeyBindingKey]: {
|
||||||
handler: insertBlankLine,
|
handler: insertBlankLine,
|
||||||
descriptionKey: 'keybindings.commands.insertBlankLine'
|
descriptionKey: 'keybindings.commands.insertBlankLine'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.SelectLineCommand]: {
|
[KeyBindingKey.SelectLineKeyBindingKey]: {
|
||||||
handler: selectLine,
|
handler: selectLine,
|
||||||
descriptionKey: 'keybindings.commands.selectLine'
|
descriptionKey: 'keybindings.commands.selectLine'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.SelectParentSyntaxCommand]: {
|
[KeyBindingKey.SelectParentSyntaxKeyBindingKey]: {
|
||||||
handler: selectParentSyntax,
|
handler: selectParentSyntax,
|
||||||
descriptionKey: 'keybindings.commands.selectParentSyntax'
|
descriptionKey: 'keybindings.commands.selectParentSyntax'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.IndentLessCommand]: {
|
[KeyBindingKey.IndentLessKeyBindingKey]: {
|
||||||
handler: indentLess,
|
handler: indentLess,
|
||||||
descriptionKey: 'keybindings.commands.indentLess'
|
descriptionKey: 'keybindings.commands.indentLess'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.IndentMoreCommand]: {
|
[KeyBindingKey.IndentMoreKeyBindingKey]: {
|
||||||
handler: indentMore,
|
handler: indentMore,
|
||||||
descriptionKey: 'keybindings.commands.indentMore'
|
descriptionKey: 'keybindings.commands.indentMore'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.IndentSelectionCommand]: {
|
[KeyBindingKey.IndentSelectionKeyBindingKey]: {
|
||||||
handler: indentSelection,
|
handler: indentSelection,
|
||||||
descriptionKey: 'keybindings.commands.indentSelection'
|
descriptionKey: 'keybindings.commands.indentSelection'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.CursorMatchingBracketCommand]: {
|
[KeyBindingKey.CursorMatchingBracketKeyBindingKey]: {
|
||||||
handler: cursorMatchingBracket,
|
handler: cursorMatchingBracket,
|
||||||
descriptionKey: 'keybindings.commands.cursorMatchingBracket'
|
descriptionKey: 'keybindings.commands.cursorMatchingBracket'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.ToggleCommentCommand]: {
|
[KeyBindingKey.ToggleCommentKeyBindingKey]: {
|
||||||
handler: toggleComment,
|
handler: toggleComment,
|
||||||
descriptionKey: 'keybindings.commands.toggleComment'
|
descriptionKey: 'keybindings.commands.toggleComment'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.ToggleBlockCommentCommand]: {
|
[KeyBindingKey.ToggleBlockCommentKeyBindingKey]: {
|
||||||
handler: toggleBlockComment,
|
handler: toggleBlockComment,
|
||||||
descriptionKey: 'keybindings.commands.toggleBlockComment'
|
descriptionKey: 'keybindings.commands.toggleBlockComment'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.InsertNewlineAndIndentCommand]: {
|
[KeyBindingKey.InsertNewlineAndIndentKeyBindingKey]: {
|
||||||
handler: insertNewlineAndIndent,
|
handler: insertNewlineAndIndent,
|
||||||
descriptionKey: 'keybindings.commands.insertNewlineAndIndent'
|
descriptionKey: 'keybindings.commands.insertNewlineAndIndent'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.DeleteCharBackwardCommand]: {
|
[KeyBindingKey.DeleteCharBackwardKeyBindingKey]: {
|
||||||
handler: deleteCharBackward,
|
handler: deleteCharBackward,
|
||||||
descriptionKey: 'keybindings.commands.deleteCharBackward'
|
descriptionKey: 'keybindings.commands.deleteCharBackward'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.DeleteCharForwardCommand]: {
|
[KeyBindingKey.DeleteCharForwardKeyBindingKey]: {
|
||||||
handler: deleteCharForward,
|
handler: deleteCharForward,
|
||||||
descriptionKey: 'keybindings.commands.deleteCharForward'
|
descriptionKey: 'keybindings.commands.deleteCharForward'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.DeleteGroupBackwardCommand]: {
|
[KeyBindingKey.DeleteGroupBackwardKeyBindingKey]: {
|
||||||
handler: deleteGroupBackward,
|
handler: deleteGroupBackward,
|
||||||
descriptionKey: 'keybindings.commands.deleteGroupBackward'
|
descriptionKey: 'keybindings.commands.deleteGroupBackward'
|
||||||
},
|
},
|
||||||
[KeyBindingCommand.DeleteGroupForwardCommand]: {
|
[KeyBindingKey.DeleteGroupForwardKeyBindingKey]: {
|
||||||
handler: deleteGroupForward,
|
handler: deleteGroupForward,
|
||||||
descriptionKey: 'keybindings.commands.deleteGroupForward'
|
descriptionKey: 'keybindings.commands.deleteGroupForward'
|
||||||
},
|
},
|
||||||
} as const;
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取命令处理函数
|
* 获取命令处理函数
|
||||||
* @param command 命令名称
|
* @param key 命令标识符
|
||||||
* @returns 对应的处理函数,如果不存在则返回 undefined
|
* @returns 对应的处理函数,如果不存在则返回 undefined
|
||||||
*/
|
*/
|
||||||
export const getCommandHandler = (command: KeyBindingCommand) => {
|
export const getCommandHandler = (key: string) => {
|
||||||
return commands[command]?.handler;
|
return commands[key]?.handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取命令描述
|
* 获取命令描述
|
||||||
* @param command 命令名称
|
* @param key 命令标识符
|
||||||
* @returns 对应的描述,如果不存在则返回 undefined
|
* @returns 对应的描述,如果不存在则返回 undefined
|
||||||
*/
|
*/
|
||||||
export const getCommandDescription = (command: KeyBindingCommand) => {
|
export const getCommandDescription = (key: string) => {
|
||||||
const descriptionKey = commands[command]?.descriptionKey;
|
const descriptionKey = commands[key]?.descriptionKey;
|
||||||
return descriptionKey ? i18n.global.t(descriptionKey) : undefined;
|
return descriptionKey ? i18n.global.t(descriptionKey) : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查命令是否已注册
|
* 检查命令是否已注册
|
||||||
* @param command 命令名称
|
* @param key 命令标识符
|
||||||
* @returns 是否已注册
|
* @returns 是否已注册
|
||||||
*/
|
*/
|
||||||
export const isCommandRegistered = (command: KeyBindingCommand): boolean => {
|
export const isCommandRegistered = (key: string): boolean => {
|
||||||
return command in commands;
|
return key in commands;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有已注册的命令
|
* 获取所有已注册的命令
|
||||||
* @returns 已注册的命令列表
|
* @returns 已注册的命令列表
|
||||||
*/
|
*/
|
||||||
export const getRegisteredCommands = (): KeyBindingCommand[] => {
|
export const getRegisteredCommands = (): string[] => {
|
||||||
return Object.keys(commands) as KeyBindingCommand[];
|
return Object.keys(commands);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Extension } from '@codemirror/state';
|
import { Extension } from '@codemirror/state';
|
||||||
import { useKeybindingStore } from '@/stores/keybindingStore';
|
import { useKeybindingStore } from '@/stores/keybindingStore';
|
||||||
import { useExtensionStore } from '@/stores/extensionStore';
|
|
||||||
import { Manager } from './manager';
|
import { Manager } from './manager';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8,22 +7,13 @@ import { Manager } from './manager';
|
|||||||
*/
|
*/
|
||||||
export const createDynamicKeymapExtension = async (): Promise<Extension> => {
|
export const createDynamicKeymapExtension = async (): Promise<Extension> => {
|
||||||
const keybindingStore = useKeybindingStore();
|
const keybindingStore = useKeybindingStore();
|
||||||
const extensionStore = useExtensionStore();
|
|
||||||
|
|
||||||
// 确保快捷键配置已加载
|
// 确保快捷键配置已加载
|
||||||
if (keybindingStore.keyBindings.length === 0) {
|
if (keybindingStore.keyBindings.length === 0) {
|
||||||
await keybindingStore.loadKeyBindings();
|
await keybindingStore.loadKeyBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保扩展配置已加载
|
return Manager.createKeymapExtension(keybindingStore.keyBindings);
|
||||||
if (extensionStore.extensions.length === 0) {
|
|
||||||
await extensionStore.loadExtensions();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取启用的扩展ID列表
|
|
||||||
const enabledExtensionIds = extensionStore.enabledExtensions.map(ext => ext.id);
|
|
||||||
|
|
||||||
return Manager.createKeymapExtension(keybindingStore.keyBindings, enabledExtensionIds);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,12 +22,7 @@ export const createDynamicKeymapExtension = async (): Promise<Extension> => {
|
|||||||
*/
|
*/
|
||||||
export const updateKeymapExtension = (view: any): void => {
|
export const updateKeymapExtension = (view: any): void => {
|
||||||
const keybindingStore = useKeybindingStore();
|
const keybindingStore = useKeybindingStore();
|
||||||
const extensionStore = useExtensionStore();
|
Manager.updateKeymap(view, keybindingStore.keyBindings);
|
||||||
|
|
||||||
// 获取启用的扩展ID列表
|
|
||||||
const enabledExtensionIds = extensionStore.enabledExtensions.map(ext => ext.id);
|
|
||||||
|
|
||||||
Manager.updateKeymap(view, keybindingStore.keyBindings, enabledExtensionIds);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 导出相关模块
|
// 导出相关模块
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {keymap} from '@codemirror/view';
|
import {keymap} from '@codemirror/view';
|
||||||
import {Extension, Compartment} from '@codemirror/state';
|
import {Extension, Compartment} from '@codemirror/state';
|
||||||
import {KeyBinding as KeyBindingConfig, ExtensionID} from '@/../bindings/voidraft/internal/models/models';
|
import {KeyBinding as KeyBindingConfig} from '@/../bindings/voidraft/internal/models/ent/models';
|
||||||
import {KeyBinding, KeymapResult} from './types';
|
import {KeyBinding, KeymapResult} from './types';
|
||||||
import {getCommandHandler, isCommandRegistered} from './commands';
|
import {getCommandHandler, isCommandRegistered} from './commands';
|
||||||
|
|
||||||
@@ -14,10 +14,9 @@ export class Manager {
|
|||||||
/**
|
/**
|
||||||
* 将后端快捷键配置转换为CodeMirror快捷键绑定
|
* 将后端快捷键配置转换为CodeMirror快捷键绑定
|
||||||
* @param keyBindings 后端快捷键配置列表
|
* @param keyBindings 后端快捷键配置列表
|
||||||
* @param enabledExtensions 启用的扩展ID列表,如果不提供则使用所有启用的快捷键
|
|
||||||
* @returns 转换结果
|
* @returns 转换结果
|
||||||
*/
|
*/
|
||||||
static convertToKeyBindings(keyBindings: KeyBindingConfig[], enabledExtensions?: ExtensionID[]): KeymapResult {
|
static convertToKeyBindings(keyBindings: KeyBindingConfig[]): KeymapResult {
|
||||||
const result: KeyBinding[] = [];
|
const result: KeyBinding[] = [];
|
||||||
|
|
||||||
for (const binding of keyBindings) {
|
for (const binding of keyBindings) {
|
||||||
@@ -26,25 +25,21 @@ export class Manager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果提供了扩展列表,则只处理启用扩展的快捷键
|
// 检查命令是否已注册(使用 key 字段作为命令标识符)
|
||||||
if (enabledExtensions && !enabledExtensions.includes(binding.extension)) {
|
if (!binding.key || !isCommandRegistered(binding.key)) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查命令是否已注册
|
|
||||||
if (!isCommandRegistered(binding.command)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取命令处理函数
|
// 获取命令处理函数
|
||||||
const handler = getCommandHandler(binding.command);
|
const handler = getCommandHandler(binding.key);
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换为CodeMirror快捷键格式
|
// 转换为CodeMirror快捷键格式
|
||||||
|
// binding.command 是快捷键组合 (如 "Mod-f"),binding.key 是命令标识符
|
||||||
const keyBinding: KeyBinding = {
|
const keyBinding: KeyBinding = {
|
||||||
key: binding.key,
|
key: binding.command || '',
|
||||||
run: handler,
|
run: handler,
|
||||||
preventDefault: true
|
preventDefault: true
|
||||||
};
|
};
|
||||||
@@ -58,13 +53,10 @@ export class Manager {
|
|||||||
/**
|
/**
|
||||||
* 创建CodeMirror快捷键扩展
|
* 创建CodeMirror快捷键扩展
|
||||||
* @param keyBindings 后端快捷键配置列表
|
* @param keyBindings 后端快捷键配置列表
|
||||||
* @param enabledExtensions 启用的扩展ID列表
|
|
||||||
* @returns CodeMirror扩展
|
* @returns CodeMirror扩展
|
||||||
*/
|
*/
|
||||||
static createKeymapExtension(keyBindings: KeyBindingConfig[], enabledExtensions?: ExtensionID[]): Extension {
|
static createKeymapExtension(keyBindings: KeyBindingConfig[]): Extension {
|
||||||
const {keyBindings: cmKeyBindings} =
|
const {keyBindings: cmKeyBindings} = this.convertToKeyBindings(keyBindings);
|
||||||
this.convertToKeyBindings(keyBindings, enabledExtensions);
|
|
||||||
|
|
||||||
return this.compartment.of(keymap.of(cmKeyBindings));
|
return this.compartment.of(keymap.of(cmKeyBindings));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,12 +64,9 @@ export class Manager {
|
|||||||
* 动态更新快捷键扩展
|
* 动态更新快捷键扩展
|
||||||
* @param view 编辑器视图
|
* @param view 编辑器视图
|
||||||
* @param keyBindings 后端快捷键配置列表
|
* @param keyBindings 后端快捷键配置列表
|
||||||
* @param enabledExtensions 启用的扩展ID列表
|
|
||||||
*/
|
*/
|
||||||
static updateKeymap(view: any, keyBindings: KeyBindingConfig[], enabledExtensions: ExtensionID[]): void {
|
static updateKeymap(view: any, keyBindings: KeyBindingConfig[]): void {
|
||||||
const {keyBindings: cmKeyBindings} =
|
const {keyBindings: cmKeyBindings} = this.convertToKeyBindings(keyBindings);
|
||||||
this.convertToKeyBindings(keyBindings, enabledExtensions);
|
|
||||||
|
|
||||||
view.dispatch({
|
view.dispatch({
|
||||||
effects: this.compartment.reconfigure(keymap.of(cmKeyBindings))
|
effects: this.compartment.reconfigure(keymap.of(cmKeyBindings))
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {Manager} from './manager';
|
import {Manager} from './manager';
|
||||||
import {ExtensionID} from '@/../bindings/voidraft/internal/models/models';
|
|
||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
import {ExtensionDefinition} from './types';
|
import {ExtensionDefinition} from './types';
|
||||||
import {Prec} from '@codemirror/state';
|
import {Prec} from '@codemirror/state';
|
||||||
@@ -15,6 +14,8 @@ import {foldGutter} from "@codemirror/language";
|
|||||||
import {highlightActiveLineGutter, highlightWhitespace, highlightTrailingWhitespace} from "@codemirror/view";
|
import {highlightActiveLineGutter, highlightWhitespace, highlightTrailingWhitespace} from "@codemirror/view";
|
||||||
import createEditorContextMenu from '../extensions/contextMenu';
|
import createEditorContextMenu from '../extensions/contextMenu';
|
||||||
import {blockLineNumbers} from '../extensions/codeblock';
|
import {blockLineNumbers} from '../extensions/codeblock';
|
||||||
|
import {createHttpClientExtension} from '../extensions/httpclient';
|
||||||
|
import {ExtensionKey} from '@/../bindings/voidraft/internal/models/models';
|
||||||
|
|
||||||
type ExtensionEntry = {
|
type ExtensionEntry = {
|
||||||
definition: ExtensionDefinition
|
definition: ExtensionDefinition
|
||||||
@@ -22,35 +23,36 @@ type ExtensionEntry = {
|
|||||||
descriptionKey: string
|
descriptionKey: string
|
||||||
};
|
};
|
||||||
|
|
||||||
type RegisteredExtensionID = Exclude<ExtensionID, ExtensionID.$zero | ExtensionID.ExtensionEditor>;
|
// 排除 $zero 的有效扩展 Key 类型
|
||||||
|
type ValidExtensionKey = Exclude<ExtensionKey, ExtensionKey.$zero>;
|
||||||
|
|
||||||
const defineExtension = (create: (config: any) => any, defaultConfig: Record<string, any> = {}): ExtensionDefinition => ({
|
const defineExtension = (create: (config: any) => any, defaultConfig: Record<string, any> = {}): ExtensionDefinition => ({
|
||||||
create,
|
create,
|
||||||
defaultConfig
|
defaultConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
const EXTENSION_REGISTRY: Record<RegisteredExtensionID, ExtensionEntry> = {
|
const EXTENSION_REGISTRY: Record<ValidExtensionKey, ExtensionEntry> = {
|
||||||
[ExtensionID.ExtensionRainbowBrackets]: {
|
[ExtensionKey.ExtensionRainbowBrackets]: {
|
||||||
definition: defineExtension(() => rainbowBrackets()),
|
definition: defineExtension(() => rainbowBrackets()),
|
||||||
displayNameKey: 'extensions.rainbowBrackets.name',
|
displayNameKey: 'extensions.rainbowBrackets.name',
|
||||||
descriptionKey: 'extensions.rainbowBrackets.description'
|
descriptionKey: 'extensions.rainbowBrackets.description'
|
||||||
},
|
},
|
||||||
[ExtensionID.ExtensionHyperlink]: {
|
[ExtensionKey.ExtensionHyperlink]: {
|
||||||
definition: defineExtension(() => hyperLink),
|
definition: defineExtension(() => hyperLink),
|
||||||
displayNameKey: 'extensions.hyperlink.name',
|
displayNameKey: 'extensions.hyperlink.name',
|
||||||
descriptionKey: 'extensions.hyperlink.description'
|
descriptionKey: 'extensions.hyperlink.description'
|
||||||
},
|
},
|
||||||
[ExtensionID.ExtensionColorSelector]: {
|
[ExtensionKey.ExtensionColorSelector]: {
|
||||||
definition: defineExtension(() => color),
|
definition: defineExtension(() => color),
|
||||||
displayNameKey: 'extensions.colorSelector.name',
|
displayNameKey: 'extensions.colorSelector.name',
|
||||||
descriptionKey: 'extensions.colorSelector.description'
|
descriptionKey: 'extensions.colorSelector.description'
|
||||||
},
|
},
|
||||||
[ExtensionID.ExtensionTranslator]: {
|
[ExtensionKey.ExtensionTranslator]: {
|
||||||
definition: defineExtension(() => createTranslatorExtension()),
|
definition: defineExtension(() => createTranslatorExtension()),
|
||||||
displayNameKey: 'extensions.translator.name',
|
displayNameKey: 'extensions.translator.name',
|
||||||
descriptionKey: 'extensions.translator.description'
|
descriptionKey: 'extensions.translator.description'
|
||||||
},
|
},
|
||||||
[ExtensionID.ExtensionMinimap]: {
|
[ExtensionKey.ExtensionMinimap]: {
|
||||||
definition: defineExtension((config: any) => minimap({
|
definition: defineExtension((config: any) => minimap({
|
||||||
displayText: config?.displayText ?? 'characters',
|
displayText: config?.displayText ?? 'characters',
|
||||||
showOverlay: config?.showOverlay ?? 'always',
|
showOverlay: config?.showOverlay ?? 'always',
|
||||||
@@ -63,85 +65,90 @@ const EXTENSION_REGISTRY: Record<RegisteredExtensionID, ExtensionEntry> = {
|
|||||||
displayNameKey: 'extensions.minimap.name',
|
displayNameKey: 'extensions.minimap.name',
|
||||||
descriptionKey: 'extensions.minimap.description'
|
descriptionKey: 'extensions.minimap.description'
|
||||||
},
|
},
|
||||||
[ExtensionID.ExtensionSearch]: {
|
[ExtensionKey.ExtensionSearch]: {
|
||||||
definition: defineExtension(() => vscodeSearch),
|
definition: defineExtension(() => vscodeSearch),
|
||||||
displayNameKey: 'extensions.search.name',
|
displayNameKey: 'extensions.search.name',
|
||||||
descriptionKey: 'extensions.search.description'
|
descriptionKey: 'extensions.search.description'
|
||||||
},
|
},
|
||||||
[ExtensionID.ExtensionFold]: {
|
[ExtensionKey.ExtensionFold]: {
|
||||||
definition: defineExtension(() => Prec.low(foldGutter())),
|
definition: defineExtension(() => Prec.low(foldGutter())),
|
||||||
displayNameKey: 'extensions.fold.name',
|
displayNameKey: 'extensions.fold.name',
|
||||||
descriptionKey: 'extensions.fold.description'
|
descriptionKey: 'extensions.fold.description'
|
||||||
},
|
},
|
||||||
[ExtensionID.ExtensionMarkdown]: {
|
[ExtensionKey.ExtensionMarkdown]: {
|
||||||
definition: defineExtension(() => markdownExtensions),
|
definition: defineExtension(() => markdownExtensions),
|
||||||
displayNameKey: 'extensions.markdown.name',
|
displayNameKey: 'extensions.markdown.name',
|
||||||
descriptionKey: 'extensions.markdown.description'
|
descriptionKey: 'extensions.markdown.description'
|
||||||
},
|
},
|
||||||
[ExtensionID.ExtensionLineNumbers]: {
|
[ExtensionKey.ExtensionLineNumbers]: {
|
||||||
definition: defineExtension(() => Prec.high([blockLineNumbers, highlightActiveLineGutter()])),
|
definition: defineExtension(() => Prec.high([blockLineNumbers, highlightActiveLineGutter()])),
|
||||||
displayNameKey: 'extensions.lineNumbers.name',
|
displayNameKey: 'extensions.lineNumbers.name',
|
||||||
descriptionKey: 'extensions.lineNumbers.description'
|
descriptionKey: 'extensions.lineNumbers.description'
|
||||||
},
|
},
|
||||||
[ExtensionID.ExtensionContextMenu]: {
|
[ExtensionKey.ExtensionContextMenu]: {
|
||||||
definition: defineExtension(() => createEditorContextMenu()),
|
definition: defineExtension(() => createEditorContextMenu()),
|
||||||
displayNameKey: 'extensions.contextMenu.name',
|
displayNameKey: 'extensions.contextMenu.name',
|
||||||
descriptionKey: 'extensions.contextMenu.description'
|
descriptionKey: 'extensions.contextMenu.description'
|
||||||
},
|
},
|
||||||
[ExtensionID.ExtensionHighlightWhitespace]: {
|
[ExtensionKey.ExtensionHighlightWhitespace]: {
|
||||||
definition: defineExtension(() => highlightWhitespace()),
|
definition: defineExtension(() => highlightWhitespace()),
|
||||||
displayNameKey: 'extensions.highlightWhitespace.name',
|
displayNameKey: 'extensions.highlightWhitespace.name',
|
||||||
descriptionKey: 'extensions.highlightWhitespace.description'
|
descriptionKey: 'extensions.highlightWhitespace.description'
|
||||||
},
|
},
|
||||||
[ExtensionID.ExtensionHighlightTrailingWhitespace]: {
|
[ExtensionKey.ExtensionHighlightTrailingWhitespace]: {
|
||||||
definition: defineExtension(() => highlightTrailingWhitespace()),
|
definition: defineExtension(() => highlightTrailingWhitespace()),
|
||||||
displayNameKey: 'extensions.highlightTrailingWhitespace.name',
|
displayNameKey: 'extensions.highlightTrailingWhitespace.name',
|
||||||
descriptionKey: 'extensions.highlightTrailingWhitespace.description'
|
descriptionKey: 'extensions.highlightTrailingWhitespace.description'
|
||||||
|
},
|
||||||
|
[ExtensionKey.ExtensionHttpClient]: {
|
||||||
|
definition: defineExtension(() => createHttpClientExtension()),
|
||||||
|
displayNameKey: 'extensions.httpClient.name',
|
||||||
|
descriptionKey: 'extensions.httpClient.description'
|
||||||
}
|
}
|
||||||
} as const;
|
};
|
||||||
|
|
||||||
const isRegisteredExtension = (id: ExtensionID): id is RegisteredExtensionID =>
|
const isRegisteredExtension = (key: string): key is ValidExtensionKey =>
|
||||||
Object.prototype.hasOwnProperty.call(EXTENSION_REGISTRY, id);
|
Object.prototype.hasOwnProperty.call(EXTENSION_REGISTRY, key);
|
||||||
|
|
||||||
const getRegistryEntry = (id: ExtensionID): ExtensionEntry | undefined => {
|
const getRegistryEntry = (key: string): ExtensionEntry | undefined => {
|
||||||
if (!isRegisteredExtension(id)) {
|
if (!isRegisteredExtension(key)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return EXTENSION_REGISTRY[id];
|
return EXTENSION_REGISTRY[key];
|
||||||
};
|
};
|
||||||
|
|
||||||
export function registerAllExtensions(manager: Manager): void {
|
export function registerAllExtensions(manager: Manager): void {
|
||||||
(Object.entries(EXTENSION_REGISTRY) as [RegisteredExtensionID, ExtensionEntry][]).forEach(([id, entry]) => {
|
(Object.entries(EXTENSION_REGISTRY) as [ValidExtensionKey, ExtensionEntry][]).forEach(([id, entry]) => {
|
||||||
manager.registerExtension(id, entry.definition);
|
manager.registerExtension(id, entry.definition);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getExtensionDisplayName(id: ExtensionID): string {
|
export function getExtensionDisplayName(key: string): string {
|
||||||
const entry = getRegistryEntry(id);
|
const entry = getRegistryEntry(key);
|
||||||
return entry?.displayNameKey ? i18n.global.t(entry.displayNameKey) : id;
|
return entry?.displayNameKey ? i18n.global.t(entry.displayNameKey) : key;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getExtensionDescription(id: ExtensionID): string {
|
export function getExtensionDescription(key: string): string {
|
||||||
const entry = getRegistryEntry(id);
|
const entry = getRegistryEntry(key);
|
||||||
return entry?.descriptionKey ? i18n.global.t(entry.descriptionKey) : '';
|
return entry?.descriptionKey ? i18n.global.t(entry.descriptionKey) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExtensionDefinition(id: ExtensionID): ExtensionDefinition | undefined {
|
function getExtensionDefinition(key: string): ExtensionDefinition | undefined {
|
||||||
return getRegistryEntry(id)?.definition;
|
return getRegistryEntry(key)?.definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getExtensionDefaultConfig(id: ExtensionID): any {
|
export function getExtensionDefaultConfig(key: string): any {
|
||||||
const definition = getExtensionDefinition(id);
|
const definition = getExtensionDefinition(key);
|
||||||
if (!definition) return {};
|
if (!definition) return {};
|
||||||
return cloneConfig(definition.defaultConfig);
|
return cloneConfig(definition.defaultConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasExtensionConfig(id: ExtensionID): boolean {
|
export function hasExtensionConfig(key: string): boolean {
|
||||||
return Object.keys(getExtensionDefaultConfig(id)).length > 0;
|
return Object.keys(getExtensionDefaultConfig(key)).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAllExtensionIds(): ExtensionID[] {
|
export function getAllExtensionIds(): string[] {
|
||||||
return Object.keys(EXTENSION_REGISTRY) as RegisteredExtensionID[];
|
return Object.keys(EXTENSION_REGISTRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cloneConfig = (config: any) => {
|
const cloneConfig = (config: any) => {
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ const extensionManager = new Manager();
|
|||||||
/**
|
/**
|
||||||
* 异步创建动态扩展
|
* 异步创建动态扩展
|
||||||
* 确保扩展配置已加载
|
* 确保扩展配置已加载
|
||||||
* @param _documentId 可选的文档ID,用于提前初始化视图
|
|
||||||
*/
|
*/
|
||||||
export const createDynamicExtensions = async (_documentId?: number): Promise<Extension[]> => {
|
export const createDynamicExtensions = async (): Promise<Extension[]> => {
|
||||||
const extensionStore = useExtensionStore();
|
const extensionStore = useExtensionStore();
|
||||||
|
|
||||||
// 注册所有扩展工厂
|
// 注册所有扩展工厂
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {Compartment, Extension} from '@codemirror/state';
|
import {Compartment, Extension} from '@codemirror/state';
|
||||||
import {EditorView} from '@codemirror/view';
|
import {EditorView} from '@codemirror/view';
|
||||||
import {Extension as ExtensionConfig, ExtensionID} from '@/../bindings/voidraft/internal/models/models';
|
import {Extension as ExtensionConfig} from '@/../bindings/voidraft/internal/models/ent/models';
|
||||||
import {ExtensionDefinition, ExtensionState} from './types';
|
import {ExtensionDefinition, ExtensionState} from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8,10 +8,10 @@ import {ExtensionDefinition, ExtensionState} from './types';
|
|||||||
* 负责注册、初始化与同步所有动态扩展
|
* 负责注册、初始化与同步所有动态扩展
|
||||||
*/
|
*/
|
||||||
export class Manager {
|
export class Manager {
|
||||||
private extensionStates = new Map<ExtensionID, ExtensionState>();
|
private extensionStates = new Map<string, ExtensionState>();
|
||||||
private views = new Map<number, EditorView>();
|
private views = new Map<number, EditorView>();
|
||||||
|
|
||||||
registerExtension(id: ExtensionID, definition: ExtensionDefinition): void {
|
registerExtension(id: string, definition: ExtensionDefinition): void {
|
||||||
const existingState = this.extensionStates.get(id);
|
const existingState = this.extensionStates.get(id);
|
||||||
if (existingState) {
|
if (existingState) {
|
||||||
existingState.definition = definition;
|
existingState.definition = definition;
|
||||||
@@ -34,10 +34,11 @@ export class Manager {
|
|||||||
|
|
||||||
initExtensions(extensionConfigs: ExtensionConfig[]): void {
|
initExtensions(extensionConfigs: ExtensionConfig[]): void {
|
||||||
for (const config of extensionConfigs) {
|
for (const config of extensionConfigs) {
|
||||||
const state = this.extensionStates.get(config.id);
|
if (!config.key) continue;
|
||||||
|
const state = this.extensionStates.get(config.key);
|
||||||
if (!state) continue;
|
if (!state) continue;
|
||||||
const resolvedConfig = this.cloneConfig(config.config ?? state.definition.defaultConfig ?? {});
|
const resolvedConfig = this.cloneConfig(config.config ?? state.definition.defaultConfig ?? {});
|
||||||
this.commitExtensionState(state, config.enabled, resolvedConfig);
|
this.commitExtensionState(state, config.enabled ?? false, resolvedConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ export class Manager {
|
|||||||
this.applyAllExtensionsToView(view);
|
this.applyAllExtensionsToView(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateExtension(id: ExtensionID, enabled: boolean, config?: any): void {
|
updateExtension(id: string, enabled: boolean, config?: any): void {
|
||||||
const state = this.extensionStates.get(id);
|
const state = this.extensionStates.get(id);
|
||||||
if (!state) return;
|
if (!state) return;
|
||||||
|
|
||||||
@@ -93,7 +94,7 @@ export class Manager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private applyExtensionToAllViews(id: ExtensionID): void {
|
private applyExtensionToAllViews(id: string): void {
|
||||||
const state = this.extensionStates.get(id);
|
const state = this.extensionStates.get(id);
|
||||||
if (!state) return;
|
if (!state) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,23 @@
|
|||||||
import {Compartment, Extension} from '@codemirror/state';
|
import {Compartment, Extension} from '@codemirror/state';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩展定义
|
||||||
|
* 标准化 create 方法和默认配置
|
||||||
|
*/
|
||||||
|
export interface ExtensionDefinition {
|
||||||
|
create(config: any): Extension
|
||||||
|
defaultConfig: Record<string, any>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩展运行时状态
|
||||||
|
*/
|
||||||
|
export interface ExtensionState {
|
||||||
|
id: string // 扩展 key
|
||||||
|
definition: ExtensionDefinition
|
||||||
|
config: any
|
||||||
|
enabled: boolean
|
||||||
|
compartment: Compartment
|
||||||
|
extension: Extension
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type {ThemeColors} from './types';
|
import type {ThemeColors} from './types';
|
||||||
import {ThemeType} from '@/../bindings/voidraft/internal/models/models';
|
import {Type as ThemeType} from '@/../bindings/voidraft/internal/models/ent/theme/models';
|
||||||
import {defaultDarkColors} from './dark/default-dark';
|
import {defaultDarkColors} from './dark/default-dark';
|
||||||
import {defaultLightColors} from './light/default-light';
|
import {defaultLightColors} from './light/default-light';
|
||||||
import {config as draculaColors} from './dark/dracula';
|
import {config as draculaColors} from './dark/dracula';
|
||||||
@@ -24,20 +24,20 @@ export interface ThemePreset {
|
|||||||
export const FALLBACK_THEME_NAME = defaultDarkColors.themeName;
|
export const FALLBACK_THEME_NAME = defaultDarkColors.themeName;
|
||||||
|
|
||||||
export const themePresetList: ThemePreset[] = [
|
export const themePresetList: ThemePreset[] = [
|
||||||
{name: defaultDarkColors.themeName, type: ThemeType.ThemeTypeDark, colors: defaultDarkColors},
|
{name: defaultDarkColors.themeName, type: ThemeType.TypeDark, colors: defaultDarkColors},
|
||||||
{name: draculaColors.themeName, type: ThemeType.ThemeTypeDark, colors: draculaColors},
|
{name: draculaColors.themeName, type: ThemeType.TypeDark, colors: draculaColors},
|
||||||
{name: auraColors.themeName, type: ThemeType.ThemeTypeDark, colors: auraColors},
|
{name: auraColors.themeName, type: ThemeType.TypeDark, colors: auraColors},
|
||||||
{name: githubDarkColors.themeName, type: ThemeType.ThemeTypeDark, colors: githubDarkColors},
|
{name: githubDarkColors.themeName, type: ThemeType.TypeDark, colors: githubDarkColors},
|
||||||
{name: materialDarkColors.themeName, type: ThemeType.ThemeTypeDark, colors: materialDarkColors},
|
{name: materialDarkColors.themeName, type: ThemeType.TypeDark, colors: materialDarkColors},
|
||||||
{name: oneDarkColors.themeName, type: ThemeType.ThemeTypeDark, colors: oneDarkColors},
|
{name: oneDarkColors.themeName, type: ThemeType.TypeDark, colors: oneDarkColors},
|
||||||
{name: solarizedDarkColors.themeName, type: ThemeType.ThemeTypeDark, colors: solarizedDarkColors},
|
{name: solarizedDarkColors.themeName, type: ThemeType.TypeDark, colors: solarizedDarkColors},
|
||||||
{name: tokyoNightColors.themeName, type: ThemeType.ThemeTypeDark, colors: tokyoNightColors},
|
{name: tokyoNightColors.themeName, type: ThemeType.TypeDark, colors: tokyoNightColors},
|
||||||
{name: tokyoNightStormColors.themeName, type: ThemeType.ThemeTypeDark, colors: tokyoNightStormColors},
|
{name: tokyoNightStormColors.themeName, type: ThemeType.TypeDark, colors: tokyoNightStormColors},
|
||||||
{name: defaultLightColors.themeName, type: ThemeType.ThemeTypeLight, colors: defaultLightColors},
|
{name: defaultLightColors.themeName, type: ThemeType.TypeLight, colors: defaultLightColors},
|
||||||
{name: githubLightColors.themeName, type: ThemeType.ThemeTypeLight, colors: githubLightColors},
|
{name: githubLightColors.themeName, type: ThemeType.TypeLight, colors: githubLightColors},
|
||||||
{name: materialLightColors.themeName, type: ThemeType.ThemeTypeLight, colors: materialLightColors},
|
{name: materialLightColors.themeName, type: ThemeType.TypeLight, colors: materialLightColors},
|
||||||
{name: solarizedLightColors.themeName, type: ThemeType.ThemeTypeLight, colors: solarizedLightColors},
|
{name: solarizedLightColors.themeName, type: ThemeType.TypeLight, colors: solarizedLightColors},
|
||||||
{name: tokyoNightDayColors.themeName, type: ThemeType.ThemeTypeLight, colors: tokyoNightDayColors},
|
{name: tokyoNightDayColors.themeName, type: ThemeType.TypeLight, colors: tokyoNightDayColors},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const themePresetMap: Record<string, ThemePreset> = themePresetList.reduce(
|
export const themePresetMap: Record<string, ThemePreset> = themePresetList.reduce(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import SettingSection from '../components/SettingSection.vue';
|
|||||||
import SettingItem from '../components/SettingItem.vue';
|
import SettingItem from '../components/SettingItem.vue';
|
||||||
import { SystemThemeType, LanguageType } from '@/../bindings/voidraft/internal/models/models';
|
import { SystemThemeType, LanguageType } from '@/../bindings/voidraft/internal/models/models';
|
||||||
import { createDebounce } from '@/common/utils/debounce';
|
import { createDebounce } from '@/common/utils/debounce';
|
||||||
import { createTimerManager } from '@/common/utils/timerUtils';
|
import { useConfirm } from '@/composables/useConfirm';
|
||||||
import PickColors from 'vue-pick-colors';
|
import PickColors from 'vue-pick-colors';
|
||||||
import type { ThemeColors } from '@/views/editor/theme/types';
|
import type { ThemeColors } from '@/views/editor/theme/types';
|
||||||
|
|
||||||
@@ -21,31 +21,22 @@ const { debouncedFn: debouncedUpdateColor } = createDebounce(
|
|||||||
{ delay: 100 }
|
{ delay: 100 }
|
||||||
);
|
);
|
||||||
|
|
||||||
const { debouncedFn: debouncedResetTheme } = createDebounce(
|
|
||||||
async () => {
|
|
||||||
const success = await themeStore.resetCurrentTheme();
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
// 重新加载临时颜色
|
|
||||||
syncTempColors();
|
|
||||||
hasUnsavedChanges.value = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ delay: 300 }
|
|
||||||
);
|
|
||||||
|
|
||||||
// 创建定时器管理器
|
|
||||||
const resetTimer = createTimerManager();
|
|
||||||
|
|
||||||
// 临时颜色状态(用于编辑)
|
// 临时颜色状态(用于编辑)
|
||||||
const tempColors = ref<ThemeColors | null>(null);
|
const tempColors = ref<ThemeColors | null>(null);
|
||||||
|
|
||||||
// 标记是否有未保存的更改
|
// 标记是否有未保存的更改
|
||||||
const hasUnsavedChanges = ref(false);
|
const hasUnsavedChanges = ref(false);
|
||||||
|
|
||||||
// 重置按钮状态
|
// 重置主题确认
|
||||||
const resetButtonState = ref({
|
const { isConfirming: isResetConfirming, requestConfirm: requestResetConfirm } = useConfirm({
|
||||||
confirming: false
|
timeout: 3000,
|
||||||
|
onConfirm: async () => {
|
||||||
|
const success = await themeStore.resetCurrentTheme();
|
||||||
|
if (success) {
|
||||||
|
syncTempColors();
|
||||||
|
hasUnsavedChanges.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 当前选中的主题名称
|
// 当前选中的主题名称
|
||||||
@@ -125,23 +116,6 @@ const toggleSearch = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理重置按钮点击
|
|
||||||
const handleResetClick = () => {
|
|
||||||
if (resetButtonState.value.confirming) {
|
|
||||||
|
|
||||||
debouncedResetTheme();
|
|
||||||
|
|
||||||
resetButtonState.value.confirming = false;
|
|
||||||
resetTimer.clear();
|
|
||||||
} else {
|
|
||||||
resetButtonState.value.confirming = true;
|
|
||||||
// 设置3秒后自动恢复
|
|
||||||
resetTimer.set(() => {
|
|
||||||
resetButtonState.value.confirming = false;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 更新本地颜色配置
|
// 更新本地颜色配置
|
||||||
const updateLocalColor = (colorKey: string, value: string) => {
|
const updateLocalColor = (colorKey: string, value: string) => {
|
||||||
if (!tempColors.value) return;
|
if (!tempColors.value) return;
|
||||||
@@ -295,10 +269,10 @@ const handlePickerClose = () => {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="!hasUnsavedChanges"
|
v-if="!hasUnsavedChanges"
|
||||||
:class="['reset-button', resetButtonState.confirming ? 'reset-button-confirming' : '']"
|
:class="['reset-button', isResetConfirming('theme') ? 'reset-button-confirming' : '']"
|
||||||
@click="handleResetClick"
|
@click="requestResetConfirm('theme')"
|
||||||
>
|
>
|
||||||
{{ resetButtonState.confirming ? t('settings.confirmReset') : t('settings.resetToDefault') }}
|
{{ isResetConfirming('theme') ? t('settings.confirmReset') : t('settings.resetToDefault') }}
|
||||||
</button>
|
</button>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<button class="apply-button" @click="applyChanges">
|
<button class="apply-button" @click="applyChanges">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import {useConfigStore} from '@/stores/configStore';
|
import {useConfigStore} from '@/stores/configStore';
|
||||||
import {useBackupStore} from '@/stores/backupStore';
|
import {useBackupStore} from '@/stores/backupStore';
|
||||||
import {useI18n} from 'vue-i18n';
|
import {useI18n} from 'vue-i18n';
|
||||||
import {computed} from 'vue';
|
import {computed, ref, watch, onUnmounted} from 'vue';
|
||||||
import SettingSection from '../components/SettingSection.vue';
|
import SettingSection from '../components/SettingSection.vue';
|
||||||
import SettingItem from '../components/SettingItem.vue';
|
import SettingItem from '../components/SettingItem.vue';
|
||||||
import ToggleSwitch from '../components/ToggleSwitch.vue';
|
import ToggleSwitch from '../components/ToggleSwitch.vue';
|
||||||
@@ -13,6 +13,37 @@ const {t} = useI18n();
|
|||||||
const configStore = useConfigStore();
|
const configStore = useConfigStore();
|
||||||
const backupStore = useBackupStore();
|
const backupStore = useBackupStore();
|
||||||
|
|
||||||
|
// 消息显示状态
|
||||||
|
const message = ref<string | null>(null);
|
||||||
|
const isError = ref(false);
|
||||||
|
let messageTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
|
const clearMessage = () => {
|
||||||
|
if (messageTimer) {
|
||||||
|
clearTimeout(messageTimer);
|
||||||
|
messageTimer = null;
|
||||||
|
}
|
||||||
|
message.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听同步完成,显示消息并自动消失
|
||||||
|
watch(() => backupStore.isSyncing, (syncing, wasSyncing) => {
|
||||||
|
if (wasSyncing && !syncing) {
|
||||||
|
clearMessage();
|
||||||
|
if (backupStore.error) {
|
||||||
|
message.value = backupStore.error;
|
||||||
|
isError.value = true;
|
||||||
|
messageTimer = setTimeout(clearMessage, 5000);
|
||||||
|
} else {
|
||||||
|
message.value = 'Sync successful';
|
||||||
|
isError.value = false;
|
||||||
|
messageTimer = setTimeout(clearMessage, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(clearMessage);
|
||||||
|
|
||||||
const authMethodOptions = computed(() => [
|
const authMethodOptions = computed(() => [
|
||||||
{value: AuthMethod.Token, label: t('settings.backup.authMethods.token')},
|
{value: AuthMethod.Token, label: t('settings.backup.authMethods.token')},
|
||||||
{value: AuthMethod.SSHKey, label: t('settings.backup.authMethods.sshKey')},
|
{value: AuthMethod.SSHKey, label: t('settings.backup.authMethods.sshKey')},
|
||||||
@@ -172,18 +203,18 @@ const selectSshKeyFile = async () => {
|
|||||||
<!-- 备份操作 -->
|
<!-- 备份操作 -->
|
||||||
<SettingSection :title="t('settings.backup.backupOperations')">
|
<SettingSection :title="t('settings.backup.backupOperations')">
|
||||||
<SettingItem
|
<SettingItem
|
||||||
:title="t('settings.backup.pushToRemote')"
|
:title="t('settings.backup.syncToRemote')"
|
||||||
:description="backupStore.message || undefined"
|
:description="message || undefined"
|
||||||
:descriptionType="backupStore.message ? (backupStore.isError ? 'error' : 'success') : 'default'"
|
:descriptionType="message ? (isError ? 'error' : 'success') : 'default'"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="push-button"
|
class="sync-button"
|
||||||
@click="backupStore.pushToRemote"
|
@click="backupStore.sync"
|
||||||
:disabled="!configStore.config.backup.enabled || !configStore.config.backup.repo_url || backupStore.isPushing"
|
:disabled="!configStore.config.backup.enabled || !configStore.config.backup.repo_url || backupStore.isSyncing"
|
||||||
:class="{ 'backing-up': backupStore.isPushing }"
|
:class="{ 'syncing': backupStore.isSyncing }"
|
||||||
>
|
>
|
||||||
<span v-if="backupStore.isPushing" class="loading-spinner"></span>
|
<span v-if="backupStore.isSyncing" class="loading-spinner"></span>
|
||||||
{{ backupStore.isPushing ? t('settings.backup.pushing') : t('settings.backup.actions.push') }}
|
{{ backupStore.isSyncing ? t('settings.backup.syncing') : t('settings.backup.actions.sync') }}
|
||||||
</button>
|
</button>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
</SettingSection>
|
</SettingSection>
|
||||||
@@ -257,7 +288,7 @@ const selectSshKeyFile = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 按钮样式
|
// 按钮样式
|
||||||
.push-button {
|
.sync-button {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
background-color: var(--settings-input-bg);
|
background-color: var(--settings-input-bg);
|
||||||
border: 1px solid var(--settings-input-border);
|
border: 1px solid var(--settings-input-border);
|
||||||
@@ -294,7 +325,7 @@ const selectSshKeyFile = async () => {
|
|||||||
animation: spin 1s linear infinite;
|
animation: spin 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.backing-up {
|
&.syncing {
|
||||||
background-color: #2196f3;
|
background-color: #2196f3;
|
||||||
border-color: #2196f3;
|
border-color: #2196f3;
|
||||||
color: white;
|
color: white;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {useI18n} from 'vue-i18n';
|
|||||||
import {useEditorStore} from '@/stores/editorStore';
|
import {useEditorStore} from '@/stores/editorStore';
|
||||||
import {useExtensionStore} from '@/stores/extensionStore';
|
import {useExtensionStore} from '@/stores/extensionStore';
|
||||||
import {ExtensionService} from '@/../bindings/voidraft/internal/services';
|
import {ExtensionService} from '@/../bindings/voidraft/internal/services';
|
||||||
import {ExtensionID} from '@/../bindings/voidraft/internal/models/models';
|
|
||||||
import {
|
import {
|
||||||
getAllExtensionIds,
|
getAllExtensionIds,
|
||||||
getExtensionDefaultConfig,
|
getExtensionDefaultConfig,
|
||||||
@@ -21,59 +20,58 @@ const editorStore = useEditorStore();
|
|||||||
const extensionStore = useExtensionStore();
|
const extensionStore = useExtensionStore();
|
||||||
|
|
||||||
// 展开状态管理
|
// 展开状态管理
|
||||||
const expandedExtensions = ref<Set<ExtensionID>>(new Set());
|
const expandedExtensions = ref<Set<string>>(new Set());
|
||||||
|
|
||||||
// 获取所有可用的扩展
|
// 获取所有可用的扩展
|
||||||
const availableExtensions = computed(() => {
|
const availableExtensions = computed(() => {
|
||||||
return getAllExtensionIds().map(id => {
|
return getAllExtensionIds().map(key => {
|
||||||
const extension = extensionStore.extensions.find(ext => ext.id === id);
|
const extension = extensionStore.extensions.find(ext => ext.key === key);
|
||||||
return {
|
return {
|
||||||
id,
|
id: key,
|
||||||
displayName: getExtensionDisplayName(id),
|
displayName: getExtensionDisplayName(key),
|
||||||
description: getExtensionDescription(id),
|
description: getExtensionDescription(key),
|
||||||
enabled: extension?.enabled || false,
|
enabled: extension?.enabled || false,
|
||||||
isDefault: extension?.isDefault || false,
|
hasConfig: hasExtensionConfig(key),
|
||||||
hasConfig: hasExtensionConfig(id),
|
|
||||||
config: extension?.config || {},
|
config: extension?.config || {},
|
||||||
defaultConfig: getExtensionDefaultConfig(id)
|
defaultConfig: getExtensionDefaultConfig(key)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 切换展开状态
|
// 切换展开状态
|
||||||
const toggleExpanded = (extensionId: ExtensionID) => {
|
const toggleExpanded = (extensionKey: string) => {
|
||||||
if (expandedExtensions.value.has(extensionId)) {
|
if (expandedExtensions.value.has(extensionKey)) {
|
||||||
expandedExtensions.value.delete(extensionId);
|
expandedExtensions.value.delete(extensionKey);
|
||||||
} else {
|
} else {
|
||||||
expandedExtensions.value.add(extensionId);
|
expandedExtensions.value.add(extensionKey);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新扩展状态
|
// 更新扩展状态
|
||||||
const updateExtension = async (extensionId: ExtensionID, enabled: boolean) => {
|
const updateExtension = async (extensionKey: string, enabled: boolean) => {
|
||||||
try {
|
try {
|
||||||
await editorStore.updateExtension(extensionId, enabled);
|
await editorStore.updateExtension(extensionKey, enabled);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update extension:', error);
|
console.error('Failed to update extension:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新扩展配置
|
// 更新扩展配置
|
||||||
const updateExtensionConfig = async (extensionId: ExtensionID, configKey: string, value: any) => {
|
const updateExtensionConfig = async (extensionKey: string, configKey: string, value: any) => {
|
||||||
try {
|
try {
|
||||||
// 获取当前扩展状态
|
// 获取当前扩展状态
|
||||||
const extension = extensionStore.extensions.find(ext => ext.id === extensionId);
|
const extension = extensionStore.extensions.find(ext => ext.key === extensionKey);
|
||||||
if (!extension) return;
|
if (!extension) return;
|
||||||
|
|
||||||
// 更新配置
|
// 更新配置
|
||||||
const updatedConfig = {...extension.config};
|
const updatedConfig = {...(extension.config || {})};
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
delete updatedConfig[configKey];
|
delete updatedConfig[configKey];
|
||||||
} else {
|
} else {
|
||||||
updatedConfig[configKey] = value;
|
updatedConfig[configKey] = value;
|
||||||
}
|
}
|
||||||
// 使用editorStore的updateExtension方法更新,确保应用到所有编辑器实例
|
// 使用editorStore的updateExtension方法更新,确保应用到所有编辑器实例
|
||||||
await editorStore.updateExtension(extensionId, extension.enabled, updatedConfig);
|
await editorStore.updateExtension(extensionKey, extension.enabled ?? false, updatedConfig);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update extension config:', error);
|
console.error('Failed to update extension config:', error);
|
||||||
@@ -81,19 +79,19 @@ const updateExtensionConfig = async (extensionId: ExtensionID, configKey: string
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 重置扩展到默认配置
|
// 重置扩展到默认配置
|
||||||
const resetExtension = async (extensionId: ExtensionID) => {
|
const resetExtension = async (extensionKey: string) => {
|
||||||
try {
|
try {
|
||||||
// 重置到默认配置
|
// 重置到默认配置
|
||||||
await ExtensionService.ResetExtensionToDefault(extensionId);
|
await ExtensionService.ResetExtensionConfig(extensionKey);
|
||||||
|
|
||||||
// 重新加载扩展状态以获取最新配置
|
// 重新加载扩展状态以获取最新配置
|
||||||
await extensionStore.loadExtensions();
|
await extensionStore.loadExtensions();
|
||||||
|
|
||||||
// 获取重置后的状态,立即应用到所有编辑器视图
|
// 获取重置后的状态,立即应用到所有编辑器视图
|
||||||
const extension = extensionStore.extensions.find(ext => ext.id === extensionId);
|
const extension = extensionStore.extensions.find(ext => ext.key === extensionKey);
|
||||||
if (extension) {
|
if (extension) {
|
||||||
// 通过editorStore更新,确保所有视图都能同步
|
// 通过editorStore更新,确保所有视图都能同步
|
||||||
await editorStore.updateExtension(extensionId, extension.enabled, extension.config);
|
await editorStore.updateExtension(extensionKey, extension.enabled ?? false, extension.config);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to reset extension:', error);
|
console.error('Failed to reset extension:', error);
|
||||||
@@ -127,7 +125,7 @@ const formatConfigValue = (value: any): string => {
|
|||||||
|
|
||||||
|
|
||||||
const handleConfigInput = async (
|
const handleConfigInput = async (
|
||||||
extensionId: ExtensionID,
|
extensionKey: string,
|
||||||
configKey: string,
|
configKey: string,
|
||||||
defaultValue: any,
|
defaultValue: any,
|
||||||
event: Event
|
event: Event
|
||||||
@@ -137,15 +135,15 @@ const handleConfigInput = async (
|
|||||||
const rawValue = target.value;
|
const rawValue = target.value;
|
||||||
const trimmedValue = rawValue.trim();
|
const trimmedValue = rawValue.trim();
|
||||||
if (!trimmedValue.length) {
|
if (!trimmedValue.length) {
|
||||||
await updateExtensionConfig(extensionId, configKey, undefined);
|
await updateExtensionConfig(extensionKey, configKey, undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsedValue = JSON.parse(trimmedValue);
|
const parsedValue = JSON.parse(trimmedValue);
|
||||||
await updateExtensionConfig(extensionId, configKey, parsedValue);
|
await updateExtensionConfig(extensionKey, configKey, parsedValue);
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
const extension = extensionStore.extensions.find(ext => ext.id === extensionId);
|
const extension = extensionStore.extensions.find(ext => ext.key === extensionKey);
|
||||||
const fallbackValue = getConfigValue(extension?.config, configKey, defaultValue);
|
const fallbackValue = getConfigValue(extension?.config, configKey, defaultValue);
|
||||||
target.value = formatConfigValue(fallbackValue);
|
target.value = formatConfigValue(fallbackValue);
|
||||||
|
|
||||||
@@ -238,10 +236,6 @@ const handleConfigInput = async (
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.settings-page {
|
|
||||||
//max-width: 1000px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.extension-item {
|
.extension-item {
|
||||||
border-bottom: 1px solid var(--settings-input-border);
|
border-bottom: 1px solid var(--settings-input-border);
|
||||||
|
|
||||||
@@ -296,36 +290,38 @@ const handleConfigInput = async (
|
|||||||
|
|
||||||
.extension-config {
|
.extension-config {
|
||||||
background-color: var(--settings-input-bg);
|
background-color: var(--settings-input-bg);
|
||||||
border-left: 3px solid var(--settings-accent);
|
border-left: 2px solid var(--settings-accent);
|
||||||
margin: 8px 0 16px 0;
|
margin: 4px 0 12px 0;
|
||||||
padding: 12px;
|
padding: 8px 10px;
|
||||||
border-radius: 6px;
|
border-radius: 2px;
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-header {
|
.config-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-title {
|
.config-title {
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: var(--settings-text);
|
color: var(--settings-text);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reset-button {
|
.reset-button {
|
||||||
padding: 6px 12px;
|
padding: 3px 8px;
|
||||||
font-size: 11px;
|
font-size: 12px;
|
||||||
border: 1px solid var(--settings-input-border);
|
border: 1px solid var(--settings-input-border);
|
||||||
border-radius: 4px;
|
border-radius: 2px;
|
||||||
background-color: var(--settings-input-bg);
|
background-color: transparent;
|
||||||
color: var(--settings-text-secondary);
|
color: var(--settings-text-secondary);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.15s ease;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -337,8 +333,7 @@ const handleConfigInput = async (
|
|||||||
|
|
||||||
.config-table-wrapper {
|
.config-table-wrapper {
|
||||||
border: 1px solid var(--settings-input-border);
|
border: 1px solid var(--settings-input-border);
|
||||||
border-radius: 6px;
|
border-radius: 2px;
|
||||||
margin-top: 8px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: var(--settings-panel, var(--settings-input-bg));
|
background-color: var(--settings-panel, var(--settings-input-bg));
|
||||||
}
|
}
|
||||||
@@ -346,7 +341,7 @@ const handleConfigInput = async (
|
|||||||
.config-table {
|
.config-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
font-size: 12px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-table tr + tr {
|
.config-table tr + tr {
|
||||||
@@ -355,38 +350,41 @@ const handleConfigInput = async (
|
|||||||
|
|
||||||
.config-table th,
|
.config-table th,
|
||||||
.config-table td {
|
.config-table td {
|
||||||
padding: 10px 12px;
|
padding: 5px 8px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-table-key {
|
.config-table-key {
|
||||||
width: 36%;
|
width: 30%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: var(--settings-text-secondary);
|
color: var(--settings-text-secondary);
|
||||||
border-right: 1px solid var(--settings-input-border);
|
border-right: 1px solid var(--settings-input-border);
|
||||||
background-color: var(--settings-input-bg);
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, monospace;
|
||||||
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-table-value {
|
.config-table-value {
|
||||||
padding: 6px;
|
padding: 3px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-value-input {
|
.config-value-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 8px 10px;
|
padding: 4px 6px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 4px;
|
border-radius: 2px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--settings-text);
|
color: var(--settings-text);
|
||||||
font-size: 12px;
|
font-size: 11px;
|
||||||
line-height: 1.4;
|
font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, monospace;
|
||||||
|
line-height: 1.3;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
transition: border-color 0.2s ease, background-color 0.2s ease;
|
transition: border-color 0.15s ease, background-color 0.15s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-value-input:hover {
|
.config-value-input:hover {
|
||||||
border-color: var(--settings-hover-border, var(--settings-input-border));
|
border-color: var(--settings-input-border);
|
||||||
background-color: var(--settings-hover);
|
background-color: var(--settings-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,133 +2,67 @@
|
|||||||
import {useConfigStore} from '@/stores/configStore';
|
import {useConfigStore} from '@/stores/configStore';
|
||||||
import {useTabStore} from '@/stores/tabStore';
|
import {useTabStore} from '@/stores/tabStore';
|
||||||
import {useI18n} from 'vue-i18n';
|
import {useI18n} from 'vue-i18n';
|
||||||
import {computed, onUnmounted, ref} from 'vue';
|
import {computed, ref} from 'vue';
|
||||||
import SettingSection from '../components/SettingSection.vue';
|
import SettingSection from '../components/SettingSection.vue';
|
||||||
import SettingItem from '../components/SettingItem.vue';
|
import SettingItem from '../components/SettingItem.vue';
|
||||||
import ToggleSwitch from '../components/ToggleSwitch.vue';
|
import ToggleSwitch from '../components/ToggleSwitch.vue';
|
||||||
import {
|
import {DialogService, MigrationService} from '@/../bindings/voidraft/internal/services';
|
||||||
DialogService,
|
|
||||||
MigrationProgress,
|
|
||||||
MigrationService,
|
|
||||||
MigrationStatus
|
|
||||||
} from '@/../bindings/voidraft/internal/services';
|
|
||||||
import {useSystemStore} from "@/stores/systemStore";
|
import {useSystemStore} from "@/stores/systemStore";
|
||||||
|
import {useConfirm, usePolling} from '@/composables';
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const configStore = useConfigStore();
|
const configStore = useConfigStore();
|
||||||
const systemStore = useSystemStore();
|
const systemStore = useSystemStore();
|
||||||
const tabStore = useTabStore();
|
const tabStore = useTabStore();
|
||||||
// 迁移进度状态
|
|
||||||
const migrationProgress = ref<MigrationProgress>(new MigrationProgress({
|
|
||||||
status: MigrationStatus.MigrationStatusCompleted,
|
|
||||||
progress: 0
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 轮询相关
|
|
||||||
let pollingTimer: number | null = null;
|
|
||||||
const isPolling = ref(false);
|
|
||||||
|
|
||||||
// 进度条显示控制
|
// 进度条显示控制
|
||||||
const showProgress = ref(false);
|
const showBar = ref(false);
|
||||||
const progressError = ref('');
|
const manualError = ref(''); // 用于捕获 MigrateDirectory 抛出的错误
|
||||||
let hideProgressTimer: any = null;
|
let hideTimer = 0;
|
||||||
|
|
||||||
// 开始轮询迁移进度
|
// 轮询迁移进度
|
||||||
const startPolling = () => {
|
const {data: progress, error: pollError, isActive: migrating, start, stop, reset} = usePolling(
|
||||||
if (isPolling.value) return;
|
() => MigrationService.GetProgress(),
|
||||||
|
{
|
||||||
isPolling.value = true;
|
interval: 300,
|
||||||
showProgress.value = true;
|
shouldStop: ({progress, error}) => !!error || progress >= 100,
|
||||||
progressError.value = '';
|
onStop: () => {
|
||||||
|
const hasError = pollError.value || progress.value?.error;
|
||||||
// 立即重置迁移进度状态,避免从之前的失败状态渐变
|
hideTimer = window.setTimeout(hideAll, hasError ? 5000 : 3000);
|
||||||
migrationProgress.value = new MigrationProgress({
|
|
||||||
status: MigrationStatus.MigrationStatusMigrating,
|
|
||||||
progress: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
pollingTimer = window.setInterval(async () => {
|
|
||||||
try {
|
|
||||||
const progress = await MigrationService.GetProgress();
|
|
||||||
migrationProgress.value = progress;
|
|
||||||
|
|
||||||
const {status, error} = progress;
|
|
||||||
const isCompleted = [MigrationStatus.MigrationStatusCompleted, MigrationStatus.MigrationStatusFailed].includes(status);
|
|
||||||
|
|
||||||
if (isCompleted) {
|
|
||||||
stopPolling();
|
|
||||||
|
|
||||||
// 设置错误信息(如果是失败状态)
|
|
||||||
progressError.value = (status === MigrationStatus.MigrationStatusFailed) ? (error || 'Migration failed') : '';
|
|
||||||
|
|
||||||
const delay = status === MigrationStatus.MigrationStatusCompleted ? 3000 : 5000;
|
|
||||||
hideProgressTimer = setTimeout(hideProgress, delay);
|
|
||||||
}
|
}
|
||||||
} catch (_error) {
|
|
||||||
stopPolling();
|
|
||||||
|
|
||||||
// 使用常量简化错误处理
|
|
||||||
const errorMsg = 'Failed to get migration progress';
|
|
||||||
Object.assign(migrationProgress.value, {
|
|
||||||
status: MigrationStatus.MigrationStatusFailed,
|
|
||||||
progress: 0,
|
|
||||||
error: errorMsg
|
|
||||||
});
|
|
||||||
progressError.value = errorMsg;
|
|
||||||
|
|
||||||
hideProgressTimer = setTimeout(hideProgress, 5000);
|
|
||||||
}
|
}
|
||||||
}, 200);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 停止轮询
|
|
||||||
const stopPolling = () => {
|
|
||||||
if (pollingTimer) {
|
|
||||||
clearInterval(pollingTimer);
|
|
||||||
pollingTimer = null;
|
|
||||||
}
|
|
||||||
isPolling.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 隐藏进度条
|
|
||||||
const hideProgress = () => {
|
|
||||||
showProgress.value = false;
|
|
||||||
progressError.value = '';
|
|
||||||
|
|
||||||
// 重置迁移状态,避免下次显示时状态不正确
|
|
||||||
migrationProgress.value = new MigrationProgress({
|
|
||||||
status: MigrationStatus.MigrationStatusCompleted,
|
|
||||||
progress: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hideProgressTimer) {
|
|
||||||
clearTimeout(hideProgressTimer);
|
|
||||||
hideProgressTimer = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 简化的迁移状态管理
|
|
||||||
const isMigrating = computed(() => migrationProgress.value.status === MigrationStatus.MigrationStatusMigrating);
|
|
||||||
|
|
||||||
// 进度条样式 - 使用 Map 简化条件判断
|
|
||||||
const statusClassMap = new Map([
|
|
||||||
[MigrationStatus.MigrationStatusMigrating, 'migrating'],
|
|
||||||
[MigrationStatus.MigrationStatusCompleted, 'success'],
|
|
||||||
[MigrationStatus.MigrationStatusFailed, 'error']
|
|
||||||
]);
|
|
||||||
|
|
||||||
const progressBarClass = computed(() =>
|
|
||||||
showProgress.value ? statusClassMap.get(migrationProgress.value.status) ?? '' : ''
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const progressBarWidth = computed(() => {
|
// 派生状态
|
||||||
if (!showProgress.value) return '0%';
|
const migrationError = computed(() => manualError.value || pollError.value || progress.value?.error || '');
|
||||||
return isMigrating.value ? `${migrationProgress.value.progress}%` : '100%';
|
const currentProgress = computed(() => progress.value?.progress ?? 0);
|
||||||
|
|
||||||
|
const barClass = computed(() => {
|
||||||
|
if (!showBar.value) return '';
|
||||||
|
return migrationError.value ? 'error' : currentProgress.value >= 100 ? 'success' : 'migrating';
|
||||||
});
|
});
|
||||||
|
|
||||||
// 重置确认状态
|
const barWidth = computed(() => {
|
||||||
const resetConfirmState = ref<'idle' | 'confirming'>('idle');
|
if (!showBar.value) return '0%';
|
||||||
let resetConfirmTimer: any = null;
|
return (migrationError.value || currentProgress.value >= 100) ? '100%' : `${currentProgress.value}%`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 隐藏进度条并清除所有状态
|
||||||
|
const hideAll = () => {
|
||||||
|
clearTimeout(hideTimer);
|
||||||
|
hideTimer = 0;
|
||||||
|
showBar.value = false;
|
||||||
|
manualError.value = '';
|
||||||
|
reset(); // 清除轮询状态
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重置设置确认
|
||||||
|
const {isConfirming: isResetConfirming, requestConfirm: requestResetConfirm} = useConfirm({
|
||||||
|
timeout: 3000,
|
||||||
|
onConfirm: async () => {
|
||||||
|
await configStore.resetConfig();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 可选键列表
|
// 可选键列表
|
||||||
const keyOptions = [
|
const keyOptions = [
|
||||||
@@ -201,7 +135,7 @@ const modifierKeys = computed(() => ({
|
|||||||
win: configStore.config.general.globalHotkey.win
|
win: configStore.config.general.globalHotkey.win
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 主键配置 - 只读计算属性
|
// 主键配置
|
||||||
const selectedKey = computed(() => configStore.config.general.globalHotkey.key);
|
const selectedKey = computed(() => configStore.config.general.globalHotkey.key);
|
||||||
|
|
||||||
// 切换修饰键
|
// 切换修饰键
|
||||||
@@ -218,29 +152,8 @@ const updateSelectedKey = (event: Event) => {
|
|||||||
configStore.setGlobalHotkey(newHotkey);
|
configStore.setGlobalHotkey(newHotkey);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重置设置
|
|
||||||
const resetSettings = () => {
|
|
||||||
if (resetConfirmState.value === 'idle') {
|
|
||||||
// 第一次点击,进入确认状态
|
|
||||||
resetConfirmState.value = 'confirming';
|
|
||||||
// 3秒后自动返回idle状态
|
|
||||||
resetConfirmTimer = setTimeout(() => {
|
|
||||||
resetConfirmState.value = 'idle';
|
|
||||||
}, 3000);
|
|
||||||
} else if (resetConfirmState.value === 'confirming') {
|
|
||||||
// 第二次点击,执行重置
|
|
||||||
clearTimeout(resetConfirmTimer);
|
|
||||||
resetConfirmState.value = 'idle';
|
|
||||||
confirmReset();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 确认重置
|
// 计算热键预览文本
|
||||||
const confirmReset = async () => {
|
|
||||||
await configStore.resetConfig();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 计算热键预览文本 - 使用现代语法简化
|
|
||||||
const hotkeyPreview = computed(() => {
|
const hotkeyPreview = computed(() => {
|
||||||
if (!enableGlobalHotkey.value) return '';
|
if (!enableGlobalHotkey.value) return '';
|
||||||
|
|
||||||
@@ -261,54 +174,30 @@ const currentDataPath = computed(() => configStore.config.general.dataPath);
|
|||||||
|
|
||||||
// 选择数据存储目录
|
// 选择数据存储目录
|
||||||
const selectDataDirectory = async () => {
|
const selectDataDirectory = async () => {
|
||||||
if (isMigrating.value) return;
|
if (migrating.value) return;
|
||||||
|
|
||||||
|
|
||||||
const selectedPath = await DialogService.SelectDirectory();
|
const selectedPath = await DialogService.SelectDirectory();
|
||||||
|
if (!selectedPath?.trim() || selectedPath === currentDataPath.value) return;
|
||||||
|
|
||||||
// 检查用户是否取消了选择或路径为空
|
const [oldPath, newPath] = [currentDataPath.value, selectedPath.trim()];
|
||||||
if (!selectedPath || !selectedPath.trim() || selectedPath === currentDataPath.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const oldPath = currentDataPath.value;
|
|
||||||
const newPath = selectedPath.trim();
|
|
||||||
|
|
||||||
// 清除之前的进度状态
|
// 清除之前的状态并开始轮询
|
||||||
hideProgress();
|
hideAll();
|
||||||
|
showBar.value = true;
|
||||||
|
manualError.value = '';
|
||||||
|
start();
|
||||||
|
|
||||||
// 开始轮询迁移进度
|
|
||||||
startPolling();
|
|
||||||
|
|
||||||
// 开始迁移
|
|
||||||
try {
|
try {
|
||||||
await MigrationService.MigrateDirectory(oldPath, newPath);
|
await MigrationService.MigrateDirectory(oldPath, newPath);
|
||||||
await configStore.setDataPath(newPath);
|
await configStore.setDataPath(newPath);
|
||||||
} catch (error) {
|
} catch (e) {
|
||||||
stopPolling();
|
stop();
|
||||||
|
// 设置手动捕获的错误(当轮询还没获取到错误时)
|
||||||
// 使用解构和默认值简化错误处理
|
manualError.value = String(e).replace(/^Error:\s*/i, '') || 'Migration failed';
|
||||||
const errorMsg = error?.toString() || 'Migration failed';
|
showBar.value = true;
|
||||||
showProgress.value = true;
|
hideTimer = window.setTimeout(hideAll, 5000);
|
||||||
|
|
||||||
Object.assign(migrationProgress.value, {
|
|
||||||
status: MigrationStatus.MigrationStatusFailed,
|
|
||||||
progress: 0,
|
|
||||||
error: errorMsg
|
|
||||||
});
|
|
||||||
progressError.value = errorMsg;
|
|
||||||
|
|
||||||
hideProgressTimer = setTimeout(hideProgress, 5000);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 清理定时器
|
|
||||||
onUnmounted(() => {
|
|
||||||
stopPolling();
|
|
||||||
hideProgress();
|
|
||||||
if (resetConfirmTimer) {
|
|
||||||
clearTimeout(resetConfirmTimer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -394,24 +283,15 @@ onUnmounted(() => {
|
|||||||
class="path-display-input"
|
class="path-display-input"
|
||||||
@click="selectDataDirectory"
|
@click="selectDataDirectory"
|
||||||
:title="t('settings.clickToSelectPath')"
|
:title="t('settings.clickToSelectPath')"
|
||||||
:disabled="isMigrating"
|
:disabled="migrating"
|
||||||
/>
|
/>
|
||||||
<!-- 简洁的进度条 -->
|
<!-- 进度条 -->
|
||||||
<div
|
<div class="progress-bar" :class="[{'active': showBar}, barClass]" :style="{width: barWidth}"/>
|
||||||
class="progress-bar"
|
|
||||||
:class="[
|
|
||||||
{ 'active': showProgress },
|
|
||||||
progressBarClass
|
|
||||||
]"
|
|
||||||
:style="{ width: progressBarWidth }"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 错误提示 -->
|
<!-- 错误提示 -->
|
||||||
<Transition name="error-fade">
|
<Transition name="error-fade">
|
||||||
<div v-if="progressError" class="progress-error">
|
<div v-if="migrationError" class="progress-error">{{ migrationError }}</div>
|
||||||
{{ progressError }}
|
|
||||||
</div>
|
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -421,15 +301,10 @@ onUnmounted(() => {
|
|||||||
<SettingItem :title="t('settings.resetAllSettings')">
|
<SettingItem :title="t('settings.resetAllSettings')">
|
||||||
<button
|
<button
|
||||||
class="reset-button"
|
class="reset-button"
|
||||||
:class="{ 'confirming': resetConfirmState === 'confirming' }"
|
:class="{ 'confirming': isResetConfirming('reset') }"
|
||||||
@click="resetSettings"
|
@click="requestResetConfirm('reset')"
|
||||||
>
|
>
|
||||||
<template v-if="resetConfirmState === 'idle'">
|
{{ isResetConfirming('reset') ? t('settings.confirmReset') : t('settings.reset') }}
|
||||||
{{ t('settings.reset') }}
|
|
||||||
</template>
|
|
||||||
<template v-else-if="resetConfirmState === 'confirming'">
|
|
||||||
{{ t('settings.confirmReset') }}
|
|
||||||
</template>
|
|
||||||
</button>
|
</button>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
</SettingSection>
|
</SettingSection>
|
||||||
@@ -656,11 +531,8 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.progress-error {
|
.progress-error {
|
||||||
margin-top: 6px;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #ef4444;
|
color: #ef4444;
|
||||||
padding: 0 2px;
|
|
||||||
line-height: 1.4;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,43 +3,37 @@ import { useI18n } from 'vue-i18n';
|
|||||||
import { onMounted, computed } from 'vue';
|
import { onMounted, computed } from 'vue';
|
||||||
import SettingSection from '../components/SettingSection.vue';
|
import SettingSection from '../components/SettingSection.vue';
|
||||||
import { useKeybindingStore } from '@/stores/keybindingStore';
|
import { useKeybindingStore } from '@/stores/keybindingStore';
|
||||||
import { useExtensionStore } from '@/stores/extensionStore';
|
|
||||||
import { useSystemStore } from '@/stores/systemStore';
|
import { useSystemStore } from '@/stores/systemStore';
|
||||||
import { getCommandDescription } from '@/views/editor/keymap/commands';
|
import { getCommandDescription } from '@/views/editor/keymap/commands';
|
||||||
import {KeyBindingCommand} from "@/../bindings/voidraft/internal/models";
|
import { KeyBindingKey } from '@/../bindings/voidraft/internal/models/models';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const keybindingStore = useKeybindingStore();
|
const keybindingStore = useKeybindingStore();
|
||||||
const extensionStore = useExtensionStore();
|
|
||||||
const systemStore = useSystemStore();
|
const systemStore = useSystemStore();
|
||||||
|
|
||||||
// 加载数据
|
// 加载数据
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await keybindingStore.loadKeyBindings();
|
await keybindingStore.loadKeyBindings();
|
||||||
await extensionStore.loadExtensions();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 从store中获取快捷键数据并转换为显示格式
|
// 从store中获取快捷键数据并转换为显示格式
|
||||||
const keyBindings = computed(() => {
|
const keyBindings = computed(() => {
|
||||||
// 只显示启用扩展的快捷键
|
|
||||||
const enabledExtensionIds = new Set(extensionStore.enabledExtensionIds);
|
|
||||||
|
|
||||||
return keybindingStore.keyBindings
|
return keybindingStore.keyBindings
|
||||||
.filter(kb => kb.enabled && enabledExtensionIds.has(kb.extension))
|
.filter(kb => kb.enabled)
|
||||||
.map(kb => ({
|
.map(kb => ({
|
||||||
id: kb.command,
|
key: kb.key,
|
||||||
keys: parseKeyBinding(kb.key, kb.command),
|
command: parseKeyBinding(kb.command || '', kb.key),
|
||||||
category: kb.extension,
|
extension: kb.extension || '',
|
||||||
description: getCommandDescription(kb.command) || kb.command
|
description: kb.key ? (getCommandDescription(kb.key) || kb.key) : ''
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
// 解析快捷键字符串为显示数组
|
// 解析快捷键字符串为显示数组
|
||||||
const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
const parseKeyBinding = (keyStr: string, keyBindingKey?: string): string[] => {
|
||||||
if (!keyStr) return [];
|
if (!keyStr) return [];
|
||||||
|
|
||||||
// 特殊处理重做快捷键的操作系统差异
|
// 特殊处理重做快捷键的操作系统差异
|
||||||
if (command === KeyBindingCommand.HistoryRedoCommand && keyStr === 'Mod-Shift-z') {
|
if (keyBindingKey === KeyBindingKey.HistoryRedoKeyBindingKey && keyStr === 'Mod-Shift-z') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', '⇧', 'Z']; // macOS: Cmd+Shift+Z
|
return ['⌘', '⇧', 'Z']; // macOS: Cmd+Shift+Z
|
||||||
} else {
|
} else {
|
||||||
@@ -48,7 +42,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 特殊处理重做选择快捷键的操作系统差异
|
// 特殊处理重做选择快捷键的操作系统差异
|
||||||
if (command === KeyBindingCommand.HistoryRedoSelectionCommand && keyStr === 'Mod-Shift-u') {
|
if (keyBindingKey === KeyBindingKey.HistoryRedoSelectionKeyBindingKey && keyStr === 'Mod-Shift-u') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', '⇧', 'U']; // macOS: Cmd+Shift+U
|
return ['⌘', '⇧', 'U']; // macOS: Cmd+Shift+U
|
||||||
} else {
|
} else {
|
||||||
@@ -57,7 +51,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 特殊处理代码折叠快捷键的操作系统差异
|
// 特殊处理代码折叠快捷键的操作系统差异
|
||||||
if (command === KeyBindingCommand.FoldCodeCommand && keyStr === 'Ctrl-Shift-[') {
|
if (keyBindingKey === KeyBindingKey.FoldCodeKeyBindingKey && keyStr === 'Ctrl-Shift-[') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', '⌥', '[']; // macOS: Cmd+Alt+[
|
return ['⌘', '⌥', '[']; // macOS: Cmd+Alt+[
|
||||||
} else {
|
} else {
|
||||||
@@ -65,7 +59,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === KeyBindingCommand.UnfoldCodeCommand && keyStr === 'Ctrl-Shift-]') {
|
if (keyBindingKey === KeyBindingKey.UnfoldCodeKeyBindingKey && keyStr === 'Ctrl-Shift-]') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', '⌥', ']']; // macOS: Cmd+Alt+]
|
return ['⌘', '⌥', ']']; // macOS: Cmd+Alt+]
|
||||||
} else {
|
} else {
|
||||||
@@ -74,7 +68,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 特殊处理编辑快捷键的操作系统差异
|
// 特殊处理编辑快捷键的操作系统差异
|
||||||
if (command === KeyBindingCommand.CursorSyntaxLeftCommand && keyStr === 'Alt-ArrowLeft') {
|
if (keyBindingKey === KeyBindingKey.CursorSyntaxLeftKeyBindingKey && keyStr === 'Alt-ArrowLeft') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['Ctrl', '←']; // macOS: Ctrl+ArrowLeft
|
return ['Ctrl', '←']; // macOS: Ctrl+ArrowLeft
|
||||||
} else {
|
} else {
|
||||||
@@ -82,7 +76,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === KeyBindingCommand.CursorSyntaxRightCommand && keyStr === 'Alt-ArrowRight') {
|
if (keyBindingKey === KeyBindingKey.CursorSyntaxRightKeyBindingKey && keyStr === 'Alt-ArrowRight') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['Ctrl', '→']; // macOS: Ctrl+ArrowRight
|
return ['Ctrl', '→']; // macOS: Ctrl+ArrowRight
|
||||||
} else {
|
} else {
|
||||||
@@ -90,7 +84,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === KeyBindingCommand.InsertBlankLineCommand && keyStr === 'Ctrl-Enter') {
|
if (keyBindingKey === KeyBindingKey.InsertBlankLineKeyBindingKey && keyStr === 'Ctrl-Enter') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', 'Enter']; // macOS: Cmd+Enter
|
return ['⌘', 'Enter']; // macOS: Cmd+Enter
|
||||||
} else {
|
} else {
|
||||||
@@ -98,7 +92,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === KeyBindingCommand.SelectLineCommand && keyStr === 'Alt-l') {
|
if (keyBindingKey === KeyBindingKey.SelectLineKeyBindingKey && keyStr === 'Alt-l') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['Ctrl', 'L']; // macOS: Ctrl+l
|
return ['Ctrl', 'L']; // macOS: Ctrl+l
|
||||||
} else {
|
} else {
|
||||||
@@ -106,7 +100,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === KeyBindingCommand.SelectParentSyntaxCommand && keyStr === 'Ctrl-i') {
|
if (keyBindingKey === KeyBindingKey.SelectParentSyntaxKeyBindingKey && keyStr === 'Ctrl-i') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', 'I']; // macOS: Cmd+i
|
return ['⌘', 'I']; // macOS: Cmd+i
|
||||||
} else {
|
} else {
|
||||||
@@ -114,7 +108,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === KeyBindingCommand.IndentLessCommand && keyStr === 'Ctrl-[') {
|
if (keyBindingKey === KeyBindingKey.IndentLessKeyBindingKey && keyStr === 'Ctrl-[') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', '[']; // macOS: Cmd+[
|
return ['⌘', '[']; // macOS: Cmd+[
|
||||||
} else {
|
} else {
|
||||||
@@ -122,7 +116,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === KeyBindingCommand.IndentMoreCommand && keyStr === 'Ctrl-]') {
|
if (keyBindingKey === KeyBindingKey.IndentMoreKeyBindingKey && keyStr === 'Ctrl-]') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', ']']; // macOS: Cmd+]
|
return ['⌘', ']']; // macOS: Cmd+]
|
||||||
} else {
|
} else {
|
||||||
@@ -130,7 +124,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === KeyBindingCommand.IndentSelectionCommand && keyStr === 'Ctrl-Alt-\\') {
|
if (keyBindingKey === KeyBindingKey.IndentSelectionKeyBindingKey && keyStr === 'Ctrl-Alt-\\') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', '⌥', '\\']; // macOS: Cmd+Alt+\
|
return ['⌘', '⌥', '\\']; // macOS: Cmd+Alt+\
|
||||||
} else {
|
} else {
|
||||||
@@ -138,7 +132,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === KeyBindingCommand.CursorMatchingBracketCommand && keyStr === 'Shift-Ctrl-\\') {
|
if (keyBindingKey === KeyBindingKey.CursorMatchingBracketKeyBindingKey && keyStr === 'Shift-Ctrl-\\') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⇧', '⌘', '\\']; // macOS: Shift+Cmd+\
|
return ['⇧', '⌘', '\\']; // macOS: Shift+Cmd+\
|
||||||
} else {
|
} else {
|
||||||
@@ -146,7 +140,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === KeyBindingCommand.ToggleCommentCommand && keyStr === 'Ctrl-/') {
|
if (keyBindingKey === KeyBindingKey.ToggleCommentKeyBindingKey && keyStr === 'Ctrl-/') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', '/']; // macOS: Cmd+/
|
return ['⌘', '/']; // macOS: Cmd+/
|
||||||
} else {
|
} else {
|
||||||
@@ -155,7 +149,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 特殊处理删除快捷键的操作系统差异
|
// 特殊处理删除快捷键的操作系统差异
|
||||||
if (command === KeyBindingCommand.DeleteGroupBackwardCommand && keyStr === 'Ctrl-Backspace') {
|
if (keyBindingKey === KeyBindingKey.DeleteGroupBackwardKeyBindingKey && keyStr === 'Ctrl-Backspace') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', 'Backspace']; // macOS: Cmd+Backspace
|
return ['⌘', 'Backspace']; // macOS: Cmd+Backspace
|
||||||
} else {
|
} else {
|
||||||
@@ -163,7 +157,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === KeyBindingCommand.DeleteGroupForwardCommand && keyStr === 'Ctrl-Delete') {
|
if (keyBindingKey === KeyBindingKey.DeleteGroupForwardKeyBindingKey && keyStr === 'Ctrl-Delete') {
|
||||||
if (systemStore.isMacOS) {
|
if (systemStore.isMacOS) {
|
||||||
return ['⌘', 'Delete']; // macOS: Cmd+Delete
|
return ['⌘', 'Delete']; // macOS: Cmd+Delete
|
||||||
} else {
|
} else {
|
||||||
@@ -204,25 +198,25 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
<div class="key-bindings-container">
|
<div class="key-bindings-container">
|
||||||
<div class="key-bindings-header">
|
<div class="key-bindings-header">
|
||||||
<div class="keybinding-col">{{ t('keybindings.headers.shortcut') }}</div>
|
<div class="keybinding-col">{{ t('keybindings.headers.shortcut') }}</div>
|
||||||
<div class="category-col">{{ t('keybindings.headers.category') }}</div>
|
<div class="extension-col">{{ t('keybindings.headers.extension') }}</div>
|
||||||
<div class="description-col">{{ t('keybindings.headers.description') }}</div>
|
<div class="description-col">{{ t('keybindings.headers.description') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-for="binding in keyBindings"
|
v-for="binding in keyBindings"
|
||||||
:key="binding.id"
|
:key="binding.key"
|
||||||
class="key-binding-row"
|
class="key-binding-row"
|
||||||
>
|
>
|
||||||
<div class="keybinding-col">
|
<div class="keybinding-col">
|
||||||
<span
|
<span
|
||||||
v-for="(key, index) in binding.keys"
|
v-for="(key, index) in binding.command"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="key-badge"
|
class="key-badge"
|
||||||
>
|
>
|
||||||
{{ key }}
|
{{ key }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="category-col">{{ binding.category }}</div>
|
<div class="extension-col">{{ binding.extension }}</div>
|
||||||
<div class="description-col">{{ binding.description }}</div>
|
<div class="description-col">{{ binding.description }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -276,7 +270,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-col {
|
.extension-col {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
padding: 0 10px 0 0;
|
padding: 0 10px 0 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|||||||
22
go.mod
22
go.mod
@@ -3,22 +3,25 @@ module voidraft
|
|||||||
go 1.25
|
go 1.25
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
entgo.io/ent v0.14.5
|
||||||
github.com/creativeprojects/go-selfupdate v1.5.1
|
github.com/creativeprojects/go-selfupdate v1.5.1
|
||||||
github.com/go-git/go-git/v5 v5.16.3
|
github.com/go-git/go-git/v5 v5.16.3
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
github.com/knadh/koanf/parsers/json v1.0.0
|
github.com/knadh/koanf/parsers/json v1.0.0
|
||||||
github.com/knadh/koanf/providers/file v1.2.0
|
github.com/knadh/koanf/providers/file v1.2.0
|
||||||
github.com/knadh/koanf/providers/structs v1.0.0
|
github.com/knadh/koanf/providers/structs v1.0.0
|
||||||
github.com/knadh/koanf/v2 v2.3.0
|
github.com/knadh/koanf/v2 v2.3.0
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/wailsapp/wails/v3 v3.0.0-alpha.41
|
github.com/wailsapp/wails/v3 v3.0.0-alpha.41
|
||||||
golang.org/x/net v0.47.0
|
golang.org/x/net v0.47.0
|
||||||
golang.org/x/sys v0.38.0
|
golang.org/x/sys v0.38.0
|
||||||
golang.org/x/text v0.31.0
|
golang.org/x/text v0.31.0
|
||||||
modernc.org/sqlite v1.40.1
|
|
||||||
resty.dev/v3 v3.0.0-beta.3
|
resty.dev/v3 v3.0.0-beta.3
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9 // indirect
|
||||||
code.gitea.io/sdk/gitea v0.22.1 // indirect
|
code.gitea.io/sdk/gitea v0.22.1 // indirect
|
||||||
dario.cat/mergo v1.0.2 // indirect
|
dario.cat/mergo v1.0.2 // indirect
|
||||||
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect
|
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect
|
||||||
@@ -27,12 +30,14 @@ require (
|
|||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||||
github.com/adrg/xdg v0.5.3 // indirect
|
github.com/adrg/xdg v0.5.3 // indirect
|
||||||
|
github.com/agext/levenshtein v1.2.3 // indirect
|
||||||
|
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||||
github.com/bep/debounce v1.2.1 // indirect
|
github.com/bep/debounce v1.2.1 // indirect
|
||||||
|
github.com/bmatcuk/doublestar v1.3.4 // indirect
|
||||||
github.com/cloudflare/circl v1.6.1 // indirect
|
github.com/cloudflare/circl v1.6.1 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
|
||||||
github.com/ebitengine/purego v0.9.1 // indirect
|
github.com/ebitengine/purego v0.9.1 // indirect
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/fatih/structs v1.1.0 // indirect
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
@@ -41,15 +46,17 @@ require (
|
|||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
|
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.2.0 // indirect
|
github.com/godbus/dbus/v5 v5.2.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||||
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/google/go-github/v30 v30.1.0 // indirect
|
github.com/google/go-github/v30 v30.1.0 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||||
|
github.com/hashicorp/hcl/v2 v2.18.1 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect
|
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
||||||
@@ -61,13 +68,12 @@ require (
|
|||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
|
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
|
||||||
github.com/pjbgf/sha1cd v0.5.0 // indirect
|
github.com/pjbgf/sha1cd v0.5.0 // indirect
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/samber/lo v1.52.0 // indirect
|
github.com/samber/lo v1.52.0 // indirect
|
||||||
github.com/sergi/go-diff v1.4.0 // indirect
|
github.com/sergi/go-diff v1.4.0 // indirect
|
||||||
@@ -77,15 +83,15 @@ require (
|
|||||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||||
github.com/xanzy/go-gitlab v0.115.0 // indirect
|
github.com/xanzy/go-gitlab v0.115.0 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
|
github.com/zclconf/go-cty v1.14.4 // indirect
|
||||||
|
github.com/zclconf/go-cty-yaml v1.1.0 // indirect
|
||||||
golang.org/x/crypto v0.45.0 // indirect
|
golang.org/x/crypto v0.45.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect
|
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect
|
||||||
golang.org/x/image v0.33.0 // indirect
|
golang.org/x/image v0.33.0 // indirect
|
||||||
|
golang.org/x/mod v0.30.0 // indirect
|
||||||
golang.org/x/oauth2 v0.33.0 // indirect
|
golang.org/x/oauth2 v0.33.0 // indirect
|
||||||
golang.org/x/time v0.14.0 // indirect
|
golang.org/x/time v0.14.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
modernc.org/libc v1.67.0 // indirect
|
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
|
||||||
modernc.org/memory v1.11.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
70
go.sum
70
go.sum
@@ -1,11 +1,17 @@
|
|||||||
|
ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9 h1:E0wvcUXTkgyN4wy4LGtNzMNGMytJN8afmIWXJVMi4cc=
|
||||||
|
ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9/go.mod h1:Oe1xWPuu5q9LzyrWfbZmEZxFYeu4BHTyzfjeW2aZp/w=
|
||||||
code.gitea.io/sdk/gitea v0.22.1 h1:7K05KjRORyTcTYULQ/AwvlVS6pawLcWyXZcTr7gHFyA=
|
code.gitea.io/sdk/gitea v0.22.1 h1:7K05KjRORyTcTYULQ/AwvlVS6pawLcWyXZcTr7gHFyA=
|
||||||
code.gitea.io/sdk/gitea v0.22.1/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM=
|
code.gitea.io/sdk/gitea v0.22.1/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM=
|
||||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||||
|
entgo.io/ent v0.14.5 h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4=
|
||||||
|
entgo.io/ent v0.14.5/go.mod h1:zTzLmWtPvGpmSwtkaayM2cm5m819NdM7z7tYPq3vN0U=
|
||||||
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 h1:N3IGoHHp9pb6mj1cbXbuaSXV/UMKwmbKLf53nQmtqMA=
|
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 h1:N3IGoHHp9pb6mj1cbXbuaSXV/UMKwmbKLf53nQmtqMA=
|
||||||
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3/go.mod h1:QtOLZGz8olr4qH2vWK0QH0w0O4T9fEIjMuWpKUsH7nc=
|
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3/go.mod h1:QtOLZGz8olr4qH2vWK0QH0w0O4T9fEIjMuWpKUsH7nc=
|
||||||
github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=
|
github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=
|
||||||
github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM=
|
github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM=
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
@@ -15,12 +21,18 @@ github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBi
|
|||||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||||
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
|
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
|
||||||
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
|
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
|
||||||
|
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||||
|
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
|
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||||
|
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||||
|
github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
|
||||||
|
github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
|
||||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||||
github.com/creativeprojects/go-selfupdate v1.5.1 h1:fuyEGFFfqcC8SxDGolcEPYPLXGQ9Mcrc5uRyRG2Mqnk=
|
github.com/creativeprojects/go-selfupdate v1.5.1 h1:fuyEGFFfqcC8SxDGolcEPYPLXGQ9Mcrc5uRyRG2Mqnk=
|
||||||
@@ -32,8 +44,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
|
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
|
||||||
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
|
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
|
||||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||||
@@ -60,6 +70,10 @@ github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6
|
|||||||
github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
|
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
|
||||||
|
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
|
||||||
|
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||||
|
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8=
|
github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8=
|
||||||
@@ -75,8 +89,6 @@ github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQF
|
|||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
@@ -87,8 +99,8 @@ github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVU
|
|||||||
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
||||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
github.com/hashicorp/hcl/v2 v2.18.1 h1:6nxnOJFku1EuSawSD81fuviYUV8DxFr3fp2dUi3ZYSo=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
github.com/hashicorp/hcl/v2 v2.18.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7kI3D85AXWkw6/+v9PwtV6M6o11sWHQ=
|
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7kI3D85AXWkw6/+v9PwtV6M6o11sWHQ=
|
||||||
@@ -114,6 +126,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
|
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
|
||||||
github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
|
github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
|
||||||
github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
|
github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
|
||||||
@@ -127,12 +141,14 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
|||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
|
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||||
|
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
|
||||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
|
||||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||||
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
|
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
|
||||||
@@ -143,8 +159,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
@@ -174,6 +188,10 @@ github.com/xanzy/go-gitlab v0.115.0 h1:6DmtItNcVe+At/liXSgfE/DZNZrGfalQmBRmOcJjO
|
|||||||
github.com/xanzy/go-gitlab v0.115.0/go.mod h1:5XCDtM7AM6WMKmfDdOiEpyRWUqui2iS9ILfvCZ2gJ5M=
|
github.com/xanzy/go-gitlab v0.115.0/go.mod h1:5XCDtM7AM6WMKmfDdOiEpyRWUqui2iS9ILfvCZ2gJ5M=
|
||||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||||
|
github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8=
|
||||||
|
github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||||
|
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=
|
||||||
|
github.com/zclconf/go-cty-yaml v1.1.0/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
@@ -196,8 +214,6 @@ golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
|||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
|
||||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -222,8 +238,6 @@ golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
|||||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
|
||||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -238,33 +252,5 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
|
||||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
|
||||||
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
|
|
||||||
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
|
|
||||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
|
||||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
|
||||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
|
||||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
|
||||||
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
|
|
||||||
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
|
||||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
|
||||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
|
||||||
modernc.org/libc v1.67.0 h1:QzL4IrKab2OFmxA3/vRYl0tLXrIamwrhD6CKD4WBVjQ=
|
|
||||||
modernc.org/libc v1.67.0/go.mod h1:QvvnnJ5P7aitu0ReNpVIEyesuhmDLQ8kaEoyMjIFZJA=
|
|
||||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
|
||||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
|
||||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
|
||||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
|
||||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
|
||||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
|
||||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
|
||||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
|
||||||
modernc.org/sqlite v1.40.1 h1:VfuXcxcUWWKRBuP8+BR9L7VnmusMgBNNnBYGEe9w/iY=
|
|
||||||
modernc.org/sqlite v1.40.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
|
|
||||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
|
||||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
|
||||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
|
||||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
|
||||||
resty.dev/v3 v3.0.0-beta.3 h1:3kEwzEgCnnS6Ob4Emlk94t+I/gClyoah7SnNi67lt+E=
|
resty.dev/v3 v3.0.0-beta.3 h1:3kEwzEgCnnS6Ob4Emlk94t+I/gClyoah7SnNi67lt+E=
|
||||||
resty.dev/v3 v3.0.0-beta.3/go.mod h1:OgkqiPvTDtOuV4MGZuUDhwOpkY8enjOsjjMzeOHefy4=
|
resty.dev/v3 v3.0.0-beta.3/go.mod h1:OgkqiPvTDtOuV4MGZuUDhwOpkY8enjOsjjMzeOHefy4=
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
// Git备份相关类型定义
|
|
||||||
type (
|
|
||||||
// AuthMethod 定义Git认证方式
|
|
||||||
AuthMethod string
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// 认证方式
|
|
||||||
Token AuthMethod = "token"
|
|
||||||
SSHKey AuthMethod = "ssh_key"
|
|
||||||
UserPass AuthMethod = "user_pass"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GitBackupConfig Git备份配置
|
|
||||||
type GitBackupConfig struct {
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
RepoURL string `json:"repo_url"`
|
|
||||||
AuthMethod AuthMethod `json:"auth_method"`
|
|
||||||
Username string `json:"username,omitempty"`
|
|
||||||
Password string `json:"password,omitempty"`
|
|
||||||
Token string `json:"token,omitempty"`
|
|
||||||
SSHKeyPath string `json:"ssh_key_path,omitempty"`
|
|
||||||
SSHKeyPass string `json:"ssh_key_passphrase,omitempty"`
|
|
||||||
BackupInterval int `json:"backup_interval"` // 分钟
|
|
||||||
AutoBackup bool `json:"auto_backup"`
|
|
||||||
}
|
|
||||||
@@ -126,6 +126,33 @@ type UpdatesConfig struct {
|
|||||||
Gitea GiteaConfig `json:"gitea"` // Gitea配置
|
Gitea GiteaConfig `json:"gitea"` // Gitea配置
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Git备份相关类型定义
|
||||||
|
type (
|
||||||
|
// AuthMethod 定义Git认证方式
|
||||||
|
AuthMethod string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 认证方式
|
||||||
|
Token AuthMethod = "token"
|
||||||
|
SSHKey AuthMethod = "ssh_key"
|
||||||
|
UserPass AuthMethod = "user_pass"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GitBackupConfig Git备份配置
|
||||||
|
type GitBackupConfig struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
RepoURL string `json:"repo_url"`
|
||||||
|
AuthMethod AuthMethod `json:"auth_method"`
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
SSHKeyPath string `json:"ssh_key_path,omitempty"`
|
||||||
|
SSHKeyPass string `json:"ssh_key_passphrase,omitempty"`
|
||||||
|
BackupInterval int `json:"backup_interval"` // 分钟
|
||||||
|
AutoBackup bool `json:"auto_backup"`
|
||||||
|
}
|
||||||
|
|
||||||
// AppConfig 应用配置 - 按照前端设置页面分类组织
|
// AppConfig 应用配置 - 按照前端设置页面分类组织
|
||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
General GeneralConfig `json:"general"` // 通用设置
|
General GeneralConfig `json:"general"` // 通用设置
|
||||||
@@ -181,7 +208,7 @@ func NewDefaultAppConfig() *AppConfig {
|
|||||||
},
|
},
|
||||||
Appearance: AppearanceConfig{
|
Appearance: AppearanceConfig{
|
||||||
Language: LangEnUS,
|
Language: LangEnUS,
|
||||||
SystemTheme: SystemThemeAuto,
|
SystemTheme: SystemThemeDark,
|
||||||
CurrentTheme: "default-dark", // 默认使用 default-dark 主题
|
CurrentTheme: "default-dark", // 默认使用 default-dark 主题
|
||||||
},
|
},
|
||||||
Updates: UpdatesConfig{
|
Updates: UpdatesConfig{
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Document represents a document in the system
|
|
||||||
type Document struct {
|
|
||||||
ID int64 `json:"id" db:"id"`
|
|
||||||
Title string `json:"title" db:"title"`
|
|
||||||
Content string `json:"content" db:"content"`
|
|
||||||
CreatedAt string `json:"createdAt" db:"created_at"`
|
|
||||||
UpdatedAt string `json:"updatedAt" db:"updated_at"`
|
|
||||||
IsDeleted bool `json:"is_deleted" db:"is_deleted"`
|
|
||||||
IsLocked bool `json:"is_locked" db:"is_locked"` // 锁定标志,锁定的文档无法被删除
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDocument 创建新文档
|
|
||||||
func NewDocument(title, content string) *Document {
|
|
||||||
now := time.Now()
|
|
||||||
return &Document{
|
|
||||||
Title: title,
|
|
||||||
Content: content,
|
|
||||||
CreatedAt: now.String(),
|
|
||||||
UpdatedAt: now.String(),
|
|
||||||
IsDeleted: false,
|
|
||||||
IsLocked: false, // 默认不锁定
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDefaultDocument 创建默认文档
|
|
||||||
func NewDefaultDocument() *Document {
|
|
||||||
return NewDocument("default", "\n∞∞∞text-a\n")
|
|
||||||
}
|
|
||||||
804
internal/models/ent/client.go
Normal file
804
internal/models/ent/client.go
Normal file
@@ -0,0 +1,804 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"voidraft/internal/models/ent/migrate"
|
||||||
|
|
||||||
|
"voidraft/internal/models/ent/document"
|
||||||
|
"voidraft/internal/models/ent/extension"
|
||||||
|
"voidraft/internal/models/ent/keybinding"
|
||||||
|
"voidraft/internal/models/ent/theme"
|
||||||
|
|
||||||
|
"entgo.io/ent"
|
||||||
|
"entgo.io/ent/dialect"
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
|
||||||
|
stdsql "database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client is the client that holds all ent builders.
|
||||||
|
type Client struct {
|
||||||
|
config
|
||||||
|
// Schema is the client for creating, migrating and dropping schema.
|
||||||
|
Schema *migrate.Schema
|
||||||
|
// Document is the client for interacting with the Document builders.
|
||||||
|
Document *DocumentClient
|
||||||
|
// Extension is the client for interacting with the Extension builders.
|
||||||
|
Extension *ExtensionClient
|
||||||
|
// KeyBinding is the client for interacting with the KeyBinding builders.
|
||||||
|
KeyBinding *KeyBindingClient
|
||||||
|
// Theme is the client for interacting with the Theme builders.
|
||||||
|
Theme *ThemeClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient creates a new client configured with the given options.
|
||||||
|
func NewClient(opts ...Option) *Client {
|
||||||
|
client := &Client{config: newConfig(opts...)}
|
||||||
|
client.init()
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) init() {
|
||||||
|
c.Schema = migrate.NewSchema(c.driver)
|
||||||
|
c.Document = NewDocumentClient(c.config)
|
||||||
|
c.Extension = NewExtensionClient(c.config)
|
||||||
|
c.KeyBinding = NewKeyBindingClient(c.config)
|
||||||
|
c.Theme = NewThemeClient(c.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
// config is the configuration for the client and its builder.
|
||||||
|
config struct {
|
||||||
|
// driver used for executing database requests.
|
||||||
|
driver dialect.Driver
|
||||||
|
// debug enable a debug logging.
|
||||||
|
debug bool
|
||||||
|
// log used for logging on debug mode.
|
||||||
|
log func(...any)
|
||||||
|
// hooks to execute on mutations.
|
||||||
|
hooks *hooks
|
||||||
|
// interceptors to execute on queries.
|
||||||
|
inters *inters
|
||||||
|
}
|
||||||
|
// Option function to configure the client.
|
||||||
|
Option func(*config)
|
||||||
|
)
|
||||||
|
|
||||||
|
// newConfig creates a new config for the client.
|
||||||
|
func newConfig(opts ...Option) config {
|
||||||
|
cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}}
|
||||||
|
cfg.options(opts...)
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// options applies the options on the config object.
|
||||||
|
func (c *config) options(opts ...Option) {
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(c)
|
||||||
|
}
|
||||||
|
if c.debug {
|
||||||
|
c.driver = dialect.Debug(c.driver, c.log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug enables debug logging on the ent.Driver.
|
||||||
|
func Debug() Option {
|
||||||
|
return func(c *config) {
|
||||||
|
c.debug = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log sets the logging function for debug mode.
|
||||||
|
func Log(fn func(...any)) Option {
|
||||||
|
return func(c *config) {
|
||||||
|
c.log = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Driver configures the client driver.
|
||||||
|
func Driver(driver dialect.Driver) Option {
|
||||||
|
return func(c *config) {
|
||||||
|
c.driver = driver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open opens a database/sql.DB specified by the driver name and
|
||||||
|
// the data source name, and returns a new client attached to it.
|
||||||
|
// Optional parameters can be added for configuring the client.
|
||||||
|
func Open(driverName, dataSourceName string, options ...Option) (*Client, error) {
|
||||||
|
switch driverName {
|
||||||
|
case dialect.MySQL, dialect.Postgres, dialect.SQLite:
|
||||||
|
drv, err := sql.Open(driverName, dataSourceName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewClient(append(options, Driver(drv))...), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported driver: %q", driverName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrTxStarted is returned when trying to start a new transaction from a transactional client.
|
||||||
|
var ErrTxStarted = errors.New("ent: cannot start a transaction within a transaction")
|
||||||
|
|
||||||
|
// Tx returns a new transactional client. The provided context
|
||||||
|
// is used until the transaction is committed or rolled back.
|
||||||
|
func (c *Client) Tx(ctx context.Context) (*Tx, error) {
|
||||||
|
if _, ok := c.driver.(*txDriver); ok {
|
||||||
|
return nil, ErrTxStarted
|
||||||
|
}
|
||||||
|
tx, err := newTx(ctx, c.driver)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ent: starting a transaction: %w", err)
|
||||||
|
}
|
||||||
|
cfg := c.config
|
||||||
|
cfg.driver = tx
|
||||||
|
return &Tx{
|
||||||
|
ctx: ctx,
|
||||||
|
config: cfg,
|
||||||
|
Document: NewDocumentClient(cfg),
|
||||||
|
Extension: NewExtensionClient(cfg),
|
||||||
|
KeyBinding: NewKeyBindingClient(cfg),
|
||||||
|
Theme: NewThemeClient(cfg),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeginTx returns a transactional client with specified options.
|
||||||
|
func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
|
||||||
|
if _, ok := c.driver.(*txDriver); ok {
|
||||||
|
return nil, errors.New("ent: cannot start a transaction within a transaction")
|
||||||
|
}
|
||||||
|
tx, err := c.driver.(interface {
|
||||||
|
BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error)
|
||||||
|
}).BeginTx(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ent: starting a transaction: %w", err)
|
||||||
|
}
|
||||||
|
cfg := c.config
|
||||||
|
cfg.driver = &txDriver{tx: tx, drv: c.driver}
|
||||||
|
return &Tx{
|
||||||
|
ctx: ctx,
|
||||||
|
config: cfg,
|
||||||
|
Document: NewDocumentClient(cfg),
|
||||||
|
Extension: NewExtensionClient(cfg),
|
||||||
|
KeyBinding: NewKeyBindingClient(cfg),
|
||||||
|
Theme: NewThemeClient(cfg),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug returns a new debug-client. It's used to get verbose logging on specific operations.
|
||||||
|
//
|
||||||
|
// client.Debug().
|
||||||
|
// Document.
|
||||||
|
// Query().
|
||||||
|
// Count(ctx)
|
||||||
|
func (c *Client) Debug() *Client {
|
||||||
|
if c.debug {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
cfg := c.config
|
||||||
|
cfg.driver = dialect.Debug(c.driver, c.log)
|
||||||
|
client := &Client{config: cfg}
|
||||||
|
client.init()
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the database connection and prevents new queries from starting.
|
||||||
|
func (c *Client) Close() error {
|
||||||
|
return c.driver.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use adds the mutation hooks to all the entity clients.
|
||||||
|
// In order to add hooks to a specific client, call: `client.Node.Use(...)`.
|
||||||
|
func (c *Client) Use(hooks ...Hook) {
|
||||||
|
c.Document.Use(hooks...)
|
||||||
|
c.Extension.Use(hooks...)
|
||||||
|
c.KeyBinding.Use(hooks...)
|
||||||
|
c.Theme.Use(hooks...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intercept adds the query interceptors to all the entity clients.
|
||||||
|
// In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`.
|
||||||
|
func (c *Client) Intercept(interceptors ...Interceptor) {
|
||||||
|
c.Document.Intercept(interceptors...)
|
||||||
|
c.Extension.Intercept(interceptors...)
|
||||||
|
c.KeyBinding.Intercept(interceptors...)
|
||||||
|
c.Theme.Intercept(interceptors...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutate implements the ent.Mutator interface.
|
||||||
|
func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
|
||||||
|
switch m := m.(type) {
|
||||||
|
case *DocumentMutation:
|
||||||
|
return c.Document.mutate(ctx, m)
|
||||||
|
case *ExtensionMutation:
|
||||||
|
return c.Extension.mutate(ctx, m)
|
||||||
|
case *KeyBindingMutation:
|
||||||
|
return c.KeyBinding.mutate(ctx, m)
|
||||||
|
case *ThemeMutation:
|
||||||
|
return c.Theme.mutate(ctx, m)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("ent: unknown mutation type %T", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DocumentClient is a client for the Document schema.
|
||||||
|
type DocumentClient struct {
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDocumentClient returns a client for the Document from the given config.
|
||||||
|
func NewDocumentClient(c config) *DocumentClient {
|
||||||
|
return &DocumentClient{config: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use adds a list of mutation hooks to the hooks stack.
|
||||||
|
// A call to `Use(f, g, h)` equals to `document.Hooks(f(g(h())))`.
|
||||||
|
func (c *DocumentClient) Use(hooks ...Hook) {
|
||||||
|
c.hooks.Document = append(c.hooks.Document, hooks...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intercept adds a list of query interceptors to the interceptors stack.
|
||||||
|
// A call to `Intercept(f, g, h)` equals to `document.Intercept(f(g(h())))`.
|
||||||
|
func (c *DocumentClient) Intercept(interceptors ...Interceptor) {
|
||||||
|
c.inters.Document = append(c.inters.Document, interceptors...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create returns a builder for creating a Document entity.
|
||||||
|
func (c *DocumentClient) Create() *DocumentCreate {
|
||||||
|
mutation := newDocumentMutation(c.config, OpCreate)
|
||||||
|
return &DocumentCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBulk returns a builder for creating a bulk of Document entities.
|
||||||
|
func (c *DocumentClient) CreateBulk(builders ...*DocumentCreate) *DocumentCreateBulk {
|
||||||
|
return &DocumentCreateBulk{config: c.config, builders: builders}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||||
|
// a builder and applies setFunc on it.
|
||||||
|
func (c *DocumentClient) MapCreateBulk(slice any, setFunc func(*DocumentCreate, int)) *DocumentCreateBulk {
|
||||||
|
rv := reflect.ValueOf(slice)
|
||||||
|
if rv.Kind() != reflect.Slice {
|
||||||
|
return &DocumentCreateBulk{err: fmt.Errorf("calling to DocumentClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||||
|
}
|
||||||
|
builders := make([]*DocumentCreate, rv.Len())
|
||||||
|
for i := 0; i < rv.Len(); i++ {
|
||||||
|
builders[i] = c.Create()
|
||||||
|
setFunc(builders[i], i)
|
||||||
|
}
|
||||||
|
return &DocumentCreateBulk{config: c.config, builders: builders}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update returns an update builder for Document.
|
||||||
|
func (c *DocumentClient) Update() *DocumentUpdate {
|
||||||
|
mutation := newDocumentMutation(c.config, OpUpdate)
|
||||||
|
return &DocumentUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOne returns an update builder for the given entity.
|
||||||
|
func (c *DocumentClient) UpdateOne(_m *Document) *DocumentUpdateOne {
|
||||||
|
mutation := newDocumentMutation(c.config, OpUpdateOne, withDocument(_m))
|
||||||
|
return &DocumentUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOneID returns an update builder for the given id.
|
||||||
|
func (c *DocumentClient) UpdateOneID(id int) *DocumentUpdateOne {
|
||||||
|
mutation := newDocumentMutation(c.config, OpUpdateOne, withDocumentID(id))
|
||||||
|
return &DocumentUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete returns a delete builder for Document.
|
||||||
|
func (c *DocumentClient) Delete() *DocumentDelete {
|
||||||
|
mutation := newDocumentMutation(c.config, OpDelete)
|
||||||
|
return &DocumentDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOne returns a builder for deleting the given entity.
|
||||||
|
func (c *DocumentClient) DeleteOne(_m *Document) *DocumentDeleteOne {
|
||||||
|
return c.DeleteOneID(_m.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOneID returns a builder for deleting the given entity by its id.
|
||||||
|
func (c *DocumentClient) DeleteOneID(id int) *DocumentDeleteOne {
|
||||||
|
builder := c.Delete().Where(document.ID(id))
|
||||||
|
builder.mutation.id = &id
|
||||||
|
builder.mutation.op = OpDeleteOne
|
||||||
|
return &DocumentDeleteOne{builder}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns a query builder for Document.
|
||||||
|
func (c *DocumentClient) Query() *DocumentQuery {
|
||||||
|
return &DocumentQuery{
|
||||||
|
config: c.config,
|
||||||
|
ctx: &QueryContext{Type: TypeDocument},
|
||||||
|
inters: c.Interceptors(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a Document entity by its id.
|
||||||
|
func (c *DocumentClient) Get(ctx context.Context, id int) (*Document, error) {
|
||||||
|
return c.Query().Where(document.ID(id)).Only(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetX is like Get, but panics if an error occurs.
|
||||||
|
func (c *DocumentClient) GetX(ctx context.Context, id int) *Document {
|
||||||
|
obj, err := c.Get(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hooks returns the client hooks.
|
||||||
|
func (c *DocumentClient) Hooks() []Hook {
|
||||||
|
hooks := c.hooks.Document
|
||||||
|
return append(hooks[:len(hooks):len(hooks)], document.Hooks[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interceptors returns the client interceptors.
|
||||||
|
func (c *DocumentClient) Interceptors() []Interceptor {
|
||||||
|
inters := c.inters.Document
|
||||||
|
return append(inters[:len(inters):len(inters)], document.Interceptors[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DocumentClient) mutate(ctx context.Context, m *DocumentMutation) (Value, error) {
|
||||||
|
switch m.Op() {
|
||||||
|
case OpCreate:
|
||||||
|
return (&DocumentCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpUpdate:
|
||||||
|
return (&DocumentUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpUpdateOne:
|
||||||
|
return (&DocumentUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpDelete, OpDeleteOne:
|
||||||
|
return (&DocumentDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("ent: unknown Document mutation op: %q", m.Op())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtensionClient is a client for the Extension schema.
|
||||||
|
type ExtensionClient struct {
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExtensionClient returns a client for the Extension from the given config.
|
||||||
|
func NewExtensionClient(c config) *ExtensionClient {
|
||||||
|
return &ExtensionClient{config: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use adds a list of mutation hooks to the hooks stack.
|
||||||
|
// A call to `Use(f, g, h)` equals to `extension.Hooks(f(g(h())))`.
|
||||||
|
func (c *ExtensionClient) Use(hooks ...Hook) {
|
||||||
|
c.hooks.Extension = append(c.hooks.Extension, hooks...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intercept adds a list of query interceptors to the interceptors stack.
|
||||||
|
// A call to `Intercept(f, g, h)` equals to `extension.Intercept(f(g(h())))`.
|
||||||
|
func (c *ExtensionClient) Intercept(interceptors ...Interceptor) {
|
||||||
|
c.inters.Extension = append(c.inters.Extension, interceptors...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create returns a builder for creating a Extension entity.
|
||||||
|
func (c *ExtensionClient) Create() *ExtensionCreate {
|
||||||
|
mutation := newExtensionMutation(c.config, OpCreate)
|
||||||
|
return &ExtensionCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBulk returns a builder for creating a bulk of Extension entities.
|
||||||
|
func (c *ExtensionClient) CreateBulk(builders ...*ExtensionCreate) *ExtensionCreateBulk {
|
||||||
|
return &ExtensionCreateBulk{config: c.config, builders: builders}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||||
|
// a builder and applies setFunc on it.
|
||||||
|
func (c *ExtensionClient) MapCreateBulk(slice any, setFunc func(*ExtensionCreate, int)) *ExtensionCreateBulk {
|
||||||
|
rv := reflect.ValueOf(slice)
|
||||||
|
if rv.Kind() != reflect.Slice {
|
||||||
|
return &ExtensionCreateBulk{err: fmt.Errorf("calling to ExtensionClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||||
|
}
|
||||||
|
builders := make([]*ExtensionCreate, rv.Len())
|
||||||
|
for i := 0; i < rv.Len(); i++ {
|
||||||
|
builders[i] = c.Create()
|
||||||
|
setFunc(builders[i], i)
|
||||||
|
}
|
||||||
|
return &ExtensionCreateBulk{config: c.config, builders: builders}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update returns an update builder for Extension.
|
||||||
|
func (c *ExtensionClient) Update() *ExtensionUpdate {
|
||||||
|
mutation := newExtensionMutation(c.config, OpUpdate)
|
||||||
|
return &ExtensionUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOne returns an update builder for the given entity.
|
||||||
|
func (c *ExtensionClient) UpdateOne(_m *Extension) *ExtensionUpdateOne {
|
||||||
|
mutation := newExtensionMutation(c.config, OpUpdateOne, withExtension(_m))
|
||||||
|
return &ExtensionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOneID returns an update builder for the given id.
|
||||||
|
func (c *ExtensionClient) UpdateOneID(id int) *ExtensionUpdateOne {
|
||||||
|
mutation := newExtensionMutation(c.config, OpUpdateOne, withExtensionID(id))
|
||||||
|
return &ExtensionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete returns a delete builder for Extension.
|
||||||
|
func (c *ExtensionClient) Delete() *ExtensionDelete {
|
||||||
|
mutation := newExtensionMutation(c.config, OpDelete)
|
||||||
|
return &ExtensionDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOne returns a builder for deleting the given entity.
|
||||||
|
func (c *ExtensionClient) DeleteOne(_m *Extension) *ExtensionDeleteOne {
|
||||||
|
return c.DeleteOneID(_m.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOneID returns a builder for deleting the given entity by its id.
|
||||||
|
func (c *ExtensionClient) DeleteOneID(id int) *ExtensionDeleteOne {
|
||||||
|
builder := c.Delete().Where(extension.ID(id))
|
||||||
|
builder.mutation.id = &id
|
||||||
|
builder.mutation.op = OpDeleteOne
|
||||||
|
return &ExtensionDeleteOne{builder}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns a query builder for Extension.
|
||||||
|
func (c *ExtensionClient) Query() *ExtensionQuery {
|
||||||
|
return &ExtensionQuery{
|
||||||
|
config: c.config,
|
||||||
|
ctx: &QueryContext{Type: TypeExtension},
|
||||||
|
inters: c.Interceptors(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a Extension entity by its id.
|
||||||
|
func (c *ExtensionClient) Get(ctx context.Context, id int) (*Extension, error) {
|
||||||
|
return c.Query().Where(extension.ID(id)).Only(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetX is like Get, but panics if an error occurs.
|
||||||
|
func (c *ExtensionClient) GetX(ctx context.Context, id int) *Extension {
|
||||||
|
obj, err := c.Get(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hooks returns the client hooks.
|
||||||
|
func (c *ExtensionClient) Hooks() []Hook {
|
||||||
|
hooks := c.hooks.Extension
|
||||||
|
return append(hooks[:len(hooks):len(hooks)], extension.Hooks[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interceptors returns the client interceptors.
|
||||||
|
func (c *ExtensionClient) Interceptors() []Interceptor {
|
||||||
|
inters := c.inters.Extension
|
||||||
|
return append(inters[:len(inters):len(inters)], extension.Interceptors[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ExtensionClient) mutate(ctx context.Context, m *ExtensionMutation) (Value, error) {
|
||||||
|
switch m.Op() {
|
||||||
|
case OpCreate:
|
||||||
|
return (&ExtensionCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpUpdate:
|
||||||
|
return (&ExtensionUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpUpdateOne:
|
||||||
|
return (&ExtensionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpDelete, OpDeleteOne:
|
||||||
|
return (&ExtensionDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("ent: unknown Extension mutation op: %q", m.Op())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyBindingClient is a client for the KeyBinding schema.
|
||||||
|
type KeyBindingClient struct {
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKeyBindingClient returns a client for the KeyBinding from the given config.
|
||||||
|
func NewKeyBindingClient(c config) *KeyBindingClient {
|
||||||
|
return &KeyBindingClient{config: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use adds a list of mutation hooks to the hooks stack.
|
||||||
|
// A call to `Use(f, g, h)` equals to `keybinding.Hooks(f(g(h())))`.
|
||||||
|
func (c *KeyBindingClient) Use(hooks ...Hook) {
|
||||||
|
c.hooks.KeyBinding = append(c.hooks.KeyBinding, hooks...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intercept adds a list of query interceptors to the interceptors stack.
|
||||||
|
// A call to `Intercept(f, g, h)` equals to `keybinding.Intercept(f(g(h())))`.
|
||||||
|
func (c *KeyBindingClient) Intercept(interceptors ...Interceptor) {
|
||||||
|
c.inters.KeyBinding = append(c.inters.KeyBinding, interceptors...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create returns a builder for creating a KeyBinding entity.
|
||||||
|
func (c *KeyBindingClient) Create() *KeyBindingCreate {
|
||||||
|
mutation := newKeyBindingMutation(c.config, OpCreate)
|
||||||
|
return &KeyBindingCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBulk returns a builder for creating a bulk of KeyBinding entities.
|
||||||
|
func (c *KeyBindingClient) CreateBulk(builders ...*KeyBindingCreate) *KeyBindingCreateBulk {
|
||||||
|
return &KeyBindingCreateBulk{config: c.config, builders: builders}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||||
|
// a builder and applies setFunc on it.
|
||||||
|
func (c *KeyBindingClient) MapCreateBulk(slice any, setFunc func(*KeyBindingCreate, int)) *KeyBindingCreateBulk {
|
||||||
|
rv := reflect.ValueOf(slice)
|
||||||
|
if rv.Kind() != reflect.Slice {
|
||||||
|
return &KeyBindingCreateBulk{err: fmt.Errorf("calling to KeyBindingClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||||
|
}
|
||||||
|
builders := make([]*KeyBindingCreate, rv.Len())
|
||||||
|
for i := 0; i < rv.Len(); i++ {
|
||||||
|
builders[i] = c.Create()
|
||||||
|
setFunc(builders[i], i)
|
||||||
|
}
|
||||||
|
return &KeyBindingCreateBulk{config: c.config, builders: builders}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update returns an update builder for KeyBinding.
|
||||||
|
func (c *KeyBindingClient) Update() *KeyBindingUpdate {
|
||||||
|
mutation := newKeyBindingMutation(c.config, OpUpdate)
|
||||||
|
return &KeyBindingUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOne returns an update builder for the given entity.
|
||||||
|
func (c *KeyBindingClient) UpdateOne(_m *KeyBinding) *KeyBindingUpdateOne {
|
||||||
|
mutation := newKeyBindingMutation(c.config, OpUpdateOne, withKeyBinding(_m))
|
||||||
|
return &KeyBindingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOneID returns an update builder for the given id.
|
||||||
|
func (c *KeyBindingClient) UpdateOneID(id int) *KeyBindingUpdateOne {
|
||||||
|
mutation := newKeyBindingMutation(c.config, OpUpdateOne, withKeyBindingID(id))
|
||||||
|
return &KeyBindingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete returns a delete builder for KeyBinding.
|
||||||
|
func (c *KeyBindingClient) Delete() *KeyBindingDelete {
|
||||||
|
mutation := newKeyBindingMutation(c.config, OpDelete)
|
||||||
|
return &KeyBindingDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOne returns a builder for deleting the given entity.
|
||||||
|
func (c *KeyBindingClient) DeleteOne(_m *KeyBinding) *KeyBindingDeleteOne {
|
||||||
|
return c.DeleteOneID(_m.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOneID returns a builder for deleting the given entity by its id.
|
||||||
|
func (c *KeyBindingClient) DeleteOneID(id int) *KeyBindingDeleteOne {
|
||||||
|
builder := c.Delete().Where(keybinding.ID(id))
|
||||||
|
builder.mutation.id = &id
|
||||||
|
builder.mutation.op = OpDeleteOne
|
||||||
|
return &KeyBindingDeleteOne{builder}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns a query builder for KeyBinding.
|
||||||
|
func (c *KeyBindingClient) Query() *KeyBindingQuery {
|
||||||
|
return &KeyBindingQuery{
|
||||||
|
config: c.config,
|
||||||
|
ctx: &QueryContext{Type: TypeKeyBinding},
|
||||||
|
inters: c.Interceptors(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a KeyBinding entity by its id.
|
||||||
|
func (c *KeyBindingClient) Get(ctx context.Context, id int) (*KeyBinding, error) {
|
||||||
|
return c.Query().Where(keybinding.ID(id)).Only(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetX is like Get, but panics if an error occurs.
|
||||||
|
func (c *KeyBindingClient) GetX(ctx context.Context, id int) *KeyBinding {
|
||||||
|
obj, err := c.Get(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hooks returns the client hooks.
|
||||||
|
func (c *KeyBindingClient) Hooks() []Hook {
|
||||||
|
hooks := c.hooks.KeyBinding
|
||||||
|
return append(hooks[:len(hooks):len(hooks)], keybinding.Hooks[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interceptors returns the client interceptors.
|
||||||
|
func (c *KeyBindingClient) Interceptors() []Interceptor {
|
||||||
|
inters := c.inters.KeyBinding
|
||||||
|
return append(inters[:len(inters):len(inters)], keybinding.Interceptors[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *KeyBindingClient) mutate(ctx context.Context, m *KeyBindingMutation) (Value, error) {
|
||||||
|
switch m.Op() {
|
||||||
|
case OpCreate:
|
||||||
|
return (&KeyBindingCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpUpdate:
|
||||||
|
return (&KeyBindingUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpUpdateOne:
|
||||||
|
return (&KeyBindingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpDelete, OpDeleteOne:
|
||||||
|
return (&KeyBindingDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("ent: unknown KeyBinding mutation op: %q", m.Op())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThemeClient is a client for the Theme schema.
|
||||||
|
type ThemeClient struct {
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewThemeClient returns a client for the Theme from the given config.
|
||||||
|
func NewThemeClient(c config) *ThemeClient {
|
||||||
|
return &ThemeClient{config: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use adds a list of mutation hooks to the hooks stack.
|
||||||
|
// A call to `Use(f, g, h)` equals to `theme.Hooks(f(g(h())))`.
|
||||||
|
func (c *ThemeClient) Use(hooks ...Hook) {
|
||||||
|
c.hooks.Theme = append(c.hooks.Theme, hooks...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intercept adds a list of query interceptors to the interceptors stack.
|
||||||
|
// A call to `Intercept(f, g, h)` equals to `theme.Intercept(f(g(h())))`.
|
||||||
|
func (c *ThemeClient) Intercept(interceptors ...Interceptor) {
|
||||||
|
c.inters.Theme = append(c.inters.Theme, interceptors...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create returns a builder for creating a Theme entity.
|
||||||
|
func (c *ThemeClient) Create() *ThemeCreate {
|
||||||
|
mutation := newThemeMutation(c.config, OpCreate)
|
||||||
|
return &ThemeCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBulk returns a builder for creating a bulk of Theme entities.
|
||||||
|
func (c *ThemeClient) CreateBulk(builders ...*ThemeCreate) *ThemeCreateBulk {
|
||||||
|
return &ThemeCreateBulk{config: c.config, builders: builders}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||||
|
// a builder and applies setFunc on it.
|
||||||
|
func (c *ThemeClient) MapCreateBulk(slice any, setFunc func(*ThemeCreate, int)) *ThemeCreateBulk {
|
||||||
|
rv := reflect.ValueOf(slice)
|
||||||
|
if rv.Kind() != reflect.Slice {
|
||||||
|
return &ThemeCreateBulk{err: fmt.Errorf("calling to ThemeClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||||
|
}
|
||||||
|
builders := make([]*ThemeCreate, rv.Len())
|
||||||
|
for i := 0; i < rv.Len(); i++ {
|
||||||
|
builders[i] = c.Create()
|
||||||
|
setFunc(builders[i], i)
|
||||||
|
}
|
||||||
|
return &ThemeCreateBulk{config: c.config, builders: builders}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update returns an update builder for Theme.
|
||||||
|
func (c *ThemeClient) Update() *ThemeUpdate {
|
||||||
|
mutation := newThemeMutation(c.config, OpUpdate)
|
||||||
|
return &ThemeUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOne returns an update builder for the given entity.
|
||||||
|
func (c *ThemeClient) UpdateOne(_m *Theme) *ThemeUpdateOne {
|
||||||
|
mutation := newThemeMutation(c.config, OpUpdateOne, withTheme(_m))
|
||||||
|
return &ThemeUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOneID returns an update builder for the given id.
|
||||||
|
func (c *ThemeClient) UpdateOneID(id int) *ThemeUpdateOne {
|
||||||
|
mutation := newThemeMutation(c.config, OpUpdateOne, withThemeID(id))
|
||||||
|
return &ThemeUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete returns a delete builder for Theme.
|
||||||
|
func (c *ThemeClient) Delete() *ThemeDelete {
|
||||||
|
mutation := newThemeMutation(c.config, OpDelete)
|
||||||
|
return &ThemeDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOne returns a builder for deleting the given entity.
|
||||||
|
func (c *ThemeClient) DeleteOne(_m *Theme) *ThemeDeleteOne {
|
||||||
|
return c.DeleteOneID(_m.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOneID returns a builder for deleting the given entity by its id.
|
||||||
|
func (c *ThemeClient) DeleteOneID(id int) *ThemeDeleteOne {
|
||||||
|
builder := c.Delete().Where(theme.ID(id))
|
||||||
|
builder.mutation.id = &id
|
||||||
|
builder.mutation.op = OpDeleteOne
|
||||||
|
return &ThemeDeleteOne{builder}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns a query builder for Theme.
|
||||||
|
func (c *ThemeClient) Query() *ThemeQuery {
|
||||||
|
return &ThemeQuery{
|
||||||
|
config: c.config,
|
||||||
|
ctx: &QueryContext{Type: TypeTheme},
|
||||||
|
inters: c.Interceptors(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a Theme entity by its id.
|
||||||
|
func (c *ThemeClient) Get(ctx context.Context, id int) (*Theme, error) {
|
||||||
|
return c.Query().Where(theme.ID(id)).Only(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetX is like Get, but panics if an error occurs.
|
||||||
|
func (c *ThemeClient) GetX(ctx context.Context, id int) *Theme {
|
||||||
|
obj, err := c.Get(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hooks returns the client hooks.
|
||||||
|
func (c *ThemeClient) Hooks() []Hook {
|
||||||
|
hooks := c.hooks.Theme
|
||||||
|
return append(hooks[:len(hooks):len(hooks)], theme.Hooks[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interceptors returns the client interceptors.
|
||||||
|
func (c *ThemeClient) Interceptors() []Interceptor {
|
||||||
|
inters := c.inters.Theme
|
||||||
|
return append(inters[:len(inters):len(inters)], theme.Interceptors[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ThemeClient) mutate(ctx context.Context, m *ThemeMutation) (Value, error) {
|
||||||
|
switch m.Op() {
|
||||||
|
case OpCreate:
|
||||||
|
return (&ThemeCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpUpdate:
|
||||||
|
return (&ThemeUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpUpdateOne:
|
||||||
|
return (&ThemeUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||||
|
case OpDelete, OpDeleteOne:
|
||||||
|
return (&ThemeDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("ent: unknown Theme mutation op: %q", m.Op())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hooks and interceptors per client, for fast access.
|
||||||
|
type (
|
||||||
|
hooks struct {
|
||||||
|
Document, Extension, KeyBinding, Theme []ent.Hook
|
||||||
|
}
|
||||||
|
inters struct {
|
||||||
|
Document, Extension, KeyBinding, Theme []ent.Interceptor
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExecContext allows calling the underlying ExecContext method of the driver if it is supported by it.
|
||||||
|
// See, database/sql#DB.ExecContext for more information.
|
||||||
|
func (c *config) ExecContext(ctx context.Context, query string, args ...any) (stdsql.Result, error) {
|
||||||
|
ex, ok := c.driver.(interface {
|
||||||
|
ExecContext(context.Context, string, ...any) (stdsql.Result, error)
|
||||||
|
})
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Driver.ExecContext is not supported")
|
||||||
|
}
|
||||||
|
return ex.ExecContext(ctx, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryContext allows calling the underlying QueryContext method of the driver if it is supported by it.
|
||||||
|
// See, database/sql#DB.QueryContext for more information.
|
||||||
|
func (c *config) QueryContext(ctx context.Context, query string, args ...any) (*stdsql.Rows, error) {
|
||||||
|
q, ok := c.driver.(interface {
|
||||||
|
QueryContext(context.Context, string, ...any) (*stdsql.Rows, error)
|
||||||
|
})
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Driver.QueryContext is not supported")
|
||||||
|
}
|
||||||
|
return q.QueryContext(ctx, query, args...)
|
||||||
|
}
|
||||||
174
internal/models/ent/document.go
Normal file
174
internal/models/ent/document.go
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"voidraft/internal/models/ent/document"
|
||||||
|
|
||||||
|
"entgo.io/ent"
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Document is the model entity for the Document schema.
|
||||||
|
type Document struct {
|
||||||
|
config `json:"-"`
|
||||||
|
// ID of the ent.
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
// UUID for cross-device sync (UUIDv7)
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
// creation time
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
// update time
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
// deleted at
|
||||||
|
DeletedAt *string `json:"deleted_at,omitempty"`
|
||||||
|
// document title
|
||||||
|
Title string `json:"title"`
|
||||||
|
// document content
|
||||||
|
Content string `json:"content"`
|
||||||
|
// document locked status
|
||||||
|
Locked bool `json:"locked"`
|
||||||
|
selectValues sql.SelectValues
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanValues returns the types for scanning values from sql.Rows.
|
||||||
|
func (*Document) scanValues(columns []string) ([]any, error) {
|
||||||
|
values := make([]any, len(columns))
|
||||||
|
for i := range columns {
|
||||||
|
switch columns[i] {
|
||||||
|
case document.FieldLocked:
|
||||||
|
values[i] = new(sql.NullBool)
|
||||||
|
case document.FieldID:
|
||||||
|
values[i] = new(sql.NullInt64)
|
||||||
|
case document.FieldUUID, document.FieldCreatedAt, document.FieldUpdatedAt, document.FieldDeletedAt, document.FieldTitle, document.FieldContent:
|
||||||
|
values[i] = new(sql.NullString)
|
||||||
|
default:
|
||||||
|
values[i] = new(sql.UnknownType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignValues assigns the values that were returned from sql.Rows (after scanning)
|
||||||
|
// to the Document fields.
|
||||||
|
func (_m *Document) assignValues(columns []string, values []any) error {
|
||||||
|
if m, n := len(values), len(columns); m < n {
|
||||||
|
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
|
||||||
|
}
|
||||||
|
for i := range columns {
|
||||||
|
switch columns[i] {
|
||||||
|
case document.FieldID:
|
||||||
|
value, ok := values[i].(*sql.NullInt64)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field id", value)
|
||||||
|
}
|
||||||
|
_m.ID = int(value.Int64)
|
||||||
|
case document.FieldUUID:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field uuid", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.UUID = value.String
|
||||||
|
}
|
||||||
|
case document.FieldCreatedAt:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field created_at", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.CreatedAt = value.String
|
||||||
|
}
|
||||||
|
case document.FieldUpdatedAt:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.UpdatedAt = value.String
|
||||||
|
}
|
||||||
|
case document.FieldDeletedAt:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field deleted_at", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.DeletedAt = new(string)
|
||||||
|
*_m.DeletedAt = value.String
|
||||||
|
}
|
||||||
|
case document.FieldTitle:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field title", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.Title = value.String
|
||||||
|
}
|
||||||
|
case document.FieldContent:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field content", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.Content = value.String
|
||||||
|
}
|
||||||
|
case document.FieldLocked:
|
||||||
|
if value, ok := values[i].(*sql.NullBool); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field locked", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.Locked = value.Bool
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
_m.selectValues.Set(columns[i], values[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the ent.Value that was dynamically selected and assigned to the Document.
|
||||||
|
// This includes values selected through modifiers, order, etc.
|
||||||
|
func (_m *Document) Value(name string) (ent.Value, error) {
|
||||||
|
return _m.selectValues.Get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update returns a builder for updating this Document.
|
||||||
|
// Note that you need to call Document.Unwrap() before calling this method if this Document
|
||||||
|
// was returned from a transaction, and the transaction was committed or rolled back.
|
||||||
|
func (_m *Document) Update() *DocumentUpdateOne {
|
||||||
|
return NewDocumentClient(_m.config).UpdateOne(_m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap unwraps the Document entity that was returned from a transaction after it was closed,
|
||||||
|
// so that all future queries will be executed through the driver which created the transaction.
|
||||||
|
func (_m *Document) Unwrap() *Document {
|
||||||
|
_tx, ok := _m.config.driver.(*txDriver)
|
||||||
|
if !ok {
|
||||||
|
panic("ent: Document is not a transactional entity")
|
||||||
|
}
|
||||||
|
_m.config.driver = _tx.drv
|
||||||
|
return _m
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements the fmt.Stringer.
|
||||||
|
func (_m *Document) String() string {
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.WriteString("Document(")
|
||||||
|
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
||||||
|
builder.WriteString("uuid=")
|
||||||
|
builder.WriteString(_m.UUID)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("created_at=")
|
||||||
|
builder.WriteString(_m.CreatedAt)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("updated_at=")
|
||||||
|
builder.WriteString(_m.UpdatedAt)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
if v := _m.DeletedAt; v != nil {
|
||||||
|
builder.WriteString("deleted_at=")
|
||||||
|
builder.WriteString(*v)
|
||||||
|
}
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("title=")
|
||||||
|
builder.WriteString(_m.Title)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("content=")
|
||||||
|
builder.WriteString(_m.Content)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("locked=")
|
||||||
|
builder.WriteString(fmt.Sprintf("%v", _m.Locked))
|
||||||
|
builder.WriteByte(')')
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Documents is a parsable slice of Document.
|
||||||
|
type Documents []*Document
|
||||||
118
internal/models/ent/document/document.go
Normal file
118
internal/models/ent/document/document.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package document
|
||||||
|
|
||||||
|
import (
|
||||||
|
"entgo.io/ent"
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Label holds the string label denoting the document type in the database.
|
||||||
|
Label = "document"
|
||||||
|
// FieldID holds the string denoting the id field in the database.
|
||||||
|
FieldID = "id"
|
||||||
|
// FieldUUID holds the string denoting the uuid field in the database.
|
||||||
|
FieldUUID = "uuid"
|
||||||
|
// FieldCreatedAt holds the string denoting the created_at field in the database.
|
||||||
|
FieldCreatedAt = "created_at"
|
||||||
|
// FieldUpdatedAt holds the string denoting the updated_at field in the database.
|
||||||
|
FieldUpdatedAt = "updated_at"
|
||||||
|
// FieldDeletedAt holds the string denoting the deleted_at field in the database.
|
||||||
|
FieldDeletedAt = "deleted_at"
|
||||||
|
// FieldTitle holds the string denoting the title field in the database.
|
||||||
|
FieldTitle = "title"
|
||||||
|
// FieldContent holds the string denoting the content field in the database.
|
||||||
|
FieldContent = "content"
|
||||||
|
// FieldLocked holds the string denoting the locked field in the database.
|
||||||
|
FieldLocked = "locked"
|
||||||
|
// Table holds the table name of the document in the database.
|
||||||
|
Table = "documents"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Columns holds all SQL columns for document fields.
|
||||||
|
var Columns = []string{
|
||||||
|
FieldID,
|
||||||
|
FieldUUID,
|
||||||
|
FieldCreatedAt,
|
||||||
|
FieldUpdatedAt,
|
||||||
|
FieldDeletedAt,
|
||||||
|
FieldTitle,
|
||||||
|
FieldContent,
|
||||||
|
FieldLocked,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidColumn reports if the column name is valid (part of the table columns).
|
||||||
|
func ValidColumn(column string) bool {
|
||||||
|
for i := range Columns {
|
||||||
|
if column == Columns[i] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that the variables below are initialized by the runtime
|
||||||
|
// package on the initialization of the application. Therefore,
|
||||||
|
// it should be imported in the main as follows:
|
||||||
|
//
|
||||||
|
// import _ "voidraft/internal/models/ent/runtime"
|
||||||
|
var (
|
||||||
|
Hooks [2]ent.Hook
|
||||||
|
Interceptors [1]ent.Interceptor
|
||||||
|
// DefaultUUID holds the default value on creation for the "uuid" field.
|
||||||
|
DefaultUUID func() string
|
||||||
|
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
|
||||||
|
DefaultCreatedAt func() string
|
||||||
|
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
|
||||||
|
DefaultUpdatedAt func() string
|
||||||
|
// TitleValidator is a validator for the "title" field. It is called by the builders before save.
|
||||||
|
TitleValidator func(string) error
|
||||||
|
// DefaultContent holds the default value on creation for the "content" field.
|
||||||
|
DefaultContent string
|
||||||
|
// DefaultLocked holds the default value on creation for the "locked" field.
|
||||||
|
DefaultLocked bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// OrderOption defines the ordering options for the Document queries.
|
||||||
|
type OrderOption func(*sql.Selector)
|
||||||
|
|
||||||
|
// ByID orders the results by the id field.
|
||||||
|
func ByID(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldID, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByUUID orders the results by the uuid field.
|
||||||
|
func ByUUID(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldUUID, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByCreatedAt orders the results by the created_at field.
|
||||||
|
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByUpdatedAt orders the results by the updated_at field.
|
||||||
|
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByDeletedAt orders the results by the deleted_at field.
|
||||||
|
func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldDeletedAt, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByTitle orders the results by the title field.
|
||||||
|
func ByTitle(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldTitle, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByContent orders the results by the content field.
|
||||||
|
func ByContent(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldContent, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByLocked orders the results by the locked field.
|
||||||
|
func ByLocked(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldLocked, opts...).ToFunc()
|
||||||
|
}
|
||||||
534
internal/models/ent/document/where.go
Normal file
534
internal/models/ent/document/where.go
Normal file
@@ -0,0 +1,534 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package document
|
||||||
|
|
||||||
|
import (
|
||||||
|
"voidraft/internal/models/ent/predicate"
|
||||||
|
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ID filters vertices based on their ID field.
|
||||||
|
func ID(id int) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDEQ applies the EQ predicate on the ID field.
|
||||||
|
func IDEQ(id int) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDNEQ applies the NEQ predicate on the ID field.
|
||||||
|
func IDNEQ(id int) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNEQ(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDIn applies the In predicate on the ID field.
|
||||||
|
func IDIn(ids ...int) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldIn(FieldID, ids...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDNotIn applies the NotIn predicate on the ID field.
|
||||||
|
func IDNotIn(ids ...int) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNotIn(FieldID, ids...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDGT applies the GT predicate on the ID field.
|
||||||
|
func IDGT(id int) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGT(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDGTE applies the GTE predicate on the ID field.
|
||||||
|
func IDGTE(id int) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGTE(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDLT applies the LT predicate on the ID field.
|
||||||
|
func IDLT(id int) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLT(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDLTE applies the LTE predicate on the ID field.
|
||||||
|
func IDLTE(id int) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLTE(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUID applies equality check predicate on the "uuid" field. It's identical to UUIDEQ.
|
||||||
|
func UUID(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
|
||||||
|
func CreatedAt(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
|
||||||
|
func UpdatedAt(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ.
|
||||||
|
func DeletedAt(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title applies equality check predicate on the "title" field. It's identical to TitleEQ.
|
||||||
|
func Title(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content applies equality check predicate on the "content" field. It's identical to ContentEQ.
|
||||||
|
func Content(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locked applies equality check predicate on the "locked" field. It's identical to LockedEQ.
|
||||||
|
func Locked(v bool) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldLocked, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDEQ applies the EQ predicate on the "uuid" field.
|
||||||
|
func UUIDEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDNEQ applies the NEQ predicate on the "uuid" field.
|
||||||
|
func UUIDNEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNEQ(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDIn applies the In predicate on the "uuid" field.
|
||||||
|
func UUIDIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldIn(FieldUUID, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDNotIn applies the NotIn predicate on the "uuid" field.
|
||||||
|
func UUIDNotIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNotIn(FieldUUID, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDGT applies the GT predicate on the "uuid" field.
|
||||||
|
func UUIDGT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGT(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDGTE applies the GTE predicate on the "uuid" field.
|
||||||
|
func UUIDGTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGTE(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDLT applies the LT predicate on the "uuid" field.
|
||||||
|
func UUIDLT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLT(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDLTE applies the LTE predicate on the "uuid" field.
|
||||||
|
func UUIDLTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLTE(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDContains applies the Contains predicate on the "uuid" field.
|
||||||
|
func UUIDContains(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContains(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDHasPrefix applies the HasPrefix predicate on the "uuid" field.
|
||||||
|
func UUIDHasPrefix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasPrefix(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDHasSuffix applies the HasSuffix predicate on the "uuid" field.
|
||||||
|
func UUIDHasSuffix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasSuffix(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDIsNil applies the IsNil predicate on the "uuid" field.
|
||||||
|
func UUIDIsNil() predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldIsNull(FieldUUID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDNotNil applies the NotNil predicate on the "uuid" field.
|
||||||
|
func UUIDNotNil() predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNotNull(FieldUUID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDEqualFold applies the EqualFold predicate on the "uuid" field.
|
||||||
|
func UUIDEqualFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEqualFold(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDContainsFold applies the ContainsFold predicate on the "uuid" field.
|
||||||
|
func UUIDContainsFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContainsFold(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
|
||||||
|
func CreatedAtEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
|
||||||
|
func CreatedAtNEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNEQ(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtIn applies the In predicate on the "created_at" field.
|
||||||
|
func CreatedAtIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldIn(FieldCreatedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
|
||||||
|
func CreatedAtNotIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNotIn(FieldCreatedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtGT applies the GT predicate on the "created_at" field.
|
||||||
|
func CreatedAtGT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGT(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtGTE applies the GTE predicate on the "created_at" field.
|
||||||
|
func CreatedAtGTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGTE(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtLT applies the LT predicate on the "created_at" field.
|
||||||
|
func CreatedAtLT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLT(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtLTE applies the LTE predicate on the "created_at" field.
|
||||||
|
func CreatedAtLTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLTE(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtContains applies the Contains predicate on the "created_at" field.
|
||||||
|
func CreatedAtContains(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContains(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtHasPrefix applies the HasPrefix predicate on the "created_at" field.
|
||||||
|
func CreatedAtHasPrefix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasPrefix(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtHasSuffix applies the HasSuffix predicate on the "created_at" field.
|
||||||
|
func CreatedAtHasSuffix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasSuffix(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtEqualFold applies the EqualFold predicate on the "created_at" field.
|
||||||
|
func CreatedAtEqualFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEqualFold(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtContainsFold applies the ContainsFold predicate on the "created_at" field.
|
||||||
|
func CreatedAtContainsFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContainsFold(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtNEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNEQ(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtIn applies the In predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldIn(FieldUpdatedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtNotIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNotIn(FieldUpdatedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtGT applies the GT predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtGT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGT(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtGTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGTE(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtLT applies the LT predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtLT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLT(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtLTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLTE(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtContains applies the Contains predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtContains(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContains(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtHasPrefix applies the HasPrefix predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtHasPrefix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasPrefix(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtHasSuffix applies the HasSuffix predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtHasSuffix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasSuffix(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtEqualFold applies the EqualFold predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtEqualFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEqualFold(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtContainsFold applies the ContainsFold predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtContainsFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContainsFold(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtNEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNEQ(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtIn applies the In predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldIn(FieldDeletedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtNotIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNotIn(FieldDeletedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtGT applies the GT predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtGT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGT(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtGTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGTE(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtLT applies the LT predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtLT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLT(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtLTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLTE(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtContains applies the Contains predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtContains(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContains(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtHasPrefix applies the HasPrefix predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtHasPrefix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasPrefix(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtHasSuffix applies the HasSuffix predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtHasSuffix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasSuffix(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtIsNil() predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldIsNull(FieldDeletedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtNotNil() predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNotNull(FieldDeletedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtEqualFold applies the EqualFold predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtEqualFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEqualFold(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtContainsFold applies the ContainsFold predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtContainsFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContainsFold(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleEQ applies the EQ predicate on the "title" field.
|
||||||
|
func TitleEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleNEQ applies the NEQ predicate on the "title" field.
|
||||||
|
func TitleNEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNEQ(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleIn applies the In predicate on the "title" field.
|
||||||
|
func TitleIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldIn(FieldTitle, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleNotIn applies the NotIn predicate on the "title" field.
|
||||||
|
func TitleNotIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNotIn(FieldTitle, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleGT applies the GT predicate on the "title" field.
|
||||||
|
func TitleGT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGT(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleGTE applies the GTE predicate on the "title" field.
|
||||||
|
func TitleGTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGTE(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleLT applies the LT predicate on the "title" field.
|
||||||
|
func TitleLT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLT(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleLTE applies the LTE predicate on the "title" field.
|
||||||
|
func TitleLTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLTE(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleContains applies the Contains predicate on the "title" field.
|
||||||
|
func TitleContains(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContains(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleHasPrefix applies the HasPrefix predicate on the "title" field.
|
||||||
|
func TitleHasPrefix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasPrefix(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleHasSuffix applies the HasSuffix predicate on the "title" field.
|
||||||
|
func TitleHasSuffix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasSuffix(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleEqualFold applies the EqualFold predicate on the "title" field.
|
||||||
|
func TitleEqualFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEqualFold(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleContainsFold applies the ContainsFold predicate on the "title" field.
|
||||||
|
func TitleContainsFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContainsFold(FieldTitle, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentEQ applies the EQ predicate on the "content" field.
|
||||||
|
func ContentEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentNEQ applies the NEQ predicate on the "content" field.
|
||||||
|
func ContentNEQ(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNEQ(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentIn applies the In predicate on the "content" field.
|
||||||
|
func ContentIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldIn(FieldContent, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentNotIn applies the NotIn predicate on the "content" field.
|
||||||
|
func ContentNotIn(vs ...string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNotIn(FieldContent, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentGT applies the GT predicate on the "content" field.
|
||||||
|
func ContentGT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGT(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentGTE applies the GTE predicate on the "content" field.
|
||||||
|
func ContentGTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldGTE(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentLT applies the LT predicate on the "content" field.
|
||||||
|
func ContentLT(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLT(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentLTE applies the LTE predicate on the "content" field.
|
||||||
|
func ContentLTE(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldLTE(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentContains applies the Contains predicate on the "content" field.
|
||||||
|
func ContentContains(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContains(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentHasPrefix applies the HasPrefix predicate on the "content" field.
|
||||||
|
func ContentHasPrefix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasPrefix(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentHasSuffix applies the HasSuffix predicate on the "content" field.
|
||||||
|
func ContentHasSuffix(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldHasSuffix(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentIsNil applies the IsNil predicate on the "content" field.
|
||||||
|
func ContentIsNil() predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldIsNull(FieldContent))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentNotNil applies the NotNil predicate on the "content" field.
|
||||||
|
func ContentNotNil() predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNotNull(FieldContent))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentEqualFold applies the EqualFold predicate on the "content" field.
|
||||||
|
func ContentEqualFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEqualFold(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentContainsFold applies the ContainsFold predicate on the "content" field.
|
||||||
|
func ContentContainsFold(v string) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldContainsFold(FieldContent, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LockedEQ applies the EQ predicate on the "locked" field.
|
||||||
|
func LockedEQ(v bool) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldEQ(FieldLocked, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LockedNEQ applies the NEQ predicate on the "locked" field.
|
||||||
|
func LockedNEQ(v bool) predicate.Document {
|
||||||
|
return predicate.Document(sql.FieldNEQ(FieldLocked, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// And groups predicates with the AND operator between them.
|
||||||
|
func And(predicates ...predicate.Document) predicate.Document {
|
||||||
|
return predicate.Document(sql.AndPredicates(predicates...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or groups predicates with the OR operator between them.
|
||||||
|
func Or(predicates ...predicate.Document) predicate.Document {
|
||||||
|
return predicate.Document(sql.OrPredicates(predicates...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not applies the not operator on the given predicate.
|
||||||
|
func Not(p predicate.Document) predicate.Document {
|
||||||
|
return predicate.Document(sql.NotPredicates(p))
|
||||||
|
}
|
||||||
343
internal/models/ent/document_create.go
Normal file
343
internal/models/ent/document_create.go
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"voidraft/internal/models/ent/document"
|
||||||
|
|
||||||
|
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||||
|
"entgo.io/ent/schema/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DocumentCreate is the builder for creating a Document entity.
|
||||||
|
type DocumentCreate struct {
|
||||||
|
config
|
||||||
|
mutation *DocumentMutation
|
||||||
|
hooks []Hook
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUUID sets the "uuid" field.
|
||||||
|
func (_c *DocumentCreate) SetUUID(v string) *DocumentCreate {
|
||||||
|
_c.mutation.SetUUID(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableUUID sets the "uuid" field if the given value is not nil.
|
||||||
|
func (_c *DocumentCreate) SetNillableUUID(v *string) *DocumentCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetUUID(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCreatedAt sets the "created_at" field.
|
||||||
|
func (_c *DocumentCreate) SetCreatedAt(v string) *DocumentCreate {
|
||||||
|
_c.mutation.SetCreatedAt(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
|
||||||
|
func (_c *DocumentCreate) SetNillableCreatedAt(v *string) *DocumentCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetCreatedAt(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUpdatedAt sets the "updated_at" field.
|
||||||
|
func (_c *DocumentCreate) SetUpdatedAt(v string) *DocumentCreate {
|
||||||
|
_c.mutation.SetUpdatedAt(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
|
||||||
|
func (_c *DocumentCreate) SetNillableUpdatedAt(v *string) *DocumentCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetUpdatedAt(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeletedAt sets the "deleted_at" field.
|
||||||
|
func (_c *DocumentCreate) SetDeletedAt(v string) *DocumentCreate {
|
||||||
|
_c.mutation.SetDeletedAt(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
|
||||||
|
func (_c *DocumentCreate) SetNillableDeletedAt(v *string) *DocumentCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetDeletedAt(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTitle sets the "title" field.
|
||||||
|
func (_c *DocumentCreate) SetTitle(v string) *DocumentCreate {
|
||||||
|
_c.mutation.SetTitle(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContent sets the "content" field.
|
||||||
|
func (_c *DocumentCreate) SetContent(v string) *DocumentCreate {
|
||||||
|
_c.mutation.SetContent(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableContent sets the "content" field if the given value is not nil.
|
||||||
|
func (_c *DocumentCreate) SetNillableContent(v *string) *DocumentCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetContent(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLocked sets the "locked" field.
|
||||||
|
func (_c *DocumentCreate) SetLocked(v bool) *DocumentCreate {
|
||||||
|
_c.mutation.SetLocked(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableLocked sets the "locked" field if the given value is not nil.
|
||||||
|
func (_c *DocumentCreate) SetNillableLocked(v *bool) *DocumentCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetLocked(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutation returns the DocumentMutation object of the builder.
|
||||||
|
func (_c *DocumentCreate) Mutation() *DocumentMutation {
|
||||||
|
return _c.mutation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save creates the Document in the database.
|
||||||
|
func (_c *DocumentCreate) Save(ctx context.Context) (*Document, error) {
|
||||||
|
if err := _c.defaults(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveX calls Save and panics if Save returns an error.
|
||||||
|
func (_c *DocumentCreate) SaveX(ctx context.Context) *Document {
|
||||||
|
v, err := _c.Save(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the query.
|
||||||
|
func (_c *DocumentCreate) Exec(ctx context.Context) error {
|
||||||
|
_, err := _c.Save(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_c *DocumentCreate) ExecX(ctx context.Context) {
|
||||||
|
if err := _c.Exec(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaults sets the default values of the builder before save.
|
||||||
|
func (_c *DocumentCreate) defaults() error {
|
||||||
|
if _, ok := _c.mutation.UUID(); !ok {
|
||||||
|
if document.DefaultUUID == nil {
|
||||||
|
return fmt.Errorf("ent: uninitialized document.DefaultUUID (forgotten import ent/runtime?)")
|
||||||
|
}
|
||||||
|
v := document.DefaultUUID()
|
||||||
|
_c.mutation.SetUUID(v)
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.CreatedAt(); !ok {
|
||||||
|
if document.DefaultCreatedAt == nil {
|
||||||
|
return fmt.Errorf("ent: uninitialized document.DefaultCreatedAt (forgotten import ent/runtime?)")
|
||||||
|
}
|
||||||
|
v := document.DefaultCreatedAt()
|
||||||
|
_c.mutation.SetCreatedAt(v)
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.UpdatedAt(); !ok {
|
||||||
|
if document.DefaultUpdatedAt == nil {
|
||||||
|
return fmt.Errorf("ent: uninitialized document.DefaultUpdatedAt (forgotten import ent/runtime?)")
|
||||||
|
}
|
||||||
|
v := document.DefaultUpdatedAt()
|
||||||
|
_c.mutation.SetUpdatedAt(v)
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.Content(); !ok {
|
||||||
|
v := document.DefaultContent
|
||||||
|
_c.mutation.SetContent(v)
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.Locked(); !ok {
|
||||||
|
v := document.DefaultLocked
|
||||||
|
_c.mutation.SetLocked(v)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check runs all checks and user-defined validators on the builder.
|
||||||
|
func (_c *DocumentCreate) check() error {
|
||||||
|
if _, ok := _c.mutation.CreatedAt(); !ok {
|
||||||
|
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "Document.created_at"`)}
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.UpdatedAt(); !ok {
|
||||||
|
return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "Document.updated_at"`)}
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.Title(); !ok {
|
||||||
|
return &ValidationError{Name: "title", err: errors.New(`ent: missing required field "Document.title"`)}
|
||||||
|
}
|
||||||
|
if v, ok := _c.mutation.Title(); ok {
|
||||||
|
if err := document.TitleValidator(v); err != nil {
|
||||||
|
return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "Document.title": %w`, err)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.Locked(); !ok {
|
||||||
|
return &ValidationError{Name: "locked", err: errors.New(`ent: missing required field "Document.locked"`)}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *DocumentCreate) sqlSave(ctx context.Context) (*Document, error) {
|
||||||
|
if err := _c.check(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_node, _spec := _c.createSpec()
|
||||||
|
if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
|
||||||
|
if sqlgraph.IsConstraintError(err) {
|
||||||
|
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
id := _spec.ID.Value.(int64)
|
||||||
|
_node.ID = int(id)
|
||||||
|
_c.mutation.id = &_node.ID
|
||||||
|
_c.mutation.done = true
|
||||||
|
return _node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) {
|
||||||
|
var (
|
||||||
|
_node = &Document{config: _c.config}
|
||||||
|
_spec = sqlgraph.NewCreateSpec(document.Table, sqlgraph.NewFieldSpec(document.FieldID, field.TypeInt))
|
||||||
|
)
|
||||||
|
if value, ok := _c.mutation.UUID(); ok {
|
||||||
|
_spec.SetField(document.FieldUUID, field.TypeString, value)
|
||||||
|
_node.UUID = value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.CreatedAt(); ok {
|
||||||
|
_spec.SetField(document.FieldCreatedAt, field.TypeString, value)
|
||||||
|
_node.CreatedAt = value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.UpdatedAt(); ok {
|
||||||
|
_spec.SetField(document.FieldUpdatedAt, field.TypeString, value)
|
||||||
|
_node.UpdatedAt = value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.DeletedAt(); ok {
|
||||||
|
_spec.SetField(document.FieldDeletedAt, field.TypeString, value)
|
||||||
|
_node.DeletedAt = &value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.Title(); ok {
|
||||||
|
_spec.SetField(document.FieldTitle, field.TypeString, value)
|
||||||
|
_node.Title = value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.Content(); ok {
|
||||||
|
_spec.SetField(document.FieldContent, field.TypeString, value)
|
||||||
|
_node.Content = value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.Locked(); ok {
|
||||||
|
_spec.SetField(document.FieldLocked, field.TypeBool, value)
|
||||||
|
_node.Locked = value
|
||||||
|
}
|
||||||
|
return _node, _spec
|
||||||
|
}
|
||||||
|
|
||||||
|
// DocumentCreateBulk is the builder for creating many Document entities in bulk.
|
||||||
|
type DocumentCreateBulk struct {
|
||||||
|
config
|
||||||
|
err error
|
||||||
|
builders []*DocumentCreate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save creates the Document entities in the database.
|
||||||
|
func (_c *DocumentCreateBulk) Save(ctx context.Context) ([]*Document, error) {
|
||||||
|
if _c.err != nil {
|
||||||
|
return nil, _c.err
|
||||||
|
}
|
||||||
|
specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
|
||||||
|
nodes := make([]*Document, len(_c.builders))
|
||||||
|
mutators := make([]Mutator, len(_c.builders))
|
||||||
|
for i := range _c.builders {
|
||||||
|
func(i int, root context.Context) {
|
||||||
|
builder := _c.builders[i]
|
||||||
|
builder.defaults()
|
||||||
|
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||||
|
mutation, ok := m.(*DocumentMutation)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||||
|
}
|
||||||
|
if err := builder.check(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
builder.mutation = mutation
|
||||||
|
var err error
|
||||||
|
nodes[i], specs[i] = builder.createSpec()
|
||||||
|
if i < len(mutators)-1 {
|
||||||
|
_, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
|
||||||
|
} else {
|
||||||
|
spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
|
||||||
|
// Invoke the actual operation on the latest mutation in the chain.
|
||||||
|
if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
|
||||||
|
if sqlgraph.IsConstraintError(err) {
|
||||||
|
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mutation.id = &nodes[i].ID
|
||||||
|
if specs[i].ID.Value != nil {
|
||||||
|
id := specs[i].ID.Value.(int64)
|
||||||
|
nodes[i].ID = int(id)
|
||||||
|
}
|
||||||
|
mutation.done = true
|
||||||
|
return nodes[i], nil
|
||||||
|
})
|
||||||
|
for i := len(builder.hooks) - 1; i >= 0; i-- {
|
||||||
|
mut = builder.hooks[i](mut)
|
||||||
|
}
|
||||||
|
mutators[i] = mut
|
||||||
|
}(i, ctx)
|
||||||
|
}
|
||||||
|
if len(mutators) > 0 {
|
||||||
|
if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveX is like Save, but panics if an error occurs.
|
||||||
|
func (_c *DocumentCreateBulk) SaveX(ctx context.Context) []*Document {
|
||||||
|
v, err := _c.Save(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the query.
|
||||||
|
func (_c *DocumentCreateBulk) Exec(ctx context.Context) error {
|
||||||
|
_, err := _c.Save(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_c *DocumentCreateBulk) ExecX(ctx context.Context) {
|
||||||
|
if err := _c.Exec(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
88
internal/models/ent/document_delete.go
Normal file
88
internal/models/ent/document_delete.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"voidraft/internal/models/ent/document"
|
||||||
|
"voidraft/internal/models/ent/predicate"
|
||||||
|
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||||
|
"entgo.io/ent/schema/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DocumentDelete is the builder for deleting a Document entity.
|
||||||
|
type DocumentDelete struct {
|
||||||
|
config
|
||||||
|
hooks []Hook
|
||||||
|
mutation *DocumentMutation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where appends a list predicates to the DocumentDelete builder.
|
||||||
|
func (_d *DocumentDelete) Where(ps ...predicate.Document) *DocumentDelete {
|
||||||
|
_d.mutation.Where(ps...)
|
||||||
|
return _d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the deletion query and returns how many vertices were deleted.
|
||||||
|
func (_d *DocumentDelete) Exec(ctx context.Context) (int, error) {
|
||||||
|
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_d *DocumentDelete) ExecX(ctx context.Context) int {
|
||||||
|
n, err := _d.Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_d *DocumentDelete) sqlExec(ctx context.Context) (int, error) {
|
||||||
|
_spec := sqlgraph.NewDeleteSpec(document.Table, sqlgraph.NewFieldSpec(document.FieldID, field.TypeInt))
|
||||||
|
if ps := _d.mutation.predicates; len(ps) > 0 {
|
||||||
|
_spec.Predicate = func(selector *sql.Selector) {
|
||||||
|
for i := range ps {
|
||||||
|
ps[i](selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
|
||||||
|
if err != nil && sqlgraph.IsConstraintError(err) {
|
||||||
|
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||||
|
}
|
||||||
|
_d.mutation.done = true
|
||||||
|
return affected, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DocumentDeleteOne is the builder for deleting a single Document entity.
|
||||||
|
type DocumentDeleteOne struct {
|
||||||
|
_d *DocumentDelete
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where appends a list predicates to the DocumentDelete builder.
|
||||||
|
func (_d *DocumentDeleteOne) Where(ps ...predicate.Document) *DocumentDeleteOne {
|
||||||
|
_d._d.mutation.Where(ps...)
|
||||||
|
return _d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the deletion query.
|
||||||
|
func (_d *DocumentDeleteOne) Exec(ctx context.Context) error {
|
||||||
|
n, err := _d._d.Exec(ctx)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case n == 0:
|
||||||
|
return &NotFoundError{document.Label}
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_d *DocumentDeleteOne) ExecX(ctx context.Context) {
|
||||||
|
if err := _d.Exec(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
577
internal/models/ent/document_query.go
Normal file
577
internal/models/ent/document_query.go
Normal file
@@ -0,0 +1,577 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"voidraft/internal/models/ent/document"
|
||||||
|
"voidraft/internal/models/ent/predicate"
|
||||||
|
|
||||||
|
"entgo.io/ent"
|
||||||
|
"entgo.io/ent/dialect"
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||||
|
"entgo.io/ent/schema/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DocumentQuery is the builder for querying Document entities.
|
||||||
|
type DocumentQuery struct {
|
||||||
|
config
|
||||||
|
ctx *QueryContext
|
||||||
|
order []document.OrderOption
|
||||||
|
inters []Interceptor
|
||||||
|
predicates []predicate.Document
|
||||||
|
modifiers []func(*sql.Selector)
|
||||||
|
// intermediate query (i.e. traversal path).
|
||||||
|
sql *sql.Selector
|
||||||
|
path func(context.Context) (*sql.Selector, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where adds a new predicate for the DocumentQuery builder.
|
||||||
|
func (_q *DocumentQuery) Where(ps ...predicate.Document) *DocumentQuery {
|
||||||
|
_q.predicates = append(_q.predicates, ps...)
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit the number of records to be returned by this query.
|
||||||
|
func (_q *DocumentQuery) Limit(limit int) *DocumentQuery {
|
||||||
|
_q.ctx.Limit = &limit
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset to start from.
|
||||||
|
func (_q *DocumentQuery) Offset(offset int) *DocumentQuery {
|
||||||
|
_q.ctx.Offset = &offset
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unique configures the query builder to filter duplicate records on query.
|
||||||
|
// By default, unique is set to true, and can be disabled using this method.
|
||||||
|
func (_q *DocumentQuery) Unique(unique bool) *DocumentQuery {
|
||||||
|
_q.ctx.Unique = &unique
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order specifies how the records should be ordered.
|
||||||
|
func (_q *DocumentQuery) Order(o ...document.OrderOption) *DocumentQuery {
|
||||||
|
_q.order = append(_q.order, o...)
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// First returns the first Document entity from the query.
|
||||||
|
// Returns a *NotFoundError when no Document was found.
|
||||||
|
func (_q *DocumentQuery) First(ctx context.Context) (*Document, error) {
|
||||||
|
nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(nodes) == 0 {
|
||||||
|
return nil, &NotFoundError{document.Label}
|
||||||
|
}
|
||||||
|
return nodes[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstX is like First, but panics if an error occurs.
|
||||||
|
func (_q *DocumentQuery) FirstX(ctx context.Context) *Document {
|
||||||
|
node, err := _q.First(ctx)
|
||||||
|
if err != nil && !IsNotFound(err) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstID returns the first Document ID from the query.
|
||||||
|
// Returns a *NotFoundError when no Document ID was found.
|
||||||
|
func (_q *DocumentQuery) FirstID(ctx context.Context) (id int, err error) {
|
||||||
|
var ids []int
|
||||||
|
if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(ids) == 0 {
|
||||||
|
err = &NotFoundError{document.Label}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return ids[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstIDX is like FirstID, but panics if an error occurs.
|
||||||
|
func (_q *DocumentQuery) FirstIDX(ctx context.Context) int {
|
||||||
|
id, err := _q.FirstID(ctx)
|
||||||
|
if err != nil && !IsNotFound(err) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only returns a single Document entity found by the query, ensuring it only returns one.
|
||||||
|
// Returns a *NotSingularError when more than one Document entity is found.
|
||||||
|
// Returns a *NotFoundError when no Document entities are found.
|
||||||
|
func (_q *DocumentQuery) Only(ctx context.Context) (*Document, error) {
|
||||||
|
nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch len(nodes) {
|
||||||
|
case 1:
|
||||||
|
return nodes[0], nil
|
||||||
|
case 0:
|
||||||
|
return nil, &NotFoundError{document.Label}
|
||||||
|
default:
|
||||||
|
return nil, &NotSingularError{document.Label}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlyX is like Only, but panics if an error occurs.
|
||||||
|
func (_q *DocumentQuery) OnlyX(ctx context.Context) *Document {
|
||||||
|
node, err := _q.Only(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlyID is like Only, but returns the only Document ID in the query.
|
||||||
|
// Returns a *NotSingularError when more than one Document ID is found.
|
||||||
|
// Returns a *NotFoundError when no entities are found.
|
||||||
|
func (_q *DocumentQuery) OnlyID(ctx context.Context) (id int, err error) {
|
||||||
|
var ids []int
|
||||||
|
if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch len(ids) {
|
||||||
|
case 1:
|
||||||
|
id = ids[0]
|
||||||
|
case 0:
|
||||||
|
err = &NotFoundError{document.Label}
|
||||||
|
default:
|
||||||
|
err = &NotSingularError{document.Label}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlyIDX is like OnlyID, but panics if an error occurs.
|
||||||
|
func (_q *DocumentQuery) OnlyIDX(ctx context.Context) int {
|
||||||
|
id, err := _q.OnlyID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
// All executes the query and returns a list of Documents.
|
||||||
|
func (_q *DocumentQuery) All(ctx context.Context) ([]*Document, error) {
|
||||||
|
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
|
||||||
|
if err := _q.prepareQuery(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
qr := querierAll[[]*Document, *DocumentQuery]()
|
||||||
|
return withInterceptors[[]*Document](ctx, _q, qr, _q.inters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllX is like All, but panics if an error occurs.
|
||||||
|
func (_q *DocumentQuery) AllX(ctx context.Context) []*Document {
|
||||||
|
nodes, err := _q.All(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDs executes the query and returns a list of Document IDs.
|
||||||
|
func (_q *DocumentQuery) IDs(ctx context.Context) (ids []int, err error) {
|
||||||
|
if _q.ctx.Unique == nil && _q.path != nil {
|
||||||
|
_q.Unique(true)
|
||||||
|
}
|
||||||
|
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
|
||||||
|
if err = _q.Select(document.FieldID).Scan(ctx, &ids); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDsX is like IDs, but panics if an error occurs.
|
||||||
|
func (_q *DocumentQuery) IDsX(ctx context.Context) []int {
|
||||||
|
ids, err := _q.IDs(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the count of the given query.
|
||||||
|
func (_q *DocumentQuery) Count(ctx context.Context) (int, error) {
|
||||||
|
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
|
||||||
|
if err := _q.prepareQuery(ctx); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return withInterceptors[int](ctx, _q, querierCount[*DocumentQuery](), _q.inters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountX is like Count, but panics if an error occurs.
|
||||||
|
func (_q *DocumentQuery) CountX(ctx context.Context) int {
|
||||||
|
count, err := _q.Count(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exist returns true if the query has elements in the graph.
|
||||||
|
func (_q *DocumentQuery) Exist(ctx context.Context) (bool, error) {
|
||||||
|
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
|
||||||
|
switch _, err := _q.FirstID(ctx); {
|
||||||
|
case IsNotFound(err):
|
||||||
|
return false, nil
|
||||||
|
case err != nil:
|
||||||
|
return false, fmt.Errorf("ent: check existence: %w", err)
|
||||||
|
default:
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistX is like Exist, but panics if an error occurs.
|
||||||
|
func (_q *DocumentQuery) ExistX(ctx context.Context) bool {
|
||||||
|
exist, err := _q.Exist(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return exist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone returns a duplicate of the DocumentQuery builder, including all associated steps. It can be
|
||||||
|
// used to prepare common query builders and use them differently after the clone is made.
|
||||||
|
func (_q *DocumentQuery) Clone() *DocumentQuery {
|
||||||
|
if _q == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &DocumentQuery{
|
||||||
|
config: _q.config,
|
||||||
|
ctx: _q.ctx.Clone(),
|
||||||
|
order: append([]document.OrderOption{}, _q.order...),
|
||||||
|
inters: append([]Interceptor{}, _q.inters...),
|
||||||
|
predicates: append([]predicate.Document{}, _q.predicates...),
|
||||||
|
// clone intermediate query.
|
||||||
|
sql: _q.sql.Clone(),
|
||||||
|
path: _q.path,
|
||||||
|
modifiers: append([]func(*sql.Selector){}, _q.modifiers...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupBy is used to group vertices by one or more fields/columns.
|
||||||
|
// It is often used with aggregate functions, like: count, max, mean, min, sum.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// var v []struct {
|
||||||
|
// UUID string `json:"uuid"`
|
||||||
|
// Count int `json:"count,omitempty"`
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// client.Document.Query().
|
||||||
|
// GroupBy(document.FieldUUID).
|
||||||
|
// Aggregate(ent.Count()).
|
||||||
|
// Scan(ctx, &v)
|
||||||
|
func (_q *DocumentQuery) GroupBy(field string, fields ...string) *DocumentGroupBy {
|
||||||
|
_q.ctx.Fields = append([]string{field}, fields...)
|
||||||
|
grbuild := &DocumentGroupBy{build: _q}
|
||||||
|
grbuild.flds = &_q.ctx.Fields
|
||||||
|
grbuild.label = document.Label
|
||||||
|
grbuild.scan = grbuild.Scan
|
||||||
|
return grbuild
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select allows the selection one or more fields/columns for the given query,
|
||||||
|
// instead of selecting all fields in the entity.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// var v []struct {
|
||||||
|
// UUID string `json:"uuid"`
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// client.Document.Query().
|
||||||
|
// Select(document.FieldUUID).
|
||||||
|
// Scan(ctx, &v)
|
||||||
|
func (_q *DocumentQuery) Select(fields ...string) *DocumentSelect {
|
||||||
|
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
|
||||||
|
sbuild := &DocumentSelect{DocumentQuery: _q}
|
||||||
|
sbuild.label = document.Label
|
||||||
|
sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
|
||||||
|
return sbuild
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregate returns a DocumentSelect configured with the given aggregations.
|
||||||
|
func (_q *DocumentQuery) Aggregate(fns ...AggregateFunc) *DocumentSelect {
|
||||||
|
return _q.Select().Aggregate(fns...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_q *DocumentQuery) prepareQuery(ctx context.Context) error {
|
||||||
|
for _, inter := range _q.inters {
|
||||||
|
if inter == nil {
|
||||||
|
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
|
||||||
|
}
|
||||||
|
if trv, ok := inter.(Traverser); ok {
|
||||||
|
if err := trv.Traverse(ctx, _q); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, f := range _q.ctx.Fields {
|
||||||
|
if !document.ValidColumn(f) {
|
||||||
|
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _q.path != nil {
|
||||||
|
prev, err := _q.path(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_q.sql = prev
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_q *DocumentQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Document, error) {
|
||||||
|
var (
|
||||||
|
nodes = []*Document{}
|
||||||
|
_spec = _q.querySpec()
|
||||||
|
)
|
||||||
|
_spec.ScanValues = func(columns []string) ([]any, error) {
|
||||||
|
return (*Document).scanValues(nil, columns)
|
||||||
|
}
|
||||||
|
_spec.Assign = func(columns []string, values []any) error {
|
||||||
|
node := &Document{config: _q.config}
|
||||||
|
nodes = append(nodes, node)
|
||||||
|
return node.assignValues(columns, values)
|
||||||
|
}
|
||||||
|
if len(_q.modifiers) > 0 {
|
||||||
|
_spec.Modifiers = _q.modifiers
|
||||||
|
}
|
||||||
|
for i := range hooks {
|
||||||
|
hooks[i](ctx, _spec)
|
||||||
|
}
|
||||||
|
if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(nodes) == 0 {
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_q *DocumentQuery) sqlCount(ctx context.Context) (int, error) {
|
||||||
|
_spec := _q.querySpec()
|
||||||
|
if len(_q.modifiers) > 0 {
|
||||||
|
_spec.Modifiers = _q.modifiers
|
||||||
|
}
|
||||||
|
_spec.Node.Columns = _q.ctx.Fields
|
||||||
|
if len(_q.ctx.Fields) > 0 {
|
||||||
|
_spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
|
||||||
|
}
|
||||||
|
return sqlgraph.CountNodes(ctx, _q.driver, _spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_q *DocumentQuery) querySpec() *sqlgraph.QuerySpec {
|
||||||
|
_spec := sqlgraph.NewQuerySpec(document.Table, document.Columns, sqlgraph.NewFieldSpec(document.FieldID, field.TypeInt))
|
||||||
|
_spec.From = _q.sql
|
||||||
|
if unique := _q.ctx.Unique; unique != nil {
|
||||||
|
_spec.Unique = *unique
|
||||||
|
} else if _q.path != nil {
|
||||||
|
_spec.Unique = true
|
||||||
|
}
|
||||||
|
if fields := _q.ctx.Fields; len(fields) > 0 {
|
||||||
|
_spec.Node.Columns = make([]string, 0, len(fields))
|
||||||
|
_spec.Node.Columns = append(_spec.Node.Columns, document.FieldID)
|
||||||
|
for i := range fields {
|
||||||
|
if fields[i] != document.FieldID {
|
||||||
|
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ps := _q.predicates; len(ps) > 0 {
|
||||||
|
_spec.Predicate = func(selector *sql.Selector) {
|
||||||
|
for i := range ps {
|
||||||
|
ps[i](selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if limit := _q.ctx.Limit; limit != nil {
|
||||||
|
_spec.Limit = *limit
|
||||||
|
}
|
||||||
|
if offset := _q.ctx.Offset; offset != nil {
|
||||||
|
_spec.Offset = *offset
|
||||||
|
}
|
||||||
|
if ps := _q.order; len(ps) > 0 {
|
||||||
|
_spec.Order = func(selector *sql.Selector) {
|
||||||
|
for i := range ps {
|
||||||
|
ps[i](selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _spec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_q *DocumentQuery) sqlQuery(ctx context.Context) *sql.Selector {
|
||||||
|
builder := sql.Dialect(_q.driver.Dialect())
|
||||||
|
t1 := builder.Table(document.Table)
|
||||||
|
columns := _q.ctx.Fields
|
||||||
|
if len(columns) == 0 {
|
||||||
|
columns = document.Columns
|
||||||
|
}
|
||||||
|
selector := builder.Select(t1.Columns(columns...)...).From(t1)
|
||||||
|
if _q.sql != nil {
|
||||||
|
selector = _q.sql
|
||||||
|
selector.Select(selector.Columns(columns...)...)
|
||||||
|
}
|
||||||
|
if _q.ctx.Unique != nil && *_q.ctx.Unique {
|
||||||
|
selector.Distinct()
|
||||||
|
}
|
||||||
|
for _, m := range _q.modifiers {
|
||||||
|
m(selector)
|
||||||
|
}
|
||||||
|
for _, p := range _q.predicates {
|
||||||
|
p(selector)
|
||||||
|
}
|
||||||
|
for _, p := range _q.order {
|
||||||
|
p(selector)
|
||||||
|
}
|
||||||
|
if offset := _q.ctx.Offset; offset != nil {
|
||||||
|
// limit is mandatory for offset clause. We start
|
||||||
|
// with default value, and override it below if needed.
|
||||||
|
selector.Offset(*offset).Limit(math.MaxInt32)
|
||||||
|
}
|
||||||
|
if limit := _q.ctx.Limit; limit != nil {
|
||||||
|
selector.Limit(*limit)
|
||||||
|
}
|
||||||
|
return selector
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForUpdate locks the selected rows against concurrent updates, and prevent them from being
|
||||||
|
// updated, deleted or "selected ... for update" by other sessions, until the transaction is
|
||||||
|
// either committed or rolled-back.
|
||||||
|
func (_q *DocumentQuery) ForUpdate(opts ...sql.LockOption) *DocumentQuery {
|
||||||
|
if _q.driver.Dialect() == dialect.Postgres {
|
||||||
|
_q.Unique(false)
|
||||||
|
}
|
||||||
|
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
|
||||||
|
s.ForUpdate(opts...)
|
||||||
|
})
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock
|
||||||
|
// on any rows that are read. Other sessions can read the rows, but cannot modify them
|
||||||
|
// until your transaction commits.
|
||||||
|
func (_q *DocumentQuery) ForShare(opts ...sql.LockOption) *DocumentQuery {
|
||||||
|
if _q.driver.Dialect() == dialect.Postgres {
|
||||||
|
_q.Unique(false)
|
||||||
|
}
|
||||||
|
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
|
||||||
|
s.ForShare(opts...)
|
||||||
|
})
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify adds a query modifier for attaching custom logic to queries.
|
||||||
|
func (_q *DocumentQuery) Modify(modifiers ...func(s *sql.Selector)) *DocumentSelect {
|
||||||
|
_q.modifiers = append(_q.modifiers, modifiers...)
|
||||||
|
return _q.Select()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DocumentGroupBy is the group-by builder for Document entities.
|
||||||
|
type DocumentGroupBy struct {
|
||||||
|
selector
|
||||||
|
build *DocumentQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregate adds the given aggregation functions to the group-by query.
|
||||||
|
func (_g *DocumentGroupBy) Aggregate(fns ...AggregateFunc) *DocumentGroupBy {
|
||||||
|
_g.fns = append(_g.fns, fns...)
|
||||||
|
return _g
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan applies the selector query and scans the result into the given value.
|
||||||
|
func (_g *DocumentGroupBy) Scan(ctx context.Context, v any) error {
|
||||||
|
ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
|
||||||
|
if err := _g.build.prepareQuery(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return scanWithInterceptors[*DocumentQuery, *DocumentGroupBy](ctx, _g.build, _g, _g.build.inters, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_g *DocumentGroupBy) sqlScan(ctx context.Context, root *DocumentQuery, v any) error {
|
||||||
|
selector := root.sqlQuery(ctx).Select()
|
||||||
|
aggregation := make([]string, 0, len(_g.fns))
|
||||||
|
for _, fn := range _g.fns {
|
||||||
|
aggregation = append(aggregation, fn(selector))
|
||||||
|
}
|
||||||
|
if len(selector.SelectedColumns()) == 0 {
|
||||||
|
columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
|
||||||
|
for _, f := range *_g.flds {
|
||||||
|
columns = append(columns, selector.C(f))
|
||||||
|
}
|
||||||
|
columns = append(columns, aggregation...)
|
||||||
|
selector.Select(columns...)
|
||||||
|
}
|
||||||
|
selector.GroupBy(selector.Columns(*_g.flds...)...)
|
||||||
|
if err := selector.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rows := &sql.Rows{}
|
||||||
|
query, args := selector.Query()
|
||||||
|
if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
return sql.ScanSlice(rows, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DocumentSelect is the builder for selecting fields of Document entities.
|
||||||
|
type DocumentSelect struct {
|
||||||
|
*DocumentQuery
|
||||||
|
selector
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregate adds the given aggregation functions to the selector query.
|
||||||
|
func (_s *DocumentSelect) Aggregate(fns ...AggregateFunc) *DocumentSelect {
|
||||||
|
_s.fns = append(_s.fns, fns...)
|
||||||
|
return _s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan applies the selector query and scans the result into the given value.
|
||||||
|
func (_s *DocumentSelect) Scan(ctx context.Context, v any) error {
|
||||||
|
ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
|
||||||
|
if err := _s.prepareQuery(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return scanWithInterceptors[*DocumentQuery, *DocumentSelect](ctx, _s.DocumentQuery, _s, _s.inters, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_s *DocumentSelect) sqlScan(ctx context.Context, root *DocumentQuery, v any) error {
|
||||||
|
selector := root.sqlQuery(ctx)
|
||||||
|
aggregation := make([]string, 0, len(_s.fns))
|
||||||
|
for _, fn := range _s.fns {
|
||||||
|
aggregation = append(aggregation, fn(selector))
|
||||||
|
}
|
||||||
|
switch n := len(*_s.selector.flds); {
|
||||||
|
case n == 0 && len(aggregation) > 0:
|
||||||
|
selector.Select(aggregation...)
|
||||||
|
case n != 0 && len(aggregation) > 0:
|
||||||
|
selector.AppendSelect(aggregation...)
|
||||||
|
}
|
||||||
|
rows := &sql.Rows{}
|
||||||
|
query, args := selector.Query()
|
||||||
|
if err := _s.driver.Query(ctx, query, args, rows); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
return sql.ScanSlice(rows, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify adds a query modifier for attaching custom logic to queries.
|
||||||
|
func (_s *DocumentSelect) Modify(modifiers ...func(s *sql.Selector)) *DocumentSelect {
|
||||||
|
_s.modifiers = append(_s.modifiers, modifiers...)
|
||||||
|
return _s
|
||||||
|
}
|
||||||
429
internal/models/ent/document_update.go
Normal file
429
internal/models/ent/document_update.go
Normal file
@@ -0,0 +1,429 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"voidraft/internal/models/ent/document"
|
||||||
|
"voidraft/internal/models/ent/predicate"
|
||||||
|
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||||
|
"entgo.io/ent/schema/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DocumentUpdate is the builder for updating Document entities.
|
||||||
|
type DocumentUpdate struct {
|
||||||
|
config
|
||||||
|
hooks []Hook
|
||||||
|
mutation *DocumentMutation
|
||||||
|
modifiers []func(*sql.UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where appends a list predicates to the DocumentUpdate builder.
|
||||||
|
func (_u *DocumentUpdate) Where(ps ...predicate.Document) *DocumentUpdate {
|
||||||
|
_u.mutation.Where(ps...)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUpdatedAt sets the "updated_at" field.
|
||||||
|
func (_u *DocumentUpdate) SetUpdatedAt(v string) *DocumentUpdate {
|
||||||
|
_u.mutation.SetUpdatedAt(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
|
||||||
|
func (_u *DocumentUpdate) SetNillableUpdatedAt(v *string) *DocumentUpdate {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetUpdatedAt(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeletedAt sets the "deleted_at" field.
|
||||||
|
func (_u *DocumentUpdate) SetDeletedAt(v string) *DocumentUpdate {
|
||||||
|
_u.mutation.SetDeletedAt(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
|
||||||
|
func (_u *DocumentUpdate) SetNillableDeletedAt(v *string) *DocumentUpdate {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetDeletedAt(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearDeletedAt clears the value of the "deleted_at" field.
|
||||||
|
func (_u *DocumentUpdate) ClearDeletedAt() *DocumentUpdate {
|
||||||
|
_u.mutation.ClearDeletedAt()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTitle sets the "title" field.
|
||||||
|
func (_u *DocumentUpdate) SetTitle(v string) *DocumentUpdate {
|
||||||
|
_u.mutation.SetTitle(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableTitle sets the "title" field if the given value is not nil.
|
||||||
|
func (_u *DocumentUpdate) SetNillableTitle(v *string) *DocumentUpdate {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetTitle(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContent sets the "content" field.
|
||||||
|
func (_u *DocumentUpdate) SetContent(v string) *DocumentUpdate {
|
||||||
|
_u.mutation.SetContent(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableContent sets the "content" field if the given value is not nil.
|
||||||
|
func (_u *DocumentUpdate) SetNillableContent(v *string) *DocumentUpdate {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetContent(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearContent clears the value of the "content" field.
|
||||||
|
func (_u *DocumentUpdate) ClearContent() *DocumentUpdate {
|
||||||
|
_u.mutation.ClearContent()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLocked sets the "locked" field.
|
||||||
|
func (_u *DocumentUpdate) SetLocked(v bool) *DocumentUpdate {
|
||||||
|
_u.mutation.SetLocked(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableLocked sets the "locked" field if the given value is not nil.
|
||||||
|
func (_u *DocumentUpdate) SetNillableLocked(v *bool) *DocumentUpdate {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetLocked(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutation returns the DocumentMutation object of the builder.
|
||||||
|
func (_u *DocumentUpdate) Mutation() *DocumentMutation {
|
||||||
|
return _u.mutation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save executes the query and returns the number of nodes affected by the update operation.
|
||||||
|
func (_u *DocumentUpdate) Save(ctx context.Context) (int, error) {
|
||||||
|
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveX is like Save, but panics if an error occurs.
|
||||||
|
func (_u *DocumentUpdate) SaveX(ctx context.Context) int {
|
||||||
|
affected, err := _u.Save(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return affected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the query.
|
||||||
|
func (_u *DocumentUpdate) Exec(ctx context.Context) error {
|
||||||
|
_, err := _u.Save(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_u *DocumentUpdate) ExecX(ctx context.Context) {
|
||||||
|
if err := _u.Exec(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check runs all checks and user-defined validators on the builder.
|
||||||
|
func (_u *DocumentUpdate) check() error {
|
||||||
|
if v, ok := _u.mutation.Title(); ok {
|
||||||
|
if err := document.TitleValidator(v); err != nil {
|
||||||
|
return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "Document.title": %w`, err)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify adds a statement modifier for attaching custom logic to the UPDATE statement.
|
||||||
|
func (_u *DocumentUpdate) Modify(modifiers ...func(u *sql.UpdateBuilder)) *DocumentUpdate {
|
||||||
|
_u.modifiers = append(_u.modifiers, modifiers...)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_u *DocumentUpdate) sqlSave(ctx context.Context) (_node int, err error) {
|
||||||
|
if err := _u.check(); err != nil {
|
||||||
|
return _node, err
|
||||||
|
}
|
||||||
|
_spec := sqlgraph.NewUpdateSpec(document.Table, document.Columns, sqlgraph.NewFieldSpec(document.FieldID, field.TypeInt))
|
||||||
|
if ps := _u.mutation.predicates; len(ps) > 0 {
|
||||||
|
_spec.Predicate = func(selector *sql.Selector) {
|
||||||
|
for i := range ps {
|
||||||
|
ps[i](selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _u.mutation.UUIDCleared() {
|
||||||
|
_spec.ClearField(document.FieldUUID, field.TypeString)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.UpdatedAt(); ok {
|
||||||
|
_spec.SetField(document.FieldUpdatedAt, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.DeletedAt(); ok {
|
||||||
|
_spec.SetField(document.FieldDeletedAt, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if _u.mutation.DeletedAtCleared() {
|
||||||
|
_spec.ClearField(document.FieldDeletedAt, field.TypeString)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Title(); ok {
|
||||||
|
_spec.SetField(document.FieldTitle, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Content(); ok {
|
||||||
|
_spec.SetField(document.FieldContent, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if _u.mutation.ContentCleared() {
|
||||||
|
_spec.ClearField(document.FieldContent, field.TypeString)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Locked(); ok {
|
||||||
|
_spec.SetField(document.FieldLocked, field.TypeBool, value)
|
||||||
|
}
|
||||||
|
_spec.AddModifiers(_u.modifiers...)
|
||||||
|
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
|
||||||
|
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||||
|
err = &NotFoundError{document.Label}
|
||||||
|
} else if sqlgraph.IsConstraintError(err) {
|
||||||
|
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
_u.mutation.done = true
|
||||||
|
return _node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DocumentUpdateOne is the builder for updating a single Document entity.
|
||||||
|
type DocumentUpdateOne struct {
|
||||||
|
config
|
||||||
|
fields []string
|
||||||
|
hooks []Hook
|
||||||
|
mutation *DocumentMutation
|
||||||
|
modifiers []func(*sql.UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUpdatedAt sets the "updated_at" field.
|
||||||
|
func (_u *DocumentUpdateOne) SetUpdatedAt(v string) *DocumentUpdateOne {
|
||||||
|
_u.mutation.SetUpdatedAt(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
|
||||||
|
func (_u *DocumentUpdateOne) SetNillableUpdatedAt(v *string) *DocumentUpdateOne {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetUpdatedAt(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeletedAt sets the "deleted_at" field.
|
||||||
|
func (_u *DocumentUpdateOne) SetDeletedAt(v string) *DocumentUpdateOne {
|
||||||
|
_u.mutation.SetDeletedAt(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
|
||||||
|
func (_u *DocumentUpdateOne) SetNillableDeletedAt(v *string) *DocumentUpdateOne {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetDeletedAt(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearDeletedAt clears the value of the "deleted_at" field.
|
||||||
|
func (_u *DocumentUpdateOne) ClearDeletedAt() *DocumentUpdateOne {
|
||||||
|
_u.mutation.ClearDeletedAt()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTitle sets the "title" field.
|
||||||
|
func (_u *DocumentUpdateOne) SetTitle(v string) *DocumentUpdateOne {
|
||||||
|
_u.mutation.SetTitle(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableTitle sets the "title" field if the given value is not nil.
|
||||||
|
func (_u *DocumentUpdateOne) SetNillableTitle(v *string) *DocumentUpdateOne {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetTitle(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContent sets the "content" field.
|
||||||
|
func (_u *DocumentUpdateOne) SetContent(v string) *DocumentUpdateOne {
|
||||||
|
_u.mutation.SetContent(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableContent sets the "content" field if the given value is not nil.
|
||||||
|
func (_u *DocumentUpdateOne) SetNillableContent(v *string) *DocumentUpdateOne {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetContent(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearContent clears the value of the "content" field.
|
||||||
|
func (_u *DocumentUpdateOne) ClearContent() *DocumentUpdateOne {
|
||||||
|
_u.mutation.ClearContent()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLocked sets the "locked" field.
|
||||||
|
func (_u *DocumentUpdateOne) SetLocked(v bool) *DocumentUpdateOne {
|
||||||
|
_u.mutation.SetLocked(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableLocked sets the "locked" field if the given value is not nil.
|
||||||
|
func (_u *DocumentUpdateOne) SetNillableLocked(v *bool) *DocumentUpdateOne {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetLocked(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutation returns the DocumentMutation object of the builder.
|
||||||
|
func (_u *DocumentUpdateOne) Mutation() *DocumentMutation {
|
||||||
|
return _u.mutation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where appends a list predicates to the DocumentUpdate builder.
|
||||||
|
func (_u *DocumentUpdateOne) Where(ps ...predicate.Document) *DocumentUpdateOne {
|
||||||
|
_u.mutation.Where(ps...)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select allows selecting one or more fields (columns) of the returned entity.
|
||||||
|
// The default is selecting all fields defined in the entity schema.
|
||||||
|
func (_u *DocumentUpdateOne) Select(field string, fields ...string) *DocumentUpdateOne {
|
||||||
|
_u.fields = append([]string{field}, fields...)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save executes the query and returns the updated Document entity.
|
||||||
|
func (_u *DocumentUpdateOne) Save(ctx context.Context) (*Document, error) {
|
||||||
|
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveX is like Save, but panics if an error occurs.
|
||||||
|
func (_u *DocumentUpdateOne) SaveX(ctx context.Context) *Document {
|
||||||
|
node, err := _u.Save(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the query on the entity.
|
||||||
|
func (_u *DocumentUpdateOne) Exec(ctx context.Context) error {
|
||||||
|
_, err := _u.Save(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_u *DocumentUpdateOne) ExecX(ctx context.Context) {
|
||||||
|
if err := _u.Exec(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check runs all checks and user-defined validators on the builder.
|
||||||
|
func (_u *DocumentUpdateOne) check() error {
|
||||||
|
if v, ok := _u.mutation.Title(); ok {
|
||||||
|
if err := document.TitleValidator(v); err != nil {
|
||||||
|
return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "Document.title": %w`, err)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify adds a statement modifier for attaching custom logic to the UPDATE statement.
|
||||||
|
func (_u *DocumentUpdateOne) Modify(modifiers ...func(u *sql.UpdateBuilder)) *DocumentUpdateOne {
|
||||||
|
_u.modifiers = append(_u.modifiers, modifiers...)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_u *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err error) {
|
||||||
|
if err := _u.check(); err != nil {
|
||||||
|
return _node, err
|
||||||
|
}
|
||||||
|
_spec := sqlgraph.NewUpdateSpec(document.Table, document.Columns, sqlgraph.NewFieldSpec(document.FieldID, field.TypeInt))
|
||||||
|
id, ok := _u.mutation.ID()
|
||||||
|
if !ok {
|
||||||
|
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Document.id" for update`)}
|
||||||
|
}
|
||||||
|
_spec.Node.ID.Value = id
|
||||||
|
if fields := _u.fields; len(fields) > 0 {
|
||||||
|
_spec.Node.Columns = make([]string, 0, len(fields))
|
||||||
|
_spec.Node.Columns = append(_spec.Node.Columns, document.FieldID)
|
||||||
|
for _, f := range fields {
|
||||||
|
if !document.ValidColumn(f) {
|
||||||
|
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
|
||||||
|
}
|
||||||
|
if f != document.FieldID {
|
||||||
|
_spec.Node.Columns = append(_spec.Node.Columns, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ps := _u.mutation.predicates; len(ps) > 0 {
|
||||||
|
_spec.Predicate = func(selector *sql.Selector) {
|
||||||
|
for i := range ps {
|
||||||
|
ps[i](selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _u.mutation.UUIDCleared() {
|
||||||
|
_spec.ClearField(document.FieldUUID, field.TypeString)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.UpdatedAt(); ok {
|
||||||
|
_spec.SetField(document.FieldUpdatedAt, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.DeletedAt(); ok {
|
||||||
|
_spec.SetField(document.FieldDeletedAt, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if _u.mutation.DeletedAtCleared() {
|
||||||
|
_spec.ClearField(document.FieldDeletedAt, field.TypeString)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Title(); ok {
|
||||||
|
_spec.SetField(document.FieldTitle, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Content(); ok {
|
||||||
|
_spec.SetField(document.FieldContent, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if _u.mutation.ContentCleared() {
|
||||||
|
_spec.ClearField(document.FieldContent, field.TypeString)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Locked(); ok {
|
||||||
|
_spec.SetField(document.FieldLocked, field.TypeBool, value)
|
||||||
|
}
|
||||||
|
_spec.AddModifiers(_u.modifiers...)
|
||||||
|
_node = &Document{config: _u.config}
|
||||||
|
_spec.Assign = _node.assignValues
|
||||||
|
_spec.ScanValues = _node.scanValues
|
||||||
|
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
|
||||||
|
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||||
|
err = &NotFoundError{document.Label}
|
||||||
|
} else if sqlgraph.IsConstraintError(err) {
|
||||||
|
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_u.mutation.done = true
|
||||||
|
return _node, nil
|
||||||
|
}
|
||||||
614
internal/models/ent/ent.go
Normal file
614
internal/models/ent/ent.go
Normal file
@@ -0,0 +1,614 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"voidraft/internal/models/ent/document"
|
||||||
|
"voidraft/internal/models/ent/extension"
|
||||||
|
"voidraft/internal/models/ent/keybinding"
|
||||||
|
"voidraft/internal/models/ent/theme"
|
||||||
|
|
||||||
|
"entgo.io/ent"
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ent aliases to avoid import conflicts in user's code.
|
||||||
|
type (
|
||||||
|
Op = ent.Op
|
||||||
|
Hook = ent.Hook
|
||||||
|
Value = ent.Value
|
||||||
|
Query = ent.Query
|
||||||
|
QueryContext = ent.QueryContext
|
||||||
|
Querier = ent.Querier
|
||||||
|
QuerierFunc = ent.QuerierFunc
|
||||||
|
Interceptor = ent.Interceptor
|
||||||
|
InterceptFunc = ent.InterceptFunc
|
||||||
|
Traverser = ent.Traverser
|
||||||
|
TraverseFunc = ent.TraverseFunc
|
||||||
|
Policy = ent.Policy
|
||||||
|
Mutator = ent.Mutator
|
||||||
|
Mutation = ent.Mutation
|
||||||
|
MutateFunc = ent.MutateFunc
|
||||||
|
)
|
||||||
|
|
||||||
|
type clientCtxKey struct{}
|
||||||
|
|
||||||
|
// FromContext returns a Client stored inside a context, or nil if there isn't one.
|
||||||
|
func FromContext(ctx context.Context) *Client {
|
||||||
|
c, _ := ctx.Value(clientCtxKey{}).(*Client)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContext returns a new context with the given Client attached.
|
||||||
|
func NewContext(parent context.Context, c *Client) context.Context {
|
||||||
|
return context.WithValue(parent, clientCtxKey{}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
type txCtxKey struct{}
|
||||||
|
|
||||||
|
// TxFromContext returns a Tx stored inside a context, or nil if there isn't one.
|
||||||
|
func TxFromContext(ctx context.Context) *Tx {
|
||||||
|
tx, _ := ctx.Value(txCtxKey{}).(*Tx)
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTxContext returns a new context with the given Tx attached.
|
||||||
|
func NewTxContext(parent context.Context, tx *Tx) context.Context {
|
||||||
|
return context.WithValue(parent, txCtxKey{}, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderFunc applies an ordering on the sql selector.
|
||||||
|
// Deprecated: Use Asc/Desc functions or the package builders instead.
|
||||||
|
type OrderFunc func(*sql.Selector)
|
||||||
|
|
||||||
|
var (
|
||||||
|
initCheck sync.Once
|
||||||
|
columnCheck sql.ColumnCheck
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkColumn checks if the column exists in the given table.
|
||||||
|
func checkColumn(t, c string) error {
|
||||||
|
initCheck.Do(func() {
|
||||||
|
columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
|
||||||
|
document.Table: document.ValidColumn,
|
||||||
|
extension.Table: extension.ValidColumn,
|
||||||
|
keybinding.Table: keybinding.ValidColumn,
|
||||||
|
theme.Table: theme.ValidColumn,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return columnCheck(t, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asc applies the given fields in ASC order.
|
||||||
|
func Asc(fields ...string) func(*sql.Selector) {
|
||||||
|
return func(s *sql.Selector) {
|
||||||
|
for _, f := range fields {
|
||||||
|
if err := checkColumn(s.TableName(), f); err != nil {
|
||||||
|
s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)})
|
||||||
|
}
|
||||||
|
s.OrderBy(sql.Asc(s.C(f)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Desc applies the given fields in DESC order.
|
||||||
|
func Desc(fields ...string) func(*sql.Selector) {
|
||||||
|
return func(s *sql.Selector) {
|
||||||
|
for _, f := range fields {
|
||||||
|
if err := checkColumn(s.TableName(), f); err != nil {
|
||||||
|
s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)})
|
||||||
|
}
|
||||||
|
s.OrderBy(sql.Desc(s.C(f)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AggregateFunc applies an aggregation step on the group-by traversal/selector.
|
||||||
|
type AggregateFunc func(*sql.Selector) string
|
||||||
|
|
||||||
|
// As is a pseudo aggregation function for renaming another other functions with custom names. For example:
|
||||||
|
//
|
||||||
|
// GroupBy(field1, field2).
|
||||||
|
// Aggregate(ent.As(ent.Sum(field1), "sum_field1"), (ent.As(ent.Sum(field2), "sum_field2")).
|
||||||
|
// Scan(ctx, &v)
|
||||||
|
func As(fn AggregateFunc, end string) AggregateFunc {
|
||||||
|
return func(s *sql.Selector) string {
|
||||||
|
return sql.As(fn(s), end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count applies the "count" aggregation function on each group.
|
||||||
|
func Count() AggregateFunc {
|
||||||
|
return func(s *sql.Selector) string {
|
||||||
|
return sql.Count("*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max applies the "max" aggregation function on the given field of each group.
|
||||||
|
func Max(field string) AggregateFunc {
|
||||||
|
return func(s *sql.Selector) string {
|
||||||
|
if err := checkColumn(s.TableName(), field); err != nil {
|
||||||
|
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return sql.Max(s.C(field))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mean applies the "mean" aggregation function on the given field of each group.
|
||||||
|
func Mean(field string) AggregateFunc {
|
||||||
|
return func(s *sql.Selector) string {
|
||||||
|
if err := checkColumn(s.TableName(), field); err != nil {
|
||||||
|
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return sql.Avg(s.C(field))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Min applies the "min" aggregation function on the given field of each group.
|
||||||
|
func Min(field string) AggregateFunc {
|
||||||
|
return func(s *sql.Selector) string {
|
||||||
|
if err := checkColumn(s.TableName(), field); err != nil {
|
||||||
|
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return sql.Min(s.C(field))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum applies the "sum" aggregation function on the given field of each group.
|
||||||
|
func Sum(field string) AggregateFunc {
|
||||||
|
return func(s *sql.Selector) string {
|
||||||
|
if err := checkColumn(s.TableName(), field); err != nil {
|
||||||
|
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return sql.Sum(s.C(field))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidationError returns when validating a field or edge fails.
|
||||||
|
type ValidationError struct {
|
||||||
|
Name string // Field or edge name.
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements the error interface.
|
||||||
|
func (e *ValidationError) Error() string {
|
||||||
|
return e.err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap implements the errors.Wrapper interface.
|
||||||
|
func (e *ValidationError) Unwrap() error {
|
||||||
|
return e.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidationError returns a boolean indicating whether the error is a validation error.
|
||||||
|
func IsValidationError(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var e *ValidationError
|
||||||
|
return errors.As(err, &e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFoundError returns when trying to fetch a specific entity and it was not found in the database.
|
||||||
|
type NotFoundError struct {
|
||||||
|
label string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements the error interface.
|
||||||
|
func (e *NotFoundError) Error() string {
|
||||||
|
return "ent: " + e.label + " not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotFound returns a boolean indicating whether the error is a not found error.
|
||||||
|
func IsNotFound(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var e *NotFoundError
|
||||||
|
return errors.As(err, &e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaskNotFound masks not found error.
|
||||||
|
func MaskNotFound(err error) error {
|
||||||
|
if IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotSingularError returns when trying to fetch a singular entity and more then one was found in the database.
|
||||||
|
type NotSingularError struct {
|
||||||
|
label string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements the error interface.
|
||||||
|
func (e *NotSingularError) Error() string {
|
||||||
|
return "ent: " + e.label + " not singular"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotSingular returns a boolean indicating whether the error is a not singular error.
|
||||||
|
func IsNotSingular(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var e *NotSingularError
|
||||||
|
return errors.As(err, &e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotLoadedError returns when trying to get a node that was not loaded by the query.
|
||||||
|
type NotLoadedError struct {
|
||||||
|
edge string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements the error interface.
|
||||||
|
func (e *NotLoadedError) Error() string {
|
||||||
|
return "ent: " + e.edge + " edge was not loaded"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotLoaded returns a boolean indicating whether the error is a not loaded error.
|
||||||
|
func IsNotLoaded(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var e *NotLoadedError
|
||||||
|
return errors.As(err, &e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConstraintError returns when trying to create/update one or more entities and
|
||||||
|
// one or more of their constraints failed. For example, violation of edge or
|
||||||
|
// field uniqueness.
|
||||||
|
type ConstraintError struct {
|
||||||
|
msg string
|
||||||
|
wrap error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements the error interface.
|
||||||
|
func (e ConstraintError) Error() string {
|
||||||
|
return "ent: constraint failed: " + e.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap implements the errors.Wrapper interface.
|
||||||
|
func (e *ConstraintError) Unwrap() error {
|
||||||
|
return e.wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsConstraintError returns a boolean indicating whether the error is a constraint failure.
|
||||||
|
func IsConstraintError(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var e *ConstraintError
|
||||||
|
return errors.As(err, &e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// selector embedded by the different Select/GroupBy builders.
|
||||||
|
type selector struct {
|
||||||
|
label string
|
||||||
|
flds *[]string
|
||||||
|
fns []AggregateFunc
|
||||||
|
scan func(context.Context, any) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScanX is like Scan, but panics if an error occurs.
|
||||||
|
func (s *selector) ScanX(ctx context.Context, v any) {
|
||||||
|
if err := s.scan(ctx, v); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strings returns list of strings from a selector. It is only allowed when selecting one field.
|
||||||
|
func (s *selector) Strings(ctx context.Context) ([]string, error) {
|
||||||
|
if len(*s.flds) > 1 {
|
||||||
|
return nil, errors.New("ent: Strings is not achievable when selecting more than 1 field")
|
||||||
|
}
|
||||||
|
var v []string
|
||||||
|
if err := s.scan(ctx, &v); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringsX is like Strings, but panics if an error occurs.
|
||||||
|
func (s *selector) StringsX(ctx context.Context) []string {
|
||||||
|
v, err := s.Strings(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a single string from a selector. It is only allowed when selecting one field.
|
||||||
|
func (s *selector) String(ctx context.Context) (_ string, err error) {
|
||||||
|
var v []string
|
||||||
|
if v, err = s.Strings(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch len(v) {
|
||||||
|
case 1:
|
||||||
|
return v[0], nil
|
||||||
|
case 0:
|
||||||
|
err = &NotFoundError{s.label}
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("ent: Strings returned %d results when one was expected", len(v))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringX is like String, but panics if an error occurs.
|
||||||
|
func (s *selector) StringX(ctx context.Context) string {
|
||||||
|
v, err := s.String(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ints returns list of ints from a selector. It is only allowed when selecting one field.
|
||||||
|
func (s *selector) Ints(ctx context.Context) ([]int, error) {
|
||||||
|
if len(*s.flds) > 1 {
|
||||||
|
return nil, errors.New("ent: Ints is not achievable when selecting more than 1 field")
|
||||||
|
}
|
||||||
|
var v []int
|
||||||
|
if err := s.scan(ctx, &v); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntsX is like Ints, but panics if an error occurs.
|
||||||
|
func (s *selector) IntsX(ctx context.Context) []int {
|
||||||
|
v, err := s.Ints(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int returns a single int from a selector. It is only allowed when selecting one field.
|
||||||
|
func (s *selector) Int(ctx context.Context) (_ int, err error) {
|
||||||
|
var v []int
|
||||||
|
if v, err = s.Ints(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch len(v) {
|
||||||
|
case 1:
|
||||||
|
return v[0], nil
|
||||||
|
case 0:
|
||||||
|
err = &NotFoundError{s.label}
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("ent: Ints returned %d results when one was expected", len(v))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntX is like Int, but panics if an error occurs.
|
||||||
|
func (s *selector) IntX(ctx context.Context) int {
|
||||||
|
v, err := s.Int(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64s returns list of float64s from a selector. It is only allowed when selecting one field.
|
||||||
|
func (s *selector) Float64s(ctx context.Context) ([]float64, error) {
|
||||||
|
if len(*s.flds) > 1 {
|
||||||
|
return nil, errors.New("ent: Float64s is not achievable when selecting more than 1 field")
|
||||||
|
}
|
||||||
|
var v []float64
|
||||||
|
if err := s.scan(ctx, &v); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64sX is like Float64s, but panics if an error occurs.
|
||||||
|
func (s *selector) Float64sX(ctx context.Context) []float64 {
|
||||||
|
v, err := s.Float64s(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 returns a single float64 from a selector. It is only allowed when selecting one field.
|
||||||
|
func (s *selector) Float64(ctx context.Context) (_ float64, err error) {
|
||||||
|
var v []float64
|
||||||
|
if v, err = s.Float64s(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch len(v) {
|
||||||
|
case 1:
|
||||||
|
return v[0], nil
|
||||||
|
case 0:
|
||||||
|
err = &NotFoundError{s.label}
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("ent: Float64s returned %d results when one was expected", len(v))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64X is like Float64, but panics if an error occurs.
|
||||||
|
func (s *selector) Float64X(ctx context.Context) float64 {
|
||||||
|
v, err := s.Float64(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bools returns list of bools from a selector. It is only allowed when selecting one field.
|
||||||
|
func (s *selector) Bools(ctx context.Context) ([]bool, error) {
|
||||||
|
if len(*s.flds) > 1 {
|
||||||
|
return nil, errors.New("ent: Bools is not achievable when selecting more than 1 field")
|
||||||
|
}
|
||||||
|
var v []bool
|
||||||
|
if err := s.scan(ctx, &v); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolsX is like Bools, but panics if an error occurs.
|
||||||
|
func (s *selector) BoolsX(ctx context.Context) []bool {
|
||||||
|
v, err := s.Bools(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool returns a single bool from a selector. It is only allowed when selecting one field.
|
||||||
|
func (s *selector) Bool(ctx context.Context) (_ bool, err error) {
|
||||||
|
var v []bool
|
||||||
|
if v, err = s.Bools(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch len(v) {
|
||||||
|
case 1:
|
||||||
|
return v[0], nil
|
||||||
|
case 0:
|
||||||
|
err = &NotFoundError{s.label}
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("ent: Bools returned %d results when one was expected", len(v))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolX is like Bool, but panics if an error occurs.
|
||||||
|
func (s *selector) BoolX(ctx context.Context) bool {
|
||||||
|
v, err := s.Bool(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// withHooks invokes the builder operation with the given hooks, if any.
|
||||||
|
func withHooks[V Value, M any, PM interface {
|
||||||
|
*M
|
||||||
|
Mutation
|
||||||
|
}](ctx context.Context, exec func(context.Context) (V, error), mutation PM, hooks []Hook) (value V, err error) {
|
||||||
|
if len(hooks) == 0 {
|
||||||
|
return exec(ctx)
|
||||||
|
}
|
||||||
|
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||||
|
mutationT, ok := any(m).(PM)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||||
|
}
|
||||||
|
// Set the mutation to the builder.
|
||||||
|
*mutation = *mutationT
|
||||||
|
return exec(ctx)
|
||||||
|
})
|
||||||
|
for i := len(hooks) - 1; i >= 0; i-- {
|
||||||
|
if hooks[i] == nil {
|
||||||
|
return value, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
|
||||||
|
}
|
||||||
|
mut = hooks[i](mut)
|
||||||
|
}
|
||||||
|
v, err := mut.Mutate(ctx, mutation)
|
||||||
|
if err != nil {
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
nv, ok := v.(V)
|
||||||
|
if !ok {
|
||||||
|
return value, fmt.Errorf("unexpected node type %T returned from %T", v, mutation)
|
||||||
|
}
|
||||||
|
return nv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setContextOp returns a new context with the given QueryContext attached (including its op) in case it does not exist.
|
||||||
|
func setContextOp(ctx context.Context, qc *QueryContext, op string) context.Context {
|
||||||
|
if ent.QueryFromContext(ctx) == nil {
|
||||||
|
qc.Op = op
|
||||||
|
ctx = ent.NewQueryContext(ctx, qc)
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func querierAll[V Value, Q interface {
|
||||||
|
sqlAll(context.Context, ...queryHook) (V, error)
|
||||||
|
}]() Querier {
|
||||||
|
return QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||||
|
query, ok := q.(Q)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected query type %T", q)
|
||||||
|
}
|
||||||
|
return query.sqlAll(ctx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func querierCount[Q interface {
|
||||||
|
sqlCount(context.Context) (int, error)
|
||||||
|
}]() Querier {
|
||||||
|
return QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||||
|
query, ok := q.(Q)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected query type %T", q)
|
||||||
|
}
|
||||||
|
return query.sqlCount(ctx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func withInterceptors[V Value](ctx context.Context, q Query, qr Querier, inters []Interceptor) (v V, err error) {
|
||||||
|
for i := len(inters) - 1; i >= 0; i-- {
|
||||||
|
qr = inters[i].Intercept(qr)
|
||||||
|
}
|
||||||
|
rv, err := qr.Query(ctx, q)
|
||||||
|
if err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
vt, ok := rv.(V)
|
||||||
|
if !ok {
|
||||||
|
return v, fmt.Errorf("unexpected type %T returned from %T. expected type: %T", vt, q, v)
|
||||||
|
}
|
||||||
|
return vt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func scanWithInterceptors[Q1 ent.Query, Q2 interface {
|
||||||
|
sqlScan(context.Context, Q1, any) error
|
||||||
|
}](ctx context.Context, rootQuery Q1, selectOrGroup Q2, inters []Interceptor, v any) error {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
var qr Querier = QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||||
|
query, ok := q.(Q1)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected query type %T", q)
|
||||||
|
}
|
||||||
|
if err := selectOrGroup.sqlScan(ctx, query, v); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if k := rv.Kind(); k == reflect.Pointer && rv.Elem().CanInterface() {
|
||||||
|
return rv.Elem().Interface(), nil
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
})
|
||||||
|
for i := len(inters) - 1; i >= 0; i-- {
|
||||||
|
qr = inters[i].Intercept(qr)
|
||||||
|
}
|
||||||
|
vv, err := qr.Query(ctx, rootQuery)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch rv2 := reflect.ValueOf(vv); {
|
||||||
|
case rv.IsNil(), rv2.IsNil(), rv.Kind() != reflect.Pointer:
|
||||||
|
case rv.Type() == rv2.Type():
|
||||||
|
rv.Elem().Set(rv2.Elem())
|
||||||
|
case rv.Elem().Type() == rv2.Type():
|
||||||
|
rv.Elem().Set(rv2)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryHook describes an internal hook for the different sqlAll methods.
|
||||||
|
type queryHook func(context.Context, *sqlgraph.QuerySpec)
|
||||||
413
internal/models/ent/entql.go
Normal file
413
internal/models/ent/entql.go
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"voidraft/internal/models/ent/document"
|
||||||
|
"voidraft/internal/models/ent/extension"
|
||||||
|
"voidraft/internal/models/ent/keybinding"
|
||||||
|
"voidraft/internal/models/ent/theme"
|
||||||
|
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||||
|
"entgo.io/ent/entql"
|
||||||
|
"entgo.io/ent/schema/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// schemaGraph holds a representation of ent/schema at runtime.
|
||||||
|
var schemaGraph = func() *sqlgraph.Schema {
|
||||||
|
graph := &sqlgraph.Schema{Nodes: make([]*sqlgraph.Node, 4)}
|
||||||
|
graph.Nodes[0] = &sqlgraph.Node{
|
||||||
|
NodeSpec: sqlgraph.NodeSpec{
|
||||||
|
Table: document.Table,
|
||||||
|
Columns: document.Columns,
|
||||||
|
ID: &sqlgraph.FieldSpec{
|
||||||
|
Type: field.TypeInt,
|
||||||
|
Column: document.FieldID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: "Document",
|
||||||
|
Fields: map[string]*sqlgraph.FieldSpec{
|
||||||
|
document.FieldUUID: {Type: field.TypeString, Column: document.FieldUUID},
|
||||||
|
document.FieldCreatedAt: {Type: field.TypeString, Column: document.FieldCreatedAt},
|
||||||
|
document.FieldUpdatedAt: {Type: field.TypeString, Column: document.FieldUpdatedAt},
|
||||||
|
document.FieldDeletedAt: {Type: field.TypeString, Column: document.FieldDeletedAt},
|
||||||
|
document.FieldTitle: {Type: field.TypeString, Column: document.FieldTitle},
|
||||||
|
document.FieldContent: {Type: field.TypeString, Column: document.FieldContent},
|
||||||
|
document.FieldLocked: {Type: field.TypeBool, Column: document.FieldLocked},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
graph.Nodes[1] = &sqlgraph.Node{
|
||||||
|
NodeSpec: sqlgraph.NodeSpec{
|
||||||
|
Table: extension.Table,
|
||||||
|
Columns: extension.Columns,
|
||||||
|
ID: &sqlgraph.FieldSpec{
|
||||||
|
Type: field.TypeInt,
|
||||||
|
Column: extension.FieldID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: "Extension",
|
||||||
|
Fields: map[string]*sqlgraph.FieldSpec{
|
||||||
|
extension.FieldUUID: {Type: field.TypeString, Column: extension.FieldUUID},
|
||||||
|
extension.FieldCreatedAt: {Type: field.TypeString, Column: extension.FieldCreatedAt},
|
||||||
|
extension.FieldUpdatedAt: {Type: field.TypeString, Column: extension.FieldUpdatedAt},
|
||||||
|
extension.FieldDeletedAt: {Type: field.TypeString, Column: extension.FieldDeletedAt},
|
||||||
|
extension.FieldKey: {Type: field.TypeString, Column: extension.FieldKey},
|
||||||
|
extension.FieldEnabled: {Type: field.TypeBool, Column: extension.FieldEnabled},
|
||||||
|
extension.FieldConfig: {Type: field.TypeJSON, Column: extension.FieldConfig},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
graph.Nodes[2] = &sqlgraph.Node{
|
||||||
|
NodeSpec: sqlgraph.NodeSpec{
|
||||||
|
Table: keybinding.Table,
|
||||||
|
Columns: keybinding.Columns,
|
||||||
|
ID: &sqlgraph.FieldSpec{
|
||||||
|
Type: field.TypeInt,
|
||||||
|
Column: keybinding.FieldID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: "KeyBinding",
|
||||||
|
Fields: map[string]*sqlgraph.FieldSpec{
|
||||||
|
keybinding.FieldUUID: {Type: field.TypeString, Column: keybinding.FieldUUID},
|
||||||
|
keybinding.FieldCreatedAt: {Type: field.TypeString, Column: keybinding.FieldCreatedAt},
|
||||||
|
keybinding.FieldUpdatedAt: {Type: field.TypeString, Column: keybinding.FieldUpdatedAt},
|
||||||
|
keybinding.FieldDeletedAt: {Type: field.TypeString, Column: keybinding.FieldDeletedAt},
|
||||||
|
keybinding.FieldKey: {Type: field.TypeString, Column: keybinding.FieldKey},
|
||||||
|
keybinding.FieldCommand: {Type: field.TypeString, Column: keybinding.FieldCommand},
|
||||||
|
keybinding.FieldExtension: {Type: field.TypeString, Column: keybinding.FieldExtension},
|
||||||
|
keybinding.FieldEnabled: {Type: field.TypeBool, Column: keybinding.FieldEnabled},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
graph.Nodes[3] = &sqlgraph.Node{
|
||||||
|
NodeSpec: sqlgraph.NodeSpec{
|
||||||
|
Table: theme.Table,
|
||||||
|
Columns: theme.Columns,
|
||||||
|
ID: &sqlgraph.FieldSpec{
|
||||||
|
Type: field.TypeInt,
|
||||||
|
Column: theme.FieldID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: "Theme",
|
||||||
|
Fields: map[string]*sqlgraph.FieldSpec{
|
||||||
|
theme.FieldUUID: {Type: field.TypeString, Column: theme.FieldUUID},
|
||||||
|
theme.FieldCreatedAt: {Type: field.TypeString, Column: theme.FieldCreatedAt},
|
||||||
|
theme.FieldUpdatedAt: {Type: field.TypeString, Column: theme.FieldUpdatedAt},
|
||||||
|
theme.FieldDeletedAt: {Type: field.TypeString, Column: theme.FieldDeletedAt},
|
||||||
|
theme.FieldKey: {Type: field.TypeString, Column: theme.FieldKey},
|
||||||
|
theme.FieldType: {Type: field.TypeEnum, Column: theme.FieldType},
|
||||||
|
theme.FieldColors: {Type: field.TypeJSON, Column: theme.FieldColors},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return graph
|
||||||
|
}()
|
||||||
|
|
||||||
|
// predicateAdder wraps the addPredicate method.
|
||||||
|
// All update, update-one and query builders implement this interface.
|
||||||
|
type predicateAdder interface {
|
||||||
|
addPredicate(func(s *sql.Selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPredicate implements the predicateAdder interface.
|
||||||
|
func (_q *DocumentQuery) addPredicate(pred func(s *sql.Selector)) {
|
||||||
|
_q.predicates = append(_q.predicates, pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns a Filter implementation to apply filters on the DocumentQuery builder.
|
||||||
|
func (_q *DocumentQuery) Filter() *DocumentFilter {
|
||||||
|
return &DocumentFilter{config: _q.config, predicateAdder: _q}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPredicate implements the predicateAdder interface.
|
||||||
|
func (m *DocumentMutation) addPredicate(pred func(s *sql.Selector)) {
|
||||||
|
m.predicates = append(m.predicates, pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns an entql.Where implementation to apply filters on the DocumentMutation builder.
|
||||||
|
func (m *DocumentMutation) Filter() *DocumentFilter {
|
||||||
|
return &DocumentFilter{config: m.config, predicateAdder: m}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DocumentFilter provides a generic filtering capability at runtime for DocumentQuery.
|
||||||
|
type DocumentFilter struct {
|
||||||
|
predicateAdder
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where applies the entql predicate on the query filter.
|
||||||
|
func (f *DocumentFilter) Where(p entql.P) {
|
||||||
|
f.addPredicate(func(s *sql.Selector) {
|
||||||
|
if err := schemaGraph.EvalP(schemaGraph.Nodes[0].Type, p, s); err != nil {
|
||||||
|
s.AddError(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereID applies the entql int predicate on the id field.
|
||||||
|
func (f *DocumentFilter) WhereID(p entql.IntP) {
|
||||||
|
f.Where(p.Field(document.FieldID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereUUID applies the entql string predicate on the uuid field.
|
||||||
|
func (f *DocumentFilter) WhereUUID(p entql.StringP) {
|
||||||
|
f.Where(p.Field(document.FieldUUID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereCreatedAt applies the entql string predicate on the created_at field.
|
||||||
|
func (f *DocumentFilter) WhereCreatedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(document.FieldCreatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereUpdatedAt applies the entql string predicate on the updated_at field.
|
||||||
|
func (f *DocumentFilter) WhereUpdatedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(document.FieldUpdatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereDeletedAt applies the entql string predicate on the deleted_at field.
|
||||||
|
func (f *DocumentFilter) WhereDeletedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(document.FieldDeletedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereTitle applies the entql string predicate on the title field.
|
||||||
|
func (f *DocumentFilter) WhereTitle(p entql.StringP) {
|
||||||
|
f.Where(p.Field(document.FieldTitle))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereContent applies the entql string predicate on the content field.
|
||||||
|
func (f *DocumentFilter) WhereContent(p entql.StringP) {
|
||||||
|
f.Where(p.Field(document.FieldContent))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereLocked applies the entql bool predicate on the locked field.
|
||||||
|
func (f *DocumentFilter) WhereLocked(p entql.BoolP) {
|
||||||
|
f.Where(p.Field(document.FieldLocked))
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPredicate implements the predicateAdder interface.
|
||||||
|
func (_q *ExtensionQuery) addPredicate(pred func(s *sql.Selector)) {
|
||||||
|
_q.predicates = append(_q.predicates, pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns a Filter implementation to apply filters on the ExtensionQuery builder.
|
||||||
|
func (_q *ExtensionQuery) Filter() *ExtensionFilter {
|
||||||
|
return &ExtensionFilter{config: _q.config, predicateAdder: _q}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPredicate implements the predicateAdder interface.
|
||||||
|
func (m *ExtensionMutation) addPredicate(pred func(s *sql.Selector)) {
|
||||||
|
m.predicates = append(m.predicates, pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns an entql.Where implementation to apply filters on the ExtensionMutation builder.
|
||||||
|
func (m *ExtensionMutation) Filter() *ExtensionFilter {
|
||||||
|
return &ExtensionFilter{config: m.config, predicateAdder: m}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtensionFilter provides a generic filtering capability at runtime for ExtensionQuery.
|
||||||
|
type ExtensionFilter struct {
|
||||||
|
predicateAdder
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where applies the entql predicate on the query filter.
|
||||||
|
func (f *ExtensionFilter) Where(p entql.P) {
|
||||||
|
f.addPredicate(func(s *sql.Selector) {
|
||||||
|
if err := schemaGraph.EvalP(schemaGraph.Nodes[1].Type, p, s); err != nil {
|
||||||
|
s.AddError(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereID applies the entql int predicate on the id field.
|
||||||
|
func (f *ExtensionFilter) WhereID(p entql.IntP) {
|
||||||
|
f.Where(p.Field(extension.FieldID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereUUID applies the entql string predicate on the uuid field.
|
||||||
|
func (f *ExtensionFilter) WhereUUID(p entql.StringP) {
|
||||||
|
f.Where(p.Field(extension.FieldUUID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereCreatedAt applies the entql string predicate on the created_at field.
|
||||||
|
func (f *ExtensionFilter) WhereCreatedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(extension.FieldCreatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereUpdatedAt applies the entql string predicate on the updated_at field.
|
||||||
|
func (f *ExtensionFilter) WhereUpdatedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(extension.FieldUpdatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereDeletedAt applies the entql string predicate on the deleted_at field.
|
||||||
|
func (f *ExtensionFilter) WhereDeletedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(extension.FieldDeletedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereKey applies the entql string predicate on the key field.
|
||||||
|
func (f *ExtensionFilter) WhereKey(p entql.StringP) {
|
||||||
|
f.Where(p.Field(extension.FieldKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereEnabled applies the entql bool predicate on the enabled field.
|
||||||
|
func (f *ExtensionFilter) WhereEnabled(p entql.BoolP) {
|
||||||
|
f.Where(p.Field(extension.FieldEnabled))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereConfig applies the entql json.RawMessage predicate on the config field.
|
||||||
|
func (f *ExtensionFilter) WhereConfig(p entql.BytesP) {
|
||||||
|
f.Where(p.Field(extension.FieldConfig))
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPredicate implements the predicateAdder interface.
|
||||||
|
func (_q *KeyBindingQuery) addPredicate(pred func(s *sql.Selector)) {
|
||||||
|
_q.predicates = append(_q.predicates, pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns a Filter implementation to apply filters on the KeyBindingQuery builder.
|
||||||
|
func (_q *KeyBindingQuery) Filter() *KeyBindingFilter {
|
||||||
|
return &KeyBindingFilter{config: _q.config, predicateAdder: _q}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPredicate implements the predicateAdder interface.
|
||||||
|
func (m *KeyBindingMutation) addPredicate(pred func(s *sql.Selector)) {
|
||||||
|
m.predicates = append(m.predicates, pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns an entql.Where implementation to apply filters on the KeyBindingMutation builder.
|
||||||
|
func (m *KeyBindingMutation) Filter() *KeyBindingFilter {
|
||||||
|
return &KeyBindingFilter{config: m.config, predicateAdder: m}
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyBindingFilter provides a generic filtering capability at runtime for KeyBindingQuery.
|
||||||
|
type KeyBindingFilter struct {
|
||||||
|
predicateAdder
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where applies the entql predicate on the query filter.
|
||||||
|
func (f *KeyBindingFilter) Where(p entql.P) {
|
||||||
|
f.addPredicate(func(s *sql.Selector) {
|
||||||
|
if err := schemaGraph.EvalP(schemaGraph.Nodes[2].Type, p, s); err != nil {
|
||||||
|
s.AddError(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereID applies the entql int predicate on the id field.
|
||||||
|
func (f *KeyBindingFilter) WhereID(p entql.IntP) {
|
||||||
|
f.Where(p.Field(keybinding.FieldID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereUUID applies the entql string predicate on the uuid field.
|
||||||
|
func (f *KeyBindingFilter) WhereUUID(p entql.StringP) {
|
||||||
|
f.Where(p.Field(keybinding.FieldUUID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereCreatedAt applies the entql string predicate on the created_at field.
|
||||||
|
func (f *KeyBindingFilter) WhereCreatedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(keybinding.FieldCreatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereUpdatedAt applies the entql string predicate on the updated_at field.
|
||||||
|
func (f *KeyBindingFilter) WhereUpdatedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(keybinding.FieldUpdatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereDeletedAt applies the entql string predicate on the deleted_at field.
|
||||||
|
func (f *KeyBindingFilter) WhereDeletedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(keybinding.FieldDeletedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereKey applies the entql string predicate on the key field.
|
||||||
|
func (f *KeyBindingFilter) WhereKey(p entql.StringP) {
|
||||||
|
f.Where(p.Field(keybinding.FieldKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereCommand applies the entql string predicate on the command field.
|
||||||
|
func (f *KeyBindingFilter) WhereCommand(p entql.StringP) {
|
||||||
|
f.Where(p.Field(keybinding.FieldCommand))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereExtension applies the entql string predicate on the extension field.
|
||||||
|
func (f *KeyBindingFilter) WhereExtension(p entql.StringP) {
|
||||||
|
f.Where(p.Field(keybinding.FieldExtension))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereEnabled applies the entql bool predicate on the enabled field.
|
||||||
|
func (f *KeyBindingFilter) WhereEnabled(p entql.BoolP) {
|
||||||
|
f.Where(p.Field(keybinding.FieldEnabled))
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPredicate implements the predicateAdder interface.
|
||||||
|
func (_q *ThemeQuery) addPredicate(pred func(s *sql.Selector)) {
|
||||||
|
_q.predicates = append(_q.predicates, pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns a Filter implementation to apply filters on the ThemeQuery builder.
|
||||||
|
func (_q *ThemeQuery) Filter() *ThemeFilter {
|
||||||
|
return &ThemeFilter{config: _q.config, predicateAdder: _q}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPredicate implements the predicateAdder interface.
|
||||||
|
func (m *ThemeMutation) addPredicate(pred func(s *sql.Selector)) {
|
||||||
|
m.predicates = append(m.predicates, pred)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns an entql.Where implementation to apply filters on the ThemeMutation builder.
|
||||||
|
func (m *ThemeMutation) Filter() *ThemeFilter {
|
||||||
|
return &ThemeFilter{config: m.config, predicateAdder: m}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThemeFilter provides a generic filtering capability at runtime for ThemeQuery.
|
||||||
|
type ThemeFilter struct {
|
||||||
|
predicateAdder
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where applies the entql predicate on the query filter.
|
||||||
|
func (f *ThemeFilter) Where(p entql.P) {
|
||||||
|
f.addPredicate(func(s *sql.Selector) {
|
||||||
|
if err := schemaGraph.EvalP(schemaGraph.Nodes[3].Type, p, s); err != nil {
|
||||||
|
s.AddError(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereID applies the entql int predicate on the id field.
|
||||||
|
func (f *ThemeFilter) WhereID(p entql.IntP) {
|
||||||
|
f.Where(p.Field(theme.FieldID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereUUID applies the entql string predicate on the uuid field.
|
||||||
|
func (f *ThemeFilter) WhereUUID(p entql.StringP) {
|
||||||
|
f.Where(p.Field(theme.FieldUUID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereCreatedAt applies the entql string predicate on the created_at field.
|
||||||
|
func (f *ThemeFilter) WhereCreatedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(theme.FieldCreatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereUpdatedAt applies the entql string predicate on the updated_at field.
|
||||||
|
func (f *ThemeFilter) WhereUpdatedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(theme.FieldUpdatedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereDeletedAt applies the entql string predicate on the deleted_at field.
|
||||||
|
func (f *ThemeFilter) WhereDeletedAt(p entql.StringP) {
|
||||||
|
f.Where(p.Field(theme.FieldDeletedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereKey applies the entql string predicate on the key field.
|
||||||
|
func (f *ThemeFilter) WhereKey(p entql.StringP) {
|
||||||
|
f.Where(p.Field(theme.FieldKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereType applies the entql string predicate on the type field.
|
||||||
|
func (f *ThemeFilter) WhereType(p entql.StringP) {
|
||||||
|
f.Where(p.Field(theme.FieldType))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhereColors applies the entql json.RawMessage predicate on the colors field.
|
||||||
|
func (f *ThemeFilter) WhereColors(p entql.BytesP) {
|
||||||
|
f.Where(p.Field(theme.FieldColors))
|
||||||
|
}
|
||||||
85
internal/models/ent/enttest/enttest.go
Normal file
85
internal/models/ent/enttest/enttest.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package enttest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"voidraft/internal/models/ent"
|
||||||
|
// required by schema hooks.
|
||||||
|
_ "voidraft/internal/models/ent/runtime"
|
||||||
|
|
||||||
|
"voidraft/internal/models/ent/migrate"
|
||||||
|
|
||||||
|
"entgo.io/ent/dialect/sql/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// TestingT is the interface that is shared between
|
||||||
|
// testing.T and testing.B and used by enttest.
|
||||||
|
TestingT interface {
|
||||||
|
FailNow()
|
||||||
|
Error(...any)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option configures client creation.
|
||||||
|
Option func(*options)
|
||||||
|
|
||||||
|
options struct {
|
||||||
|
opts []ent.Option
|
||||||
|
migrateOpts []schema.MigrateOption
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithOptions forwards options to client creation.
|
||||||
|
func WithOptions(opts ...ent.Option) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.opts = append(o.opts, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMigrateOptions forwards options to auto migration.
|
||||||
|
func WithMigrateOptions(opts ...schema.MigrateOption) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.migrateOpts = append(o.migrateOpts, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOptions(opts []Option) *options {
|
||||||
|
o := &options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(o)
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open calls ent.Open and auto-run migration.
|
||||||
|
func Open(t TestingT, driverName, dataSourceName string, opts ...Option) *ent.Client {
|
||||||
|
o := newOptions(opts)
|
||||||
|
c, err := ent.Open(driverName, dataSourceName, o.opts...)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
migrateSchema(t, c, o)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient calls ent.NewClient and auto-run migration.
|
||||||
|
func NewClient(t TestingT, opts ...Option) *ent.Client {
|
||||||
|
o := newOptions(opts)
|
||||||
|
c := ent.NewClient(o.opts...)
|
||||||
|
migrateSchema(t, c, o)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
func migrateSchema(t TestingT, c *ent.Client, o *options) {
|
||||||
|
tables, err := schema.CopyTables(migrate.Tables)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if err := migrate.Create(context.Background(), c.Schema, tables, o.migrateOpts...); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
179
internal/models/ent/extension.go
Normal file
179
internal/models/ent/extension.go
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"voidraft/internal/models/ent/extension"
|
||||||
|
|
||||||
|
"entgo.io/ent"
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Extension is the model entity for the Extension schema.
|
||||||
|
type Extension struct {
|
||||||
|
config `json:"-"`
|
||||||
|
// ID of the ent.
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
// UUID for cross-device sync (UUIDv7)
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
// creation time
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
// update time
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
// deleted at
|
||||||
|
DeletedAt *string `json:"deleted_at,omitempty"`
|
||||||
|
// extension key
|
||||||
|
Key string `json:"key"`
|
||||||
|
// extension enabled or not
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
// extension config
|
||||||
|
Config map[string]interface{} `json:"config"`
|
||||||
|
selectValues sql.SelectValues
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanValues returns the types for scanning values from sql.Rows.
|
||||||
|
func (*Extension) scanValues(columns []string) ([]any, error) {
|
||||||
|
values := make([]any, len(columns))
|
||||||
|
for i := range columns {
|
||||||
|
switch columns[i] {
|
||||||
|
case extension.FieldConfig:
|
||||||
|
values[i] = new([]byte)
|
||||||
|
case extension.FieldEnabled:
|
||||||
|
values[i] = new(sql.NullBool)
|
||||||
|
case extension.FieldID:
|
||||||
|
values[i] = new(sql.NullInt64)
|
||||||
|
case extension.FieldUUID, extension.FieldCreatedAt, extension.FieldUpdatedAt, extension.FieldDeletedAt, extension.FieldKey:
|
||||||
|
values[i] = new(sql.NullString)
|
||||||
|
default:
|
||||||
|
values[i] = new(sql.UnknownType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignValues assigns the values that were returned from sql.Rows (after scanning)
|
||||||
|
// to the Extension fields.
|
||||||
|
func (_m *Extension) assignValues(columns []string, values []any) error {
|
||||||
|
if m, n := len(values), len(columns); m < n {
|
||||||
|
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
|
||||||
|
}
|
||||||
|
for i := range columns {
|
||||||
|
switch columns[i] {
|
||||||
|
case extension.FieldID:
|
||||||
|
value, ok := values[i].(*sql.NullInt64)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field id", value)
|
||||||
|
}
|
||||||
|
_m.ID = int(value.Int64)
|
||||||
|
case extension.FieldUUID:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field uuid", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.UUID = value.String
|
||||||
|
}
|
||||||
|
case extension.FieldCreatedAt:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field created_at", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.CreatedAt = value.String
|
||||||
|
}
|
||||||
|
case extension.FieldUpdatedAt:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.UpdatedAt = value.String
|
||||||
|
}
|
||||||
|
case extension.FieldDeletedAt:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field deleted_at", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.DeletedAt = new(string)
|
||||||
|
*_m.DeletedAt = value.String
|
||||||
|
}
|
||||||
|
case extension.FieldKey:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field key", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.Key = value.String
|
||||||
|
}
|
||||||
|
case extension.FieldEnabled:
|
||||||
|
if value, ok := values[i].(*sql.NullBool); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field enabled", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.Enabled = value.Bool
|
||||||
|
}
|
||||||
|
case extension.FieldConfig:
|
||||||
|
if value, ok := values[i].(*[]byte); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field config", values[i])
|
||||||
|
} else if value != nil && len(*value) > 0 {
|
||||||
|
if err := json.Unmarshal(*value, &_m.Config); err != nil {
|
||||||
|
return fmt.Errorf("unmarshal field config: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
_m.selectValues.Set(columns[i], values[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the ent.Value that was dynamically selected and assigned to the Extension.
|
||||||
|
// This includes values selected through modifiers, order, etc.
|
||||||
|
func (_m *Extension) Value(name string) (ent.Value, error) {
|
||||||
|
return _m.selectValues.Get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update returns a builder for updating this Extension.
|
||||||
|
// Note that you need to call Extension.Unwrap() before calling this method if this Extension
|
||||||
|
// was returned from a transaction, and the transaction was committed or rolled back.
|
||||||
|
func (_m *Extension) Update() *ExtensionUpdateOne {
|
||||||
|
return NewExtensionClient(_m.config).UpdateOne(_m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap unwraps the Extension entity that was returned from a transaction after it was closed,
|
||||||
|
// so that all future queries will be executed through the driver which created the transaction.
|
||||||
|
func (_m *Extension) Unwrap() *Extension {
|
||||||
|
_tx, ok := _m.config.driver.(*txDriver)
|
||||||
|
if !ok {
|
||||||
|
panic("ent: Extension is not a transactional entity")
|
||||||
|
}
|
||||||
|
_m.config.driver = _tx.drv
|
||||||
|
return _m
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements the fmt.Stringer.
|
||||||
|
func (_m *Extension) String() string {
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.WriteString("Extension(")
|
||||||
|
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
||||||
|
builder.WriteString("uuid=")
|
||||||
|
builder.WriteString(_m.UUID)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("created_at=")
|
||||||
|
builder.WriteString(_m.CreatedAt)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("updated_at=")
|
||||||
|
builder.WriteString(_m.UpdatedAt)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
if v := _m.DeletedAt; v != nil {
|
||||||
|
builder.WriteString("deleted_at=")
|
||||||
|
builder.WriteString(*v)
|
||||||
|
}
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("key=")
|
||||||
|
builder.WriteString(_m.Key)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("enabled=")
|
||||||
|
builder.WriteString(fmt.Sprintf("%v", _m.Enabled))
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("config=")
|
||||||
|
builder.WriteString(fmt.Sprintf("%v", _m.Config))
|
||||||
|
builder.WriteByte(')')
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extensions is a parsable slice of Extension.
|
||||||
|
type Extensions []*Extension
|
||||||
111
internal/models/ent/extension/extension.go
Normal file
111
internal/models/ent/extension/extension.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"entgo.io/ent"
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Label holds the string label denoting the extension type in the database.
|
||||||
|
Label = "extension"
|
||||||
|
// FieldID holds the string denoting the id field in the database.
|
||||||
|
FieldID = "id"
|
||||||
|
// FieldUUID holds the string denoting the uuid field in the database.
|
||||||
|
FieldUUID = "uuid"
|
||||||
|
// FieldCreatedAt holds the string denoting the created_at field in the database.
|
||||||
|
FieldCreatedAt = "created_at"
|
||||||
|
// FieldUpdatedAt holds the string denoting the updated_at field in the database.
|
||||||
|
FieldUpdatedAt = "updated_at"
|
||||||
|
// FieldDeletedAt holds the string denoting the deleted_at field in the database.
|
||||||
|
FieldDeletedAt = "deleted_at"
|
||||||
|
// FieldKey holds the string denoting the key field in the database.
|
||||||
|
FieldKey = "key"
|
||||||
|
// FieldEnabled holds the string denoting the enabled field in the database.
|
||||||
|
FieldEnabled = "enabled"
|
||||||
|
// FieldConfig holds the string denoting the config field in the database.
|
||||||
|
FieldConfig = "config"
|
||||||
|
// Table holds the table name of the extension in the database.
|
||||||
|
Table = "extensions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Columns holds all SQL columns for extension fields.
|
||||||
|
var Columns = []string{
|
||||||
|
FieldID,
|
||||||
|
FieldUUID,
|
||||||
|
FieldCreatedAt,
|
||||||
|
FieldUpdatedAt,
|
||||||
|
FieldDeletedAt,
|
||||||
|
FieldKey,
|
||||||
|
FieldEnabled,
|
||||||
|
FieldConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidColumn reports if the column name is valid (part of the table columns).
|
||||||
|
func ValidColumn(column string) bool {
|
||||||
|
for i := range Columns {
|
||||||
|
if column == Columns[i] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that the variables below are initialized by the runtime
|
||||||
|
// package on the initialization of the application. Therefore,
|
||||||
|
// it should be imported in the main as follows:
|
||||||
|
//
|
||||||
|
// import _ "voidraft/internal/models/ent/runtime"
|
||||||
|
var (
|
||||||
|
Hooks [2]ent.Hook
|
||||||
|
Interceptors [1]ent.Interceptor
|
||||||
|
// DefaultUUID holds the default value on creation for the "uuid" field.
|
||||||
|
DefaultUUID func() string
|
||||||
|
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
|
||||||
|
DefaultCreatedAt func() string
|
||||||
|
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
|
||||||
|
DefaultUpdatedAt func() string
|
||||||
|
// KeyValidator is a validator for the "key" field. It is called by the builders before save.
|
||||||
|
KeyValidator func(string) error
|
||||||
|
// DefaultEnabled holds the default value on creation for the "enabled" field.
|
||||||
|
DefaultEnabled bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// OrderOption defines the ordering options for the Extension queries.
|
||||||
|
type OrderOption func(*sql.Selector)
|
||||||
|
|
||||||
|
// ByID orders the results by the id field.
|
||||||
|
func ByID(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldID, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByUUID orders the results by the uuid field.
|
||||||
|
func ByUUID(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldUUID, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByCreatedAt orders the results by the created_at field.
|
||||||
|
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByUpdatedAt orders the results by the updated_at field.
|
||||||
|
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByDeletedAt orders the results by the deleted_at field.
|
||||||
|
func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldDeletedAt, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByKey orders the results by the key field.
|
||||||
|
func ByKey(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldKey, opts...).ToFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByEnabled orders the results by the enabled field.
|
||||||
|
func ByEnabled(opts ...sql.OrderTermOption) OrderOption {
|
||||||
|
return sql.OrderByField(FieldEnabled, opts...).ToFunc()
|
||||||
|
}
|
||||||
464
internal/models/ent/extension/where.go
Normal file
464
internal/models/ent/extension/where.go
Normal file
@@ -0,0 +1,464 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"voidraft/internal/models/ent/predicate"
|
||||||
|
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ID filters vertices based on their ID field.
|
||||||
|
func ID(id int) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDEQ applies the EQ predicate on the ID field.
|
||||||
|
func IDEQ(id int) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDNEQ applies the NEQ predicate on the ID field.
|
||||||
|
func IDNEQ(id int) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNEQ(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDIn applies the In predicate on the ID field.
|
||||||
|
func IDIn(ids ...int) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldIn(FieldID, ids...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDNotIn applies the NotIn predicate on the ID field.
|
||||||
|
func IDNotIn(ids ...int) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNotIn(FieldID, ids...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDGT applies the GT predicate on the ID field.
|
||||||
|
func IDGT(id int) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGT(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDGTE applies the GTE predicate on the ID field.
|
||||||
|
func IDGTE(id int) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGTE(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDLT applies the LT predicate on the ID field.
|
||||||
|
func IDLT(id int) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLT(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDLTE applies the LTE predicate on the ID field.
|
||||||
|
func IDLTE(id int) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLTE(FieldID, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUID applies equality check predicate on the "uuid" field. It's identical to UUIDEQ.
|
||||||
|
func UUID(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
|
||||||
|
func CreatedAt(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
|
||||||
|
func UpdatedAt(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ.
|
||||||
|
func DeletedAt(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key applies equality check predicate on the "key" field. It's identical to KeyEQ.
|
||||||
|
func Key(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled applies equality check predicate on the "enabled" field. It's identical to EnabledEQ.
|
||||||
|
func Enabled(v bool) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldEnabled, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDEQ applies the EQ predicate on the "uuid" field.
|
||||||
|
func UUIDEQ(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDNEQ applies the NEQ predicate on the "uuid" field.
|
||||||
|
func UUIDNEQ(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNEQ(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDIn applies the In predicate on the "uuid" field.
|
||||||
|
func UUIDIn(vs ...string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldIn(FieldUUID, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDNotIn applies the NotIn predicate on the "uuid" field.
|
||||||
|
func UUIDNotIn(vs ...string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNotIn(FieldUUID, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDGT applies the GT predicate on the "uuid" field.
|
||||||
|
func UUIDGT(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGT(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDGTE applies the GTE predicate on the "uuid" field.
|
||||||
|
func UUIDGTE(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGTE(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDLT applies the LT predicate on the "uuid" field.
|
||||||
|
func UUIDLT(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLT(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDLTE applies the LTE predicate on the "uuid" field.
|
||||||
|
func UUIDLTE(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLTE(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDContains applies the Contains predicate on the "uuid" field.
|
||||||
|
func UUIDContains(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldContains(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDHasPrefix applies the HasPrefix predicate on the "uuid" field.
|
||||||
|
func UUIDHasPrefix(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldHasPrefix(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDHasSuffix applies the HasSuffix predicate on the "uuid" field.
|
||||||
|
func UUIDHasSuffix(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldHasSuffix(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDIsNil applies the IsNil predicate on the "uuid" field.
|
||||||
|
func UUIDIsNil() predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldIsNull(FieldUUID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDNotNil applies the NotNil predicate on the "uuid" field.
|
||||||
|
func UUIDNotNil() predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNotNull(FieldUUID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDEqualFold applies the EqualFold predicate on the "uuid" field.
|
||||||
|
func UUIDEqualFold(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEqualFold(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUIDContainsFold applies the ContainsFold predicate on the "uuid" field.
|
||||||
|
func UUIDContainsFold(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldContainsFold(FieldUUID, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
|
||||||
|
func CreatedAtEQ(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
|
||||||
|
func CreatedAtNEQ(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNEQ(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtIn applies the In predicate on the "created_at" field.
|
||||||
|
func CreatedAtIn(vs ...string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldIn(FieldCreatedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
|
||||||
|
func CreatedAtNotIn(vs ...string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNotIn(FieldCreatedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtGT applies the GT predicate on the "created_at" field.
|
||||||
|
func CreatedAtGT(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGT(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtGTE applies the GTE predicate on the "created_at" field.
|
||||||
|
func CreatedAtGTE(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGTE(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtLT applies the LT predicate on the "created_at" field.
|
||||||
|
func CreatedAtLT(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLT(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtLTE applies the LTE predicate on the "created_at" field.
|
||||||
|
func CreatedAtLTE(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLTE(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtContains applies the Contains predicate on the "created_at" field.
|
||||||
|
func CreatedAtContains(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldContains(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtHasPrefix applies the HasPrefix predicate on the "created_at" field.
|
||||||
|
func CreatedAtHasPrefix(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldHasPrefix(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtHasSuffix applies the HasSuffix predicate on the "created_at" field.
|
||||||
|
func CreatedAtHasSuffix(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldHasSuffix(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtEqualFold applies the EqualFold predicate on the "created_at" field.
|
||||||
|
func CreatedAtEqualFold(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEqualFold(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatedAtContainsFold applies the ContainsFold predicate on the "created_at" field.
|
||||||
|
func CreatedAtContainsFold(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldContainsFold(FieldCreatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtEQ(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtNEQ(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNEQ(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtIn applies the In predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtIn(vs ...string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldIn(FieldUpdatedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtNotIn(vs ...string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNotIn(FieldUpdatedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtGT applies the GT predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtGT(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGT(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtGTE(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGTE(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtLT applies the LT predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtLT(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLT(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtLTE(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLTE(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtContains applies the Contains predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtContains(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldContains(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtHasPrefix applies the HasPrefix predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtHasPrefix(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldHasPrefix(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtHasSuffix applies the HasSuffix predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtHasSuffix(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldHasSuffix(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtEqualFold applies the EqualFold predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtEqualFold(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEqualFold(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedAtContainsFold applies the ContainsFold predicate on the "updated_at" field.
|
||||||
|
func UpdatedAtContainsFold(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldContainsFold(FieldUpdatedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtEQ(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtNEQ(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNEQ(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtIn applies the In predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtIn(vs ...string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldIn(FieldDeletedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtNotIn(vs ...string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNotIn(FieldDeletedAt, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtGT applies the GT predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtGT(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGT(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtGTE(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGTE(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtLT applies the LT predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtLT(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLT(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtLTE(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLTE(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtContains applies the Contains predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtContains(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldContains(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtHasPrefix applies the HasPrefix predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtHasPrefix(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldHasPrefix(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtHasSuffix applies the HasSuffix predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtHasSuffix(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldHasSuffix(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtIsNil() predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldIsNull(FieldDeletedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtNotNil() predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNotNull(FieldDeletedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtEqualFold applies the EqualFold predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtEqualFold(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEqualFold(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedAtContainsFold applies the ContainsFold predicate on the "deleted_at" field.
|
||||||
|
func DeletedAtContainsFold(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldContainsFold(FieldDeletedAt, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyEQ applies the EQ predicate on the "key" field.
|
||||||
|
func KeyEQ(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyNEQ applies the NEQ predicate on the "key" field.
|
||||||
|
func KeyNEQ(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNEQ(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyIn applies the In predicate on the "key" field.
|
||||||
|
func KeyIn(vs ...string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldIn(FieldKey, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyNotIn applies the NotIn predicate on the "key" field.
|
||||||
|
func KeyNotIn(vs ...string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNotIn(FieldKey, vs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyGT applies the GT predicate on the "key" field.
|
||||||
|
func KeyGT(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGT(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyGTE applies the GTE predicate on the "key" field.
|
||||||
|
func KeyGTE(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldGTE(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyLT applies the LT predicate on the "key" field.
|
||||||
|
func KeyLT(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLT(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyLTE applies the LTE predicate on the "key" field.
|
||||||
|
func KeyLTE(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldLTE(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyContains applies the Contains predicate on the "key" field.
|
||||||
|
func KeyContains(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldContains(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyHasPrefix applies the HasPrefix predicate on the "key" field.
|
||||||
|
func KeyHasPrefix(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldHasPrefix(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyHasSuffix applies the HasSuffix predicate on the "key" field.
|
||||||
|
func KeyHasSuffix(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldHasSuffix(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyEqualFold applies the EqualFold predicate on the "key" field.
|
||||||
|
func KeyEqualFold(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEqualFold(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyContainsFold applies the ContainsFold predicate on the "key" field.
|
||||||
|
func KeyContainsFold(v string) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldContainsFold(FieldKey, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnabledEQ applies the EQ predicate on the "enabled" field.
|
||||||
|
func EnabledEQ(v bool) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldEQ(FieldEnabled, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnabledNEQ applies the NEQ predicate on the "enabled" field.
|
||||||
|
func EnabledNEQ(v bool) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNEQ(FieldEnabled, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigIsNil applies the IsNil predicate on the "config" field.
|
||||||
|
func ConfigIsNil() predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldIsNull(FieldConfig))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigNotNil applies the NotNil predicate on the "config" field.
|
||||||
|
func ConfigNotNil() predicate.Extension {
|
||||||
|
return predicate.Extension(sql.FieldNotNull(FieldConfig))
|
||||||
|
}
|
||||||
|
|
||||||
|
// And groups predicates with the AND operator between them.
|
||||||
|
func And(predicates ...predicate.Extension) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.AndPredicates(predicates...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or groups predicates with the OR operator between them.
|
||||||
|
func Or(predicates ...predicate.Extension) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.OrPredicates(predicates...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not applies the not operator on the given predicate.
|
||||||
|
func Not(p predicate.Extension) predicate.Extension {
|
||||||
|
return predicate.Extension(sql.NotPredicates(p))
|
||||||
|
}
|
||||||
331
internal/models/ent/extension_create.go
Normal file
331
internal/models/ent/extension_create.go
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"voidraft/internal/models/ent/extension"
|
||||||
|
|
||||||
|
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||||
|
"entgo.io/ent/schema/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtensionCreate is the builder for creating a Extension entity.
|
||||||
|
type ExtensionCreate struct {
|
||||||
|
config
|
||||||
|
mutation *ExtensionMutation
|
||||||
|
hooks []Hook
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUUID sets the "uuid" field.
|
||||||
|
func (_c *ExtensionCreate) SetUUID(v string) *ExtensionCreate {
|
||||||
|
_c.mutation.SetUUID(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableUUID sets the "uuid" field if the given value is not nil.
|
||||||
|
func (_c *ExtensionCreate) SetNillableUUID(v *string) *ExtensionCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetUUID(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCreatedAt sets the "created_at" field.
|
||||||
|
func (_c *ExtensionCreate) SetCreatedAt(v string) *ExtensionCreate {
|
||||||
|
_c.mutation.SetCreatedAt(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
|
||||||
|
func (_c *ExtensionCreate) SetNillableCreatedAt(v *string) *ExtensionCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetCreatedAt(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUpdatedAt sets the "updated_at" field.
|
||||||
|
func (_c *ExtensionCreate) SetUpdatedAt(v string) *ExtensionCreate {
|
||||||
|
_c.mutation.SetUpdatedAt(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
|
||||||
|
func (_c *ExtensionCreate) SetNillableUpdatedAt(v *string) *ExtensionCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetUpdatedAt(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeletedAt sets the "deleted_at" field.
|
||||||
|
func (_c *ExtensionCreate) SetDeletedAt(v string) *ExtensionCreate {
|
||||||
|
_c.mutation.SetDeletedAt(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
|
||||||
|
func (_c *ExtensionCreate) SetNillableDeletedAt(v *string) *ExtensionCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetDeletedAt(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKey sets the "key" field.
|
||||||
|
func (_c *ExtensionCreate) SetKey(v string) *ExtensionCreate {
|
||||||
|
_c.mutation.SetKey(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEnabled sets the "enabled" field.
|
||||||
|
func (_c *ExtensionCreate) SetEnabled(v bool) *ExtensionCreate {
|
||||||
|
_c.mutation.SetEnabled(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableEnabled sets the "enabled" field if the given value is not nil.
|
||||||
|
func (_c *ExtensionCreate) SetNillableEnabled(v *bool) *ExtensionCreate {
|
||||||
|
if v != nil {
|
||||||
|
_c.SetEnabled(*v)
|
||||||
|
}
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConfig sets the "config" field.
|
||||||
|
func (_c *ExtensionCreate) SetConfig(v map[string]interface{}) *ExtensionCreate {
|
||||||
|
_c.mutation.SetConfig(v)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutation returns the ExtensionMutation object of the builder.
|
||||||
|
func (_c *ExtensionCreate) Mutation() *ExtensionMutation {
|
||||||
|
return _c.mutation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save creates the Extension in the database.
|
||||||
|
func (_c *ExtensionCreate) Save(ctx context.Context) (*Extension, error) {
|
||||||
|
if err := _c.defaults(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveX calls Save and panics if Save returns an error.
|
||||||
|
func (_c *ExtensionCreate) SaveX(ctx context.Context) *Extension {
|
||||||
|
v, err := _c.Save(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the query.
|
||||||
|
func (_c *ExtensionCreate) Exec(ctx context.Context) error {
|
||||||
|
_, err := _c.Save(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_c *ExtensionCreate) ExecX(ctx context.Context) {
|
||||||
|
if err := _c.Exec(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaults sets the default values of the builder before save.
|
||||||
|
func (_c *ExtensionCreate) defaults() error {
|
||||||
|
if _, ok := _c.mutation.UUID(); !ok {
|
||||||
|
if extension.DefaultUUID == nil {
|
||||||
|
return fmt.Errorf("ent: uninitialized extension.DefaultUUID (forgotten import ent/runtime?)")
|
||||||
|
}
|
||||||
|
v := extension.DefaultUUID()
|
||||||
|
_c.mutation.SetUUID(v)
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.CreatedAt(); !ok {
|
||||||
|
if extension.DefaultCreatedAt == nil {
|
||||||
|
return fmt.Errorf("ent: uninitialized extension.DefaultCreatedAt (forgotten import ent/runtime?)")
|
||||||
|
}
|
||||||
|
v := extension.DefaultCreatedAt()
|
||||||
|
_c.mutation.SetCreatedAt(v)
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.UpdatedAt(); !ok {
|
||||||
|
if extension.DefaultUpdatedAt == nil {
|
||||||
|
return fmt.Errorf("ent: uninitialized extension.DefaultUpdatedAt (forgotten import ent/runtime?)")
|
||||||
|
}
|
||||||
|
v := extension.DefaultUpdatedAt()
|
||||||
|
_c.mutation.SetUpdatedAt(v)
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.Enabled(); !ok {
|
||||||
|
v := extension.DefaultEnabled
|
||||||
|
_c.mutation.SetEnabled(v)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check runs all checks and user-defined validators on the builder.
|
||||||
|
func (_c *ExtensionCreate) check() error {
|
||||||
|
if _, ok := _c.mutation.CreatedAt(); !ok {
|
||||||
|
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "Extension.created_at"`)}
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.UpdatedAt(); !ok {
|
||||||
|
return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "Extension.updated_at"`)}
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.Key(); !ok {
|
||||||
|
return &ValidationError{Name: "key", err: errors.New(`ent: missing required field "Extension.key"`)}
|
||||||
|
}
|
||||||
|
if v, ok := _c.mutation.Key(); ok {
|
||||||
|
if err := extension.KeyValidator(v); err != nil {
|
||||||
|
return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "Extension.key": %w`, err)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := _c.mutation.Enabled(); !ok {
|
||||||
|
return &ValidationError{Name: "enabled", err: errors.New(`ent: missing required field "Extension.enabled"`)}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *ExtensionCreate) sqlSave(ctx context.Context) (*Extension, error) {
|
||||||
|
if err := _c.check(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_node, _spec := _c.createSpec()
|
||||||
|
if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
|
||||||
|
if sqlgraph.IsConstraintError(err) {
|
||||||
|
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
id := _spec.ID.Value.(int64)
|
||||||
|
_node.ID = int(id)
|
||||||
|
_c.mutation.id = &_node.ID
|
||||||
|
_c.mutation.done = true
|
||||||
|
return _node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *ExtensionCreate) createSpec() (*Extension, *sqlgraph.CreateSpec) {
|
||||||
|
var (
|
||||||
|
_node = &Extension{config: _c.config}
|
||||||
|
_spec = sqlgraph.NewCreateSpec(extension.Table, sqlgraph.NewFieldSpec(extension.FieldID, field.TypeInt))
|
||||||
|
)
|
||||||
|
if value, ok := _c.mutation.UUID(); ok {
|
||||||
|
_spec.SetField(extension.FieldUUID, field.TypeString, value)
|
||||||
|
_node.UUID = value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.CreatedAt(); ok {
|
||||||
|
_spec.SetField(extension.FieldCreatedAt, field.TypeString, value)
|
||||||
|
_node.CreatedAt = value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.UpdatedAt(); ok {
|
||||||
|
_spec.SetField(extension.FieldUpdatedAt, field.TypeString, value)
|
||||||
|
_node.UpdatedAt = value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.DeletedAt(); ok {
|
||||||
|
_spec.SetField(extension.FieldDeletedAt, field.TypeString, value)
|
||||||
|
_node.DeletedAt = &value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.Key(); ok {
|
||||||
|
_spec.SetField(extension.FieldKey, field.TypeString, value)
|
||||||
|
_node.Key = value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.Enabled(); ok {
|
||||||
|
_spec.SetField(extension.FieldEnabled, field.TypeBool, value)
|
||||||
|
_node.Enabled = value
|
||||||
|
}
|
||||||
|
if value, ok := _c.mutation.Config(); ok {
|
||||||
|
_spec.SetField(extension.FieldConfig, field.TypeJSON, value)
|
||||||
|
_node.Config = value
|
||||||
|
}
|
||||||
|
return _node, _spec
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtensionCreateBulk is the builder for creating many Extension entities in bulk.
|
||||||
|
type ExtensionCreateBulk struct {
|
||||||
|
config
|
||||||
|
err error
|
||||||
|
builders []*ExtensionCreate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save creates the Extension entities in the database.
|
||||||
|
func (_c *ExtensionCreateBulk) Save(ctx context.Context) ([]*Extension, error) {
|
||||||
|
if _c.err != nil {
|
||||||
|
return nil, _c.err
|
||||||
|
}
|
||||||
|
specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
|
||||||
|
nodes := make([]*Extension, len(_c.builders))
|
||||||
|
mutators := make([]Mutator, len(_c.builders))
|
||||||
|
for i := range _c.builders {
|
||||||
|
func(i int, root context.Context) {
|
||||||
|
builder := _c.builders[i]
|
||||||
|
builder.defaults()
|
||||||
|
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||||
|
mutation, ok := m.(*ExtensionMutation)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||||
|
}
|
||||||
|
if err := builder.check(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
builder.mutation = mutation
|
||||||
|
var err error
|
||||||
|
nodes[i], specs[i] = builder.createSpec()
|
||||||
|
if i < len(mutators)-1 {
|
||||||
|
_, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
|
||||||
|
} else {
|
||||||
|
spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
|
||||||
|
// Invoke the actual operation on the latest mutation in the chain.
|
||||||
|
if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
|
||||||
|
if sqlgraph.IsConstraintError(err) {
|
||||||
|
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mutation.id = &nodes[i].ID
|
||||||
|
if specs[i].ID.Value != nil {
|
||||||
|
id := specs[i].ID.Value.(int64)
|
||||||
|
nodes[i].ID = int(id)
|
||||||
|
}
|
||||||
|
mutation.done = true
|
||||||
|
return nodes[i], nil
|
||||||
|
})
|
||||||
|
for i := len(builder.hooks) - 1; i >= 0; i-- {
|
||||||
|
mut = builder.hooks[i](mut)
|
||||||
|
}
|
||||||
|
mutators[i] = mut
|
||||||
|
}(i, ctx)
|
||||||
|
}
|
||||||
|
if len(mutators) > 0 {
|
||||||
|
if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveX is like Save, but panics if an error occurs.
|
||||||
|
func (_c *ExtensionCreateBulk) SaveX(ctx context.Context) []*Extension {
|
||||||
|
v, err := _c.Save(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the query.
|
||||||
|
func (_c *ExtensionCreateBulk) Exec(ctx context.Context) error {
|
||||||
|
_, err := _c.Save(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_c *ExtensionCreateBulk) ExecX(ctx context.Context) {
|
||||||
|
if err := _c.Exec(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
88
internal/models/ent/extension_delete.go
Normal file
88
internal/models/ent/extension_delete.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"voidraft/internal/models/ent/extension"
|
||||||
|
"voidraft/internal/models/ent/predicate"
|
||||||
|
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||||
|
"entgo.io/ent/schema/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtensionDelete is the builder for deleting a Extension entity.
|
||||||
|
type ExtensionDelete struct {
|
||||||
|
config
|
||||||
|
hooks []Hook
|
||||||
|
mutation *ExtensionMutation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where appends a list predicates to the ExtensionDelete builder.
|
||||||
|
func (_d *ExtensionDelete) Where(ps ...predicate.Extension) *ExtensionDelete {
|
||||||
|
_d.mutation.Where(ps...)
|
||||||
|
return _d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the deletion query and returns how many vertices were deleted.
|
||||||
|
func (_d *ExtensionDelete) Exec(ctx context.Context) (int, error) {
|
||||||
|
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_d *ExtensionDelete) ExecX(ctx context.Context) int {
|
||||||
|
n, err := _d.Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_d *ExtensionDelete) sqlExec(ctx context.Context) (int, error) {
|
||||||
|
_spec := sqlgraph.NewDeleteSpec(extension.Table, sqlgraph.NewFieldSpec(extension.FieldID, field.TypeInt))
|
||||||
|
if ps := _d.mutation.predicates; len(ps) > 0 {
|
||||||
|
_spec.Predicate = func(selector *sql.Selector) {
|
||||||
|
for i := range ps {
|
||||||
|
ps[i](selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
|
||||||
|
if err != nil && sqlgraph.IsConstraintError(err) {
|
||||||
|
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||||
|
}
|
||||||
|
_d.mutation.done = true
|
||||||
|
return affected, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtensionDeleteOne is the builder for deleting a single Extension entity.
|
||||||
|
type ExtensionDeleteOne struct {
|
||||||
|
_d *ExtensionDelete
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where appends a list predicates to the ExtensionDelete builder.
|
||||||
|
func (_d *ExtensionDeleteOne) Where(ps ...predicate.Extension) *ExtensionDeleteOne {
|
||||||
|
_d._d.mutation.Where(ps...)
|
||||||
|
return _d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the deletion query.
|
||||||
|
func (_d *ExtensionDeleteOne) Exec(ctx context.Context) error {
|
||||||
|
n, err := _d._d.Exec(ctx)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return err
|
||||||
|
case n == 0:
|
||||||
|
return &NotFoundError{extension.Label}
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_d *ExtensionDeleteOne) ExecX(ctx context.Context) {
|
||||||
|
if err := _d.Exec(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
577
internal/models/ent/extension_query.go
Normal file
577
internal/models/ent/extension_query.go
Normal file
@@ -0,0 +1,577 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"voidraft/internal/models/ent/extension"
|
||||||
|
"voidraft/internal/models/ent/predicate"
|
||||||
|
|
||||||
|
"entgo.io/ent"
|
||||||
|
"entgo.io/ent/dialect"
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||||
|
"entgo.io/ent/schema/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtensionQuery is the builder for querying Extension entities.
|
||||||
|
type ExtensionQuery struct {
|
||||||
|
config
|
||||||
|
ctx *QueryContext
|
||||||
|
order []extension.OrderOption
|
||||||
|
inters []Interceptor
|
||||||
|
predicates []predicate.Extension
|
||||||
|
modifiers []func(*sql.Selector)
|
||||||
|
// intermediate query (i.e. traversal path).
|
||||||
|
sql *sql.Selector
|
||||||
|
path func(context.Context) (*sql.Selector, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where adds a new predicate for the ExtensionQuery builder.
|
||||||
|
func (_q *ExtensionQuery) Where(ps ...predicate.Extension) *ExtensionQuery {
|
||||||
|
_q.predicates = append(_q.predicates, ps...)
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit the number of records to be returned by this query.
|
||||||
|
func (_q *ExtensionQuery) Limit(limit int) *ExtensionQuery {
|
||||||
|
_q.ctx.Limit = &limit
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset to start from.
|
||||||
|
func (_q *ExtensionQuery) Offset(offset int) *ExtensionQuery {
|
||||||
|
_q.ctx.Offset = &offset
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unique configures the query builder to filter duplicate records on query.
|
||||||
|
// By default, unique is set to true, and can be disabled using this method.
|
||||||
|
func (_q *ExtensionQuery) Unique(unique bool) *ExtensionQuery {
|
||||||
|
_q.ctx.Unique = &unique
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order specifies how the records should be ordered.
|
||||||
|
func (_q *ExtensionQuery) Order(o ...extension.OrderOption) *ExtensionQuery {
|
||||||
|
_q.order = append(_q.order, o...)
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// First returns the first Extension entity from the query.
|
||||||
|
// Returns a *NotFoundError when no Extension was found.
|
||||||
|
func (_q *ExtensionQuery) First(ctx context.Context) (*Extension, error) {
|
||||||
|
nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(nodes) == 0 {
|
||||||
|
return nil, &NotFoundError{extension.Label}
|
||||||
|
}
|
||||||
|
return nodes[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstX is like First, but panics if an error occurs.
|
||||||
|
func (_q *ExtensionQuery) FirstX(ctx context.Context) *Extension {
|
||||||
|
node, err := _q.First(ctx)
|
||||||
|
if err != nil && !IsNotFound(err) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstID returns the first Extension ID from the query.
|
||||||
|
// Returns a *NotFoundError when no Extension ID was found.
|
||||||
|
func (_q *ExtensionQuery) FirstID(ctx context.Context) (id int, err error) {
|
||||||
|
var ids []int
|
||||||
|
if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(ids) == 0 {
|
||||||
|
err = &NotFoundError{extension.Label}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return ids[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstIDX is like FirstID, but panics if an error occurs.
|
||||||
|
func (_q *ExtensionQuery) FirstIDX(ctx context.Context) int {
|
||||||
|
id, err := _q.FirstID(ctx)
|
||||||
|
if err != nil && !IsNotFound(err) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only returns a single Extension entity found by the query, ensuring it only returns one.
|
||||||
|
// Returns a *NotSingularError when more than one Extension entity is found.
|
||||||
|
// Returns a *NotFoundError when no Extension entities are found.
|
||||||
|
func (_q *ExtensionQuery) Only(ctx context.Context) (*Extension, error) {
|
||||||
|
nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch len(nodes) {
|
||||||
|
case 1:
|
||||||
|
return nodes[0], nil
|
||||||
|
case 0:
|
||||||
|
return nil, &NotFoundError{extension.Label}
|
||||||
|
default:
|
||||||
|
return nil, &NotSingularError{extension.Label}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlyX is like Only, but panics if an error occurs.
|
||||||
|
func (_q *ExtensionQuery) OnlyX(ctx context.Context) *Extension {
|
||||||
|
node, err := _q.Only(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlyID is like Only, but returns the only Extension ID in the query.
|
||||||
|
// Returns a *NotSingularError when more than one Extension ID is found.
|
||||||
|
// Returns a *NotFoundError when no entities are found.
|
||||||
|
func (_q *ExtensionQuery) OnlyID(ctx context.Context) (id int, err error) {
|
||||||
|
var ids []int
|
||||||
|
if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch len(ids) {
|
||||||
|
case 1:
|
||||||
|
id = ids[0]
|
||||||
|
case 0:
|
||||||
|
err = &NotFoundError{extension.Label}
|
||||||
|
default:
|
||||||
|
err = &NotSingularError{extension.Label}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlyIDX is like OnlyID, but panics if an error occurs.
|
||||||
|
func (_q *ExtensionQuery) OnlyIDX(ctx context.Context) int {
|
||||||
|
id, err := _q.OnlyID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
// All executes the query and returns a list of Extensions.
|
||||||
|
func (_q *ExtensionQuery) All(ctx context.Context) ([]*Extension, error) {
|
||||||
|
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
|
||||||
|
if err := _q.prepareQuery(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
qr := querierAll[[]*Extension, *ExtensionQuery]()
|
||||||
|
return withInterceptors[[]*Extension](ctx, _q, qr, _q.inters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllX is like All, but panics if an error occurs.
|
||||||
|
func (_q *ExtensionQuery) AllX(ctx context.Context) []*Extension {
|
||||||
|
nodes, err := _q.All(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDs executes the query and returns a list of Extension IDs.
|
||||||
|
func (_q *ExtensionQuery) IDs(ctx context.Context) (ids []int, err error) {
|
||||||
|
if _q.ctx.Unique == nil && _q.path != nil {
|
||||||
|
_q.Unique(true)
|
||||||
|
}
|
||||||
|
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
|
||||||
|
if err = _q.Select(extension.FieldID).Scan(ctx, &ids); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDsX is like IDs, but panics if an error occurs.
|
||||||
|
func (_q *ExtensionQuery) IDsX(ctx context.Context) []int {
|
||||||
|
ids, err := _q.IDs(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the count of the given query.
|
||||||
|
func (_q *ExtensionQuery) Count(ctx context.Context) (int, error) {
|
||||||
|
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
|
||||||
|
if err := _q.prepareQuery(ctx); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return withInterceptors[int](ctx, _q, querierCount[*ExtensionQuery](), _q.inters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountX is like Count, but panics if an error occurs.
|
||||||
|
func (_q *ExtensionQuery) CountX(ctx context.Context) int {
|
||||||
|
count, err := _q.Count(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exist returns true if the query has elements in the graph.
|
||||||
|
func (_q *ExtensionQuery) Exist(ctx context.Context) (bool, error) {
|
||||||
|
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
|
||||||
|
switch _, err := _q.FirstID(ctx); {
|
||||||
|
case IsNotFound(err):
|
||||||
|
return false, nil
|
||||||
|
case err != nil:
|
||||||
|
return false, fmt.Errorf("ent: check existence: %w", err)
|
||||||
|
default:
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistX is like Exist, but panics if an error occurs.
|
||||||
|
func (_q *ExtensionQuery) ExistX(ctx context.Context) bool {
|
||||||
|
exist, err := _q.Exist(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return exist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone returns a duplicate of the ExtensionQuery builder, including all associated steps. It can be
|
||||||
|
// used to prepare common query builders and use them differently after the clone is made.
|
||||||
|
func (_q *ExtensionQuery) Clone() *ExtensionQuery {
|
||||||
|
if _q == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &ExtensionQuery{
|
||||||
|
config: _q.config,
|
||||||
|
ctx: _q.ctx.Clone(),
|
||||||
|
order: append([]extension.OrderOption{}, _q.order...),
|
||||||
|
inters: append([]Interceptor{}, _q.inters...),
|
||||||
|
predicates: append([]predicate.Extension{}, _q.predicates...),
|
||||||
|
// clone intermediate query.
|
||||||
|
sql: _q.sql.Clone(),
|
||||||
|
path: _q.path,
|
||||||
|
modifiers: append([]func(*sql.Selector){}, _q.modifiers...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupBy is used to group vertices by one or more fields/columns.
|
||||||
|
// It is often used with aggregate functions, like: count, max, mean, min, sum.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// var v []struct {
|
||||||
|
// UUID string `json:"uuid"`
|
||||||
|
// Count int `json:"count,omitempty"`
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// client.Extension.Query().
|
||||||
|
// GroupBy(extension.FieldUUID).
|
||||||
|
// Aggregate(ent.Count()).
|
||||||
|
// Scan(ctx, &v)
|
||||||
|
func (_q *ExtensionQuery) GroupBy(field string, fields ...string) *ExtensionGroupBy {
|
||||||
|
_q.ctx.Fields = append([]string{field}, fields...)
|
||||||
|
grbuild := &ExtensionGroupBy{build: _q}
|
||||||
|
grbuild.flds = &_q.ctx.Fields
|
||||||
|
grbuild.label = extension.Label
|
||||||
|
grbuild.scan = grbuild.Scan
|
||||||
|
return grbuild
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select allows the selection one or more fields/columns for the given query,
|
||||||
|
// instead of selecting all fields in the entity.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// var v []struct {
|
||||||
|
// UUID string `json:"uuid"`
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// client.Extension.Query().
|
||||||
|
// Select(extension.FieldUUID).
|
||||||
|
// Scan(ctx, &v)
|
||||||
|
func (_q *ExtensionQuery) Select(fields ...string) *ExtensionSelect {
|
||||||
|
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
|
||||||
|
sbuild := &ExtensionSelect{ExtensionQuery: _q}
|
||||||
|
sbuild.label = extension.Label
|
||||||
|
sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
|
||||||
|
return sbuild
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregate returns a ExtensionSelect configured with the given aggregations.
|
||||||
|
func (_q *ExtensionQuery) Aggregate(fns ...AggregateFunc) *ExtensionSelect {
|
||||||
|
return _q.Select().Aggregate(fns...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_q *ExtensionQuery) prepareQuery(ctx context.Context) error {
|
||||||
|
for _, inter := range _q.inters {
|
||||||
|
if inter == nil {
|
||||||
|
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
|
||||||
|
}
|
||||||
|
if trv, ok := inter.(Traverser); ok {
|
||||||
|
if err := trv.Traverse(ctx, _q); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, f := range _q.ctx.Fields {
|
||||||
|
if !extension.ValidColumn(f) {
|
||||||
|
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _q.path != nil {
|
||||||
|
prev, err := _q.path(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_q.sql = prev
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_q *ExtensionQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Extension, error) {
|
||||||
|
var (
|
||||||
|
nodes = []*Extension{}
|
||||||
|
_spec = _q.querySpec()
|
||||||
|
)
|
||||||
|
_spec.ScanValues = func(columns []string) ([]any, error) {
|
||||||
|
return (*Extension).scanValues(nil, columns)
|
||||||
|
}
|
||||||
|
_spec.Assign = func(columns []string, values []any) error {
|
||||||
|
node := &Extension{config: _q.config}
|
||||||
|
nodes = append(nodes, node)
|
||||||
|
return node.assignValues(columns, values)
|
||||||
|
}
|
||||||
|
if len(_q.modifiers) > 0 {
|
||||||
|
_spec.Modifiers = _q.modifiers
|
||||||
|
}
|
||||||
|
for i := range hooks {
|
||||||
|
hooks[i](ctx, _spec)
|
||||||
|
}
|
||||||
|
if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(nodes) == 0 {
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_q *ExtensionQuery) sqlCount(ctx context.Context) (int, error) {
|
||||||
|
_spec := _q.querySpec()
|
||||||
|
if len(_q.modifiers) > 0 {
|
||||||
|
_spec.Modifiers = _q.modifiers
|
||||||
|
}
|
||||||
|
_spec.Node.Columns = _q.ctx.Fields
|
||||||
|
if len(_q.ctx.Fields) > 0 {
|
||||||
|
_spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
|
||||||
|
}
|
||||||
|
return sqlgraph.CountNodes(ctx, _q.driver, _spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_q *ExtensionQuery) querySpec() *sqlgraph.QuerySpec {
|
||||||
|
_spec := sqlgraph.NewQuerySpec(extension.Table, extension.Columns, sqlgraph.NewFieldSpec(extension.FieldID, field.TypeInt))
|
||||||
|
_spec.From = _q.sql
|
||||||
|
if unique := _q.ctx.Unique; unique != nil {
|
||||||
|
_spec.Unique = *unique
|
||||||
|
} else if _q.path != nil {
|
||||||
|
_spec.Unique = true
|
||||||
|
}
|
||||||
|
if fields := _q.ctx.Fields; len(fields) > 0 {
|
||||||
|
_spec.Node.Columns = make([]string, 0, len(fields))
|
||||||
|
_spec.Node.Columns = append(_spec.Node.Columns, extension.FieldID)
|
||||||
|
for i := range fields {
|
||||||
|
if fields[i] != extension.FieldID {
|
||||||
|
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ps := _q.predicates; len(ps) > 0 {
|
||||||
|
_spec.Predicate = func(selector *sql.Selector) {
|
||||||
|
for i := range ps {
|
||||||
|
ps[i](selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if limit := _q.ctx.Limit; limit != nil {
|
||||||
|
_spec.Limit = *limit
|
||||||
|
}
|
||||||
|
if offset := _q.ctx.Offset; offset != nil {
|
||||||
|
_spec.Offset = *offset
|
||||||
|
}
|
||||||
|
if ps := _q.order; len(ps) > 0 {
|
||||||
|
_spec.Order = func(selector *sql.Selector) {
|
||||||
|
for i := range ps {
|
||||||
|
ps[i](selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _spec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_q *ExtensionQuery) sqlQuery(ctx context.Context) *sql.Selector {
|
||||||
|
builder := sql.Dialect(_q.driver.Dialect())
|
||||||
|
t1 := builder.Table(extension.Table)
|
||||||
|
columns := _q.ctx.Fields
|
||||||
|
if len(columns) == 0 {
|
||||||
|
columns = extension.Columns
|
||||||
|
}
|
||||||
|
selector := builder.Select(t1.Columns(columns...)...).From(t1)
|
||||||
|
if _q.sql != nil {
|
||||||
|
selector = _q.sql
|
||||||
|
selector.Select(selector.Columns(columns...)...)
|
||||||
|
}
|
||||||
|
if _q.ctx.Unique != nil && *_q.ctx.Unique {
|
||||||
|
selector.Distinct()
|
||||||
|
}
|
||||||
|
for _, m := range _q.modifiers {
|
||||||
|
m(selector)
|
||||||
|
}
|
||||||
|
for _, p := range _q.predicates {
|
||||||
|
p(selector)
|
||||||
|
}
|
||||||
|
for _, p := range _q.order {
|
||||||
|
p(selector)
|
||||||
|
}
|
||||||
|
if offset := _q.ctx.Offset; offset != nil {
|
||||||
|
// limit is mandatory for offset clause. We start
|
||||||
|
// with default value, and override it below if needed.
|
||||||
|
selector.Offset(*offset).Limit(math.MaxInt32)
|
||||||
|
}
|
||||||
|
if limit := _q.ctx.Limit; limit != nil {
|
||||||
|
selector.Limit(*limit)
|
||||||
|
}
|
||||||
|
return selector
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForUpdate locks the selected rows against concurrent updates, and prevent them from being
|
||||||
|
// updated, deleted or "selected ... for update" by other sessions, until the transaction is
|
||||||
|
// either committed or rolled-back.
|
||||||
|
func (_q *ExtensionQuery) ForUpdate(opts ...sql.LockOption) *ExtensionQuery {
|
||||||
|
if _q.driver.Dialect() == dialect.Postgres {
|
||||||
|
_q.Unique(false)
|
||||||
|
}
|
||||||
|
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
|
||||||
|
s.ForUpdate(opts...)
|
||||||
|
})
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock
|
||||||
|
// on any rows that are read. Other sessions can read the rows, but cannot modify them
|
||||||
|
// until your transaction commits.
|
||||||
|
func (_q *ExtensionQuery) ForShare(opts ...sql.LockOption) *ExtensionQuery {
|
||||||
|
if _q.driver.Dialect() == dialect.Postgres {
|
||||||
|
_q.Unique(false)
|
||||||
|
}
|
||||||
|
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
|
||||||
|
s.ForShare(opts...)
|
||||||
|
})
|
||||||
|
return _q
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify adds a query modifier for attaching custom logic to queries.
|
||||||
|
func (_q *ExtensionQuery) Modify(modifiers ...func(s *sql.Selector)) *ExtensionSelect {
|
||||||
|
_q.modifiers = append(_q.modifiers, modifiers...)
|
||||||
|
return _q.Select()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtensionGroupBy is the group-by builder for Extension entities.
|
||||||
|
type ExtensionGroupBy struct {
|
||||||
|
selector
|
||||||
|
build *ExtensionQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregate adds the given aggregation functions to the group-by query.
|
||||||
|
func (_g *ExtensionGroupBy) Aggregate(fns ...AggregateFunc) *ExtensionGroupBy {
|
||||||
|
_g.fns = append(_g.fns, fns...)
|
||||||
|
return _g
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan applies the selector query and scans the result into the given value.
|
||||||
|
func (_g *ExtensionGroupBy) Scan(ctx context.Context, v any) error {
|
||||||
|
ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
|
||||||
|
if err := _g.build.prepareQuery(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return scanWithInterceptors[*ExtensionQuery, *ExtensionGroupBy](ctx, _g.build, _g, _g.build.inters, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_g *ExtensionGroupBy) sqlScan(ctx context.Context, root *ExtensionQuery, v any) error {
|
||||||
|
selector := root.sqlQuery(ctx).Select()
|
||||||
|
aggregation := make([]string, 0, len(_g.fns))
|
||||||
|
for _, fn := range _g.fns {
|
||||||
|
aggregation = append(aggregation, fn(selector))
|
||||||
|
}
|
||||||
|
if len(selector.SelectedColumns()) == 0 {
|
||||||
|
columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
|
||||||
|
for _, f := range *_g.flds {
|
||||||
|
columns = append(columns, selector.C(f))
|
||||||
|
}
|
||||||
|
columns = append(columns, aggregation...)
|
||||||
|
selector.Select(columns...)
|
||||||
|
}
|
||||||
|
selector.GroupBy(selector.Columns(*_g.flds...)...)
|
||||||
|
if err := selector.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rows := &sql.Rows{}
|
||||||
|
query, args := selector.Query()
|
||||||
|
if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
return sql.ScanSlice(rows, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtensionSelect is the builder for selecting fields of Extension entities.
|
||||||
|
type ExtensionSelect struct {
|
||||||
|
*ExtensionQuery
|
||||||
|
selector
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregate adds the given aggregation functions to the selector query.
|
||||||
|
func (_s *ExtensionSelect) Aggregate(fns ...AggregateFunc) *ExtensionSelect {
|
||||||
|
_s.fns = append(_s.fns, fns...)
|
||||||
|
return _s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan applies the selector query and scans the result into the given value.
|
||||||
|
func (_s *ExtensionSelect) Scan(ctx context.Context, v any) error {
|
||||||
|
ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
|
||||||
|
if err := _s.prepareQuery(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return scanWithInterceptors[*ExtensionQuery, *ExtensionSelect](ctx, _s.ExtensionQuery, _s, _s.inters, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_s *ExtensionSelect) sqlScan(ctx context.Context, root *ExtensionQuery, v any) error {
|
||||||
|
selector := root.sqlQuery(ctx)
|
||||||
|
aggregation := make([]string, 0, len(_s.fns))
|
||||||
|
for _, fn := range _s.fns {
|
||||||
|
aggregation = append(aggregation, fn(selector))
|
||||||
|
}
|
||||||
|
switch n := len(*_s.selector.flds); {
|
||||||
|
case n == 0 && len(aggregation) > 0:
|
||||||
|
selector.Select(aggregation...)
|
||||||
|
case n != 0 && len(aggregation) > 0:
|
||||||
|
selector.AppendSelect(aggregation...)
|
||||||
|
}
|
||||||
|
rows := &sql.Rows{}
|
||||||
|
query, args := selector.Query()
|
||||||
|
if err := _s.driver.Query(ctx, query, args, rows); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
return sql.ScanSlice(rows, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify adds a query modifier for attaching custom logic to queries.
|
||||||
|
func (_s *ExtensionSelect) Modify(modifiers ...func(s *sql.Selector)) *ExtensionSelect {
|
||||||
|
_s.modifiers = append(_s.modifiers, modifiers...)
|
||||||
|
return _s
|
||||||
|
}
|
||||||
413
internal/models/ent/extension_update.go
Normal file
413
internal/models/ent/extension_update.go
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"voidraft/internal/models/ent/extension"
|
||||||
|
"voidraft/internal/models/ent/predicate"
|
||||||
|
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||||
|
"entgo.io/ent/schema/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtensionUpdate is the builder for updating Extension entities.
|
||||||
|
type ExtensionUpdate struct {
|
||||||
|
config
|
||||||
|
hooks []Hook
|
||||||
|
mutation *ExtensionMutation
|
||||||
|
modifiers []func(*sql.UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where appends a list predicates to the ExtensionUpdate builder.
|
||||||
|
func (_u *ExtensionUpdate) Where(ps ...predicate.Extension) *ExtensionUpdate {
|
||||||
|
_u.mutation.Where(ps...)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUpdatedAt sets the "updated_at" field.
|
||||||
|
func (_u *ExtensionUpdate) SetUpdatedAt(v string) *ExtensionUpdate {
|
||||||
|
_u.mutation.SetUpdatedAt(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
|
||||||
|
func (_u *ExtensionUpdate) SetNillableUpdatedAt(v *string) *ExtensionUpdate {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetUpdatedAt(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeletedAt sets the "deleted_at" field.
|
||||||
|
func (_u *ExtensionUpdate) SetDeletedAt(v string) *ExtensionUpdate {
|
||||||
|
_u.mutation.SetDeletedAt(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
|
||||||
|
func (_u *ExtensionUpdate) SetNillableDeletedAt(v *string) *ExtensionUpdate {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetDeletedAt(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearDeletedAt clears the value of the "deleted_at" field.
|
||||||
|
func (_u *ExtensionUpdate) ClearDeletedAt() *ExtensionUpdate {
|
||||||
|
_u.mutation.ClearDeletedAt()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKey sets the "key" field.
|
||||||
|
func (_u *ExtensionUpdate) SetKey(v string) *ExtensionUpdate {
|
||||||
|
_u.mutation.SetKey(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableKey sets the "key" field if the given value is not nil.
|
||||||
|
func (_u *ExtensionUpdate) SetNillableKey(v *string) *ExtensionUpdate {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetKey(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEnabled sets the "enabled" field.
|
||||||
|
func (_u *ExtensionUpdate) SetEnabled(v bool) *ExtensionUpdate {
|
||||||
|
_u.mutation.SetEnabled(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableEnabled sets the "enabled" field if the given value is not nil.
|
||||||
|
func (_u *ExtensionUpdate) SetNillableEnabled(v *bool) *ExtensionUpdate {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetEnabled(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConfig sets the "config" field.
|
||||||
|
func (_u *ExtensionUpdate) SetConfig(v map[string]interface{}) *ExtensionUpdate {
|
||||||
|
_u.mutation.SetConfig(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearConfig clears the value of the "config" field.
|
||||||
|
func (_u *ExtensionUpdate) ClearConfig() *ExtensionUpdate {
|
||||||
|
_u.mutation.ClearConfig()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutation returns the ExtensionMutation object of the builder.
|
||||||
|
func (_u *ExtensionUpdate) Mutation() *ExtensionMutation {
|
||||||
|
return _u.mutation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save executes the query and returns the number of nodes affected by the update operation.
|
||||||
|
func (_u *ExtensionUpdate) Save(ctx context.Context) (int, error) {
|
||||||
|
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveX is like Save, but panics if an error occurs.
|
||||||
|
func (_u *ExtensionUpdate) SaveX(ctx context.Context) int {
|
||||||
|
affected, err := _u.Save(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return affected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the query.
|
||||||
|
func (_u *ExtensionUpdate) Exec(ctx context.Context) error {
|
||||||
|
_, err := _u.Save(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_u *ExtensionUpdate) ExecX(ctx context.Context) {
|
||||||
|
if err := _u.Exec(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check runs all checks and user-defined validators on the builder.
|
||||||
|
func (_u *ExtensionUpdate) check() error {
|
||||||
|
if v, ok := _u.mutation.Key(); ok {
|
||||||
|
if err := extension.KeyValidator(v); err != nil {
|
||||||
|
return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "Extension.key": %w`, err)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify adds a statement modifier for attaching custom logic to the UPDATE statement.
|
||||||
|
func (_u *ExtensionUpdate) Modify(modifiers ...func(u *sql.UpdateBuilder)) *ExtensionUpdate {
|
||||||
|
_u.modifiers = append(_u.modifiers, modifiers...)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_u *ExtensionUpdate) sqlSave(ctx context.Context) (_node int, err error) {
|
||||||
|
if err := _u.check(); err != nil {
|
||||||
|
return _node, err
|
||||||
|
}
|
||||||
|
_spec := sqlgraph.NewUpdateSpec(extension.Table, extension.Columns, sqlgraph.NewFieldSpec(extension.FieldID, field.TypeInt))
|
||||||
|
if ps := _u.mutation.predicates; len(ps) > 0 {
|
||||||
|
_spec.Predicate = func(selector *sql.Selector) {
|
||||||
|
for i := range ps {
|
||||||
|
ps[i](selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _u.mutation.UUIDCleared() {
|
||||||
|
_spec.ClearField(extension.FieldUUID, field.TypeString)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.UpdatedAt(); ok {
|
||||||
|
_spec.SetField(extension.FieldUpdatedAt, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.DeletedAt(); ok {
|
||||||
|
_spec.SetField(extension.FieldDeletedAt, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if _u.mutation.DeletedAtCleared() {
|
||||||
|
_spec.ClearField(extension.FieldDeletedAt, field.TypeString)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Key(); ok {
|
||||||
|
_spec.SetField(extension.FieldKey, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Enabled(); ok {
|
||||||
|
_spec.SetField(extension.FieldEnabled, field.TypeBool, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Config(); ok {
|
||||||
|
_spec.SetField(extension.FieldConfig, field.TypeJSON, value)
|
||||||
|
}
|
||||||
|
if _u.mutation.ConfigCleared() {
|
||||||
|
_spec.ClearField(extension.FieldConfig, field.TypeJSON)
|
||||||
|
}
|
||||||
|
_spec.AddModifiers(_u.modifiers...)
|
||||||
|
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
|
||||||
|
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||||
|
err = &NotFoundError{extension.Label}
|
||||||
|
} else if sqlgraph.IsConstraintError(err) {
|
||||||
|
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
_u.mutation.done = true
|
||||||
|
return _node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtensionUpdateOne is the builder for updating a single Extension entity.
|
||||||
|
type ExtensionUpdateOne struct {
|
||||||
|
config
|
||||||
|
fields []string
|
||||||
|
hooks []Hook
|
||||||
|
mutation *ExtensionMutation
|
||||||
|
modifiers []func(*sql.UpdateBuilder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUpdatedAt sets the "updated_at" field.
|
||||||
|
func (_u *ExtensionUpdateOne) SetUpdatedAt(v string) *ExtensionUpdateOne {
|
||||||
|
_u.mutation.SetUpdatedAt(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
|
||||||
|
func (_u *ExtensionUpdateOne) SetNillableUpdatedAt(v *string) *ExtensionUpdateOne {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetUpdatedAt(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeletedAt sets the "deleted_at" field.
|
||||||
|
func (_u *ExtensionUpdateOne) SetDeletedAt(v string) *ExtensionUpdateOne {
|
||||||
|
_u.mutation.SetDeletedAt(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
|
||||||
|
func (_u *ExtensionUpdateOne) SetNillableDeletedAt(v *string) *ExtensionUpdateOne {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetDeletedAt(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearDeletedAt clears the value of the "deleted_at" field.
|
||||||
|
func (_u *ExtensionUpdateOne) ClearDeletedAt() *ExtensionUpdateOne {
|
||||||
|
_u.mutation.ClearDeletedAt()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKey sets the "key" field.
|
||||||
|
func (_u *ExtensionUpdateOne) SetKey(v string) *ExtensionUpdateOne {
|
||||||
|
_u.mutation.SetKey(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableKey sets the "key" field if the given value is not nil.
|
||||||
|
func (_u *ExtensionUpdateOne) SetNillableKey(v *string) *ExtensionUpdateOne {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetKey(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEnabled sets the "enabled" field.
|
||||||
|
func (_u *ExtensionUpdateOne) SetEnabled(v bool) *ExtensionUpdateOne {
|
||||||
|
_u.mutation.SetEnabled(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNillableEnabled sets the "enabled" field if the given value is not nil.
|
||||||
|
func (_u *ExtensionUpdateOne) SetNillableEnabled(v *bool) *ExtensionUpdateOne {
|
||||||
|
if v != nil {
|
||||||
|
_u.SetEnabled(*v)
|
||||||
|
}
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConfig sets the "config" field.
|
||||||
|
func (_u *ExtensionUpdateOne) SetConfig(v map[string]interface{}) *ExtensionUpdateOne {
|
||||||
|
_u.mutation.SetConfig(v)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearConfig clears the value of the "config" field.
|
||||||
|
func (_u *ExtensionUpdateOne) ClearConfig() *ExtensionUpdateOne {
|
||||||
|
_u.mutation.ClearConfig()
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutation returns the ExtensionMutation object of the builder.
|
||||||
|
func (_u *ExtensionUpdateOne) Mutation() *ExtensionMutation {
|
||||||
|
return _u.mutation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where appends a list predicates to the ExtensionUpdate builder.
|
||||||
|
func (_u *ExtensionUpdateOne) Where(ps ...predicate.Extension) *ExtensionUpdateOne {
|
||||||
|
_u.mutation.Where(ps...)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select allows selecting one or more fields (columns) of the returned entity.
|
||||||
|
// The default is selecting all fields defined in the entity schema.
|
||||||
|
func (_u *ExtensionUpdateOne) Select(field string, fields ...string) *ExtensionUpdateOne {
|
||||||
|
_u.fields = append([]string{field}, fields...)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save executes the query and returns the updated Extension entity.
|
||||||
|
func (_u *ExtensionUpdateOne) Save(ctx context.Context) (*Extension, error) {
|
||||||
|
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveX is like Save, but panics if an error occurs.
|
||||||
|
func (_u *ExtensionUpdateOne) SaveX(ctx context.Context) *Extension {
|
||||||
|
node, err := _u.Save(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes the query on the entity.
|
||||||
|
func (_u *ExtensionUpdateOne) Exec(ctx context.Context) error {
|
||||||
|
_, err := _u.Save(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecX is like Exec, but panics if an error occurs.
|
||||||
|
func (_u *ExtensionUpdateOne) ExecX(ctx context.Context) {
|
||||||
|
if err := _u.Exec(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check runs all checks and user-defined validators on the builder.
|
||||||
|
func (_u *ExtensionUpdateOne) check() error {
|
||||||
|
if v, ok := _u.mutation.Key(); ok {
|
||||||
|
if err := extension.KeyValidator(v); err != nil {
|
||||||
|
return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "Extension.key": %w`, err)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify adds a statement modifier for attaching custom logic to the UPDATE statement.
|
||||||
|
func (_u *ExtensionUpdateOne) Modify(modifiers ...func(u *sql.UpdateBuilder)) *ExtensionUpdateOne {
|
||||||
|
_u.modifiers = append(_u.modifiers, modifiers...)
|
||||||
|
return _u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_u *ExtensionUpdateOne) sqlSave(ctx context.Context) (_node *Extension, err error) {
|
||||||
|
if err := _u.check(); err != nil {
|
||||||
|
return _node, err
|
||||||
|
}
|
||||||
|
_spec := sqlgraph.NewUpdateSpec(extension.Table, extension.Columns, sqlgraph.NewFieldSpec(extension.FieldID, field.TypeInt))
|
||||||
|
id, ok := _u.mutation.ID()
|
||||||
|
if !ok {
|
||||||
|
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Extension.id" for update`)}
|
||||||
|
}
|
||||||
|
_spec.Node.ID.Value = id
|
||||||
|
if fields := _u.fields; len(fields) > 0 {
|
||||||
|
_spec.Node.Columns = make([]string, 0, len(fields))
|
||||||
|
_spec.Node.Columns = append(_spec.Node.Columns, extension.FieldID)
|
||||||
|
for _, f := range fields {
|
||||||
|
if !extension.ValidColumn(f) {
|
||||||
|
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
|
||||||
|
}
|
||||||
|
if f != extension.FieldID {
|
||||||
|
_spec.Node.Columns = append(_spec.Node.Columns, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ps := _u.mutation.predicates; len(ps) > 0 {
|
||||||
|
_spec.Predicate = func(selector *sql.Selector) {
|
||||||
|
for i := range ps {
|
||||||
|
ps[i](selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _u.mutation.UUIDCleared() {
|
||||||
|
_spec.ClearField(extension.FieldUUID, field.TypeString)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.UpdatedAt(); ok {
|
||||||
|
_spec.SetField(extension.FieldUpdatedAt, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.DeletedAt(); ok {
|
||||||
|
_spec.SetField(extension.FieldDeletedAt, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if _u.mutation.DeletedAtCleared() {
|
||||||
|
_spec.ClearField(extension.FieldDeletedAt, field.TypeString)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Key(); ok {
|
||||||
|
_spec.SetField(extension.FieldKey, field.TypeString, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Enabled(); ok {
|
||||||
|
_spec.SetField(extension.FieldEnabled, field.TypeBool, value)
|
||||||
|
}
|
||||||
|
if value, ok := _u.mutation.Config(); ok {
|
||||||
|
_spec.SetField(extension.FieldConfig, field.TypeJSON, value)
|
||||||
|
}
|
||||||
|
if _u.mutation.ConfigCleared() {
|
||||||
|
_spec.ClearField(extension.FieldConfig, field.TypeJSON)
|
||||||
|
}
|
||||||
|
_spec.AddModifiers(_u.modifiers...)
|
||||||
|
_node = &Extension{config: _u.config}
|
||||||
|
_spec.Assign = _node.assignValues
|
||||||
|
_spec.ScanValues = _node.scanValues
|
||||||
|
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
|
||||||
|
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||||
|
err = &NotFoundError{extension.Label}
|
||||||
|
} else if sqlgraph.IsConstraintError(err) {
|
||||||
|
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_u.mutation.done = true
|
||||||
|
return _node, nil
|
||||||
|
}
|
||||||
1
internal/models/ent/generate.go
Normal file
1
internal/models/ent/generate.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package ent
|
||||||
234
internal/models/ent/hook/hook.go
Normal file
234
internal/models/ent/hook/hook.go
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package hook
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"voidraft/internal/models/ent"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The DocumentFunc type is an adapter to allow the use of ordinary
|
||||||
|
// function as Document mutator.
|
||||||
|
type DocumentFunc func(context.Context, *ent.DocumentMutation) (ent.Value, error)
|
||||||
|
|
||||||
|
// Mutate calls f(ctx, m).
|
||||||
|
func (f DocumentFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
|
||||||
|
if mv, ok := m.(*ent.DocumentMutation); ok {
|
||||||
|
return f(ctx, mv)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.DocumentMutation", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ExtensionFunc type is an adapter to allow the use of ordinary
|
||||||
|
// function as Extension mutator.
|
||||||
|
type ExtensionFunc func(context.Context, *ent.ExtensionMutation) (ent.Value, error)
|
||||||
|
|
||||||
|
// Mutate calls f(ctx, m).
|
||||||
|
func (f ExtensionFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
|
||||||
|
if mv, ok := m.(*ent.ExtensionMutation); ok {
|
||||||
|
return f(ctx, mv)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ExtensionMutation", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The KeyBindingFunc type is an adapter to allow the use of ordinary
|
||||||
|
// function as KeyBinding mutator.
|
||||||
|
type KeyBindingFunc func(context.Context, *ent.KeyBindingMutation) (ent.Value, error)
|
||||||
|
|
||||||
|
// Mutate calls f(ctx, m).
|
||||||
|
func (f KeyBindingFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
|
||||||
|
if mv, ok := m.(*ent.KeyBindingMutation); ok {
|
||||||
|
return f(ctx, mv)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.KeyBindingMutation", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ThemeFunc type is an adapter to allow the use of ordinary
|
||||||
|
// function as Theme mutator.
|
||||||
|
type ThemeFunc func(context.Context, *ent.ThemeMutation) (ent.Value, error)
|
||||||
|
|
||||||
|
// Mutate calls f(ctx, m).
|
||||||
|
func (f ThemeFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
|
||||||
|
if mv, ok := m.(*ent.ThemeMutation); ok {
|
||||||
|
return f(ctx, mv)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ThemeMutation", m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Condition is a hook condition function.
|
||||||
|
type Condition func(context.Context, ent.Mutation) bool
|
||||||
|
|
||||||
|
// And groups conditions with the AND operator.
|
||||||
|
func And(first, second Condition, rest ...Condition) Condition {
|
||||||
|
return func(ctx context.Context, m ent.Mutation) bool {
|
||||||
|
if !first(ctx, m) || !second(ctx, m) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, cond := range rest {
|
||||||
|
if !cond(ctx, m) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or groups conditions with the OR operator.
|
||||||
|
func Or(first, second Condition, rest ...Condition) Condition {
|
||||||
|
return func(ctx context.Context, m ent.Mutation) bool {
|
||||||
|
if first(ctx, m) || second(ctx, m) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, cond := range rest {
|
||||||
|
if cond(ctx, m) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not negates a given condition.
|
||||||
|
func Not(cond Condition) Condition {
|
||||||
|
return func(ctx context.Context, m ent.Mutation) bool {
|
||||||
|
return !cond(ctx, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasOp is a condition testing mutation operation.
|
||||||
|
func HasOp(op ent.Op) Condition {
|
||||||
|
return func(_ context.Context, m ent.Mutation) bool {
|
||||||
|
return m.Op().Is(op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasAddedFields is a condition validating `.AddedField` on fields.
|
||||||
|
func HasAddedFields(field string, fields ...string) Condition {
|
||||||
|
return func(_ context.Context, m ent.Mutation) bool {
|
||||||
|
if _, exists := m.AddedField(field); !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, field := range fields {
|
||||||
|
if _, exists := m.AddedField(field); !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasClearedFields is a condition validating `.FieldCleared` on fields.
|
||||||
|
func HasClearedFields(field string, fields ...string) Condition {
|
||||||
|
return func(_ context.Context, m ent.Mutation) bool {
|
||||||
|
if exists := m.FieldCleared(field); !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, field := range fields {
|
||||||
|
if exists := m.FieldCleared(field); !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasFields is a condition validating `.Field` on fields.
|
||||||
|
func HasFields(field string, fields ...string) Condition {
|
||||||
|
return func(_ context.Context, m ent.Mutation) bool {
|
||||||
|
if _, exists := m.Field(field); !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, field := range fields {
|
||||||
|
if _, exists := m.Field(field); !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If executes the given hook under condition.
|
||||||
|
//
|
||||||
|
// hook.If(ComputeAverage, And(HasFields(...), HasAddedFields(...)))
|
||||||
|
func If(hk ent.Hook, cond Condition) ent.Hook {
|
||||||
|
return func(next ent.Mutator) ent.Mutator {
|
||||||
|
return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
|
||||||
|
if cond(ctx, m) {
|
||||||
|
return hk(next).Mutate(ctx, m)
|
||||||
|
}
|
||||||
|
return next.Mutate(ctx, m)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On executes the given hook only for the given operation.
|
||||||
|
//
|
||||||
|
// hook.On(Log, ent.Delete|ent.Create)
|
||||||
|
func On(hk ent.Hook, op ent.Op) ent.Hook {
|
||||||
|
return If(hk, HasOp(op))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unless skips the given hook only for the given operation.
|
||||||
|
//
|
||||||
|
// hook.Unless(Log, ent.Update|ent.UpdateOne)
|
||||||
|
func Unless(hk ent.Hook, op ent.Op) ent.Hook {
|
||||||
|
return If(hk, Not(HasOp(op)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FixedError is a hook returning a fixed error.
|
||||||
|
func FixedError(err error) ent.Hook {
|
||||||
|
return func(ent.Mutator) ent.Mutator {
|
||||||
|
return ent.MutateFunc(func(context.Context, ent.Mutation) (ent.Value, error) {
|
||||||
|
return nil, err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject returns a hook that rejects all operations that match op.
|
||||||
|
//
|
||||||
|
// func (T) Hooks() []ent.Hook {
|
||||||
|
// return []ent.Hook{
|
||||||
|
// Reject(ent.Delete|ent.Update),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
func Reject(op ent.Op) ent.Hook {
|
||||||
|
hk := FixedError(fmt.Errorf("%s operation is not allowed", op))
|
||||||
|
return On(hk, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chain acts as a list of hooks and is effectively immutable.
|
||||||
|
// Once created, it will always hold the same set of hooks in the same order.
|
||||||
|
type Chain struct {
|
||||||
|
hooks []ent.Hook
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChain creates a new chain of hooks.
|
||||||
|
func NewChain(hooks ...ent.Hook) Chain {
|
||||||
|
return Chain{append([]ent.Hook(nil), hooks...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook chains the list of hooks and returns the final hook.
|
||||||
|
func (c Chain) Hook() ent.Hook {
|
||||||
|
return func(mutator ent.Mutator) ent.Mutator {
|
||||||
|
for i := len(c.hooks) - 1; i >= 0; i-- {
|
||||||
|
mutator = c.hooks[i](mutator)
|
||||||
|
}
|
||||||
|
return mutator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append extends a chain, adding the specified hook
|
||||||
|
// as the last ones in the mutation flow.
|
||||||
|
func (c Chain) Append(hooks ...ent.Hook) Chain {
|
||||||
|
newHooks := make([]ent.Hook, 0, len(c.hooks)+len(hooks))
|
||||||
|
newHooks = append(newHooks, c.hooks...)
|
||||||
|
newHooks = append(newHooks, hooks...)
|
||||||
|
return Chain{newHooks}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend extends a chain, adding the specified chain
|
||||||
|
// as the last ones in the mutation flow.
|
||||||
|
func (c Chain) Extend(chain Chain) Chain {
|
||||||
|
return c.Append(chain.hooks...)
|
||||||
|
}
|
||||||
240
internal/models/ent/intercept/intercept.go
Normal file
240
internal/models/ent/intercept/intercept.go
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package intercept
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"voidraft/internal/models/ent"
|
||||||
|
"voidraft/internal/models/ent/document"
|
||||||
|
"voidraft/internal/models/ent/extension"
|
||||||
|
"voidraft/internal/models/ent/keybinding"
|
||||||
|
"voidraft/internal/models/ent/predicate"
|
||||||
|
"voidraft/internal/models/ent/theme"
|
||||||
|
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The Query interface represents an operation that queries a graph.
|
||||||
|
// By using this interface, users can write generic code that manipulates
|
||||||
|
// query builders of different types.
|
||||||
|
type Query interface {
|
||||||
|
// Type returns the string representation of the query type.
|
||||||
|
Type() string
|
||||||
|
// Limit the number of records to be returned by this query.
|
||||||
|
Limit(int)
|
||||||
|
// Offset to start from.
|
||||||
|
Offset(int)
|
||||||
|
// Unique configures the query builder to filter duplicate records.
|
||||||
|
Unique(bool)
|
||||||
|
// Order specifies how the records should be ordered.
|
||||||
|
Order(...func(*sql.Selector))
|
||||||
|
// WhereP appends storage-level predicates to the query builder. Using this method, users
|
||||||
|
// can use type-assertion to append predicates that do not depend on any generated package.
|
||||||
|
WhereP(...func(*sql.Selector))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Func type is an adapter that allows ordinary functions to be used as interceptors.
|
||||||
|
// Unlike traversal functions, interceptors are skipped during graph traversals. Note that the
|
||||||
|
// implementation of Func is different from the one defined in entgo.io/ent.InterceptFunc.
|
||||||
|
type Func func(context.Context, Query) error
|
||||||
|
|
||||||
|
// Intercept calls f(ctx, q) and then applied the next Querier.
|
||||||
|
func (f Func) Intercept(next ent.Querier) ent.Querier {
|
||||||
|
return ent.QuerierFunc(func(ctx context.Context, q ent.Query) (ent.Value, error) {
|
||||||
|
query, err := NewQuery(q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := f(ctx, query); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return next.Query(ctx, q)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// The TraverseFunc type is an adapter to allow the use of ordinary function as Traverser.
|
||||||
|
// If f is a function with the appropriate signature, TraverseFunc(f) is a Traverser that calls f.
|
||||||
|
type TraverseFunc func(context.Context, Query) error
|
||||||
|
|
||||||
|
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
|
||||||
|
func (f TraverseFunc) Intercept(next ent.Querier) ent.Querier {
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse calls f(ctx, q).
|
||||||
|
func (f TraverseFunc) Traverse(ctx context.Context, q ent.Query) error {
|
||||||
|
query, err := NewQuery(q)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return f(ctx, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The DocumentFunc type is an adapter to allow the use of ordinary function as a Querier.
|
||||||
|
type DocumentFunc func(context.Context, *ent.DocumentQuery) (ent.Value, error)
|
||||||
|
|
||||||
|
// Query calls f(ctx, q).
|
||||||
|
func (f DocumentFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
|
||||||
|
if q, ok := q.(*ent.DocumentQuery); ok {
|
||||||
|
return f(ctx, q)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected query type %T. expect *ent.DocumentQuery", q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The TraverseDocument type is an adapter to allow the use of ordinary function as Traverser.
|
||||||
|
type TraverseDocument func(context.Context, *ent.DocumentQuery) error
|
||||||
|
|
||||||
|
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
|
||||||
|
func (f TraverseDocument) Intercept(next ent.Querier) ent.Querier {
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse calls f(ctx, q).
|
||||||
|
func (f TraverseDocument) Traverse(ctx context.Context, q ent.Query) error {
|
||||||
|
if q, ok := q.(*ent.DocumentQuery); ok {
|
||||||
|
return f(ctx, q)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unexpected query type %T. expect *ent.DocumentQuery", q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ExtensionFunc type is an adapter to allow the use of ordinary function as a Querier.
|
||||||
|
type ExtensionFunc func(context.Context, *ent.ExtensionQuery) (ent.Value, error)
|
||||||
|
|
||||||
|
// Query calls f(ctx, q).
|
||||||
|
func (f ExtensionFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
|
||||||
|
if q, ok := q.(*ent.ExtensionQuery); ok {
|
||||||
|
return f(ctx, q)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected query type %T. expect *ent.ExtensionQuery", q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The TraverseExtension type is an adapter to allow the use of ordinary function as Traverser.
|
||||||
|
type TraverseExtension func(context.Context, *ent.ExtensionQuery) error
|
||||||
|
|
||||||
|
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
|
||||||
|
func (f TraverseExtension) Intercept(next ent.Querier) ent.Querier {
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse calls f(ctx, q).
|
||||||
|
func (f TraverseExtension) Traverse(ctx context.Context, q ent.Query) error {
|
||||||
|
if q, ok := q.(*ent.ExtensionQuery); ok {
|
||||||
|
return f(ctx, q)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unexpected query type %T. expect *ent.ExtensionQuery", q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The KeyBindingFunc type is an adapter to allow the use of ordinary function as a Querier.
|
||||||
|
type KeyBindingFunc func(context.Context, *ent.KeyBindingQuery) (ent.Value, error)
|
||||||
|
|
||||||
|
// Query calls f(ctx, q).
|
||||||
|
func (f KeyBindingFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
|
||||||
|
if q, ok := q.(*ent.KeyBindingQuery); ok {
|
||||||
|
return f(ctx, q)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected query type %T. expect *ent.KeyBindingQuery", q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The TraverseKeyBinding type is an adapter to allow the use of ordinary function as Traverser.
|
||||||
|
type TraverseKeyBinding func(context.Context, *ent.KeyBindingQuery) error
|
||||||
|
|
||||||
|
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
|
||||||
|
func (f TraverseKeyBinding) Intercept(next ent.Querier) ent.Querier {
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse calls f(ctx, q).
|
||||||
|
func (f TraverseKeyBinding) Traverse(ctx context.Context, q ent.Query) error {
|
||||||
|
if q, ok := q.(*ent.KeyBindingQuery); ok {
|
||||||
|
return f(ctx, q)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unexpected query type %T. expect *ent.KeyBindingQuery", q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ThemeFunc type is an adapter to allow the use of ordinary function as a Querier.
|
||||||
|
type ThemeFunc func(context.Context, *ent.ThemeQuery) (ent.Value, error)
|
||||||
|
|
||||||
|
// Query calls f(ctx, q).
|
||||||
|
func (f ThemeFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
|
||||||
|
if q, ok := q.(*ent.ThemeQuery); ok {
|
||||||
|
return f(ctx, q)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected query type %T. expect *ent.ThemeQuery", q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The TraverseTheme type is an adapter to allow the use of ordinary function as Traverser.
|
||||||
|
type TraverseTheme func(context.Context, *ent.ThemeQuery) error
|
||||||
|
|
||||||
|
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
|
||||||
|
func (f TraverseTheme) Intercept(next ent.Querier) ent.Querier {
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse calls f(ctx, q).
|
||||||
|
func (f TraverseTheme) Traverse(ctx context.Context, q ent.Query) error {
|
||||||
|
if q, ok := q.(*ent.ThemeQuery); ok {
|
||||||
|
return f(ctx, q)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unexpected query type %T. expect *ent.ThemeQuery", q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewQuery returns the generic Query interface for the given typed query.
|
||||||
|
func NewQuery(q ent.Query) (Query, error) {
|
||||||
|
switch q := q.(type) {
|
||||||
|
case *ent.DocumentQuery:
|
||||||
|
return &query[*ent.DocumentQuery, predicate.Document, document.OrderOption]{typ: ent.TypeDocument, tq: q}, nil
|
||||||
|
case *ent.ExtensionQuery:
|
||||||
|
return &query[*ent.ExtensionQuery, predicate.Extension, extension.OrderOption]{typ: ent.TypeExtension, tq: q}, nil
|
||||||
|
case *ent.KeyBindingQuery:
|
||||||
|
return &query[*ent.KeyBindingQuery, predicate.KeyBinding, keybinding.OrderOption]{typ: ent.TypeKeyBinding, tq: q}, nil
|
||||||
|
case *ent.ThemeQuery:
|
||||||
|
return &query[*ent.ThemeQuery, predicate.Theme, theme.OrderOption]{typ: ent.TypeTheme, tq: q}, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown query type %T", q)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type query[T any, P ~func(*sql.Selector), R ~func(*sql.Selector)] struct {
|
||||||
|
typ string
|
||||||
|
tq interface {
|
||||||
|
Limit(int) T
|
||||||
|
Offset(int) T
|
||||||
|
Unique(bool) T
|
||||||
|
Order(...R) T
|
||||||
|
Where(...P) T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q query[T, P, R]) Type() string {
|
||||||
|
return q.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q query[T, P, R]) Limit(limit int) {
|
||||||
|
q.tq.Limit(limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q query[T, P, R]) Offset(offset int) {
|
||||||
|
q.tq.Offset(offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q query[T, P, R]) Unique(unique bool) {
|
||||||
|
q.tq.Unique(unique)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q query[T, P, R]) Order(orders ...func(*sql.Selector)) {
|
||||||
|
rs := make([]R, len(orders))
|
||||||
|
for i := range orders {
|
||||||
|
rs[i] = orders[i]
|
||||||
|
}
|
||||||
|
q.tq.Order(rs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q query[T, P, R]) WhereP(ps ...func(*sql.Selector)) {
|
||||||
|
p := make([]P, len(ps))
|
||||||
|
for i := range ps {
|
||||||
|
p[i] = ps[i]
|
||||||
|
}
|
||||||
|
q.tq.Where(p...)
|
||||||
|
}
|
||||||
185
internal/models/ent/keybinding.go
Normal file
185
internal/models/ent/keybinding.go
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
// Code generated by ent, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"voidraft/internal/models/ent/keybinding"
|
||||||
|
|
||||||
|
"entgo.io/ent"
|
||||||
|
"entgo.io/ent/dialect/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyBinding is the model entity for the KeyBinding schema.
|
||||||
|
type KeyBinding struct {
|
||||||
|
config `json:"-"`
|
||||||
|
// ID of the ent.
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
// UUID for cross-device sync (UUIDv7)
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
// creation time
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
// update time
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
// deleted at
|
||||||
|
DeletedAt *string `json:"deleted_at,omitempty"`
|
||||||
|
// key binding key
|
||||||
|
Key string `json:"key"`
|
||||||
|
// key binding command
|
||||||
|
Command string `json:"command"`
|
||||||
|
// key binding extension
|
||||||
|
Extension string `json:"extension,omitempty"`
|
||||||
|
// key binding enabled
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
selectValues sql.SelectValues
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanValues returns the types for scanning values from sql.Rows.
|
||||||
|
func (*KeyBinding) scanValues(columns []string) ([]any, error) {
|
||||||
|
values := make([]any, len(columns))
|
||||||
|
for i := range columns {
|
||||||
|
switch columns[i] {
|
||||||
|
case keybinding.FieldEnabled:
|
||||||
|
values[i] = new(sql.NullBool)
|
||||||
|
case keybinding.FieldID:
|
||||||
|
values[i] = new(sql.NullInt64)
|
||||||
|
case keybinding.FieldUUID, keybinding.FieldCreatedAt, keybinding.FieldUpdatedAt, keybinding.FieldDeletedAt, keybinding.FieldKey, keybinding.FieldCommand, keybinding.FieldExtension:
|
||||||
|
values[i] = new(sql.NullString)
|
||||||
|
default:
|
||||||
|
values[i] = new(sql.UnknownType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignValues assigns the values that were returned from sql.Rows (after scanning)
|
||||||
|
// to the KeyBinding fields.
|
||||||
|
func (_m *KeyBinding) assignValues(columns []string, values []any) error {
|
||||||
|
if m, n := len(values), len(columns); m < n {
|
||||||
|
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
|
||||||
|
}
|
||||||
|
for i := range columns {
|
||||||
|
switch columns[i] {
|
||||||
|
case keybinding.FieldID:
|
||||||
|
value, ok := values[i].(*sql.NullInt64)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field id", value)
|
||||||
|
}
|
||||||
|
_m.ID = int(value.Int64)
|
||||||
|
case keybinding.FieldUUID:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field uuid", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.UUID = value.String
|
||||||
|
}
|
||||||
|
case keybinding.FieldCreatedAt:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field created_at", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.CreatedAt = value.String
|
||||||
|
}
|
||||||
|
case keybinding.FieldUpdatedAt:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.UpdatedAt = value.String
|
||||||
|
}
|
||||||
|
case keybinding.FieldDeletedAt:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field deleted_at", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.DeletedAt = new(string)
|
||||||
|
*_m.DeletedAt = value.String
|
||||||
|
}
|
||||||
|
case keybinding.FieldKey:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field key", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.Key = value.String
|
||||||
|
}
|
||||||
|
case keybinding.FieldCommand:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field command", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.Command = value.String
|
||||||
|
}
|
||||||
|
case keybinding.FieldExtension:
|
||||||
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field extension", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.Extension = value.String
|
||||||
|
}
|
||||||
|
case keybinding.FieldEnabled:
|
||||||
|
if value, ok := values[i].(*sql.NullBool); !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for field enabled", values[i])
|
||||||
|
} else if value.Valid {
|
||||||
|
_m.Enabled = value.Bool
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
_m.selectValues.Set(columns[i], values[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the ent.Value that was dynamically selected and assigned to the KeyBinding.
|
||||||
|
// This includes values selected through modifiers, order, etc.
|
||||||
|
func (_m *KeyBinding) Value(name string) (ent.Value, error) {
|
||||||
|
return _m.selectValues.Get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update returns a builder for updating this KeyBinding.
|
||||||
|
// Note that you need to call KeyBinding.Unwrap() before calling this method if this KeyBinding
|
||||||
|
// was returned from a transaction, and the transaction was committed or rolled back.
|
||||||
|
func (_m *KeyBinding) Update() *KeyBindingUpdateOne {
|
||||||
|
return NewKeyBindingClient(_m.config).UpdateOne(_m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap unwraps the KeyBinding entity that was returned from a transaction after it was closed,
|
||||||
|
// so that all future queries will be executed through the driver which created the transaction.
|
||||||
|
func (_m *KeyBinding) Unwrap() *KeyBinding {
|
||||||
|
_tx, ok := _m.config.driver.(*txDriver)
|
||||||
|
if !ok {
|
||||||
|
panic("ent: KeyBinding is not a transactional entity")
|
||||||
|
}
|
||||||
|
_m.config.driver = _tx.drv
|
||||||
|
return _m
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements the fmt.Stringer.
|
||||||
|
func (_m *KeyBinding) String() string {
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.WriteString("KeyBinding(")
|
||||||
|
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
||||||
|
builder.WriteString("uuid=")
|
||||||
|
builder.WriteString(_m.UUID)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("created_at=")
|
||||||
|
builder.WriteString(_m.CreatedAt)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("updated_at=")
|
||||||
|
builder.WriteString(_m.UpdatedAt)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
if v := _m.DeletedAt; v != nil {
|
||||||
|
builder.WriteString("deleted_at=")
|
||||||
|
builder.WriteString(*v)
|
||||||
|
}
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("key=")
|
||||||
|
builder.WriteString(_m.Key)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("command=")
|
||||||
|
builder.WriteString(_m.Command)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("extension=")
|
||||||
|
builder.WriteString(_m.Extension)
|
||||||
|
builder.WriteString(", ")
|
||||||
|
builder.WriteString("enabled=")
|
||||||
|
builder.WriteString(fmt.Sprintf("%v", _m.Enabled))
|
||||||
|
builder.WriteByte(')')
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyBindings is a parsable slice of KeyBinding.
|
||||||
|
type KeyBindings []*KeyBinding
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user