Compare commits
10 Commits
b24f2a5e7b
...
afe98e6b1a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afe98e6b1a | ||
|
|
f15aa0032a | ||
|
|
9e4d4518bd | ||
|
|
3138a88bb1 | ||
|
|
db5aca2b8d | ||
|
|
dd1b36c148 | ||
|
|
4579e49465 | ||
|
|
882d3b3f50 | ||
|
|
667d487a0f | ||
|
|
36b3542371 |
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1857,7 +1857,7 @@
|
||||
},
|
||||
"node_modules/@quartz-community/utils": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "git+ssh://git@github.com/quartz-community/utils.git#7a43501ac1c728e8a7bfa75db35e8791bfa1f41c",
|
||||
"resolved": "git+ssh://git@github.com/quartz-community/utils.git#e09d8b0bbccb46073b23c00affaf3e40a2c3b0c4",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -70,6 +70,9 @@ plugins:
|
||||
- source: github:quartz-community/table-of-contents
|
||||
enabled: true
|
||||
order: 50
|
||||
layout:
|
||||
position: right
|
||||
priority: 30
|
||||
- source: github:quartz-community/crawl-links
|
||||
enabled: true
|
||||
options:
|
||||
@@ -142,7 +145,7 @@ plugins:
|
||||
enabled: true
|
||||
layout:
|
||||
position: right
|
||||
priority: 30
|
||||
priority: 50
|
||||
- source: github:quartz-community/article-title
|
||||
enabled: true
|
||||
layout:
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
"bases-page": {
|
||||
"source": "github:quartz-community/bases-page",
|
||||
"resolved": "https://github.com/quartz-community/bases-page.git",
|
||||
"commit": "077ebacd657a86bc92adc4ec392a238cacbfc298",
|
||||
"installedAt": "2026-05-24T19:26:20.456Z"
|
||||
"commit": "ec5711f91e40b1e8477a101f6e2604926e4f0222",
|
||||
"installedAt": "2026-05-26T16:15:16.668Z"
|
||||
},
|
||||
"breadcrumbs": {
|
||||
"source": "github:quartz-community/breadcrumbs",
|
||||
@@ -76,8 +76,8 @@
|
||||
"crawl-links": {
|
||||
"source": "github:quartz-community/crawl-links",
|
||||
"resolved": "https://github.com/quartz-community/crawl-links.git",
|
||||
"commit": "533a2213b673c3ea2a67f6e160f4e6a1262e8a6d",
|
||||
"installedAt": "2026-05-24T15:36:16.458Z"
|
||||
"commit": "69497e22046d62ee601415ad832bcde89a70a544",
|
||||
"installedAt": "2026-05-26T15:56:37.042Z"
|
||||
},
|
||||
"created-modified-date": {
|
||||
"source": "github:quartz-community/created-modified-date",
|
||||
@@ -142,8 +142,8 @@
|
||||
"graph": {
|
||||
"source": "github:quartz-community/graph",
|
||||
"resolved": "https://github.com/quartz-community/graph.git",
|
||||
"commit": "27eb4dd71ca10c8bd204cf1537da4448811c15f3",
|
||||
"installedAt": "2026-05-24T15:36:16.989Z"
|
||||
"commit": "67ac4993907618a0ea5bd33726f792c572cddbad",
|
||||
"installedAt": "2026-05-26T17:45:56.210Z"
|
||||
},
|
||||
"hard-line-breaks": {
|
||||
"source": "github:quartz-community/hard-line-breaks",
|
||||
@@ -160,20 +160,20 @@
|
||||
"note-properties": {
|
||||
"source": "github:quartz-community/note-properties",
|
||||
"resolved": "https://github.com/quartz-community/note-properties.git",
|
||||
"commit": "cccd275468e93ea59115e755d4bb8599192c45b9",
|
||||
"installedAt": "2026-05-24T15:36:16.987Z"
|
||||
"commit": "874712a9910299b906606c885ea1fe9602114ca7",
|
||||
"installedAt": "2026-05-26T16:15:17.177Z"
|
||||
},
|
||||
"obsidian-flavored-markdown": {
|
||||
"source": "github:quartz-community/obsidian-flavored-markdown",
|
||||
"resolved": "https://github.com/quartz-community/obsidian-flavored-markdown.git",
|
||||
"commit": "186d6bd990a1b88ab35c4f66ea4f257f1e7b4a51",
|
||||
"installedAt": "2026-05-25T23:55:00.345Z"
|
||||
"commit": "7f61d4bcf7537d056528f6cecaf5c35b39f74fe8",
|
||||
"installedAt": "2026-05-27T18:30:09.459Z"
|
||||
},
|
||||
"obsidian-plugin-excalidraw": {
|
||||
"source": "github:quartz-community/obsidian-plugin-excalidraw",
|
||||
"resolved": "https://github.com/quartz-community/obsidian-plugin-excalidraw.git",
|
||||
"commit": "9c5ff6bd6d2ee27814aeaf98ac4ddfd025a985fa",
|
||||
"installedAt": "2026-05-25T17:40:01.376Z"
|
||||
"commit": "554ae8af7ac4d7fdff8d78e65bfa9b4dd53cbb87",
|
||||
"installedAt": "2026-05-27T18:30:09.649Z"
|
||||
},
|
||||
"obsidian-plugin-ttrpg-tools-maps": {
|
||||
"source": "github:quartz-community/obsidian-plugin-ttrpg-tools-maps",
|
||||
@@ -206,9 +206,9 @@
|
||||
"subdir": "plugin"
|
||||
},
|
||||
"resolved": "https://github.com/saberzero1/quartz-themes.git",
|
||||
"commit": "b82021d7866c11353a0cc55b75ac3198d988d7d7",
|
||||
"commit": "0da7355c49aa68cb375726913f41c64ac1f11733",
|
||||
"subdir": "plugin",
|
||||
"installedAt": "2026-05-25T15:05:52.311Z"
|
||||
"installedAt": "2026-05-27T18:31:15.006Z"
|
||||
},
|
||||
"reader-mode": {
|
||||
"source": "github:quartz-community/reader-mode",
|
||||
@@ -219,8 +219,8 @@
|
||||
"recent-notes": {
|
||||
"source": "github:quartz-community/recent-notes",
|
||||
"resolved": "https://github.com/quartz-community/recent-notes.git",
|
||||
"commit": "74cb2588deae80fd7c13bf5fdeedebffe547d214",
|
||||
"installedAt": "2026-05-24T15:36:17.428Z"
|
||||
"commit": "4505adba9c213967ddf94ea5fc05039245fd9fe9",
|
||||
"installedAt": "2026-05-26T20:20:42.557Z"
|
||||
},
|
||||
"remove-draft": {
|
||||
"source": "github:quartz-community/remove-draft",
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
gitPull,
|
||||
popContentFolder,
|
||||
stashContentFolder,
|
||||
symlinkOrCopy,
|
||||
} from "./helpers.js"
|
||||
import {
|
||||
handlePluginRestore,
|
||||
@@ -210,7 +211,7 @@ export async function handleCreate(argv) {
|
||||
preserveTimestamps: true,
|
||||
})
|
||||
} else if (setupStrategy === "symlink") {
|
||||
await fs.promises.symlink(originalFolder, contentFolder, "dir")
|
||||
await symlinkOrCopy(originalFolder, contentFolder)
|
||||
}
|
||||
} else if (setupStrategy === "new") {
|
||||
await fs.promises.writeFile(
|
||||
|
||||
@@ -3,6 +3,7 @@ import { styleText } from "util"
|
||||
import { contentCacheFolder } from "./constants.js"
|
||||
import { spawnSync } from "child_process"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
|
||||
export function escapePath(fp) {
|
||||
return fp
|
||||
@@ -33,7 +34,7 @@ export async function stashContentFolder(contentFolder) {
|
||||
}
|
||||
|
||||
export function gitPull(origin, branch) {
|
||||
const flags = ["--no-rebase", "--autostash", "--no-edit"]
|
||||
const flags = ["--no-rebase", "--autostash", "--no-edit", "--allow-unrelated-histories"]
|
||||
const out = spawnSync("git", ["pull", ...flags, origin, branch], { stdio: "inherit" })
|
||||
if (out.stderr) {
|
||||
throw new Error(styleText("red", `Error while pulling updates: ${out.stderr}`))
|
||||
@@ -52,3 +53,57 @@ export async function popContentFolder(contentFolder) {
|
||||
})
|
||||
await fs.promises.rm(contentCacheFolder, { force: true, recursive: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a directory symlink with Windows fallback.
|
||||
*
|
||||
* On Windows, creating symlinks requires Developer Mode or admin privileges.
|
||||
* When that fails (EPERM), we try a junction first (no elevation needed),
|
||||
* then fall back to a recursive copy as a last resort.
|
||||
*
|
||||
* @param {string} target Symlink target (may be relative)
|
||||
* @param {string} linkPath Path where the link is created
|
||||
*/
|
||||
export function symlinkOrCopySync(target, linkPath) {
|
||||
try {
|
||||
fs.symlinkSync(target, linkPath, "dir")
|
||||
} catch (err) {
|
||||
if (err.code === "EEXIST") return
|
||||
if (err.code === "EPERM" && process.platform === "win32") {
|
||||
try {
|
||||
fs.symlinkSync(target, linkPath, "junction")
|
||||
return
|
||||
} catch {
|
||||
const resolvedTarget = path.resolve(path.dirname(linkPath), target)
|
||||
fs.cpSync(resolvedTarget, linkPath, { recursive: true })
|
||||
return
|
||||
}
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async version of {@link symlinkOrCopySync}.
|
||||
*
|
||||
* @param {string} target Symlink target (may be relative)
|
||||
* @param {string} linkPath Path where the link is created
|
||||
*/
|
||||
export async function symlinkOrCopy(target, linkPath) {
|
||||
try {
|
||||
await fs.promises.symlink(target, linkPath, "dir")
|
||||
} catch (err) {
|
||||
if (err.code === "EEXIST") return
|
||||
if (err.code === "EPERM" && process.platform === "win32") {
|
||||
try {
|
||||
await fs.promises.symlink(target, linkPath, "junction")
|
||||
return
|
||||
} catch {
|
||||
const resolvedTarget = path.resolve(path.dirname(linkPath), target)
|
||||
await fs.promises.cp(resolvedTarget, linkPath, { recursive: true })
|
||||
return
|
||||
}
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
237
quartz/cli/helpers.test.js
Normal file
237
quartz/cli/helpers.test.js
Normal file
@@ -0,0 +1,237 @@
|
||||
import test, { describe, beforeEach, afterEach, mock } from "node:test"
|
||||
import assert from "node:assert"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import os from "os"
|
||||
import { symlinkOrCopySync, symlinkOrCopy } from "./helpers.js"
|
||||
|
||||
function makeTmpDir() {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), "quartz-symlink-test-"))
|
||||
}
|
||||
|
||||
function makeTarget(tmpDir) {
|
||||
const target = path.join(tmpDir, "target")
|
||||
fs.mkdirSync(target)
|
||||
fs.writeFileSync(path.join(target, "marker.txt"), "hello")
|
||||
return target
|
||||
}
|
||||
|
||||
describe("symlinkOrCopySync", () => {
|
||||
let tmpDir
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = makeTmpDir()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test("creates a symlink on success", () => {
|
||||
const target = makeTarget(tmpDir)
|
||||
const linkPath = path.join(tmpDir, "link")
|
||||
|
||||
symlinkOrCopySync(target, linkPath)
|
||||
|
||||
const stat = fs.lstatSync(linkPath)
|
||||
assert.ok(stat.isSymbolicLink())
|
||||
assert.ok(fs.existsSync(path.join(linkPath, "marker.txt")))
|
||||
})
|
||||
|
||||
test("silently succeeds when link already exists (EEXIST)", () => {
|
||||
const target = makeTarget(tmpDir)
|
||||
const linkPath = path.join(tmpDir, "link")
|
||||
|
||||
symlinkOrCopySync(target, linkPath)
|
||||
assert.doesNotThrow(() => symlinkOrCopySync(target, linkPath))
|
||||
})
|
||||
|
||||
test("re-throws non-EPERM errors", () => {
|
||||
const target = makeTarget(tmpDir)
|
||||
const linkPath = path.join(tmpDir, "nonexistent-parent", "link")
|
||||
|
||||
assert.throws(
|
||||
() => symlinkOrCopySync(target, linkPath),
|
||||
(err) => {
|
||||
return err.code === "ENOENT"
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
test("falls back to junction on Windows EPERM", () => {
|
||||
const target = makeTarget(tmpDir)
|
||||
const linkPath = path.join(tmpDir, "link")
|
||||
|
||||
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform")
|
||||
Object.defineProperty(process, "platform", { value: "win32", configurable: true })
|
||||
|
||||
let callCount = 0
|
||||
const originalSymlinkSync = fs.symlinkSync
|
||||
mock.method(fs, "symlinkSync", (t, lp, type) => {
|
||||
callCount++
|
||||
if (callCount === 1 && type === "dir") {
|
||||
const err = new Error("EPERM: operation not permitted, symlink")
|
||||
err.code = "EPERM"
|
||||
err.errno = -4048
|
||||
err.syscall = "symlink"
|
||||
throw err
|
||||
}
|
||||
return originalSymlinkSync(t, lp, type)
|
||||
})
|
||||
|
||||
try {
|
||||
symlinkOrCopySync(target, linkPath)
|
||||
assert.ok(fs.existsSync(path.join(linkPath, "marker.txt")))
|
||||
assert.strictEqual(callCount, 2)
|
||||
} finally {
|
||||
fs.symlinkSync.mock.restore()
|
||||
if (originalPlatform) {
|
||||
Object.defineProperty(process, "platform", originalPlatform)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test("falls back to copy when both symlink and junction fail on Windows", () => {
|
||||
const target = makeTarget(tmpDir)
|
||||
const linkPath = path.join(tmpDir, "link")
|
||||
|
||||
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform")
|
||||
Object.defineProperty(process, "platform", { value: "win32", configurable: true })
|
||||
|
||||
const originalSymlinkSync = fs.symlinkSync
|
||||
mock.method(fs, "symlinkSync", (_t, _lp, _type) => {
|
||||
const err = new Error("EPERM: operation not permitted, symlink")
|
||||
err.code = "EPERM"
|
||||
err.errno = -4048
|
||||
err.syscall = "symlink"
|
||||
throw err
|
||||
})
|
||||
|
||||
try {
|
||||
symlinkOrCopySync(target, linkPath)
|
||||
|
||||
const stat = fs.lstatSync(linkPath)
|
||||
assert.ok(stat.isDirectory(), "fallback should produce a real directory, not a symlink")
|
||||
assert.strictEqual(fs.readFileSync(path.join(linkPath, "marker.txt"), "utf-8"), "hello")
|
||||
} finally {
|
||||
fs.symlinkSync.mock.restore()
|
||||
if (originalPlatform) {
|
||||
Object.defineProperty(process, "platform", originalPlatform)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test("does not fall back on EPERM when not on Windows", () => {
|
||||
const target = makeTarget(tmpDir)
|
||||
const linkPath = path.join(tmpDir, "link")
|
||||
|
||||
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform")
|
||||
Object.defineProperty(process, "platform", { value: "linux", configurable: true })
|
||||
|
||||
const originalSymlinkSync = fs.symlinkSync
|
||||
mock.method(fs, "symlinkSync", (_t, _lp, _type) => {
|
||||
const err = new Error("EPERM: operation not permitted, symlink")
|
||||
err.code = "EPERM"
|
||||
throw err
|
||||
})
|
||||
|
||||
try {
|
||||
assert.throws(
|
||||
() => symlinkOrCopySync(target, linkPath),
|
||||
(err) => err.code === "EPERM",
|
||||
)
|
||||
} finally {
|
||||
fs.symlinkSync.mock.restore()
|
||||
if (originalPlatform) {
|
||||
Object.defineProperty(process, "platform", originalPlatform)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("symlinkOrCopy", () => {
|
||||
let tmpDir
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = makeTmpDir()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test("creates a symlink on success", async () => {
|
||||
const target = makeTarget(tmpDir)
|
||||
const linkPath = path.join(tmpDir, "link")
|
||||
|
||||
await symlinkOrCopy(target, linkPath)
|
||||
|
||||
const stat = fs.lstatSync(linkPath)
|
||||
assert.ok(stat.isSymbolicLink())
|
||||
assert.ok(fs.existsSync(path.join(linkPath, "marker.txt")))
|
||||
})
|
||||
|
||||
test("silently succeeds when link already exists (EEXIST)", async () => {
|
||||
const target = makeTarget(tmpDir)
|
||||
const linkPath = path.join(tmpDir, "link")
|
||||
|
||||
await symlinkOrCopy(target, linkPath)
|
||||
await assert.doesNotReject(() => symlinkOrCopy(target, linkPath))
|
||||
})
|
||||
|
||||
test("falls back to copy when both symlink and junction fail on Windows", async () => {
|
||||
const target = makeTarget(tmpDir)
|
||||
const linkPath = path.join(tmpDir, "link")
|
||||
|
||||
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform")
|
||||
Object.defineProperty(process, "platform", { value: "win32", configurable: true })
|
||||
|
||||
const originalSymlink = fs.promises.symlink
|
||||
mock.method(fs.promises, "symlink", async (_t, _lp, _type) => {
|
||||
const err = new Error("EPERM: operation not permitted, symlink")
|
||||
err.code = "EPERM"
|
||||
err.errno = -4048
|
||||
err.syscall = "symlink"
|
||||
throw err
|
||||
})
|
||||
|
||||
try {
|
||||
await symlinkOrCopy(target, linkPath)
|
||||
|
||||
const stat = fs.lstatSync(linkPath)
|
||||
assert.ok(stat.isDirectory(), "fallback should produce a real directory, not a symlink")
|
||||
assert.strictEqual(fs.readFileSync(path.join(linkPath, "marker.txt"), "utf-8"), "hello")
|
||||
} finally {
|
||||
fs.promises.symlink.mock.restore()
|
||||
if (originalPlatform) {
|
||||
Object.defineProperty(process, "platform", originalPlatform)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test("does not fall back on EPERM when not on Windows", async () => {
|
||||
const target = makeTarget(tmpDir)
|
||||
const linkPath = path.join(tmpDir, "link")
|
||||
|
||||
const originalPlatform = Object.getOwnPropertyDescriptor(process, "platform")
|
||||
Object.defineProperty(process, "platform", { value: "linux", configurable: true })
|
||||
|
||||
mock.method(fs.promises, "symlink", async (_t, _lp, _type) => {
|
||||
const err = new Error("EPERM: operation not permitted, symlink")
|
||||
err.code = "EPERM"
|
||||
throw err
|
||||
})
|
||||
|
||||
try {
|
||||
await assert.rejects(
|
||||
() => symlinkOrCopy(target, linkPath),
|
||||
(err) => err.code === "EPERM",
|
||||
)
|
||||
} finally {
|
||||
fs.promises.symlink.mock.restore()
|
||||
if (originalPlatform) {
|
||||
Object.defineProperty(process, "platform", originalPlatform)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
resolveLockfileName,
|
||||
getNameOverrides,
|
||||
} from "./plugin-data.js"
|
||||
import { symlinkOrCopySync } from "./helpers.js"
|
||||
|
||||
const INTERNAL_EXPORTS = new Set(["manifest", "default"])
|
||||
|
||||
@@ -128,12 +129,7 @@ function needsBuild(pluginDir) {
|
||||
* share a single copy of packages like unified, vfile, rehype-raw, etc.
|
||||
*/
|
||||
function trySymlink(target, linkPath) {
|
||||
try {
|
||||
fs.symlinkSync(target, linkPath, "dir")
|
||||
} catch (err) {
|
||||
if (err.code === "EEXIST") return
|
||||
throw err
|
||||
}
|
||||
symlinkOrCopySync(target, linkPath)
|
||||
}
|
||||
|
||||
function linkPeerPlugins(pluginDir) {
|
||||
@@ -621,7 +617,7 @@ export async function handlePluginInstallUnified({
|
||||
}
|
||||
console.log(styleText("cyan", `→ Linking ${name} from ${resolvedPath}...`))
|
||||
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
|
||||
fs.symlinkSync(resolvedPath, pluginDir, "dir")
|
||||
symlinkOrCopySync(resolvedPath, pluginDir)
|
||||
lockfile.plugins[name] = {
|
||||
source: entry.source,
|
||||
resolved: resolvedPath,
|
||||
@@ -810,7 +806,7 @@ export async function handlePluginInstallUnified({
|
||||
continue
|
||||
}
|
||||
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
|
||||
fs.symlinkSync(entry.resolved, pluginDir, "dir")
|
||||
symlinkOrCopySync(entry.resolved, pluginDir)
|
||||
console.log(styleText("green", `✓ ${name} restored (local symlink)`))
|
||||
restoredPlugins.push({ name, pluginDir })
|
||||
installed++
|
||||
@@ -1045,7 +1041,7 @@ export async function handlePluginInstallUnified({
|
||||
continue
|
||||
}
|
||||
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
|
||||
fs.symlinkSync(entry.resolved, pluginDir, "dir")
|
||||
symlinkOrCopySync(entry.resolved, pluginDir)
|
||||
console.log(styleText("green", ` ✓ ${name} (local) linked`))
|
||||
pluginsToBuild.push({ name, pluginDir })
|
||||
installed++
|
||||
@@ -1222,7 +1218,7 @@ export async function handlePluginAdd(
|
||||
}
|
||||
console.log(styleText("cyan", `→ Adding ${name} from local path ${resolvedPath}...`))
|
||||
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
|
||||
fs.symlinkSync(resolvedPath, pluginDir, "dir")
|
||||
symlinkOrCopySync(resolvedPath, pluginDir)
|
||||
lockfile.plugins[name] = {
|
||||
source,
|
||||
resolved: resolvedPath,
|
||||
@@ -1315,13 +1311,13 @@ export async function handlePluginAdd(
|
||||
}
|
||||
|
||||
if (manifest?.components) {
|
||||
const layoutPositions = new Set(["left", "right", "beforeBody", "afterBody"])
|
||||
const firstComponentKey = Object.keys(manifest.components)[0]
|
||||
const comp = manifest.components[firstComponentKey]
|
||||
if (comp?.defaultPosition) {
|
||||
if (comp?.defaultPosition && layoutPositions.has(comp.defaultPosition)) {
|
||||
newEntry.layout = {
|
||||
position: comp.defaultPosition,
|
||||
priority: comp.defaultPriority ?? 50,
|
||||
display: "all",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,9 @@ plugins:
|
||||
- source: github:quartz-community/table-of-contents
|
||||
enabled: true
|
||||
order: 50
|
||||
layout:
|
||||
position: right
|
||||
priority: 30
|
||||
- source: github:quartz-community/crawl-links
|
||||
enabled: true
|
||||
options:
|
||||
@@ -156,7 +159,7 @@ plugins:
|
||||
enabled: true
|
||||
layout:
|
||||
position: right
|
||||
priority: 30
|
||||
priority: 50
|
||||
- source: github:quartz-community/article-title
|
||||
enabled: true
|
||||
layout:
|
||||
|
||||
@@ -72,6 +72,9 @@ plugins:
|
||||
- source: github:quartz-community/table-of-contents
|
||||
enabled: true
|
||||
order: 50
|
||||
layout:
|
||||
position: right
|
||||
priority: 30
|
||||
- source: github:quartz-community/crawl-links
|
||||
enabled: true
|
||||
options:
|
||||
@@ -161,7 +164,7 @@ plugins:
|
||||
enabled: true
|
||||
layout:
|
||||
position: right
|
||||
priority: 30
|
||||
priority: 50
|
||||
- source: github:quartz-community/article-title
|
||||
enabled: true
|
||||
layout:
|
||||
|
||||
@@ -82,6 +82,9 @@ plugins:
|
||||
- source: github:quartz-community/table-of-contents
|
||||
enabled: true
|
||||
order: 50
|
||||
layout:
|
||||
position: right
|
||||
priority: 30
|
||||
- source: github:quartz-community/crawl-links
|
||||
enabled: true
|
||||
options:
|
||||
@@ -172,7 +175,7 @@ plugins:
|
||||
enabled: true
|
||||
layout:
|
||||
position: right
|
||||
priority: 30
|
||||
priority: 50
|
||||
- source: github:quartz-community/article-title
|
||||
enabled: true
|
||||
layout:
|
||||
|
||||
@@ -82,6 +82,9 @@ plugins:
|
||||
- source: github:quartz-community/table-of-contents
|
||||
enabled: true
|
||||
order: 50
|
||||
layout:
|
||||
position: right
|
||||
priority: 30
|
||||
- source: github:quartz-community/crawl-links
|
||||
enabled: true
|
||||
options:
|
||||
@@ -172,7 +175,7 @@ plugins:
|
||||
enabled: true
|
||||
layout:
|
||||
position: right
|
||||
priority: 30
|
||||
priority: 50
|
||||
- source: github:quartz-community/article-title
|
||||
enabled: true
|
||||
layout:
|
||||
|
||||
Reference in New Issue
Block a user