chore: 迁移到 vite

This commit is contained in:
pipipi-pikachu 2024-01-03 19:56:16 +08:00
parent 1e03c658ae
commit 7d26335d38
37 changed files with 3230 additions and 21013 deletions

View File

@ -1,3 +0,0 @@
> 1%
last 2 versions
not dead

View File

@ -1,20 +1,15 @@
// https://eslint.org/docs/rules/ /* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
const isProduction = process.env.NODE_ENV === 'production'
module.exports = { module.exports = {
root: true, root: true,
env: {
node: true,
'vue/setup-compiler-macros': true,
},
extends: [ extends: [
'plugin:vue/vue3-essential', 'plugin:vue/vue3-essential',
'eslint:recommended', 'eslint:recommended',
'@vue/typescript/recommended', '@vue/eslint-config-typescript'
], ],
parserOptions: { parserOptions: {
ecmaVersion: 2020, ecmaVersion: 'latest'
}, },
rules: { rules: {
'curly': ['error', 'multi-line'], 'curly': ['error', 'multi-line'],
@ -64,9 +59,9 @@ module.exports = {
'no-eval': 'error', 'no-eval': 'error',
'no-var': 'error', 'no-var': 'error',
'no-with': 'error', 'no-with': 'error',
'no-alert': isProduction ? 'error' : 'warn', 'no-alert': 'warn',
'no-console': isProduction ? 'error' : 'warn', 'no-console': 'warn',
'no-debugger': isProduction ? 'error' : 'warn', 'no-debugger': 'error',
'@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/ban-types': ['error', { '@typescript-eslint/ban-types': ['error', {
'extendDefaults': true, 'extendDefaults': true,
@ -78,16 +73,5 @@ module.exports = {
'@typescript-eslint/consistent-type-imports': 'error', '@typescript-eslint/consistent-type-imports': 'error',
'vue/multi-word-component-names': 'off', 'vue/multi-word-component-names': 'off',
'vue/no-reserved-component-names': 'off', 'vue/no-reserved-component-names': 'off',
}, }
overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
],
env: {
jest: true,
},
},
],
} }

28
.gitignore vendored
View File

@ -1,22 +1,30 @@
.DS_Store # Logs
node_modules logs
/dist *.log
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files # Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea .idea
.vscode
*.suo *.suo
*.ntvs* *.ntvs*
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
*.tsbuildinfo

4
.husky/commit-msg Normal file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install commitlint -e $HUSKY_GIT_PARAMS

View File

@ -1,58 +0,0 @@
// https://stylelint.io/user-guide/rules/list
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-html/html',
'stylelint-config-html/vue',
],
rules: {
'indentation': 2,
'max-nesting-depth': 5,
'string-no-newline': true,
'string-quotes': 'single',
'color-hex-case': 'lower',
'color-hex-length': 'short',
'color-no-invalid-hex': true,
'font-weight-notation': 'numeric',
'function-calc-no-unspaced-operator': true,
'function-url-quotes': 'never',
'block-no-empty': true,
'no-eol-whitespace': true,
'block-opening-brace-newline-after': 'always',
'block-opening-brace-space-before': 'always',
'declaration-block-no-duplicate-properties': [true, {
ignoreProperties: ['overflow'],
}],
'declaration-block-semicolon-newline-after': 'always',
'declaration-block-trailing-semicolon': 'always',
'selector-pseudo-element-colon-notation': 'double',
'declaration-block-no-redundant-longhand-properties': [true, {
ignoreShorthands: ['inset'],
}],
'alpha-value-notation': 'number',
'max-empty-lines': null,
'no-missing-end-of-source-newline': null,
'no-descending-specificity': null,
'at-rule-empty-line-before': null,
'at-rule-no-unknown': null,
'rule-empty-line-before': null,
'number-leading-zero': null,
'selector-pseudo-element-no-unknown': null,
'selector-list-comma-newline-after': null,
'no-invalid-double-slash-comments': null,
'color-function-notation': null,
'font-family-no-missing-generic-family-keyword': null,
'selector-class-pattern': null,
'custom-property-pattern': null,
'keyframes-name-pattern': null,
'shorthand-property-no-redundant-values': null,
},
overrides: [
{
files: ['*.scss', '**/*.scss'],
customSyntax: 'postcss-scss'
},
],
}

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

@ -0,0 +1,7 @@
{
"recommendations": [
"Vue.volar",
"Vue.vscode-typescript-vue-plugin",
"dbaeumer.vscode-eslint"
]
}

View File

@ -22,7 +22,7 @@
# 🎨 PPTist # 🎨 PPTist
> 一个基于 Vue3.x + TypeScript 的在线演示文稿(幻灯片)应用,还原了大部分 Office PowerPoint 常用功能,支持 文字、图片、形状、线条、图表、表格、视频、音频、公式 几种最常用的元素类型,每一种元素都拥有高度可编辑能力,同时支持丰富的快捷键和右键菜单,力求还原桌面应用级体验。支持导出本地 PPTX 文件,支持移动端基础编辑和预览,支持 PWA。您可以在此基础上搭建自己的在线幻灯片应用。 > 一个基于 Vue3.x + TypeScript 的在线演示文稿(幻灯片)应用,还原了大部分 Office PowerPoint 常用功能,支持 文字、图片、形状、线条、图表、表格、视频、音频、公式 几种最常用的元素类型,每一种元素都拥有高度可编辑能力,同时支持丰富的快捷键和右键菜单,力求还原桌面应用级体验。支持导出本地 PPTX 文件,支持移动端基础编辑和预览。您可以在此基础上搭建自己的在线幻灯片应用。
<b>在线体验地址👉:[https://pipipi-pikachu.github.io/PPTist/](https://pipipi-pikachu.github.io/PPTist/)</b> <b>在线体验地址👉:[https://pipipi-pikachu.github.io/PPTist/](https://pipipi-pikachu.github.io/PPTist/)</b>
@ -39,7 +39,7 @@
``` ```
npm install npm install
npm run serve npm run dev
``` ```
@ -166,18 +166,6 @@ npm run serve
- 播放预览 - 播放预览
# 📅 后续重点规划
- 项目工程升级:
- 升级 TypeScript v5.x
- 将 npm 更换到 pnpm
- 将 Vue CLI 更换到 Vite 生态;
- 支持 Iframe 引用;
- 组合元素重构:能够支持组合元素进行旋转、缩放、整体执行动画等;
- 支持多屏放映;
- 导出HTML文件
- 补充注释/文档;
- 导入PPTX持续优化
# 🎯 开发 # 🎯 开发
目前没有完整的开发文档,但下面这些文档可能会对你有一些帮助: 目前没有完整的开发文档,但下面这些文档可能会对你有一些帮助:
- [项目目录与数据结构](https://github.com/pipipi-pikachu/PPTist/blob/master/doc/DirectoryAndData.md) - [项目目录与数据结构](https://github.com/pipipi-pikachu/PPTist/blob/master/doc/DirectoryAndData.md)
@ -185,36 +173,17 @@ npm run serve
- [如何自定义一个元素](https://github.com/pipipi-pikachu/PPTist/blob/master/doc/CustomElement.md) - [如何自定义一个元素](https://github.com/pipipi-pikachu/PPTist/blob/master/doc/CustomElement.md)
# 💻 贡献代码
首先感谢关注本项目的朋友,非常欢迎每一位对本项目感兴趣的朋友贡献代码。
### 具体参考如下:
- fork 源码,下载到本地并运行项目
- 添加/修改代码
- <b>对相关改动进行全面的自我测试(这非常重要,否则作者对提交代码进行阅读、测试、修复的成本很可能远大于自己重新实现)</b>
- 确认无误后提交修改到 Github
- 提交 Pull Request
### 另外需要注意的是:
- 每一次 Pull Request 都不应该提交过多的代码,且务必说明本次改动的具体目的,例如:修复了某个 bug、优化了某个方法 等,方便进行 Code Review
- 对于 bug 的修复,应该将本次 Pull Request 和相对应 bug 的 issue 关联起来,让别人知道该问题已经被修复;
- 对于较大的新功能,你需要先提交 Issues例如 “添加 XXX 功能”,确认该功能有被添加的必要后,再开始工作;
- 对于一些主观的样式、交互逻辑调整:如颜色、图标的使用,某些预设配置的增减修改等,一般不予通过。但可以在 Issues 中进行讨论;
- 其他如简单的代码优化、文档修正等,只要修改合理都会被接受。
在此感谢每一位贡献者。
# 📄 版权声明/开源协议 # 📄 版权声明/开源协议
[AGPL-3.0 License](https://github.com/pipipi-pikachu/PPTist/blob/master/LICENSE) | Copyright © 2020-PRESENT [pipipi-pikachu](https://github.com/pipipi-pikachu) [AGPL-3.0 License](https://github.com/pipipi-pikachu/PPTist/blob/master/LICENSE) | Copyright © 2020-PRESENT [pipipi-pikachu](https://github.com/pipipi-pikachu)
# 🧮 商业用途 # 🧮 商业用途
- 如果你希望将本项目商用盈利,我希望你能严格遵循 AGPL-3.0 协议,回馈开源社区; - 如果你希望将本项目商用盈利,我希望你能尊重开源,严格遵循 AGPL-3.0 协议,回馈开源社区;
- 此外,如果你真的需要闭源商用,无法执行 AGPL-3.0 协议,可以选择: - 如果你因为任何原因,必须要闭源商用,无法执行 AGPL-3.0 协议,可以选择:
1. 使用 [Apache 2.0 LICENSE 版本](https://github.com/pipipi-pikachu/PPTist/archive/f1a35bb8e045124e37dcafd6acbf40b4531b69aa.zip)(建议); 1. 使用早期的 [Apache 2.0 LICENSE 版本](https://github.com/pipipi-pikachu/PPTist/archive/f1a35bb8e045124e37dcafd6acbf40b4531b69aa.zip)(建议);
2. 成为项目的贡献者,大致包括: 2. 成为项目的贡献者,大致包括:
- 你的代码被本项目作为依赖引用; - 你的代码被本项目作为依赖引用;
- 你提交的 PR 被本项目合并(仅限有价值的,不包括简单的错别字或拼写错误修改等); - 你给本项目提交过重要的 PR 并且被合并;
- 你参与过本项目的设计、实现(也包括对各种功能/模块的实现或Bug的修复提供了有价值的思路 - 你参与过本项目的设计,也包括对各种功能/模块的实现或Bug的修复提供了有价值的思路
- 注:先违反协议后再成为贡献者不在此范围; - 注1满足条件的单位在商用前请先联系作者确认资格避免误会。
3. 邮件联系作者付费商用(不建议,我更希望你一起加入到开源社区中来); - 注2先违反协议后再成为贡献者不在此项范围
3. 邮件联系作者付费商用(如果可以,我更希望你一起加入到开源社区中来);

View File

@ -1,16 +0,0 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
],
plugins: [
[
'import',
{
libraryName: '@icon-park/vue-next',
libraryDirectory: 'es/icons',
camel2DashComponentName: false,
},
'iconPark',
],
],
}

View File

@ -10,9 +10,7 @@
* style 代码格式修改 * style 代码格式修改
* test 测试用例修改 * test 测试用例修改
*/ */
/* eslint-env node */
module.exports = { module.exports = {
extends: [ extends: ['@commitlint/config-conventional'],
'@commitlint/config-conventional',
],
} }

2
env.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
// eslint-disable-next-line spaced-comment
/// <reference types="vite/client" />

View File

@ -1,13 +1,13 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="utf-8" /> <meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width,initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="基于 Vue3.x + TypeScript 的在线演示文稿(幻灯片)应用,还原了大部分 Office PowerPoint 常用功能实现在线PPT的编辑、演示。支持导出PPT文件。" /> <meta name="description" content="基于 Vue3.x + TypeScript 的在线演示文稿(幻灯片)应用,还原了大部分 Office PowerPoint 常用功能实现在线PPT的编辑、演示。支持导出PPT文件。" />
<meta name="keywords" content="ppt,powerpoint,office powerpoint,在线ppt,幻灯片,演示文稿,ppt在线制作,Vue3,TypeScript" /> <meta name="keywords" content="ppt,powerpoint,office powerpoint,在线ppt,幻灯片,演示文稿,ppt在线制作,Vue3,TypeScript" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>PPTIST - 在线演示文稿</title> <title>PPTIST - 在线演示文稿</title>
<style> <style>
@ -47,18 +47,16 @@
</style> </style>
</head> </head>
<body> <body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"> <div id="app">
<div class="first-screen-loading"> <div class="first-screen-loading">
<div class="first-screen-loading-spinner"></div> <div class="first-screen-loading-spinner"></div>
<div class="first-screen-loading-text">正在加载中,请稍等 ...</div> <div class="first-screen-loading-text">正在加载中,请稍等 ...</div>
</div> </div>
</div> </div>
<script type="module" src="/src/main.ts"></script>
</body>
<script> <script>
document.oncontextmenu = e => e.preventDefault() document.oncontextmenu = e => e.preventDefault()
</script> </script>
</body>
</html> </html>

23472
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,85 +1,73 @@
{ {
"name": "pptist", "name": "pptist",
"version": "0.0.1", "version": "1.0.0",
"private": true, "private": true,
"type": "module",
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "dev": "vite",
"build": "vue-cli-service build", "build": "run-p type-check \"build-only {@}\" --",
"lint": "vue-cli-service lint", "preview": "vite preview",
"build:fonts": "node scripts/build-fonts" "build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"prepare": "husky install"
}, },
"dependencies": { "dependencies": {
"@icon-park/vue-next": "^1.4.2", "@icon-park/vue-next": "^1.4.2",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"chartist": "^1.3.0", "chartist": "^1.3.0",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"core-js": "^3.8.3", "crypto-js": "^4.2.0",
"crypto-js": "^4.0.0",
"dexie": "3.0.3", "dexie": "3.0.3",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"hfmath": "0.0.2", "hfmath": "^0.0.2",
"html-to-image": "^1.11.11", "html-to-image": "^1.11.11",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"nanoid": "^4.0.2", "nanoid": "^5.0.4",
"number-precision": "^1.6.0", "number-precision": "^1.6.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"pptxgenjs": "^3.12.0", "pptxgenjs": "^3.12.0",
"pptxtojson": "^0.1.2", "pptxtojson": "^0.1.2",
"prosemirror-commands": "^1.3.0", "prosemirror-commands": "^1.5.2",
"prosemirror-dropcursor": "^1.6.0", "prosemirror-dropcursor": "^1.8.1",
"prosemirror-gapcursor": "^1.3.1", "prosemirror-gapcursor": "^1.3.2",
"prosemirror-history": "^1.3.0", "prosemirror-history": "^1.3.2",
"prosemirror-inputrules": "^1.2.0", "prosemirror-inputrules": "^1.3.0",
"prosemirror-keymap": "^1.2.0", "prosemirror-keymap": "^1.2.2",
"prosemirror-model": "^1.18.1", "prosemirror-model": "^1.19.4",
"prosemirror-schema-basic": "^1.2.0", "prosemirror-schema-basic": "^1.2.2",
"prosemirror-schema-list": "^1.2.1", "prosemirror-schema-list": "^1.3.0",
"prosemirror-state": "^1.4.1", "prosemirror-state": "^1.4.3",
"prosemirror-view": "^1.27.2", "prosemirror-view": "^1.32.7",
"register-service-worker": "^1.7.2",
"svg-arc-to-cubic-bezier": "^3.2.0", "svg-arc-to-cubic-bezier": "^3.2.0",
"svg-pathdata": "^6.0.3", "svg-pathdata": "^6.0.3",
"tinycolor2": "^1.6.0", "tinycolor2": "^1.6.0",
"tippy.js": "^6.3.7", "tippy.js": "^6.3.7",
"vue": "^3.3.7", "vue": "^3.3.11",
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^17.6.6", "@commitlint/cli": "^18.4.3",
"@commitlint/config-conventional": "^17.6.6", "@commitlint/config-conventional": "^18.4.3",
"@types/crypto-js": "^4.1.1", "@rushstack/eslint-patch": "^1.3.3",
"@types/file-saver": "^2.0.5", "@tsconfig/node18": "^18.2.2",
"@types/lodash": "^4.14.181", "@types/crypto-js": "^4.2.1",
"@types/resize-observer-browser": "^0.1.4", "@types/file-saver": "^2.0.7",
"@types/svg-arc-to-cubic-bezier": "^3.2.0", "@types/lodash": "^4.14.202",
"@types/tinycolor2": "^1.4.3", "@types/node": "^18.19.3",
"@typescript-eslint/eslint-plugin": "^5.4.0", "@types/svg-arc-to-cubic-bezier": "^3.2.2",
"@typescript-eslint/parser": "^5.4.0", "@types/tinycolor2": "^1.4.6",
"@vue/cli-plugin-babel": "~5.0.0", "@vitejs/plugin-vue": "^4.5.2",
"@vue/cli-plugin-eslint": "~5.0.0", "@vue/eslint-config-typescript": "^12.0.0",
"@vue/cli-plugin-pwa": "~5.0.0", "@vue/tsconfig": "^0.5.0",
"@vue/cli-plugin-typescript": "~5.0.0", "eslint": "^8.49.0",
"@vue/cli-service": "~5.0.0", "eslint-plugin-vue": "^9.17.0",
"@vue/compiler-sfc": "^3.3.7", "husky": "^8.0.3",
"@vue/eslint-config-typescript": "^11.0.2", "npm-run-all2": "^6.1.1",
"babel-plugin-import": "^1.13.3", "sass": "^1.69.6",
"eslint": "^8.34.0", "typescript": "~5.3.0",
"eslint-plugin-vue": "^9.9.0", "vite": "^5.0.10",
"husky": "8.0.3", "vue-tsc": "^1.8.25"
"postcss-html": "^1.5.0",
"postcss-scss": "^4.0.6",
"sass": "^1.32.13",
"sass-loader": "^8.0.2",
"stylelint": "^15.10.2",
"stylelint-config-html": "^1.1.0",
"stylelint-config-standard": "^34.0.0",
"stylelint-webpack-plugin": "^4.1.1",
"typescript": "~4.7.4"
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,45 +0,0 @@
// 自动获取 src\assets\fonts 路径下的字体文件列表,并替换相关的 SCSS 变量
/* eslint-disable no-console */
const fs = require('fs')
const path = require('path')
const assetsPath = path.join(__dirname, '../src/assets')
const fontVarPath = path.join(assetsPath, 'styles/font.scss')
const fontsPath = path.join(assetsPath, 'fonts')
const scssFontListVar = '$fontList'
const replaceSCSSVariable = (names) => {
fs.readFile(fontVarPath, 'utf-8', (errs, content) => {
if (errs) {
console.error(errs)
process.exit(1)
}
const reg = new RegExp(`(\\${scssFontListVar}:\\s*)(.*)(;.*)`, 'g')
const newContent = content.replace(reg, `$1${names}$3`)
fs.writeFile(fontVarPath, newContent, errs => {
if (errs) {
console.error(errs)
process.exit(1)
}
console.log('自动生成自定义字体列表完成')
})
})
}
fs.readdir(fontsPath, { withFileTypes: true }, (errs, files) => {
if (errs) {
console.error(errs)
process.exit(1)
}
const woff2Fonts = files.filter(({ name }) => name.endsWith('.woff2'))
const fontList = woff2Fonts.map(({ name }) => name.replace('.woff2', ''))
const names = fontList.reduce((result, name, i) => {
if (i === 0) return `'${name}'`
return `${result}, '${name}'`
}, '')
replaceSCSSVariable(names)
})

View File

@ -4,6 +4,8 @@
<Mobile v-else /> <Mobile v-else />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue' import { onMounted } from 'vue'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
@ -23,7 +25,7 @@ const snapshotStore = useSnapshotStore()
const { databaseId } = storeToRefs(mainStore) const { databaseId } = storeToRefs(mainStore)
const { screening } = storeToRefs(useScreenStore()) const { screening } = storeToRefs(useScreenStore())
if (process.env.NODE_ENV === 'production') { if (import.meta.env.MODE !== 'development') {
window.onbeforeunload = () => false window.onbeforeunload = () => false
} }

View File

@ -355,7 +355,7 @@ export default () => {
if (!cell.text) return cell if (!cell.text) return cell
return { return {
...cell, ...cell,
text: cell.text.replaceAll(searchWord.value, replaceWord.value), text: cell.text.replace(new RegExp(searchWord.value, 'g'), replaceWord.value),
} }
}) })
}) })

View File

@ -1,12 +1,10 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import App from './App.vue' import App from './App.vue'
import './registerServiceWorker'
import '@icon-park/vue-next/styles/index.css' import '@icon-park/vue-next/styles/index.css'
import 'prosemirror-view/style/prosemirror.css' import 'prosemirror-view/style/prosemirror.css'
import 'animate.css' import 'animate.css'
import '@/assets/styles/prosemirror.scss' import '@/assets/styles/prosemirror.scss'
import '@/assets/styles/global.scss' import '@/assets/styles/global.scss'
import '@/assets/styles/font.scss' import '@/assets/styles/font.scss'
@ -15,9 +13,7 @@ import Icon from '@/plugins/icon'
import Directive from '@/plugins/directive' import Directive from '@/plugins/directive'
const app = createApp(App) const app = createApp(App)
app.use(Icon) app.use(Icon)
app.use(Directive) app.use(Directive)
app.use(createPinia()) app.use(createPinia())
app.mount('#app') app.mount('#app')

View File

@ -1,32 +0,0 @@
/* eslint-disable no-console */
import { register } from 'register-service-worker'
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready() {
console.log(
'App is being served from cache by a service worker.\n' +
'For more details, visit https://goo.gl/AFskqB'
)
},
registered() {
console.log('Service worker has been registered.')
},
cached() {
console.log('Content has been cached for offline use.')
},
updatefound() {
console.log('New content is downloading.')
},
updated() {
console.log('New content is available; please refresh.')
},
offline() {
console.log('No internet connection found. App is running in offline mode.')
},
error(error) {
console.error('Error during service worker registration:', error)
}
})
}

7
src/shims-vue.d.ts vendored
View File

@ -1,7 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

View File

@ -96,11 +96,11 @@ export const useSlidesStore = defineStore('slides', {
const subColor = tinycolor(fontColor).isDark() ? 'rgba(230, 230, 230, 0.5)' : 'rgba(180, 180, 180, 0.5)' const subColor = tinycolor(fontColor).isDark() ? 'rgba(230, 230, 230, 0.5)' : 'rgba(180, 180, 180, 0.5)'
const layoutsString = JSON.stringify(layouts) const layoutsString = JSON.stringify(layouts)
.replaceAll('{{themeColor}}', themeColor) .replace(/{{themeColor}}/g, themeColor)
.replaceAll('{{fontColor}}', fontColor) .replace(/{{fontColor}}/g, fontColor)
.replaceAll('{{fontName}}', fontName) .replace(/{{fontName}}/g, fontName)
.replaceAll('{{backgroundColor}}', backgroundColor) .replace(/{{backgroundColor}}/g, backgroundColor)
.replaceAll('{{subColor}}', subColor) .replace(/{{subColor}}/g, subColor)
return JSON.parse(layoutsString) return JSON.parse(layoutsString)
}, },

View File

@ -4,7 +4,7 @@ import { lexer } from './lexer'
import { parser } from './parser' import { parser } from './parser'
import { format } from './format' import { format } from './format'
import { toHTML } from './stringify' import { toHTML } from './stringify'
import type { AST } from './types' export type { AST } from './types'
export const toAST = (str: string) => { export const toAST = (str: string) => {
const tokens = lexer(str) const tokens = lexer(str)
@ -12,4 +12,4 @@ export const toAST = (str: string) => {
return format(nodes) return format(nodes)
} }
export { toHTML, AST } export { toHTML }

View File

@ -1,6 +1,98 @@
// @ts-ignore
import { SVGPathData } from 'svg-pathdata' import { SVGPathData } from 'svg-pathdata'
import arcToBezier from 'svg-arc-to-cubic-bezier' import arcToBezier from 'svg-arc-to-cubic-bezier'
type CommandM = {
relative: boolean
type: typeof SVGPathData.MOVE_TO
x: number
y: number
}
type CommandL = {
relative: boolean
type: typeof SVGPathData.LINE_TO
x: number
y: number
}
type CommandH = {
relative: boolean
type: typeof SVGPathData.HORIZ_LINE_TO
x: number
}
type CommandV = {
relative: boolean
type: typeof SVGPathData.VERT_LINE_TO
y: number
}
type CommandZ = {
type: typeof SVGPathData.CLOSE_PATH
}
type CommandQ = {
relative: boolean
type: typeof SVGPathData.QUAD_TO
x1: number
y1: number
x: number
y: number
}
type CommandT = {
relative: boolean
type: typeof SVGPathData.SMOOTH_QUAD_TO
x: number
y: number
}
type CommandC = {
relative: boolean
type: typeof SVGPathData.CURVE_TO
x1: number
y1: number
x2: number
y2: number
x: number
y: number
}
type CommandS = {
relative: boolean
type: typeof SVGPathData.SMOOTH_CURVE_TO
x2: number
y2: number
x: number
y: number
}
type CommandA = {
relative: boolean
type: typeof SVGPathData.ARC
rX: number
rY: number
xRot: number
sweepFlag: 0 | 1
lArcFlag: 0 | 1
x: number
y: number
cX?: number
cY?: number
phi1?: number
phi2?: number
}
type SVGCommand = CommandM | CommandL | CommandH | CommandV | CommandZ | CommandQ | CommandT | CommandC | CommandS | CommandA
declare class SVGPathData {
commands: SVGCommand[]
constructor(content: string | SVGCommand[])
static readonly CLOSE_PATH: 1
static readonly MOVE_TO: 2
static readonly HORIZ_LINE_TO: 4
static readonly VERT_LINE_TO: 8
static readonly LINE_TO: 16
static readonly CURVE_TO: 32
static readonly SMOOTH_CURVE_TO: 64
static readonly QUAD_TO: 128
static readonly SMOOTH_QUAD_TO: 256
static readonly ARC: 512
static readonly LINE_COMMANDS: number
static readonly DRAWING_COMMANDS: number
}
const typeMap = { const typeMap = {
1: 'Z', 1: 'Z',
2: 'M', 2: 'M',

View File

@ -57,7 +57,7 @@
<div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')"> <div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')">
<IconDownload class="icon" /> <IconDownload class="icon" />
</div> </div>
<a class="github-link" href="https://github.com/pipipi-pikachu/PPTist" target="_blank"> <a class="github-link" v-tooltip="'Copyright © 2020-PRESENT pipipi-pikachu'" href="https://github.com/pipipi-pikachu/PPTist" target="_blank">
<div class="menu-item"><IconGithub class="icon" /></div> <div class="menu-item"><IconGithub class="icon" /></div>
</a> </a>
</div> </div>

View File

@ -170,7 +170,7 @@ const updateText = (content: string) => {
const checkEmptyText = () => { const checkEmptyText = () => {
if (!props.elementInfo.text) return if (!props.elementInfo.text) return
const pureText = props.elementInfo.text.content.replaceAll(/<[^>]+>/g, '') const pureText = props.elementInfo.text.content.replace(/<[^>]+>/g, '')
if (!pureText) { if (!pureText) {
slidesStore.removeElementProps({ id: props.elementInfo.id, propName: 'text' }) slidesStore.removeElementProps({ id: props.elementInfo.id, propName: 'text' })
addHistorySnapshot() addHistorySnapshot()

View File

@ -167,7 +167,7 @@ const updateContent = (content: string) => {
} }
const checkEmptyText = debounce(function() { const checkEmptyText = debounce(function() {
const pureText = props.elementInfo.content.replaceAll(/<[^>]+>/g, '') const pureText = props.elementInfo.content.replace(/<[^>]+>/g, '')
if (!pureText) slidesStore.deleteElement(props.elementInfo.id) if (!pureText) slidesStore.deleteElement(props.elementInfo.id)
}, 300, { trailing: true }) }, 300, { trailing: true })

13
tsconfig.app.json Normal file
View File

@ -0,0 +1,13 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

View File

@ -1,40 +1,11 @@
{ {
"compilerOptions": { "files": [],
"target": "esnext", "references": [
"module": "esnext", {
"strict": true, "path": "./tsconfig.node.json"
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env",
"resize-observer-browser"
],
"paths": {
"@/*": [
"src/*"
]
}, },
"lib": [ {
"esnext", "path": "./tsconfig.app.json"
"dom", }
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
] ]
} }

17
tsconfig.node.json Normal file
View File

@ -0,0 +1,17 @@
{
"extends": "@tsconfig/node18/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

26
vite.config.ts Normal file
View File

@ -0,0 +1,26 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
css: {
preprocessorOptions: {
scss: {
additionalData: `
@import '@/assets/styles/variable.scss';
@import '@/assets/styles/mixin.scss';
`
},
},
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})

View File

@ -1,85 +0,0 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const StyleLintPlugin = require('stylelint-webpack-plugin')
module.exports = {
publicPath: './',
css: {
loaderOptions: {
sass: {
prependData: `
@import '~@/assets/styles/variable.scss';
@import '~@/assets/styles/mixin.scss';
`,
},
},
},
configureWebpack: {
plugins: [
new StyleLintPlugin({
files: ['src/**/*.{vue,html,css,scss}'],
failOnError: false,
cache: false,
fix: false,
}),
],
},
pwa: {
name: 'PPTist',
themeColor: '#d14424',
iconPaths: {
faviconSVG: null,
favicon32: 'icons/favicon-32x32.png',
favicon16: 'icons/favicon-16x16.png',
appleTouchIcon: 'icons/apple-touch-icon-152x152.png',
maskIcon: null,
msTileImage: null,
},
manifestOptions: {
name: 'PPTist',
short_name: 'PPTist',
theme_color: '#d14424',
icons: [{
src: 'icons/android-chrome-192x192.png',
sizes: '192x192',
type: 'image/png'
}, {
src: 'icons/android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png'
}, {
src: 'icons/android-chrome-maskable-192x192.png',
sizes: '192x192',
type: 'image/png',
purpose: 'maskable'
}, {
src: 'icons/android-chrome-maskable-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'maskable'
}],
start_url: '.',
display: 'standalone',
background_color: '#000000',
},
workboxOptions: {
runtimeCaching: [{
urlPattern: /.*/,
handler: 'NetworkFirst',
options: {
cacheName: 'PPTist',
expiration: {
maxAgeSeconds: 60 * 60 * 10,
},
cacheableResponse: {
statuses: [0, 200]
}
}
}],
include: [
/\.ttf$/,
],
skipWaiting: true,
}
},
}