├── packages
├── docs
│ ├── .gitignore
│ ├── public
│ │ ├── favicon.ico
│ │ └── logo.svg
│ ├── reference
│ │ ├── support.md
│ │ └── deploy.md
│ ├── more
│ │ └── feedback.md
│ ├── .vitepress
│ │ ├── components
│ │ │ └── home-hero-after.vue
│ │ ├── theme
│ │ │ ├── index.ts
│ │ │ └── style.scss
│ │ └── config.ts
│ ├── package.json
│ ├── index.md
│ └── guide
│ │ ├── config.md
│ │ ├── importmap.md
│ │ └── start.md
├── website
│ ├── src
│ │ ├── main.ts
│ │ ├── vite-env.d.ts
│ │ ├── App.vue
│ │ └── type.ts
│ ├── tsconfig.node.json
│ ├── .gitignore
│ ├── index.html
│ ├── tsconfig.json
│ ├── dist
│ │ ├── index.html
│ │ ├── assets
│ │ │ ├── index-eba15397.js
│ │ │ ├── azcli-deb61577-f1f7c576.js
│ │ │ ├── javascript-3aab9677-e6c2c027.js
│ │ │ ├── ini-0cf51c96-18bf1153.js
│ │ │ ├── csp-80865a3e-d207cac4.js
│ │ │ ├── pla-5b3c3b2c-1b3e1614.js
│ │ │ ├── scheme-bd6c73dd-ff6e5671.js
│ │ │ ├── flow9-88286753-42a61225.js
│ │ │ ├── sb-190efae3-2020a5af.js
│ │ │ ├── bat-19ed4cb4-86aea46a.js
│ │ │ ├── dockerfile-d28c5166-bbf114b2.js
│ │ │ ├── pascaligo-c74f23f5-385edc0e.js
│ │ │ ├── lua-250761cb-9adddcd9.js
│ │ │ ├── cameligo-145c2c73-8043f913.js
│ │ │ ├── graphql-edbbae08-387d549c.js
│ │ │ ├── objective-c-4607378b-951ded7b.js
│ │ │ ├── lexon-81c06df2-8d4b0444.js
│ │ │ ├── xml-1c94689a-3dcda303.js
│ │ │ ├── bicep-f7f58c3d-392558ac.js
│ │ │ ├── sparql-bcef85eb-e42fb28a.js
│ │ │ ├── mips-6984085e-bdd96c5a.js
│ │ │ ├── go-e4123ca1-8759e9f7.js
│ │ │ ├── sophia-ff9a60c4-12eb7ba8.js
│ │ │ ├── m3-cd35fba5-aa2dcf72.js
│ │ │ ├── qsharp-d902dfd3-e125d03f.js
│ │ │ ├── fsharp-5c8798af-8abe6da0.js
│ │ │ ├── pascal-c142b200-7442fd46.js
│ │ │ ├── shell-325a523c-3c06def6.js
│ │ │ ├── r-1974245b-e0a01d4f.js
│ │ │ ├── java-067b9e51-000a6283.js
│ │ │ ├── powershell-9e30c619-d3380668.js
│ │ │ ├── cypher-6571dbc8-49f5f839.js
│ │ │ ├── kotlin-5d8c4070-06ee8898.js
│ │ │ └── redis-a4651a9b-d0a12fea.js
│ │ └── logo-ff272dde.svg
│ ├── vite.config.ts
│ ├── package.json
│ ├── scripts
│ │ └── index.js
│ ├── logo.svg
│ └── README.md
└── core
│ ├── src
│ ├── templates
│ │ ├── index.ts
│ │ ├── javascript.ts
│ │ ├── typescript.ts
│ │ ├── react.ts
│ │ ├── vue3.ts
│ │ ├── html.ts
│ │ └── vue2.ts
│ ├── utils
│ │ ├── index.ts
│ │ ├── debounce.ts
│ │ ├── convert.ts
│ │ ├── message.ts
│ │ ├── str.ts
│ │ └── dialog.ts
│ ├── assets
│ │ ├── default_file.svg
│ │ ├── vue.svg
│ │ ├── js.svg
│ │ ├── ts.svg
│ │ ├── html.svg
│ │ ├── css.svg
│ │ ├── sass.svg
│ │ ├── scss.svg
│ │ ├── json.svg
│ │ ├── jsx.svg
│ │ └── tsx.svg
│ ├── compiler
│ │ ├── plugins
│ │ │ └── module-factory.ts
│ │ ├── transform
│ │ │ └── plugins
│ │ │ │ ├── transform-css.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── transform-less.ts
│ │ │ │ ├── transform-scss.ts
│ │ │ │ ├── transform-react.ts
│ │ │ │ └── transform-ts.ts
│ │ ├── type.ts
│ │ ├── file-system
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── index.ts
│ ├── vite-env.d.ts
│ ├── components
│ │ ├── preview
│ │ │ └── index.less
│ │ ├── monaco-editor
│ │ │ └── utils.ts
│ │ ├── toolbar
│ │ │ ├── icons
│ │ │ │ ├── icon.less
│ │ │ │ ├── refresh.vue
│ │ │ │ ├── share.vue
│ │ │ │ ├── docs.vue
│ │ │ │ ├── copy.vue
│ │ │ │ └── git.vue
│ │ │ └── index.vue
│ │ ├── index.less
│ │ ├── file-bar
│ │ │ ├── index.less
│ │ │ ├── icons
│ │ │ │ ├── add-file.vue
│ │ │ │ ├── rename-file.vue
│ │ │ │ ├── home-file.vue
│ │ │ │ └── delete-file.vue
│ │ │ └── file-input.vue
│ │ ├── splitter
│ │ │ └── index.less
│ │ ├── menus
│ │ │ ├── utils
│ │ │ │ ├── getInfo.ts
│ │ │ │ └── system.ts
│ │ │ └── type.ts
│ │ └── loading
│ │ │ └── index.vue
│ ├── constant
│ │ ├── tooltip.ts
│ │ └── index.ts
│ ├── style
│ │ ├── variable.less
│ │ └── global.less
│ ├── type.ts
│ └── store
│ │ └── index.ts
│ ├── tsconfig.node.json
│ ├── .gitignore
│ ├── index.html
│ ├── tsconfig.json
│ ├── vite.config.ts
│ ├── logo.svg
│ └── package.json
├── .gitignore
├── .vscode
└── extensions.json
├── package.json
└── LICENSE
/packages/docs/.gitignore:
--------------------------------------------------------------------------------
1 | dist
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | types
3 | dist.zip
4 |
5 | .cache
6 | .temp
7 | stats.html
8 | cache
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/docs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zh-lx/codeplayer/HEAD/packages/docs/public/favicon.ico
--------------------------------------------------------------------------------
/packages/docs/reference/support.md:
--------------------------------------------------------------------------------
1 | # 支持语法
2 |
3 | - ✅ html
4 | - ✅ js/ts
5 | - ✅ css/less/scss
6 | - ✅ jsx/tsx
7 | - ✅ vue2/3
8 | - ✅ react
9 |
--------------------------------------------------------------------------------
/packages/website/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import '../../core/dist/style.css';
3 |
4 | import App from './App.vue';
5 |
6 | const app = createApp(App);
7 | app.mount('#app');
8 |
--------------------------------------------------------------------------------
/packages/core/src/templates/index.ts:
--------------------------------------------------------------------------------
1 | export * from './html';
2 | export * from './javascript';
3 | export * from './react';
4 | export * from './typescript';
5 | export * from './vue2';
6 | export * from './vue3';
7 |
--------------------------------------------------------------------------------
/packages/core/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './debounce';
2 | export * from './str';
3 | export * from './module-compiler';
4 | export * from './convert';
5 | export * from './message';
6 | export * from './dialog';
7 |
--------------------------------------------------------------------------------
/packages/core/src/assets/default_file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/website/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/core/src/compiler/plugins/module-factory.ts:
--------------------------------------------------------------------------------
1 | import { Hooks } from '@/compiler/type'
2 | import { compileModulesForPreview } from '@/compiler/module'
3 |
4 | export default function(hooks: Hooks) {
5 | hooks.hook('compile-module', compileModulesForPreview)
6 | }
--------------------------------------------------------------------------------
/packages/website/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module '*.vue' {
4 | import { defineComponent } from 'vue';
5 | const Component: ReturnType;
6 | export default Component;
7 | }
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/core/src/utils/debounce.ts:
--------------------------------------------------------------------------------
1 | export function debounce(fn: Function, n = 100) {
2 | let handle: any;
3 | return (...args: any[]) => {
4 | if (handle) clearTimeout(handle);
5 | handle = setTimeout(() => {
6 | fn(...args);
7 | }, n);
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/packages/core/src/index.ts:
--------------------------------------------------------------------------------
1 | import CodePlayer from './components/index.vue';
2 | import '@/style/global.less';
3 | import '@/style/index.less';
4 | import 'tippy.js/dist/tippy.css';
5 | import 'tippy.js/themes/light.css';
6 | export type { CodePlayerOptions } from './type';
7 |
8 | export default CodePlayer;
9 |
--------------------------------------------------------------------------------
/packages/core/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module '*.vue' {
4 | import { defineComponent } from 'vue';
5 | const Component: ReturnType;
6 | export default Component;
7 | }
8 |
9 | declare interface Window {
10 | __eruda: any;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/website/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist-ssr
12 | *.local
13 |
14 | # Editor directories and files
15 | .vscode/*
16 | !.vscode/extensions.json
17 | .idea
18 | .DS_Store
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/packages/core/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/packages/core/src/components/preview/index.less:
--------------------------------------------------------------------------------
1 | .codeplayer-iframe-container {
2 | position: relative;
3 | height: 100%;
4 | width: 100%;
5 | background-color: @bg-container;
6 | }
7 | .codeplayer-iframe {
8 | border: none;
9 | position: relative;
10 | height: 100%;
11 | width: 100%;
12 | user-select: none;
13 | &:hover {
14 | user-select: initial;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/core/src/components/monaco-editor/utils.ts:
--------------------------------------------------------------------------------
1 | import { Uri, editor } from 'monaco-editor';
2 |
3 | export function getOrCreateModel(
4 | uri: Uri,
5 | lang: string | undefined,
6 | value: string
7 | ) {
8 | const model = editor.getModel(uri);
9 | if (model) {
10 | model.setValue(value);
11 | return model;
12 | }
13 | return editor.createModel(value, lang, uri);
14 | }
15 |
--------------------------------------------------------------------------------
/packages/core/src/utils/convert.ts:
--------------------------------------------------------------------------------
1 | export const convertToNumber = (len: number | string, total: number) => {
2 | if (typeof len === 'number') {
3 | return len;
4 | }
5 | if (len.endsWith('%')) {
6 | return Number(len.slice(0, len.length - 1)) * total * 0.01;
7 | }
8 | if (len.endsWith('px')) {
9 | return Number(len.slice(0, len.length - 2));
10 | }
11 | return Number(len);
12 | };
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codeplayer",
3 | "version": "1.0.0",
4 | "description": "一个浏览器代码编辑&可视化运行 web 组件,支持 vue/react 等代码的浏览器编辑及运行",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "private": true,
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "workspaces": [
14 | "packages/*"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/packages/docs/more/feedback.md:
--------------------------------------------------------------------------------
1 | # 交流与反馈
2 |
3 | ## 加入用户群
4 |
5 | 任何使用问题欢迎添加作者微信 `zhoulx1688888` 或者 QQ 群 `245077601` 进行咨询与反馈:
6 |
7 |
8 |

9 |

10 |
11 |
--------------------------------------------------------------------------------
/packages/core/src/assets/vue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/docs/.vitepress/components/home-hero-after.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |

7 |
8 |
9 |
10 |
11 |
12 |
18 |
--------------------------------------------------------------------------------
/packages/website/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | CodePlayer
8 |
9 |
10 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/core/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + Vue + TS
8 |
9 |
10 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@codeplayer/website",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "docs:dev": "vitepress dev",
9 | "docs:build": "vitepress build",
10 | "docs:preview": "vitepress preview"
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "ISC",
15 | "devDependencies": {
16 | "vitepress": "1.0.0-beta.2"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/website/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
20 |
--------------------------------------------------------------------------------
/packages/core/src/components/toolbar/icons/icon.less:
--------------------------------------------------------------------------------
1 | .toolbar-icon {
2 | color: var(--codeplayer-text-secondary);
3 | cursor: pointer;
4 | display: flex;
5 | align-items: center;
6 | justify-content: center;
7 | position: relative;
8 | width: 24px;
9 | height: 24px;
10 | border-radius: @border-radius-small;
11 | &:hover {
12 | color: var(--codeplayer-brand-hover);
13 | // background-color: @fill;
14 | }
15 | &:active {
16 | color: var(--codeplayer-brand-active);
17 | // background-color: @fill;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/docs/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | // https://vitepress.dev/guide/custom-theme
2 | import { h } from 'vue';
3 | import Theme from 'vitepress/theme';
4 | // import HomeHeroAfter from '../components/home-hero-after.vue';
5 | import './style.scss';
6 |
7 | export default {
8 | ...Theme,
9 | Layout: () => {
10 | return h(Theme.Layout, null, {
11 | // 'home-features-after': () => h(HomeHeroAfter),
12 | // https://vitepress.dev/guide/extending-default-theme#layout-slots
13 | });
14 | },
15 | enhanceApp({ app, router, siteData }) {
16 | // ...
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/packages/core/src/compiler/transform/plugins/transform-css.ts:
--------------------------------------------------------------------------------
1 | import { Hooks, ComplierPluginParams } from '@/compiler/type';
2 |
3 | export async function transformCSS(
4 | params: ComplierPluginParams
5 | ): Promise {
6 | const { fileMap } = params;
7 | const files = Object.keys(fileMap).map((filename) => fileMap[filename]);
8 |
9 | await Promise.all(
10 | files
11 | .filter((file) => file.filename.endsWith('.css'))
12 | .map((file) => {
13 | const { code } = file;
14 |
15 | file.compiled.css = code;
16 | })
17 | );
18 |
19 | return;
20 | }
21 |
22 | export default function (hooks: Hooks) {
23 | hooks.hook('transform', transformCSS);
24 | }
25 |
--------------------------------------------------------------------------------
/packages/core/src/assets/js.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/core/src/utils/message.ts:
--------------------------------------------------------------------------------
1 | let messageRef: HTMLDivElement | null = null;
2 |
3 | export const message = (
4 | text: string,
5 | options?: { duration?: number; type?: 'info' | 'success' | 'danger' }
6 | ) => {
7 | if (messageRef) {
8 | messageRef.remove();
9 | messageRef = null;
10 | }
11 | messageRef = document.createElement('div');
12 | messageRef.innerText = text;
13 | messageRef.classList.add('codeplayer-message');
14 | messageRef.classList.add('codeplayer-message-' + options?.type || 'info');
15 | document.body.append(messageRef);
16 | setTimeout(() => {
17 | if (messageRef) {
18 | messageRef.remove();
19 | messageRef = null;
20 | }
21 | }, options?.duration || 3000);
22 | };
23 |
--------------------------------------------------------------------------------
/packages/website/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "moduleResolution": "Node",
7 | "strict": true,
8 | "jsx": "preserve",
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "esModuleInterop": true,
12 | "lib": ["ESNext", "DOM"],
13 | "skipLibCheck": true,
14 | "noEmit": true,
15 | "declaration": true,
16 | "paths": {
17 | "@/*": ["./src/*"]
18 | }
19 | },
20 | "include": [
21 | "src/**/*.ts",
22 | "src/*.d.ts",
23 | "src/**/*.tsx",
24 | "src/**/*.vue",
25 | "src/**/*.ce.vue"
26 | ],
27 | "references": [{ "path": "./tsconfig.node.json" }]
28 | }
29 |
--------------------------------------------------------------------------------
/packages/core/src/constant/tooltip.ts:
--------------------------------------------------------------------------------
1 | export const TooltipText = {
2 | AddFile: 'Add a new file',
3 | RenameFile: 'Rename',
4 | DeleteFile: 'Delete',
5 | isEntry: 'Entry File',
6 | SetEntry: 'Set to entry',
7 | ToggleFiles: (show: boolean) => `${show ? 'Hide' : 'Show'} FileBar`,
8 | ToggleCode: (show: boolean) => `${show ? 'Hide' : 'Show'} Code Editor`,
9 | Settings: 'Settings',
10 | ToggleWebPreview: (show: boolean) => `${show ? 'Hide' : 'Show'} Web Preview`,
11 | RefreshWebPreview: 'Refresh Web Preview',
12 | SwapLayout: 'Swap the position of CodeEditor and WebPreview',
13 | CopyCode: 'Copy code to clipboard',
14 | Share: 'Share the page',
15 | Docs: 'View documents of codeplayer',
16 | Github: 'View codeplayer on github',
17 | };
18 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "moduleResolution": "Node",
7 | "strict": true,
8 | "jsx": "preserve",
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "esModuleInterop": true,
12 | "lib": ["ESNext", "DOM"],
13 | "skipLibCheck": true,
14 | "noEmit": true,
15 | "declaration": true,
16 | "declarationDir": "./types",
17 | "paths": {
18 | "@/*": ["./src/*"]
19 | }
20 | },
21 | "include": [
22 | "src/**/*.ts",
23 | "src/*.d.ts",
24 | "src/**/*.tsx",
25 | "src/**/*.vue",
26 | "src/**/*.ce.vue"
27 | ],
28 | "references": [{ "path": "./tsconfig.node.json" }]
29 | }
30 |
--------------------------------------------------------------------------------
/packages/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | # https://vitepress.dev/reference/default-theme-home-page
3 | layout: home
4 |
5 | hero:
6 | name: 'CodePlayer'
7 | text: '超快的 WebIDE'
8 | tagline: 一款轻量、免登录、极快运行速度的 WebIDE
9 | image:
10 | src: /logo.svg
11 | alt: logo
12 | actions:
13 | - theme: brand
14 | text: 快速开始
15 | link: /guide/start
16 | - theme: alt
17 | text: 加入用户群
18 | link: /more/feedback
19 |
20 | features:
21 | - icon: 🚀
22 | title: 极致的运行速度
23 | details: 能在 3 秒内完成依赖下载、代码编译及运行等全部流程,速度远超 CodeSandbox、Stackblitz 等同类型产品
24 | - icon: 📖
25 | title: 免登录直接使用
26 | details: 无需注册登录,直接在线编写 demo,demo 文件及代码将通过 Hash 和压缩后自动同步至 url 以进行保存及分享
27 | - icon: 🎨
28 | title: 广泛的语法支持
29 | details: 支持 html、javascript、typescript、react、vue、css、less、sass 等多种 web 相关的代码编写和运行
30 | ---
31 |
--------------------------------------------------------------------------------
/packages/core/src/utils/str.ts:
--------------------------------------------------------------------------------
1 | import { zlibSync, unzlibSync, strToU8, strFromU8 } from 'fflate';
2 |
3 | // 将 utf8 转换为压缩字符串
4 | export function utoa(data: string): string {
5 | const buffer = strToU8(data);
6 | const zipped = zlibSync(buffer, { level: 9 });
7 | const binary = strFromU8(zipped, true);
8 | return btoa(binary);
9 | }
10 |
11 | // 将压缩字符串转换为utf8
12 | export function atou(base64: string): string {
13 | const binary = atob(base64);
14 |
15 | // zlib header (x78), level 9 (xDA)
16 | if (binary.startsWith('\x78\xDA')) {
17 | const buffer = strToU8(binary, true);
18 | const unzipped = unzlibSync(buffer);
19 | return strFromU8(unzipped);
20 | }
21 |
22 | // old unicode hacks for backward compatibility
23 | // https://base64.guru/developers/javascript/examples/unicode-strings
24 | return decodeURIComponent(escape(binary));
25 | }
26 |
--------------------------------------------------------------------------------
/packages/core/src/components/index.less:
--------------------------------------------------------------------------------
1 | @import './splitter/index.less';
2 | @import './file-bar/index.less';
3 | @import './preview/index.less';
4 | .codeplayer-container {
5 | border-radius: 4px;
6 | display: flex;
7 | flex-direction: column;
8 | ::-webkit-scrollbar {
9 | background-color: @bg-layout;
10 | width: 8px;
11 | height: 8px;
12 | // width: 36px;
13 | }
14 | ::-webkit-scrollbar-thumb {
15 | background-color: @border-color;
16 | border-radius: @border-radius-circle;
17 | &:hover {
18 | background-color: @border-color-hover;
19 | }
20 | &:active {
21 | background-color: @border-color-active;
22 | }
23 | }
24 | .main-content {
25 | display: flex;
26 | position: relative;
27 | height: calc(100% - 28px);
28 | width: 100%;
29 | order: 2;
30 | }
31 | .main-content-top {
32 | order: 1;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/core/src/compiler/transform/plugins/index.ts:
--------------------------------------------------------------------------------
1 | import transformTsPlugin from './transform-ts';
2 | import transformVue3Plugin from './transform-vue3';
3 | import transformVue2Plugin from './transform-vue2';
4 | import transformReactPlugin from './transform-react';
5 | import transformCssPlugin from './transform-css';
6 | import transformScssPlugin from './transform-scss';
7 | import transformLessPlugin from './transform-less';
8 | import compileModulePlugin from '@/compiler/plugins/module-factory';
9 | import emitHtmlPlugin from '@/compiler/plugins/emit-html';
10 |
11 | export const builtInPlugins = (vueVersion = 3) => [
12 | transformTsPlugin,
13 | vueVersion.toString() === '2' ? transformVue2Plugin : transformVue3Plugin,
14 | transformReactPlugin,
15 | transformCssPlugin,
16 | transformScssPlugin,
17 | transformLessPlugin,
18 | compileModulePlugin,
19 | emitHtmlPlugin,
20 | ];
21 |
--------------------------------------------------------------------------------
/packages/website/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | CodePlayer
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/packages/core/src/compiler/transform/plugins/transform-less.ts:
--------------------------------------------------------------------------------
1 | import less from 'less';
2 | import { Hooks, ComplierPluginParams } from '@/compiler/type';
3 |
4 | export async function transformLess(
5 | params: ComplierPluginParams
6 | ): Promise {
7 | const { fileMap } = params;
8 | const files = Object.keys(fileMap).map((filename) => fileMap[filename]);
9 | const errors: Error[] = [];
10 |
11 | await Promise.all(
12 | files
13 | .filter((file) => file.filename.endsWith('.less'))
14 | .map(async (file) => {
15 | let { code } = file;
16 |
17 | try {
18 | code = (await less.render(code)).css;
19 | file.compiled.css = code;
20 | } catch (error) {
21 | errors.push(error as Error);
22 | }
23 | })
24 | );
25 |
26 | return errors.length ? errors : undefined;
27 | }
28 |
29 | export default function (hooks: Hooks) {
30 | hooks.hook('transform', transformLess);
31 | }
32 |
--------------------------------------------------------------------------------
/packages/core/src/compiler/type.ts:
--------------------------------------------------------------------------------
1 | import { Hookable } from 'hookable';
2 | import * as monaco from 'monaco-editor';
3 |
4 | export type Hooks = Hookable, string>;
5 |
6 | export type Plugin = (hooks: Hooks) => void;
7 |
8 | export interface ComplierPluginParams {
9 | fileMap: Record;
10 | result: { errors: Error[] };
11 | entry: string;
12 | iframe: HTMLIFrameElement;
13 | render: boolean;
14 | }
15 |
16 | export interface ComplierPluginResult {
17 | modules: Array;
18 | styles: Array;
19 | links: Array;
20 | html: Array;
21 | }
22 |
23 | export class File {
24 | filename: string;
25 | code: string;
26 | compiled = {
27 | js: '',
28 | css: '',
29 | };
30 | editorViewState: monaco.editor.ICodeEditorViewState | null;
31 |
32 | constructor(filename: string, code = '') {
33 | this.filename = filename;
34 | this.code = code;
35 | this.editorViewState = null;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/website/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import vue from '@vitejs/plugin-vue';
3 | import path from 'path';
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | base:
8 | process.env.NODE_ENV == 'production'
9 | ? 'https://cdn.jsdelivr.net/gh/zh-lx/codeplayer/packages/website/dist/'
10 | : './',
11 | plugins: [vue()],
12 | resolve: {
13 | alias: {
14 | '@': path.resolve(__dirname, '../core/src'),
15 | '~@': path.resolve(__dirname, '../core/src'),
16 | path: 'path-browserify',
17 | },
18 | },
19 | css: {
20 | preprocessorOptions: {
21 | less: {
22 | additionalData: `@import "../core/src/style/index.less";`,
23 | },
24 | },
25 | },
26 | server: {
27 | host: '0.0.0.0',
28 | fs: {
29 | strict: false,
30 | },
31 | },
32 |
33 | build: {
34 | rollupOptions: {
35 | output: {
36 | assetFileNames: () => `[name]-[hash].[ext]`,
37 | },
38 | },
39 | },
40 | });
41 |
--------------------------------------------------------------------------------
/packages/core/src/assets/ts.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/core/src/compiler/transform/plugins/transform-scss.ts:
--------------------------------------------------------------------------------
1 | import { Hooks, ComplierPluginParams } from '@/compiler/type';
2 | import { compileString } from 'sass';
3 |
4 | export async function transformSaSS(
5 | params: ComplierPluginParams
6 | ): Promise {
7 | const { fileMap } = params;
8 | const files = Object.keys(fileMap).map((filename) => fileMap[filename]);
9 | const errors: Error[] = [];
10 |
11 | await Promise.all(
12 | files
13 | .filter(
14 | ({ filename }) =>
15 | filename.endsWith('.sass') || filename.endsWith('.scss')
16 | )
17 | .map(async (file) => {
18 | let { code } = file;
19 |
20 | try {
21 | code = (await compileString(code)).css;
22 | file.compiled.css = code;
23 | } catch (error) {
24 | errors.push(error as Error);
25 | }
26 | })
27 | );
28 |
29 | return errors.length ? errors : undefined;
30 | }
31 |
32 | export default function (hooks: Hooks) {
33 | hooks.hook('transform', transformSaSS);
34 | }
35 |
--------------------------------------------------------------------------------
/packages/website/dist/assets/index-eba15397.js:
--------------------------------------------------------------------------------
1 | import{d as i,c as p,u as a,o as l,E as u,a as f}from"./index-8e8c1687-18fcf4d9.js";(function(){const o=document.createElement("link").relList;if(o&&o.supports&&o.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))n(e);new MutationObserver(e=>{for(const t of e)if(t.type==="childList")for(const c of t.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&n(c)}).observe(document,{childList:!0,subtree:!0});function r(e){const t={};return e.integrity&&(t.integrity=e.integrity),e.referrerpolicy&&(t.referrerPolicy=e.referrerpolicy),e.crossorigin==="use-credentials"?t.credentials="include":e.crossorigin==="anonymous"?t.credentials="omit":t.credentials="same-origin",t}function n(e){if(e.ep)return;e.ep=!0;const t=r(e);fetch(e.href,t)}})();const d=i({__name:"App",setup(s){const o={appType:"vue3"};return(r,n)=>(l(),p(a(u),{options:o,class:"codeplayer-container"}))}});const _=(s,o)=>{const r=s.__vccOpts||s;for(const[n,e]of o)r[n]=e;return r},m=_(d,[["__scopeId","data-v-230cf52d"]]),y=f(m);y.mount("#app");
2 |
--------------------------------------------------------------------------------
/packages/core/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import vue from '@vitejs/plugin-vue';
3 | import dts from 'vite-plugin-dts';
4 | import path from 'path';
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | plugins: [
9 | vue(),
10 | dts({
11 | outDir: './types',
12 | }),
13 | ],
14 | resolve: {
15 | alias: {
16 | '@': path.resolve(__dirname, './src'),
17 | '~@': path.resolve(__dirname, './src'),
18 | path: 'path-browserify',
19 | },
20 | },
21 | css: {
22 | preprocessorOptions: {
23 | less: {
24 | additionalData: `@import "src/style/index.less";`,
25 | },
26 | },
27 | },
28 | define: {
29 | 'process.env.NODE_ENV': '"production"',
30 | },
31 | base: './',
32 | build: {
33 | emptyOutDir: true,
34 | lib: {
35 | entry: {
36 | index: './src/index.ts',
37 | },
38 | formats: ['es', 'cjs'],
39 | fileName: '[name]',
40 | },
41 | rollupOptions: {
42 | external: [/^vue/],
43 | output: {},
44 | },
45 | },
46 | });
47 |
--------------------------------------------------------------------------------
/packages/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "website",
3 | "version": "0.0.1",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "vue-tsc && vite build && node ./scripts/index.js",
7 | "preview": "vite preview"
8 | },
9 | "devDependencies": {
10 | "@babel/standalone": "^7.20.11",
11 | "@babel/types": "^7.20.7",
12 | "@types/hash-sum": "^1.0.0",
13 | "@types/less": "^3.0.3",
14 | "@types/node": "^18.11.18",
15 | "@vitejs/plugin-vue": "^4.0.0",
16 | "@vue/compiler-sfc": "^3.2.45",
17 | "fflate": "^0.7.4",
18 | "hash-sum": "^2.0.0",
19 | "hookable": "^5.5.3",
20 | "less": "^4.2.0",
21 | "monaco-editor-core": "0.41.0",
22 | "monaco-volar": "^0.4.0",
23 | "rollup-plugin-visualizer": "^5.9.0",
24 | "sass": "^1.66.1",
25 | "sucrase": "^3.29.0",
26 | "tslib": "^2.5.0",
27 | "typescript": "^4.9.3",
28 | "vite": "^4.1.0",
29 | "vue": "^3.2.45",
30 | "vue-tsc": "^1.0.24"
31 | },
32 | "dependencies": {
33 | "codeplayer": "0.0.1-beta.2",
34 | "path-browserify": "^1.0.1",
35 | "tippy.js": "^6.3.7"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/website/dist/assets/azcli-deb61577-f1f7c576.js:
--------------------------------------------------------------------------------
1 | /*!-----------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Version: 0.41.0(38e1e3d097f84e336c311d071a9ffb5191d4ffd1)
4 | * Released under the MIT license
5 | * https://github.com/microsoft/monaco-editor/blob/main/LICENSE.txt
6 | *-----------------------------------------------------------------------------*/var e={comments:{lineComment:"#"}},t={defaultToken:"keyword",ignoreCase:!0,tokenPostfix:".azcli",str:/[^#\s]/,tokenizer:{root:[{include:"@comment"},[/\s-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":{token:"key.identifier",next:"@type"}}}],[/^-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":{token:"key.identifier",next:"@type"}}}]],type:[{include:"@comment"},[/-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":"key.identifier"}}],[/@str+\s*/,{cases:{"@eos":{token:"string",next:"@popall"},"@default":"string"}}]],comment:[[/#.*$/,{cases:{"@eos":{token:"comment",next:"@popall"}}}]]}};export{e as conf,t as language};
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 zhoulixiang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/core/src/compiler/transform/plugins/transform-react.ts:
--------------------------------------------------------------------------------
1 | import { transform } from 'sucrase';
2 | import { Hooks, ComplierPluginParams } from '@/compiler/type';
3 |
4 | export async function transformReact(
5 | params: ComplierPluginParams
6 | ): Promise {
7 | const { fileMap } = params;
8 | const files = Object.keys(fileMap).map((filename) => fileMap[filename]);
9 | const errors: Error[] = [];
10 |
11 | await Promise.all(
12 | files
13 | .filter(
14 | ({ filename }) => filename.endsWith('.tsx') || filename.endsWith('.jsx')
15 | )
16 | .map(async (file) => {
17 | let { code } = file;
18 |
19 | try {
20 | code = await transform(code, {
21 | transforms: ['typescript', 'jsx'],
22 | production: true,
23 | }).code;
24 | file.compiled.js = code;
25 | } catch (error) {
26 | errors.push(error as Error);
27 | }
28 | })
29 | );
30 |
31 | return errors.length ? errors : undefined;
32 | }
33 |
34 | export default function (hooks: Hooks) {
35 | hooks.hook('transform', transformReact);
36 | }
37 |
--------------------------------------------------------------------------------
/packages/docs/reference/deploy.md:
--------------------------------------------------------------------------------
1 | # 组件式使用
2 |
3 | codeplayer 本身是一个 vue3 组件,因此支持在 vue3 项目中以组件的方式使用,以便于进入进行私有化部署。
4 |
5 | ## 安装
6 |
7 | 选择一个你喜欢的包管理器进行安装:
8 |
9 | ```sh
10 | npm i codeplayer
11 | # or
12 | yarn add codeplayer
13 | # or
14 | pnpm add codeplayer
15 | ```
16 |
17 | ## 使用
18 |
19 | 如下代码是一个引入 `codeplayer` 并使用的示例:
20 |
21 | ::: code-group
22 |
23 | ```html [App.vue]
24 |
25 |
26 |
27 |
28 |
35 |
36 |
42 | ```
43 |
44 | ```ts [main.ts]
45 | import { createApp } from 'vue';
46 | import 'codeplayer/dist/style.css';
47 |
48 | import App from './App.vue';
49 |
50 | const app = createApp(App);
51 | app.mount('#app');
52 | ```
53 |
54 | :::
55 |
56 | ## 配置
57 |
58 | 可以通过 `options` 参数对 `codeplayer` 进行配置,`options` 参数的属性同[指南-配置](/guide/config) 一节。
59 |
--------------------------------------------------------------------------------
/packages/core/src/constant/index.ts:
--------------------------------------------------------------------------------
1 | export const COMP_IDENTIFIER = `__sfc__`;
2 | export const modulesKey = `__modules__`;
3 | export const exportKey = `__export__`;
4 | export const dynamicImportKey = `__dynamic_import__`;
5 | export const moduleKey = `__module__`;
6 | export const nextKey = '__next__';
7 | export const scriptRE = /
17 |