first commit

This commit is contained in:
2025-02-23 21:26:12 +08:00
commit 3b26fe4adc
23 changed files with 2527 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

1
.prettierrc.toml Normal file
View File

@@ -0,0 +1 @@
semi=false

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).

14
index.html Normal file
View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="zh-Hans-CN">
<head>
<meta charset="UTF-8" />
<link rel="shortcut icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>前端开发网站</title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

1875
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

31
package.json Normal file
View File

@@ -0,0 +1,31 @@
{
"name": "webfe",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"start": "vite",
"build": "vue-tsc -b && vite build",
"fmt": "prettier --write src/"
},
"dependencies": {
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-javascript": "^6.2.3",
"@vueuse/core": "^12.7.0",
"codemirror": "^6.0.1",
"naive-ui": "^2.41.0",
"normalize.css": "^8.0.1",
"vue": "^3.5.13",
"vue-codemirror": "^6.1.1"
},
"devDependencies": {
"@codemirror/state": "^6.5.2",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/tsconfig": "^0.7.0",
"prettier": "^3.5.2",
"typescript": "^5.7.3",
"vite": "^6.1.1",
"vue-tsc": "^2.2.2"
}
}

BIN
public/Monaco.ttf Normal file

Binary file not shown.

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

4
public/style.css Normal file
View File

@@ -0,0 +1,4 @@
@font-face {
font-family: "Monaco";
src: url(/Monaco.ttf);
}

54
src/App.vue Normal file
View File

@@ -0,0 +1,54 @@
<script setup lang="ts">
import { darkTheme, dateZhCN, zhCN } from "naive-ui"
import { useDark, useMagicKeys, whenever } from "@vueuse/core"
import Editor from "./components/Editor.vue"
import Preview from "./components/Preview.vue"
import { html, css } from "./store.ts"
const isDark = useDark()
const { ctrl_s } = useMagicKeys({
passive: false,
onEventFired(e) {
if (e.ctrlKey && e.key === "s" && e.type === "keydown") e.preventDefault()
},
})
const { ctrl_r } = useMagicKeys({
passive: false,
onEventFired(e) {
if (e.ctrlKey && e.key === "r" && e.type === "keydown") e.preventDefault()
},
})
whenever(ctrl_s, () => {})
whenever(ctrl_r, () => {})
</script>
<template>
<n-config-provider
:locale="zhCN"
:date-locale="dateZhCN"
:theme="isDark ? darkTheme : null"
>
<n-split :max="0.75" :min="0.25">
<template #1>
<n-tabs default-value="html" type="segment">
<n-tab-pane name="html" tab="HTML">
<Editor language="html" v-model:value="html" />
</n-tab-pane>
<n-tab-pane name="css" tab="CSS">
<Editor language="css" v-model:value="css" />
</n-tab-pane>
<!-- <n-tab-pane name="js" tab="JS">
<Editor language="js" v-model:value="js" />
</n-tab-pane> -->
</n-tabs>
</template>
<template #2>
<Preview />
</template>
</n-split>
</n-config-provider>
</template>
<style scoped></style>

56
src/components/Editor.vue Normal file
View File

@@ -0,0 +1,56 @@
<script lang="ts" setup>
import { EditorView } from "@codemirror/view"
import { Codemirror } from "vue-codemirror"
import { oneDark } from "../themes/oneDark.ts"
import { smoothy } from "../themes/smoothy.ts"
import { useDark } from "@vueuse/core"
import { computed } from "vue"
import { css } from "@codemirror/lang-css"
import { javascript } from "@codemirror/lang-javascript"
import { html } from "@codemirror/lang-html"
const styleTheme = EditorView.baseTheme({
"& .cm-scroller": {
"font-family": "Monaco",
},
"&.cm-editor.cm-focused": {
outline: "none",
},
"&.cm-editor .cm-tooltip.cm-tooltip-autocomplete ul": {
"font-family": "Monaco",
},
})
interface Props {
language?: "html" | "css" | "js"
fontSize?: number
}
const props = withDefaults(defineProps<Props>(), {
language: "html",
fontSize: 20,
})
const code = defineModel<string>("value")
const isDark = useDark()
const lang = computed(() => {
if (props.language === "html") {
return html()
} else if (props.language === "css") {
return css()
} else {
return javascript()
}
})
</script>
<template>
<Codemirror
v-model="code"
indentWithTab
:extensions="[styleTheme, lang, isDark ? oneDark : smoothy]"
:tabSize="4"
:style="{ height: 'calc(100vh - 52px)', fontSize: props.fontSize + 'px' }"
/>
</template>

View File

@@ -0,0 +1,41 @@
<template>
<iframe class="iframe" ref="iframe"></iframe>
</template>
<script lang="ts" setup>
import { html, css } from "../store.ts"
import { onMounted, useTemplateRef, watch } from "vue"
const iframe = useTemplateRef<HTMLIFrameElement>("iframe")
function preview() {
if (!iframe.value) return
const doc = iframe.value.contentDocument
doc!.open()
doc!.write(`<!DOCTYPE html>
<html lang="zh-Hans-CN">
<head>
<meta charset="UTF-8" />
<title>预览</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>${css.value}</style>
</head>
<body>
${html.value}
</body>
</html>`)
doc!.close()
}
watch([html, css], preview)
onMounted(preview)
</script>
<style scoped>
.iframe {
width: 100%;
height: 100%;
border: none;
outline: none;
}
</style>

19
src/main.ts Normal file
View File

@@ -0,0 +1,19 @@
import { createApp } from "vue"
import {
create,
NButton,
NFlex,
NSplit,
NTabs,
NTabPane,
NConfigProvider,
} from "naive-ui"
import "normalize.css"
import App from "./App.vue"
const app = createApp(App)
const naive = create({
components: [NButton, NSplit, NFlex, NTabs, NTabPane, NConfigProvider],
})
app.use(naive)
app.mount("#app")

5
src/store.ts Normal file
View File

@@ -0,0 +1,5 @@
import { useStorage } from "@vueuse/core";
export const html = useStorage("web-html", `<div class="welcome">黄岩一职</div>`)
export const css = useStorage("web-css", `.welcome { color: red; }`)
export const js = useStorage("web-js", "")

110
src/themes/createTheme.ts Normal file
View File

@@ -0,0 +1,110 @@
import {
HighlightStyle,
type TagStyle,
syntaxHighlighting,
} from "@codemirror/language"
import { type Extension } from "@codemirror/state"
import { EditorView } from "@codemirror/view"
interface Options {
/**
* Theme variant. Determines which styles CodeMirror will apply by default.
*/
variant: Variant
/**
* Settings to customize the look of the editor, like background, gutter, selection and others.
*/
settings: Settings
/**
* Syntax highlighting styles.
*/
styles: TagStyle[]
}
type Variant = "light" | "dark"
interface Settings {
/**
* Editor background.
*/
background: string
/**
* Default text color.
*/
foreground: string
/**
* Caret color.
*/
caret: string
/**
* Selection background.
*/
selection: string
/**
* Background of highlighted lines.
*/
lineHighlight: string
/**
* Gutter background.
*/
gutterBackground: string
/**
* Text color inside gutter.
*/
gutterForeground: string
gutterBorderRight: string
}
export const createTheme = ({
variant,
settings,
styles,
}: Options): Extension => {
const theme = EditorView.theme(
{
// eslint-disable-next-line @typescript-eslint/naming-convention
"&": {
backgroundColor: settings.background,
color: settings.foreground,
},
".cm-content": {
caretColor: settings.caret,
},
".cm-cursor, .cm-dropCursor": {
borderLeftColor: settings.caret,
},
"&.cm-focused .cm-selectionBackgroundm .cm-selectionBackground, .cm-content ::selection":
{
backgroundColor: settings.selection,
},
".cm-activeLine": {
backgroundColor: settings.lineHighlight,
},
".cm-gutters": {
backgroundColor: settings.gutterBackground,
borderRight: settings.gutterBorderRight,
color: settings.gutterForeground,
},
".cm-activeLineGutter": {
backgroundColor: settings.lineHighlight,
},
},
{
dark: variant === "dark",
},
)
const highlightStyle = HighlightStyle.define(styles)
const extension = [theme, syntaxHighlighting(highlightStyle)]
return extension
}

149
src/themes/oneDark.ts Normal file
View File

@@ -0,0 +1,149 @@
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"
import { type Extension } from "@codemirror/state"
import { EditorView } from "@codemirror/view"
import { tags as t } from "@lezer/highlight"
// Using https://github.com/one-dark/vscode-one-dark-theme/ as reference for the colors
const chalky = "#e5c07b",
coral = "#e06c75",
cyan = "#56b6c2",
invalid = "#ffffff",
ivory = "#abb2bf",
stone = "#7d8799", // Brightened compared to original to increase contrast
malibu = "#61afef",
sage = "#98c379",
whiskey = "#d19a66",
violet = "#c678dd",
darkBackground = "#26262a",
highlightBackground = "#2c313a",
background = "#101014", // naive-ui
tooltipBackground = "#353a42",
selection = "#3E4451",
cursor = "#528bff"
/// The editor theme styles for One Dark.
const oneDarkTheme = EditorView.theme(
{
"&": {
color: ivory,
backgroundColor: background,
},
".cm-content": {
caretColor: cursor,
},
".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor },
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
{ backgroundColor: selection },
".cm-panels": { backgroundColor: darkBackground, color: ivory },
".cm-panels.cm-panels-top": { borderBottom: "2px solid black" },
".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" },
".cm-searchMatch": {
backgroundColor: "#72a1ff59",
outline: "1px solid #457dff",
},
".cm-searchMatch.cm-searchMatch-selected": {
backgroundColor: "#6199ff2f",
},
".cm-activeLine": { backgroundColor: "#6699ff0b" },
".cm-selectionMatch": { backgroundColor: "#aafe661a" },
"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": {
backgroundColor: "#bad0f847",
},
".cm-gutters": {
backgroundColor: background,
color: stone,
border: "none",
},
".cm-activeLineGutter": {
backgroundColor: highlightBackground,
},
".cm-foldPlaceholder": {
backgroundColor: "transparent",
border: "none",
color: "#ddd",
},
".cm-tooltip": {
border: "none",
backgroundColor: tooltipBackground,
},
".cm-tooltip .cm-tooltip-arrow:before": {
borderTopColor: "transparent",
borderBottomColor: "transparent",
},
".cm-tooltip .cm-tooltip-arrow:after": {
borderTopColor: tooltipBackground,
borderBottomColor: tooltipBackground,
},
".cm-tooltip-autocomplete": {
"& > ul > li[aria-selected]": {
backgroundColor: highlightBackground,
color: ivory,
},
},
},
{ dark: true },
)
/// The highlighting style for code in the One Dark theme.
const oneDarkHighlightStyle = HighlightStyle.define([
{ tag: t.keyword, color: violet },
{
tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName],
color: coral,
},
{ tag: [t.function(t.variableName), t.labelName], color: malibu },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: whiskey },
{ tag: [t.definition(t.name), t.separator], color: ivory },
{
tag: [
t.typeName,
t.className,
t.number,
t.changed,
t.annotation,
t.modifier,
t.self,
t.namespace,
],
color: chalky,
},
{
tag: [
t.operator,
t.operatorKeyword,
t.url,
t.escape,
t.regexp,
t.link,
t.special(t.string),
],
color: cyan,
},
{ tag: [t.meta, t.comment], color: stone },
{ tag: t.strong, fontWeight: "bold" },
{ tag: t.emphasis, fontStyle: "italic" },
{ tag: t.strikethrough, textDecoration: "line-through" },
{ tag: t.link, color: stone, textDecoration: "underline" },
{ tag: t.heading, fontWeight: "bold", color: coral },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: whiskey },
{ tag: [t.processingInstruction, t.string, t.inserted], color: sage },
{ tag: t.invalid, color: invalid },
])
/// Extension to enable the One Dark theme (both the editor theme and
/// the highlight style).
export const oneDark: Extension = [
oneDarkTheme,
syntaxHighlighting(oneDarkHighlightStyle),
]

83
src/themes/smoothy.ts Normal file
View File

@@ -0,0 +1,83 @@
import { tags as t } from "@lezer/highlight"
import { createTheme } from "./createTheme"
// Author: Kenneth Reitz
export const smoothy = createTheme({
variant: "light",
settings: {
background: "#FFFFFF",
foreground: "#000000",
caret: "#000000",
selection: "#FFFD0054",
gutterBackground: "#FFFFFF",
gutterForeground: "#00000070",
gutterBorderRight: "none",
lineHighlight: "#00000008",
},
styles: [
{
tag: t.comment,
color: "#CFCFCF",
},
{
tag: [t.number, t.bool, t.null],
color: "#E66C29",
},
{
tag: [
t.className,
t.definition(t.propertyName),
t.function(t.variableName),
t.labelName,
t.definition(t.typeName),
],
color: "#2EB43B",
},
{
tag: t.keyword,
color: "#D8B229",
},
{
tag: t.operator,
color: "#4EA44E",
fontWeight: "bold",
},
{
tag: [t.definitionKeyword, t.modifier],
color: "#925A47",
},
{
tag: t.string,
color: "#704D3D",
},
{
tag: t.typeName,
color: "#2F8996",
},
{
tag: [t.variableName, t.propertyName],
color: "#77ACB0",
},
{
tag: t.self,
color: "#77ACB0",
fontWeight: "bold",
},
{
tag: t.regexp,
color: "#E3965E",
},
{
tag: [t.tagName, t.angleBracket],
color: "#BAA827",
},
{
tag: t.attributeName,
color: "#B06520",
},
{
tag: t.derefOperator,
color: "#000",
},
],
})

1
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

14
tsconfig.app.json Normal file
View File

@@ -0,0 +1,14 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

7
tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

24
tsconfig.node.json Normal file
View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

7
vite.config.ts Normal file
View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
})