Initial commit

This commit is contained in:
anghunk
2026-01-14 10:02:58 +08:00
commit 8f2fde5188
86 changed files with 74925 additions and 0 deletions

View File

@@ -0,0 +1,275 @@
import {
useMediaQuery
} from "./chunk-LJKO4TMH.js";
import {
computed,
ref,
shallowRef,
watch
} from "./chunk-QE257C5J.js";
// node_modules/vitepress/dist/client/theme-default/index.js
import "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/styles/fonts.css";
// node_modules/vitepress/dist/client/theme-default/without-fonts.js
import "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/styles/vars.css";
import "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/styles/base.css";
import "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/styles/icons.css";
import "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/styles/utils.css";
import "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/styles/components/custom-block.css";
import "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/styles/components/vp-code.css";
import "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/styles/components/vp-code-group.css";
import "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/styles/components/vp-doc.css";
import "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/styles/components/vp-sponsor.css";
import VPBadge from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPBadge.vue";
import Layout from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/Layout.vue";
import { default as default2 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPBadge.vue";
import { default as default3 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPButton.vue";
import { default as default4 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPDocAsideSponsors.vue";
import { default as default5 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPFeatures.vue";
import { default as default6 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPHomeContent.vue";
import { default as default7 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPHomeFeatures.vue";
import { default as default8 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPHomeHero.vue";
import { default as default9 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPHomeSponsors.vue";
import { default as default10 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPImage.vue";
import { default as default11 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPLink.vue";
import { default as default12 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPNavBarSearch.vue";
import { default as default13 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPSocialLink.vue";
import { default as default14 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPSocialLinks.vue";
import { default as default15 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPSponsors.vue";
import { default as default16 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPTeamMembers.vue";
import { default as default17 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPTeamPage.vue";
import { default as default18 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPTeamPageSection.vue";
import { default as default19 } from "D:/zsh-code/seo/demo/GitHub-Projects/cwd-comments/docs/node_modules/vitepress/dist/client/theme-default/components/VPTeamPageTitle.vue";
// node_modules/vitepress/dist/client/theme-default/composables/local-nav.js
import { onContentUpdated } from "vitepress";
// node_modules/vitepress/dist/client/theme-default/composables/outline.js
import { getScrollOffset } from "vitepress";
// node_modules/vitepress/dist/client/theme-default/support/utils.js
import { withBase } from "vitepress";
// node_modules/vitepress/dist/client/theme-default/composables/data.js
import { useData as useData$ } from "vitepress";
var useData = useData$;
// node_modules/vitepress/dist/client/theme-default/support/utils.js
function ensureStartingSlash(path) {
return path.startsWith("/") ? path : `/${path}`;
}
// node_modules/vitepress/dist/client/theme-default/support/sidebar.js
function getSidebar(_sidebar, path) {
if (Array.isArray(_sidebar))
return addBase(_sidebar);
if (_sidebar == null)
return [];
path = ensureStartingSlash(path);
const dir = Object.keys(_sidebar).sort((a, b) => {
return b.split("/").length - a.split("/").length;
}).find((dir2) => {
return path.startsWith(ensureStartingSlash(dir2));
});
const sidebar = dir ? _sidebar[dir] : [];
return Array.isArray(sidebar) ? addBase(sidebar) : addBase(sidebar.items, sidebar.base);
}
function getSidebarGroups(sidebar) {
const groups = [];
let lastGroupIndex = 0;
for (const index in sidebar) {
const item = sidebar[index];
if (item.items) {
lastGroupIndex = groups.push(item);
continue;
}
if (!groups[lastGroupIndex]) {
groups.push({ items: [] });
}
groups[lastGroupIndex].items.push(item);
}
return groups;
}
function addBase(items, _base) {
return [...items].map((_item) => {
const item = { ..._item };
const base = item.base || _base;
if (base && item.link)
item.link = base + item.link;
if (item.items)
item.items = addBase(item.items, base);
return item;
});
}
// node_modules/vitepress/dist/client/theme-default/composables/sidebar.js
function useSidebar() {
const { frontmatter, page, theme: theme2 } = useData();
const is960 = useMediaQuery("(min-width: 960px)");
const isOpen = ref(false);
const _sidebar = computed(() => {
const sidebarConfig = theme2.value.sidebar;
const relativePath = page.value.relativePath;
return sidebarConfig ? getSidebar(sidebarConfig, relativePath) : [];
});
const sidebar = ref(_sidebar.value);
watch(_sidebar, (next, prev) => {
if (JSON.stringify(next) !== JSON.stringify(prev))
sidebar.value = _sidebar.value;
});
const hasSidebar = computed(() => {
return frontmatter.value.sidebar !== false && sidebar.value.length > 0 && frontmatter.value.layout !== "home";
});
const leftAside = computed(() => {
if (hasAside)
return frontmatter.value.aside == null ? theme2.value.aside === "left" : frontmatter.value.aside === "left";
return false;
});
const hasAside = computed(() => {
if (frontmatter.value.layout === "home")
return false;
if (frontmatter.value.aside != null)
return !!frontmatter.value.aside;
return theme2.value.aside !== false;
});
const isSidebarEnabled = computed(() => hasSidebar.value && is960.value);
const sidebarGroups = computed(() => {
return hasSidebar.value ? getSidebarGroups(sidebar.value) : [];
});
function open() {
isOpen.value = true;
}
function close() {
isOpen.value = false;
}
function toggle() {
isOpen.value ? close() : open();
}
return {
isOpen,
sidebar,
sidebarGroups,
hasSidebar,
hasAside,
leftAside,
isSidebarEnabled,
open,
close,
toggle
};
}
// node_modules/vitepress/dist/client/theme-default/composables/outline.js
var ignoreRE = /\b(?:VPBadge|header-anchor|footnote-ref|ignore-header)\b/;
var resolvedHeaders = [];
function getHeaders(range) {
const headers = [
...document.querySelectorAll(".VPDoc :where(h1,h2,h3,h4,h5,h6)")
].filter((el) => el.id && el.hasChildNodes()).map((el) => {
const level = Number(el.tagName[1]);
return {
element: el,
title: serializeHeader(el),
link: "#" + el.id,
level
};
});
return resolveHeaders(headers, range);
}
function serializeHeader(h) {
let ret = "";
for (const node of h.childNodes) {
if (node.nodeType === 1) {
if (ignoreRE.test(node.className))
continue;
ret += node.textContent;
} else if (node.nodeType === 3) {
ret += node.textContent;
}
}
return ret.trim();
}
function resolveHeaders(headers, range) {
if (range === false) {
return [];
}
const levelsRange = (typeof range === "object" && !Array.isArray(range) ? range.level : range) || 2;
const [high, low] = typeof levelsRange === "number" ? [levelsRange, levelsRange] : levelsRange === "deep" ? [2, 6] : levelsRange;
return buildTree(headers, high, low);
}
function buildTree(data, min, max) {
resolvedHeaders.length = 0;
const result = [];
const stack = [];
data.forEach((item) => {
const node = { ...item, children: [] };
let parent = stack[stack.length - 1];
while (parent && parent.level >= node.level) {
stack.pop();
parent = stack[stack.length - 1];
}
if (node.element.classList.contains("ignore-header") || parent && "shouldIgnore" in parent) {
stack.push({ level: node.level, shouldIgnore: true });
return;
}
if (node.level > max || node.level < min)
return;
resolvedHeaders.push({ element: node.element, link: node.link });
if (parent)
parent.children.push(node);
else
result.push(node);
stack.push(node);
});
return result;
}
// node_modules/vitepress/dist/client/theme-default/composables/local-nav.js
function useLocalNav() {
const { theme: theme2, frontmatter } = useData();
const headers = shallowRef([]);
const hasLocalNav = computed(() => {
return headers.value.length > 0;
});
onContentUpdated(() => {
headers.value = getHeaders(frontmatter.value.outline ?? theme2.value.outline);
});
return {
headers,
hasLocalNav
};
}
// node_modules/vitepress/dist/client/theme-default/without-fonts.js
var theme = {
Layout,
enhanceApp: ({ app }) => {
app.component("Badge", VPBadge);
}
};
var without_fonts_default = theme;
export {
default2 as VPBadge,
default3 as VPButton,
default4 as VPDocAsideSponsors,
default5 as VPFeatures,
default6 as VPHomeContent,
default7 as VPHomeFeatures,
default8 as VPHomeHero,
default9 as VPHomeSponsors,
default10 as VPImage,
default11 as VPLink,
default12 as VPNavBarSearch,
default13 as VPSocialLink,
default14 as VPSocialLinks,
default15 as VPSponsors,
default16 as VPTeamMembers,
default17 as VPTeamPage,
default18 as VPTeamPageSection,
default19 as VPTeamPageTitle,
without_fonts_default as default,
useLocalNav,
useSidebar
};
//# sourceMappingURL=@theme_index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,40 @@
{
"hash": "5f34213c",
"configHash": "7b2d39cb",
"lockfileHash": "7a0ef686",
"browserHash": "6fbd3ab3",
"optimized": {
"vue": {
"src": "../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "b6766662",
"needsInterop": false
},
"vitepress > @vue/devtools-api": {
"src": "../../../node_modules/@vue/devtools-api/dist/index.js",
"file": "vitepress___@vue_devtools-api.js",
"fileHash": "840fef28",
"needsInterop": false
},
"vitepress > @vueuse/core": {
"src": "../../../node_modules/@vueuse/core/index.mjs",
"file": "vitepress___@vueuse_core.js",
"fileHash": "35da9dd7",
"needsInterop": false
},
"@theme/index": {
"src": "../../../node_modules/vitepress/dist/client/theme-default/index.js",
"file": "@theme_index.js",
"fileHash": "ec77e1de",
"needsInterop": false
}
},
"chunks": {
"chunk-LJKO4TMH": {
"file": "chunk-LJKO4TMH.js"
},
"chunk-QE257C5J": {
"file": "chunk-QE257C5J.js"
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,583 @@
import {
DefaultMagicKeysAliasMap,
StorageSerializers,
TransitionPresets,
assert,
breakpointsAntDesign,
breakpointsBootstrapV5,
breakpointsElement,
breakpointsMasterCss,
breakpointsPrimeFlex,
breakpointsQuasar,
breakpointsSematic,
breakpointsTailwind,
breakpointsVuetify,
breakpointsVuetifyV2,
breakpointsVuetifyV3,
bypassFilter,
camelize,
clamp,
cloneFnJSON,
computedAsync,
computedEager,
computedInject,
computedWithControl,
containsProp,
controlledRef,
createEventHook,
createFetch,
createFilterWrapper,
createGlobalState,
createInjectionState,
createRef,
createReusableTemplate,
createSharedComposable,
createSingletonPromise,
createTemplatePromise,
createUnrefFn,
customStorageEventName,
debounceFilter,
defaultDocument,
defaultLocation,
defaultNavigator,
defaultWindow,
executeTransition,
extendRef,
formatDate,
formatTimeAgo,
get,
getLifeCycleTarget,
getSSRHandler,
hasOwn,
hyphenate,
identity,
increaseWithUnit,
injectLocal,
invoke,
isClient,
isDef,
isDefined,
isIOS,
isObject,
isWorker,
makeDestructurable,
mapGamepadToXbox360Controller,
noop,
normalizeDate,
notNullish,
now,
objectEntries,
objectOmit,
objectPick,
onClickOutside,
onElementRemoval,
onKeyDown,
onKeyPressed,
onKeyStroke,
onKeyUp,
onLongPress,
onStartTyping,
pausableFilter,
promiseTimeout,
provideLocal,
provideSSRWidth,
pxValue,
rand,
reactify,
reactifyObject,
reactiveComputed,
reactiveOmit,
reactivePick,
refAutoReset,
refDebounced,
refDefault,
refThrottled,
refWithControl,
resolveRef,
resolveUnref,
set,
setSSRHandler,
syncRef,
syncRefs,
templateRef,
throttleFilter,
timestamp,
toArray,
toReactive,
toRef,
toRefs,
toValue,
tryOnBeforeMount,
tryOnBeforeUnmount,
tryOnMounted,
tryOnScopeDispose,
tryOnUnmounted,
unrefElement,
until,
useActiveElement,
useAnimate,
useArrayDifference,
useArrayEvery,
useArrayFilter,
useArrayFind,
useArrayFindIndex,
useArrayFindLast,
useArrayIncludes,
useArrayJoin,
useArrayMap,
useArrayReduce,
useArraySome,
useArrayUnique,
useAsyncQueue,
useAsyncState,
useBase64,
useBattery,
useBluetooth,
useBreakpoints,
useBroadcastChannel,
useBrowserLocation,
useCached,
useClipboard,
useClipboardItems,
useCloned,
useColorMode,
useConfirmDialog,
useCountdown,
useCounter,
useCssVar,
useCurrentElement,
useCycleList,
useDark,
useDateFormat,
useDebounceFn,
useDebouncedRefHistory,
useDeviceMotion,
useDeviceOrientation,
useDevicePixelRatio,
useDevicesList,
useDisplayMedia,
useDocumentVisibility,
useDraggable,
useDropZone,
useElementBounding,
useElementByPoint,
useElementHover,
useElementSize,
useElementVisibility,
useEventBus,
useEventListener,
useEventSource,
useEyeDropper,
useFavicon,
useFetch,
useFileDialog,
useFileSystemAccess,
useFocus,
useFocusWithin,
useFps,
useFullscreen,
useGamepad,
useGeolocation,
useIdle,
useImage,
useInfiniteScroll,
useIntersectionObserver,
useInterval,
useIntervalFn,
useKeyModifier,
useLastChanged,
useLocalStorage,
useMagicKeys,
useManualRefHistory,
useMediaControls,
useMediaQuery,
useMemoize,
useMemory,
useMounted,
useMouse,
useMouseInElement,
useMousePressed,
useMutationObserver,
useNavigatorLanguage,
useNetwork,
useNow,
useObjectUrl,
useOffsetPagination,
useOnline,
usePageLeave,
useParallax,
useParentElement,
usePerformanceObserver,
usePermission,
usePointer,
usePointerLock,
usePointerSwipe,
usePreferredColorScheme,
usePreferredContrast,
usePreferredDark,
usePreferredLanguages,
usePreferredReducedMotion,
usePreferredReducedTransparency,
usePrevious,
useRafFn,
useRefHistory,
useResizeObserver,
useSSRWidth,
useScreenOrientation,
useScreenSafeArea,
useScriptTag,
useScroll,
useScrollLock,
useSessionStorage,
useShare,
useSorted,
useSpeechRecognition,
useSpeechSynthesis,
useStepper,
useStorage,
useStorageAsync,
useStyleTag,
useSupported,
useSwipe,
useTemplateRefsList,
useTextDirection,
useTextSelection,
useTextareaAutosize,
useThrottleFn,
useThrottledRefHistory,
useTimeAgo,
useTimeout,
useTimeoutFn,
useTimeoutPoll,
useTimestamp,
useTitle,
useToNumber,
useToString,
useToggle,
useTransition,
useUrlSearchParams,
useUserMedia,
useVModel,
useVModels,
useVibrate,
useVirtualList,
useWakeLock,
useWebNotification,
useWebSocket,
useWebWorker,
useWebWorkerFn,
useWindowFocus,
useWindowScroll,
useWindowSize,
watchArray,
watchAtMost,
watchDebounced,
watchDeep,
watchIgnorable,
watchImmediate,
watchOnce,
watchPausable,
watchThrottled,
watchTriggerable,
watchWithFilter,
whenever
} from "./chunk-LJKO4TMH.js";
import "./chunk-QE257C5J.js";
export {
DefaultMagicKeysAliasMap,
StorageSerializers,
TransitionPresets,
assert,
computedAsync as asyncComputed,
refAutoReset as autoResetRef,
breakpointsAntDesign,
breakpointsBootstrapV5,
breakpointsElement,
breakpointsMasterCss,
breakpointsPrimeFlex,
breakpointsQuasar,
breakpointsSematic,
breakpointsTailwind,
breakpointsVuetify,
breakpointsVuetifyV2,
breakpointsVuetifyV3,
bypassFilter,
camelize,
clamp,
cloneFnJSON,
computedAsync,
computedEager,
computedInject,
computedWithControl,
containsProp,
computedWithControl as controlledComputed,
controlledRef,
createEventHook,
createFetch,
createFilterWrapper,
createGlobalState,
createInjectionState,
reactify as createReactiveFn,
createRef,
createReusableTemplate,
createSharedComposable,
createSingletonPromise,
createTemplatePromise,
createUnrefFn,
customStorageEventName,
debounceFilter,
refDebounced as debouncedRef,
watchDebounced as debouncedWatch,
defaultDocument,
defaultLocation,
defaultNavigator,
defaultWindow,
computedEager as eagerComputed,
executeTransition,
extendRef,
formatDate,
formatTimeAgo,
get,
getLifeCycleTarget,
getSSRHandler,
hasOwn,
hyphenate,
identity,
watchIgnorable as ignorableWatch,
increaseWithUnit,
injectLocal,
invoke,
isClient,
isDef,
isDefined,
isIOS,
isObject,
isWorker,
makeDestructurable,
mapGamepadToXbox360Controller,
noop,
normalizeDate,
notNullish,
now,
objectEntries,
objectOmit,
objectPick,
onClickOutside,
onElementRemoval,
onKeyDown,
onKeyPressed,
onKeyStroke,
onKeyUp,
onLongPress,
onStartTyping,
pausableFilter,
watchPausable as pausableWatch,
promiseTimeout,
provideLocal,
provideSSRWidth,
pxValue,
rand,
reactify,
reactifyObject,
reactiveComputed,
reactiveOmit,
reactivePick,
refAutoReset,
refDebounced,
refDefault,
refThrottled,
refWithControl,
resolveRef,
resolveUnref,
set,
setSSRHandler,
syncRef,
syncRefs,
templateRef,
throttleFilter,
refThrottled as throttledRef,
watchThrottled as throttledWatch,
timestamp,
toArray,
toReactive,
toRef,
toRefs,
toValue,
tryOnBeforeMount,
tryOnBeforeUnmount,
tryOnMounted,
tryOnScopeDispose,
tryOnUnmounted,
unrefElement,
until,
useActiveElement,
useAnimate,
useArrayDifference,
useArrayEvery,
useArrayFilter,
useArrayFind,
useArrayFindIndex,
useArrayFindLast,
useArrayIncludes,
useArrayJoin,
useArrayMap,
useArrayReduce,
useArraySome,
useArrayUnique,
useAsyncQueue,
useAsyncState,
useBase64,
useBattery,
useBluetooth,
useBreakpoints,
useBroadcastChannel,
useBrowserLocation,
useCached,
useClipboard,
useClipboardItems,
useCloned,
useColorMode,
useConfirmDialog,
useCountdown,
useCounter,
useCssVar,
useCurrentElement,
useCycleList,
useDark,
useDateFormat,
refDebounced as useDebounce,
useDebounceFn,
useDebouncedRefHistory,
useDeviceMotion,
useDeviceOrientation,
useDevicePixelRatio,
useDevicesList,
useDisplayMedia,
useDocumentVisibility,
useDraggable,
useDropZone,
useElementBounding,
useElementByPoint,
useElementHover,
useElementSize,
useElementVisibility,
useEventBus,
useEventListener,
useEventSource,
useEyeDropper,
useFavicon,
useFetch,
useFileDialog,
useFileSystemAccess,
useFocus,
useFocusWithin,
useFps,
useFullscreen,
useGamepad,
useGeolocation,
useIdle,
useImage,
useInfiniteScroll,
useIntersectionObserver,
useInterval,
useIntervalFn,
useKeyModifier,
useLastChanged,
useLocalStorage,
useMagicKeys,
useManualRefHistory,
useMediaControls,
useMediaQuery,
useMemoize,
useMemory,
useMounted,
useMouse,
useMouseInElement,
useMousePressed,
useMutationObserver,
useNavigatorLanguage,
useNetwork,
useNow,
useObjectUrl,
useOffsetPagination,
useOnline,
usePageLeave,
useParallax,
useParentElement,
usePerformanceObserver,
usePermission,
usePointer,
usePointerLock,
usePointerSwipe,
usePreferredColorScheme,
usePreferredContrast,
usePreferredDark,
usePreferredLanguages,
usePreferredReducedMotion,
usePreferredReducedTransparency,
usePrevious,
useRafFn,
useRefHistory,
useResizeObserver,
useSSRWidth,
useScreenOrientation,
useScreenSafeArea,
useScriptTag,
useScroll,
useScrollLock,
useSessionStorage,
useShare,
useSorted,
useSpeechRecognition,
useSpeechSynthesis,
useStepper,
useStorage,
useStorageAsync,
useStyleTag,
useSupported,
useSwipe,
useTemplateRefsList,
useTextDirection,
useTextSelection,
useTextareaAutosize,
refThrottled as useThrottle,
useThrottleFn,
useThrottledRefHistory,
useTimeAgo,
useTimeout,
useTimeoutFn,
useTimeoutPoll,
useTimestamp,
useTitle,
useToNumber,
useToString,
useToggle,
useTransition,
useUrlSearchParams,
useUserMedia,
useVModel,
useVModels,
useVibrate,
useVirtualList,
useWakeLock,
useWebNotification,
useWebSocket,
useWebWorker,
useWebWorkerFn,
useWindowFocus,
useWindowScroll,
useWindowSize,
watchArray,
watchAtMost,
watchDebounced,
watchDeep,
watchIgnorable,
watchImmediate,
watchOnce,
watchPausable,
watchThrottled,
watchTriggerable,
watchWithFilter,
whenever
};
//# sourceMappingURL=vitepress___@vueuse_core.js.map

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}

347
docs/.vitepress/cache/deps/vue.js vendored Normal file
View File

@@ -0,0 +1,347 @@
import {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBaseVNode,
createBlock,
createCommentVNode,
createElementBlock,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
nodeOps,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
patchProp,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
} from "./chunk-QE257C5J.js";
export {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBlock,
createCommentVNode,
createElementBlock,
createBaseVNode as createElementVNode,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
nodeOps,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
patchProp,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
};
//# sourceMappingURL=vue.js.map

7
docs/.vitepress/cache/deps/vue.js.map vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,347 @@
import {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBaseVNode,
createBlock,
createCommentVNode,
createElementBlock,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
nodeOps,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
patchProp,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
} from "./chunk-QE257C5J.js";
export {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBlock,
createCommentVNode,
createElementBlock,
createBaseVNode as createElementVNode,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
nodeOps,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
patchProp,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
};
//# sourceMappingURL=vue.js.map

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}

View File

@@ -0,0 +1,43 @@
import { defineConfig } from 'vitepress'
export default defineConfig({
title: 'CWD 评论系统',
description: '基于 Cloudflare Workers 的轻量级评论系统文档',
lang: 'zh-CN',
themeConfig: {
nav: [
{ text: '首页', link: '/' },
{ text: '配置', link: '/guide/getting-started' },
{ text: 'API', link: '/api/overview' }
],
sidebar: [
{
text: '配置',
items: [
{ text: '快速开始', link: '/guide/getting-started' },
{ text: '后端配置', link: '/guide/backend-config' },
{ text: '前端配置', link: '/guide/frontend-config' },
]
},
{
text: 'API 文档',
items: [
{ text: '概览', link: '/api/overview' },
{ text: '公开 API', link: '/api/public' },
{ text: '管理员 API', link: '/api/admin' }
]
}
],
socialLinks: [
{ icon: 'github', link: 'https://github.com/anghunk/cwd-comments' }
],
footer: {
message: '基于 Cloudflare Workers 构建',
copyright: 'Copyright © 2026'
}
}
})

150
docs/api/admin.md Normal file
View File

@@ -0,0 +1,150 @@
# 管理员 API
所有管理员 API 都需要在请求头中携带 Bearer Token
```http
Authorization: Bearer <token>
```
## 管理员登录
获取管理员 Token。
### 请求
```http
POST /admin/login
Content-Type: application/json
```
### 请求体
```json
{
"username": "admin",
"password": "your_password"
}
```
### 响应示例
```json
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 604800
}
}
```
## 获取评论管理列表
获取所有评论列表(包括待审核的评论)。
### 请求
```http
GET /admin/comments/list?page={page}&pageSize={pageSize}&status={status}
Authorization: Bearer <token>
```
### 参数
| 参数 | 类型 | 必填 | 说明 |
| -------- | ------ | ---- | ----------------------------------- |
| page | number | 否 | 页码,默认 1 |
| pageSize | number | 否 | 每页数量,默认 20 |
| status | string | 否 | 状态筛选pending/approved/rejected |
### 响应示例
```json
{
"success": true,
"data": {
"comments": [
{
"id": 1,
"path": "/blog/hello-world",
"author": "张三",
"email": "zhangsan@example.com",
"content": "很棒的文章!",
"parent_id": null,
"status": "pending",
"created_at": "2026-01-13T10:00:00Z"
}
],
"total": 1,
"page": 1,
"pageSize": 20
}
}
```
## 更新评论状态
审核评论,更新评论状态。
### 请求
```http
PUT /admin/comments/status
Authorization: Bearer <token>
Content-Type: application/json
```
### 请求体
```json
{
"id": 1,
"status": "approved"
}
```
### 参数说明
| 参数 | 类型 | 必填 | 说明 |
| ------ | ------ | ---- | ---------------------------------------- |
| id | number | 是 | 评论 ID |
| status | string | 是 | 状态approved通过/ rejected拒绝 |
### 响应示例
```json
{
"success": true,
"data": {
"message": "评论状态已更新"
}
}
```
## 删除评论
删除指定评论。
### 请求
```http
DELETE /admin/comments/delete?id={id}
Authorization: Bearer <token>
```
### 参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | number | 是 | 评论 ID |
### 响应示例
```json
{
"success": true,
"data": {
"message": "评论已删除"
}
}
```

48
docs/api/overview.md Normal file
View File

@@ -0,0 +1,48 @@
# API 概览
## 基础信息
- **Base URL**: `https://your-worker.workers.dev`
- **数据格式**: JSON
- **字符编码**: UTF-8
## 认证方式
管理员 API 需要使用 Bearer Token 认证:
```http
Authorization: Bearer <token>
```
Token 通过登录接口获取,有效期为 24 小时。
## 响应格式
### 成功响应
```json
{
"success": true,
"data": { ... }
}
```
### 错误响应
```json
{
"success": false,
"error": "错误信息"
}
```
## HTTP 状态码
| 状态码 | 说明 |
| ------ | ------------ |
| 200 | 请求成功 |
| 400 | 请求参数错误 |
| 401 | 未授权 |
| 403 | 禁止访问 |
| 404 | 资源不存在 |
| 500 | 服务器错误 |

90
docs/api/public.md Normal file
View File

@@ -0,0 +1,90 @@
# 公开 API
## 获取评论列表
获取指定页面的评论列表。
### 请求
```http
GET /api/comments?path={path}&page={page}&pageSize={pageSize}
```
### 参数
| 参数 | 类型 | 必填 | 说明 |
| -------- | ------ | ---- | ----------------- |
| path | string | 是 | 页面路径 |
| page | number | 否 | 页码,默认 1 |
| pageSize | number | 否 | 每页数量,默认 10 |
### 响应示例
```json
{
"success": true,
"data": {
"comments": [
{
"id": 1,
"path": "/blog/hello-world",
"author": "张三",
"email": "zhangsan@example.com",
"content": "很棒的文章!",
"parent_id": null,
"status": "approved",
"created_at": "2026-01-13T10:00:00Z",
"avatar": "https://gravatar.com/avatar/..."
}
],
"total": 1,
"page": 1,
"pageSize": 10
}
}
```
## 提交评论
提交新评论。
### 请求
```http
POST /api/comments
Content-Type: application/json
```
### 请求体
```json
{
"path": "/blog/hello-world",
"author": "张三",
"email": "zhangsan@example.com",
"content": "很棒的文章!",
"parent_id": null
}
```
### 参数说明
| 参数 | 类型 | 必填 | 说明 |
| --------- | ------ | ---- | ----------------------- |
| path | string | 是 | 页面路径 |
| author | string | 是 | 评论者昵称 |
| email | string | 是 | 评论者邮箱 |
| content | string | 是 | 评论内容 |
| parent_id | number | 否 | 父评论 ID回复时使用 |
### 响应示例
```json
{
"success": true,
"data": {
"id": 1,
"message": "评论提交成功,等待审核"
}
}
```

View File

@@ -0,0 +1,103 @@
# 后端配置
## 部署条件
* 拥有一个 Cloudflare 账号(使用邮箱即可注册,[官网地址](https://www.cloudflare.com/)
* 拥有一个 Node.js 运行环境,版本 >= 22本地部署需要
* 拥有一个域名并托管在 Cloudflare 上(这个不是必须项,但可以提高国内访问速度,也更方便)
## 部署
### 本地部署
#### 1. 下载代码,安装依赖
可以直接克隆仓库代码
```
npm install
```
#### 2. 配置 Cloudflare Workers
对于 D1 和 KV 配置,有两种方法,第一种是直接使用命令行配置,第二种是使用网页面板创建后填写配置文件,这里推荐使用第一种方法。如果想要使用之前 Cloudflare 上面已经创建的数据库,可以选择自行配置 `wrangler.jsonc` 文件。
下面介绍第一种方法。
* **登录到 Cloudflare**
```bash
npx wrangler login
```
* **创建数据库和数据库表**,如果遇到提示,请按回车继续
```bash
npx wrangler d1 create CWD_DB
npx wrangler d1 execute CWD_DB --remote --file=./schemas/comment.sql
```
运行完成后可以确认一下 `wrangler.jsonc` 中是否有如下配置
```jsonc
"d1_databases": [
{
"binding": "CWD_DB",
"database_name": "CWD_DB",
"database_id": "xxxxxx" // D1 数据库 ID
}
]
```
如果`binding`字段不是`CWD_DB`,请修改为`CWD_DB`
* **创建 KV 存储**,如果遇到提示,按回车继续
```bash
npx wrangler kv namespace create CWD_AUTH_KV
```
运行完成后可以确认一下 `wrangler.jsonc` 中是否有如下配置
```jsonc
"kv_namespaces": [
{
"binding": "CWD_AUTH_KV",
"id": "xxxxxxx" // KV 存储 ID
}
]
```
* **部署上线**
```bash
npm run deploy
```
没有异常报错后可以进入Cloudflare Workers 面板查看是否部署成功,若显示存在一个名称为 `cwd-backend-worker` 的项目即推送成功。
#### 3. 配置环境变量
* 登录 Worker 面板,点击项目右侧的 Settings (设置) 选项卡,选择`查看设置`
* 点击变量和机密右侧的添加按钮,给项目添加环境变量,环境变量[参考](#环境变量)
* 部署生效:点击底部的 Save and deploy (保存并部署)。
#### 4. 检测部署情况
部署成功后回得到一个域名,即为后端的域名(格式一般为`https://cwd-backend-worker.xxx.workers.dev`。访问该域名,如果显示后端管理页面并可以正常登录则部署成功,将此域名填写到博客的配置文件中即可使用评论功能。
当然也可以使用自定义域名,注意不要使用三级域名,即`*.*.example.com`。
## 环境变量
所需环境变量如下表所示,请参考 [`.dev.vars.example`](.dev.vars.example) 文件
| 变量名 | 描述 |
| ------------------- | -------------------------------------------------------------------------------- |
| `ALLOW_ORIGIN` | 允许跨域请求的域名,用逗号分隔 |
| `RESEND_API_KEY` | Resend API Key用于启用邮件通知服务如**果不需要邮件通知服务,可以不填** |
| `RESEND_FROM_EMAIL` | Resend 发送邮件的邮箱,用于邮件通知服务,**如果不需要邮件通知服务,可以不填** |
| `EMAIL_ADDRESS` | 管理员接收通知邮件的邮箱,用于邮件通知服务,**如果不需要邮件通知服务,可以不填** |
| `ADMIN_NAME` | 管理员登录名称,默认为 admin |
| `ADMIN_PASSWORD` | 管理员登录密码,默认密码为 password |
**注:** [Resend 官网](https://resend.com/)
## 本地测试
如果需要本地测试,环境变量可以使用 `.dev.vars` 文件来设置
```bash
cp .dev.vars.example .dev.vars
# 编辑 .dev.vars 文件
npm run dev
```

View File

@@ -0,0 +1,75 @@
# 前端配置
**这里仅提供一套开箱即用的方案,如果是个人开发者可以根据 API 文档自行编写前端评论组件。**
[接口 API](../api/public.md)
## 初始化
在初始化 `CWDComments` 实例时,可以传入以下配置参数:
```html
<script src="cwd-comments.js"></script>
```
```html
<script>
const comments = new CWDComments({
el: '#comments',
apiBaseUrl: 'https://your-api.example.com',
postSlug: 'my-post',
postTitle: '文章标题',
postUrl: 'https://example.com/my-post',
theme: 'light',
pageSize: 20,
avatarPrefix: 'https://gravatar.com/avatar',
adminEmail: 'admin@example.com',
adminBadge: '博主'
});
comments.mount();
</script>
```
## 参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| -------------- | ----------------------- | ---- | ----------------------------- | -------------------------- |
| `el` | `string \| HTMLElement` | 是 | - | 挂载元素选择器或 DOM 元素 |
| `apiBaseUrl` | `string` | 是 | - | API 基础地址 |
| `postSlug` | `string` | 是 | - | 文章唯一标识符 |
| `postTitle` | `string` | 否 | - | 文章标题,用于邮件通知 |
| `postUrl` | `string` | 否 | - | 文章 URL用于邮件通知 |
| `theme` | `'light' \| 'dark'` | 否 | `'light'` | 主题模式 |
| `pageSize` | `number` | 否 | `20` | 每页显示评论数 |
| `avatarPrefix` | `string` | 否 | `https://gravatar.com/avatar` | 头像服务前缀 |
| `adminEmail` | `string` | 否 | - | 博主邮箱,用于显示博主标识 |
| `adminBadge` | `string` | 否 | `博主` | 博主标识文字 |
## 头像服务前缀
常用的 Gravatar 镜像服务:
| 服务 | 前缀地址 |
| --------------- | -------------------------------- |
| Gravatar 官方 | `https://gravatar.com/avatar` |
| Cravatar (国内) | `https://cravatar.cn/avatar` |
| 自定义镜像 | `https://your-mirror.com/avatar` |
## 实例方法
| 方法 | 说明 |
| ---------------------- | ------------------------------ |
| `mount()` | 挂载组件到 DOM |
| `unmount()` | 卸载组件 |
| `updateConfig(config)` | 更新配置(支持动态切换主题等) |
| `getConfig()` | 获取当前配置 |
## 使用示例
```javascript
// 动态切换主题
comments.updateConfig({ theme: 'dark' });
// 切换文章
comments.updateConfig({ postSlug: 'another-post' });
```

View File

@@ -0,0 +1,34 @@
# 快速开始
## 简介
CWD 评论系统是一个基于 Cloudflare Workers 的轻量级评论解决方案,使用 Hono 框架构建,数据存储使用 Cloudflare D1SQLite和 KV。
## 特性
- ⚡️ **极速响应**:基于 Cloudflare 全球边缘网络
- 🔒 **安全可靠**内置管理员认证、CORS 保护
- 📧 **邮件通知**:支持 Resend 邮件服务
- 🎨 **易于集成**:提供完整的 REST API
## 前置要求
- Node.js 16+
- Cloudflare 账号
- Wrangler CLI
## 安装
```bash
# 克隆项目
git clone https://github.com/anghunk/cwd-comments
cd cwd-comments
# 安装依赖
npm install
```
## 配置
- [后端配置](./backend-config.md)
- [前端配置](./frontend-config.md)

28
docs/index.md Normal file
View File

@@ -0,0 +1,28 @@
---
layout: home
hero:
name: "CWD 评论系统"
tagline: 轻量级、高性能的评论解决方案, 基于 Cloudflare Workers + D1 + KV 构建
actions:
- theme: brand
text: 快速开始
link: /guide/getting-started
- theme: alt
text: API 文档
link: /api/overview
features:
- icon: ⚡️
title: 极速响应
details: 基于 Cloudflare 全球边缘网络,毫秒级响应速度
- icon: 🔒
title: 安全可靠
details: 内置管理员认证、CORS 保护、SQL 注入防护
- icon: 📧
title: 邮件通知
details: 支持 Resend 邮件服务,实时通知新评论
- icon: 🎨
title: 易于集成
details: 提供完整的 REST API支持任意前端框架
---

14
docs/package.json Normal file
View File

@@ -0,0 +1,14 @@
{
"name": "docs",
"version": "0.0.1",
"description": "CWD 评论系统文档",
"type": "module",
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview"
},
"devDependencies": {
"vitepress": "^1.6.4"
}
}