Commit 8fbaaca2 authored by rock王's avatar rock王

feat: 插件正式版

parent 5e3ae703
{
"name": "ggg3",
"version": "0.0.1",
"name": "vuepress-plugin-demo-block-edit",
"version": "2.0.0-beta.61",
"description": "Vuepress plugin for demo-block & code-edit",
"main": "dist/ggg3.es.js",
"module": "dist/ggg3.es.js",
"license": "MIT",
"keywords": [
"vuepress-plugin",
"vuepress",
"demo",
"code",
"edit"
],
"main": "dist/index.js",
"module": "dist/index.js",
"type": "module",
"scripts": {
"build": "vite build ."
"build": "rm -rf dist && tsc --project tsconfig.json",
"clean": "rimraf lib *.tsbuildinfo"
},
"dependencies": {
"@codemirror/lang-javascript": "^6.1.4",
"@codemirror/theme-one-dark": "^6.1.1",
"@rollup/plugin-commonjs": "24.1.0-0",
"@vue/shared": "^3.2.37",
"@vuepress/core": "2.0.0-beta.61",
"@vueuse/core": "^9.1.0",
"ansi-styles": "^6.2.1",
"chalk": "^4.1.2",
......@@ -24,19 +32,21 @@
"markdown-it": "^13.0.1",
"markdown-it-container": "^3.0.0",
"prismjs": "^1.29.0",
"vue-codemirror": "^6.1.1"
"vue-codemirror": "^6.1.1",
"@vuepress/client": "^2.0.0-beta.61",
"@vuepress/core": "^2.0.0-beta.61",
"@vuepress/utils": "2.0.0-beta.61"
},
"peerDependencies": {
"@vuepress/client": "2.0.0-beta.61",
"@vuepress/core": "2.0.0-beta.61",
"vue": "^3.2.47",
"vuepress": "2.0.0-beta.61"
"vuepress": "^2.0.0-beta.61"
},
"devDependencies": {
"@types/markdown-it": "^12.2.3",
"@vuepress/client": "2.0.0-beta.61",
"vite": "^2.9.15",
"@types/node": "^20.3.2",
"typescript": "^5.1.3",
"vite": "^4.3.9",
"vue": "^3.2.47",
"vuepress": "2.0.0-beta.61"
"vuepress": "^2.0.0-beta.61"
}
}
This diff is collapsed.
......@@ -42,7 +42,7 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useClipboard, useToggle } from '@vueuse/core'
import { useToggle } from '@vueuse/core'
const props = defineProps<{
demos: object
source: string
......@@ -50,10 +50,22 @@ const props = defineProps<{
rawSource: string
description?: string
}>()
const { copy } = useClipboard({
source: decodeURIComponent(props.rawSource),
read: false,
})
function copy(content) {
const textarea = document.createElement('textarea')
textarea.readOnly = true
textarea.style.position = 'absolute'
textarea.style.left = '-9999px'
// 将要 copy 的值赋给 textarea 标签的 value 属性
textarea.value = content
// 将 textarea 插入到 body 中
document.body.appendChild(textarea)
// 选中值并复制
textarea.select()
textarea.setSelectionRange(0, textarea.value.length)
const result = document.execCommand('copy')
document.body.removeChild(textarea)
return result
}
let hovering = ref(false);
let copyMessage = ref('复制代码');
......@@ -63,10 +75,10 @@ const formatPathDemos = computed(() => {
return props.demos
})
const iconClass = computed(() => {
return sourceVisible ? '▲' : '▼';
return sourceVisible.value ? '▲' : '▼';
})
const controlText = computed(() => {
return sourceVisible ? '隐藏代码' : '显示代码';
return sourceVisible.value ? '隐藏代码' : '显示代码';
})
const decodedDescription = computed(() =>
......@@ -77,7 +89,7 @@ const decodedCode = computed(() =>
)
async function handleCopy() {
await copy();
copy(decodeURIComponent(props.rawSource));
copyMessage.value = '复制成功🎉';
setTimeout(() => {
copyMessage.value = '复制代码';
......@@ -98,15 +110,15 @@ function handleCodeView() {
padding: 20px;
border-radius: 3px;
transition: 0.3s;
.code-slide-enter,
.code-slide-enter-active,
.code-slide-leave,
.code-slide-leave-active {
transition:
0.3s max-height ease-in-out,
0.3s padding-top ease-in-out,
0.3s padding-bottom ease-in-out;
}
// .code-slide-enter,
// .code-slide-enter-active,
// .code-slide-leave,
// .code-slide-leave-active {
// transition:
// 0.3s max-height ease-in-out,
// 0.3s padding-top ease-in-out,
// 0.3s padding-bottom ease-in-out;
// }
.example {
margin-bottom: 20px;
}
......@@ -176,14 +188,14 @@ function handleCodeView() {
&.is-fixed {
position: sticky;
top: 0;
bottom: 20px;
bottom: 0px;
}
>i {
position: absolute;
transform: translateX(-30px);
font-size: 14px;
line-height: 44px;
transition: 0.3s;
// transition: 0.3s;
display: inline-block;
color: #1f93ff;
}
......
<script>
import { Codemirror } from 'vue-codemirror'
import { javascript } from '@codemirror/lang-javascript'
import { oneDark } from '@codemirror/theme-one-dark'
export default {
name: 'CodeEdit',
components: {
Codemirror,
},
data() {
return {
source: '',
codeSource: '',
extensions: [javascript(), oneDark],
}
},
mounted() {
const source = sessionStorage.getItem('gcodeSource')
this.source = source
this.codeSource = decodeURIComponent(this.source)
this.handleRun()
},
methods: {
onCtrlSClick(event) {
event.preventDefault()
this.handleRun()
},
handleRun() {
fetch(`/updateTemp?codesource=${encodeURIComponent(this.codeSource)}`)
},
handleReset() {
const res = window.confirm('确认要重置吗?')
if (res) {
const code = sessionStorage.getItem('gcodeSource')
this.source = code
this.codeSource = decodeURIComponent(this.source)
}
},
handleRefresh() {
this.$refs.gframe.contentWindow.location.reload()
},
},
}
</script>
<template>
<ClientOnly>
<div class="kf-preview-block">
<div class="operate-container">
<span class="btn" @click="handleRun"> 运行(ctrl + s) </span>
<span class="btn" @click="handleReset"> 重置代码 </span>
<span class="btn" @click="handleRefresh"> 刷新效果 </span>
</div>
<div class="preview-panel">
<div class="preview-source" @keydown.ctrl.s="onCtrlSClick">
<Codemirror
v-model="codeSource" placeholder="Code goes here..." :autofocus="true" language="javascript"
:extensions="extensions" :indent-with-tab="true" :tab-size="2"
/>
</div>
<div class="preview-code">
<iframe ref="gframe" src="/gpreview" />
</div>
</div>
</div>
</ClientOnly>
</template>
<style>
/* 重写样式 ==============start============== */
.navbar {
display: none;
}
.theme-default-content {
max-width: 100% !important;
}
iframe {
width: 100%;
height: 100%;
}
/* 重新样式 ===============end============= */
.kf-preview-block .btn {
color: #1f93ff;
cursor: pointer;
margin-left: 16px;
}
.kf-preview-block {
background: #fff;
display: flex;
flex-direction: column;
border: solid 1px #ebebeb;
border-radius: 3px;
transition: 0.3s;
height: 85vh;
overflow: hidden;
}
.kf-preview-block .operate-container {
text-align: right;
padding-right: 40px;
border-bottom: solid 1px #ebebeb;
}
.kf-preview-block .preview-header {
display: flex;
align-items: center;
height: 60px;
}
.kf-preview-block .preview-panel {
display: flex;
flex: 1;
overflow: hidden;
}
.kf-preview-block .preview-source {
display: block;
width: 50%;
background-color: #f3f4f5;
overflow: auto;
}
.kf-preview-block .preview-code {
display: block;
width: 50%;
padding: 24px;
}
.kf-preview-block .CodeMirror.cm-s-monokai {
height: 100%;
}
</style>
<template>
<ClientOnly>
<div class="kf-demo-block" :class="[{ hover: hovering }]" @mouseenter="hovering = true"
@mouseleave="hovering = false">
<!-- danger here DO NOT USE INLINE SCRIPT TAG -->
<p text="sm" v-html="decodedDescription" />
<div class="example">
<component :is="formatPathDemos[path]"></component>
</div>
<transition name="code-slide">
<div v-show="sourceVisible" class="example-source-wrapper">
<div class="example-source language-vue" v-html="decodedCode" />
</div>
</transition>
<div ref="control" :class="['kf-demo-block-control', { 'is-fixed': sourceVisible }]"
@click="toggleSourceVisible(!sourceVisible)">
<transition name="text-slide">
<i v-show="hovering || sourceVisible">{{ iconClass }}</i>
</transition>
<transition name="text-slide">
<span v-show="hovering || sourceVisible" class="btn">{{ controlText }}</span>
</transition>
<div class="control-button-container">
<span v-show="hovering || sourceVisible" size="small" type="text" class="control-button copy-button btn"
@click.stop="handleCopy">
{{ copyMessage }}
</span>
<transition name="text-slide">
<span v-show="hovering || sourceVisible" class="control-button run-online-button btn"
@click.stop="handleCodeView">
在线运行
</span>
</transition>
</div>
</div>
</div>
</ClientOnly>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useClipboard, useToggle } from '@vueuse/core'
const props = defineProps<{
demos: object
source: string
path: string
rawSource: string
description?: string
}>()
const { copy } = useClipboard({
source: decodeURIComponent(props.rawSource),
read: false,
})
let hovering = ref(false);
let copyMessage = ref('复制代码');
const [sourceVisible, toggleSourceVisible] = useToggle()
const formatPathDemos = computed(() => {
return props.demos
})
const iconClass = computed(() => {
return sourceVisible ? '▲' : '▼';
})
const controlText = computed(() => {
return sourceVisible ? '隐藏代码' : '显示代码';
})
const decodedDescription = computed(() =>
decodeURIComponent(props.description!)
)
const decodedCode = computed(() =>
decodeURIComponent(props.source!)
)
async function handleCopy() {
await copy();
copyMessage.value = '复制成功🎉';
setTimeout(() => {
copyMessage.value = '复制代码';
}, 2000);
}
function handleCodeView() {
sessionStorage.setItem('gcodeSource', props.rawSource);
window.open('/gedit');
}
</script>
<style lang="scss">
.kf-demo-block {
position: relative;
border: solid 1px #ebebeb;
padding: 20px;
border-radius: 3px;
transition: 0.3s;
.code-slide-enter,
.code-slide-enter-active,
.code-slide-leave,
.code-slide-leave-active {
transition:
0.3s max-height ease-in-out,
0.3s padding-top ease-in-out,
0.3s padding-bottom ease-in-out;
}
.example {
margin-bottom: 20px;
}
.btn {
color: #1f93ff;
cursor: pointer;
margin-left: 16px;
}
&.hover {
box-shadow: 0 0 8px 0 rgb(232 237 250 / 60%), 0 2px 4px 0 rgb(232 237 250 / 50%);
}
code {
font-family: Menlo, Monaco, Consolas, Courier, monospace;
}
.demo-button {
float: right;
}
.source {
padding: 24px;
}
.meta {
border-top: solid 1px #eaeefb;
overflow: hidden;
height: 0;
transition: height 0.3s;
}
.description {
padding: 20px;
box-sizing: border-box;
border: solid 1px #ebebeb;
border-radius: 3px;
font-size: 14px;
line-height: 22px;
color: #666;
word-break: break-word;
margin: 10px;
p {
margin: 0;
line-height: 26px;
}
}
.highlight {
div[class*="language-"] {
border-radius: 0;
}
}
#highlight {
& > .language-vue > .language-vue {
padding-top: 0;
margin-top: 0;
}
}
.kf-demo-block-control {
position: relative;
z-index: 9;
border-top: solid 1px #eaeefb;
height: 44px;
box-sizing: border-box;
// border-bottom-left-radius: 4px;
// border-bottom-right-radius: 4px;
border: 1px solid #d3dce6;
background-color: #eaeefb;
text-align: center;
margin-top: -1px;
color: #d3dce6;
cursor: pointer;
&.is-fixed {
position: sticky;
top: 0;
bottom: 20px;
}
>i {
position: absolute;
transform: translateX(-30px);
font-size: 14px;
line-height: 44px;
transition: 0.3s;
display: inline-block;
color: #1f93ff;
}
>span {
position: absolute;
transform: translateX(-30px);
font-size: 14px;
line-height: 44px;
transition: 0.3s;
display: inline-block;
}
&:hover {
// background-color: #f9fafc;
}
& .text-slide-enter,
& .text-slide-leave-active {
opacity: 0;
transform: translateX(10px);
}
.control-button-container {
line-height: 40px;
position: absolute;
top: 0;
right: 0;
padding-left: 5px;
padding-right: 25px;
}
.control-button {
font-size: 14px;
margin: 0 10px;
}
}
}
</style>
<script setup>
import CodeEdit from '@temp/CodeEdit.vue'
</script>
<ClientOnly>
<CodeEdit />
</ClientOnly>
<script setup>
import tempCmp from '@temp/tempCode.vue';
</script>
<ClientOnly>
<tempCmp />
</ClientOnly>
import { createPage } from '@vuepress/core'
import type MarkdownIt from 'markdown-it'
import { mdPlugin } from './plugins/plugins'
import { MarkdownTransform } from './plugins/markdown-transform'
import { HotUpdate } from './plugins/hot-update'
import { mdPlugin } from './plugins/plugins.js'
import { MarkdownTransform } from './plugins/markdown-transform.js'
import fs from 'fs'
import path from 'path'
import { HotUpdate } from './plugins/hot-update.js'
import { getDirname, path } from '@vuepress/utils'
const __dirname = getDirname(import.meta.url)
export default function preview2edit() {
return {
name: 'test1',
name: 'code-demo-edit',
multiple: false,
alias: {
'@docs': path.resolve('', 'docs'),
'@docs': path.resolve(process.env.PWD, 'docs'),
},
extendsMarkdown: async (md: MarkdownIt, app) => {
mdPlugin(md, app)
......@@ -20,21 +23,21 @@ export default function preview2edit() {
return;
}
await Promise.all([
app.writeTemp('CodeEdit.vue', fs.readFileSync(path.resolve(__dirname, './CodeEdit.vue'))),
app.writeTemp('Demo.vue', fs.readFileSync(path.resolve(__dirname, './Demo.vue'))),
app.writeTemp('CodeEdit.vue', fs.readFileSync(path.resolve(__dirname, '../public/CodeEdit.vue'))),
app.writeTemp('Demo.vue', fs.readFileSync(path.resolve(__dirname, '../public/Demo.vue'))),
app.writeTemp('tempCode.vue', ''),
])
const editPage = await createPage(app, {
path: '/gedit.html',
filePath: path.resolve(__dirname, '../../demo/gedit.md')
// filePath: path.resolve(__dirname, './gedit.md')
// filePath: path.resolve(__dirname, '../../demo/gedit.md')
filePath: path.resolve(__dirname, '../public/gedit.md')
})
app.pages.push(editPage)
const previewPage = await createPage(app, {
path: '/gpreview.html',
filePath: path.resolve(__dirname, '../../demo/gpreview.md')
// filePath: path.resolve(__dirname, './gpreview.md')
// filePath: path.resolve(__dirname, '../../demo/gpreview.md')
filePath: path.resolve(__dirname, '../public/gpreview.md')
})
app.pages.push(previewPage)
},
......
......@@ -2,8 +2,8 @@ import path from 'path'
import fs from 'fs'
import MarkdownIt from 'markdown-it'
import mdContainer from 'markdown-it-container'
import tag from '../plugins/tag'
// import { highlight } from '../utils/highlight'
import tag from './tag.js'
import { highlight } from '../utils/highlight.js'
import type Token from 'markdown-it/lib/token'
import type Renderer from 'markdown-it/lib/renderer'
......@@ -44,7 +44,7 @@ export const mdPlugin = (md: MarkdownIt, app) => {
if (!source) throw new Error(`Incorrect source file: ${sourceFile}`)
return `<Demo :demos="demos" source="${encodeURIComponent(
source
highlight(source, 'vue')
)}" path="${sourceFile.split('/')[1]}" raw-source="${encodeURIComponent(
source
)}" description="${encodeURIComponent(localMd.render(description))}">`
......
{
"compilerOptions": {
"baseUrl": ".",
"target": "esnext",
"useDefineForClassFields": true,
"rootDir": "./src",
"outDir": "./dist",
"target": "ES2020",
"skipLibCheck": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"allowSyntheticDefaultImports": true,
"moduleResolution": "Node",
"lib": ["ES2020"],
"composite": true,
"strict": false,
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"types": [
"node"
],
"paths": {
"@/*": ["src/*"]
}
"noImplicitReturns": false,
"declaration": true,
"types": ["node"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "server-dev.js"]
"include": ["./src"]
}
\ No newline at end of file
import type { ConfigEnv } from 'vite'
import path from 'path';
import { defineConfig, loadEnv } from 'vite'
export default ({ mode, command }: ConfigEnv) => {
const env = loadEnv(mode, process.cwd())
return defineConfig({
build: {
// target: 'node16',
assetsDir: './',
lib: {
entry: path.resolve(__dirname, './src/index.ts'),
formats: ['es', 'cjs'],
// fileName: 'index'
},
// sourcemap: true,
// rollupOptions: {
// input: path.resolve(__dirname, './src/index.ts'),
// // format: 'es',
// output: {
// // dir: 'a',
// // format: 'es'
// },
// // bundle: {
// // path: `a/es`,
// // },
// }
},
})
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment