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/
const isProduction = process.env.NODE_ENV === 'production'
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
env: {
node: true,
'vue/setup-compiler-macros': true,
},
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/typescript/recommended',
'@vue/eslint-config-typescript'
],
parserOptions: {
ecmaVersion: 2020,
ecmaVersion: 'latest'
},
rules: {
'curly': ['error', 'multi-line'],
@ -64,9 +59,9 @@ module.exports = {
'no-eval': 'error',
'no-var': 'error',
'no-with': 'error',
'no-alert': isProduction ? 'error' : 'warn',
'no-console': isProduction ? 'error' : 'warn',
'no-debugger': isProduction ? 'error' : 'warn',
'no-alert': 'warn',
'no-console': 'warn',
'no-debugger': 'error',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/ban-types': ['error', {
'extendDefaults': true,
@ -78,16 +73,5 @@ module.exports = {
'@typescript-eslint/consistent-type-imports': 'error',
'vue/multi-word-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
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.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
> 一个基于 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>
@ -39,7 +39,7 @@
```
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)
@ -185,36 +173,17 @@ npm run serve
- [如何自定义一个元素](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 协议,回馈开源社区;
- 此外,如果你真的需要闭源商用,无法执行 AGPL-3.0 协议,可以选择:
1. 使用 [Apache 2.0 LICENSE 版本](https://github.com/pipipi-pikachu/PPTist/archive/f1a35bb8e045124e37dcafd6acbf40b4531b69aa.zip)(建议);
- 如果你希望将本项目商用盈利,我希望你能尊重开源,严格遵循 AGPL-3.0 协议,回馈开源社区;
- 如果你因为任何原因,必须要闭源商用,无法执行 AGPL-3.0 协议,可以选择:
1. 使用早期的 [Apache 2.0 LICENSE 版本](https://github.com/pipipi-pikachu/PPTist/archive/f1a35bb8e045124e37dcafd6acbf40b4531b69aa.zip)(建议);
2. 成为项目的贡献者,大致包括:
- 你的代码被本项目作为依赖引用;
- 你提交的 PR 被本项目合并(仅限有价值的,不包括简单的错别字或拼写错误修改等);
- 你参与过本项目的设计、实现(也包括对各种功能/模块的实现或Bug的修复提供了有价值的思路
- 注:先违反协议后再成为贡献者不在此范围;
3. 邮件联系作者付费商用(不建议,我更希望你一起加入到开源社区中来);
- 你给本项目提交过重要的 PR 并且被合并;
- 你参与过本项目的设计,也包括对各种功能/模块的实现或Bug的修复提供了有价值的思路
- 注1满足条件的单位在商用前请先联系作者确认资格避免误会。
- 注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

@ -1,18 +1,16 @@
/**
* build 编译相关的修改例如发布版本对项目构建或者依赖的改动
* chore 其他修改, 比如改变构建流程或者增加依赖库工具等
* docs 文档修改
* feat 新特性新功能
* fix 修改bug
* perf 优化相关比如提升性能体验
* refactor 代码重构
* revert 回滚到上一个版本
* style 代码格式修改
* test 测试用例修改
*/
module.exports = {
extends: [
'@commitlint/config-conventional',
],
/**
* build 编译相关的修改例如发布版本对项目构建或者依赖的改动
* chore 其他修改, 比如改变构建流程或者增加依赖库工具等
* docs 文档修改
* feat 新特性新功能
* fix 修改bug
* perf 优化相关比如提升性能体验
* refactor 代码重构
* revert 回滚到上一个版本
* style 代码格式修改
* test 测试用例修改
*/
/* eslint-env node */
module.exports = {
extends: ['@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>
<html lang="zh-CN">
<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 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="keywords" content="ppt,powerpoint,office powerpoint,在线ppt,幻灯片,演示文稿,ppt在线制作,Vue3,TypeScript" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>PPTIST - 在线演示文稿</title>
<style>
@ -47,18 +47,16 @@
</style>
</head>
<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 class="first-screen-loading">
<div class="first-screen-loading-spinner"></div>
<div class="first-screen-loading-text">正在加载中,请稍等 ...</div>
</div>
</div>
<script>
document.oncontextmenu = e => e.preventDefault()
</script>
<script type="module" src="/src/main.ts"></script>
</body>
<script>
document.oncontextmenu = e => e.preventDefault()
</script>
</html>

23500
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

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

View File

@ -355,7 +355,7 @@ export default () => {
if (!cell.text) return cell
return {
...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 { createPinia } from 'pinia'
import App from './App.vue'
import './registerServiceWorker'
import '@icon-park/vue-next/styles/index.css'
import 'prosemirror-view/style/prosemirror.css'
import 'animate.css'
import '@/assets/styles/prosemirror.scss'
import '@/assets/styles/global.scss'
import '@/assets/styles/font.scss'
@ -15,9 +13,7 @@ import Icon from '@/plugins/icon'
import Directive from '@/plugins/directive'
const app = createApp(App)
app.use(Icon)
app.use(Directive)
app.use(createPinia())
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 layoutsString = JSON.stringify(layouts)
.replaceAll('{{themeColor}}', themeColor)
.replaceAll('{{fontColor}}', fontColor)
.replaceAll('{{fontName}}', fontName)
.replaceAll('{{backgroundColor}}', backgroundColor)
.replaceAll('{{subColor}}', subColor)
.replace(/{{themeColor}}/g, themeColor)
.replace(/{{fontColor}}/g, fontColor)
.replace(/{{fontName}}/g, fontName)
.replace(/{{backgroundColor}}/g, backgroundColor)
.replace(/{{subColor}}/g, subColor)
return JSON.parse(layoutsString)
},

View File

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

View File

@ -1,6 +1,98 @@
// @ts-ignore
import { SVGPathData } from 'svg-pathdata'
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 = {
1: 'Z',
2: 'M',

View File

@ -57,7 +57,7 @@
<div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')">
<IconDownload class="icon" />
</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>
</a>
</div>

View File

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

View File

@ -167,7 +167,7 @@ const updateContent = (content: string) => {
}
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)
}, 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": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env",
"resize-observer-browser"
],
"paths": {
"@/*": [
"src/*"
]
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
{
"path": "./tsconfig.app.json"
}
]
}

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,
}
},
}