├── .nvmrc
├── docs
├── spike
│ ├── analyses.md
│ ├── export.md
│ ├── ui.md
│ ├── plugins.md
│ ├── auto.md
│ ├── sexy.md
│ ├── webview.md
│ ├── icon.md
│ ├── terminal.md
│ ├── documents.md
│ ├── git.md
│ ├── cli.md
│ ├── browser.md
│ ├── slides.md
│ ├── python.md
│ ├── file-save.md
│ ├── editor.md
│ ├── open-files.md
│ ├── mindmap.md
│ ├── multiple-windows.md
│ ├── search-server.md
│ ├── chinese-improve.md
│ ├── storage.md
│ ├── spell-check.md
│ ├── shortcuts.md
│ ├── communication.md
│ └── nlp.md
├── CNAME
├── phodit.jpg
└── adr
│ ├── 0003-支持批注.md
│ ├── 0008-另存为功能.md
│ ├── 0016-引入操作日志.md
│ ├── 0009-搜索引擎或者是数据库.md
│ ├── 0010-全文搜索文本支持.md
│ ├── 0015-可配置的搜索菜单.md
│ ├── 0017-支持记住文件的行号.md
│ ├── 0019-支持选择本地图片.md
│ ├── 0031-屏保或定时休息提醒.md
│ ├── 0025-添加复制、粘贴图片功能.md
│ ├── 0032-touchbar-支持.md
│ ├── 0006-新增复制图片下载到本地的功能.md
│ ├── 0024-添加不同的显示字体切换.md
│ ├── 0029-[feat]-添加审校功能.md
│ ├── 0018-支持内容中的待办事项列表-todo.md
│ ├── 0005-段落折叠功能.md
│ ├── 0028-[bug]-修改重命名文件后的保存问题.md
│ ├── 0030-进度条:根据行数和当前行数来显示百分比.md
│ ├── 0033-multiple-instances.md
│ ├── 0020-submline-text-类似的管口滚动.md
│ ├── 0014-实现重命名文件等对话框.md
│ ├── 0027-粘贴内容到编辑器时转为-markdown-格式.md
│ ├── 0004-统计打字速度及显示活跃时间.md
│ ├── 0022-[tabs]-支持.md
│ ├── 0001-系统进行-discover-设计.md
│ ├── 0023-[pdf]-支持编译文章成-pdf.md
│ ├── 0013-使用-toast-来实现简单的提醒功能.md
│ ├── 0007-autocomplete->-冒号后自动换行并加-item-标识符.md
│ ├── 0026-添加粘贴图片到目录的支持.md
│ ├── 0011-监测到文件变化自动更新目录.md
│ ├── 0012-使用-pandoc-来转换-slide.md
│ ├── 0021-自动发布系统-dsl.md
│ ├── 0002-中文-nlp-相关功能.md
│ └── README.md
├── CNAME
├── components
├── interact
│ ├── src
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── styles.css
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── tsconfig.app.json
│ │ ├── app
│ │ │ ├── interact-bar.component.html
│ │ │ ├── app.module.ts
│ │ │ ├── interact-bar.component.css
│ │ │ └── interact-bar.component.ts
│ │ ├── tsconfig.spec.json
│ │ ├── tslint.json
│ │ ├── browserslist
│ │ ├── main.ts
│ │ ├── index.html
│ │ ├── test.ts
│ │ ├── karma.conf.js
│ │ └── polyfills.ts
│ ├── e2e
│ │ ├── src
│ │ │ ├── app.po.ts
│ │ │ └── app.e2e-spec.ts
│ │ ├── tsconfig.e2e.json
│ │ └── protractor.conf.js
│ ├── .editorconfig
│ ├── tsconfig.json
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── tslint.json
│ └── angular.json
├── tree-view
│ ├── public
│ │ ├── favicon.ico
│ │ └── index.html
│ ├── README.md
│ ├── src
│ │ ├── index.js
│ │ ├── index.css
│ │ └── App.js
│ ├── .gitignore
│ ├── package.json
│ └── package-lock.json
└── header
│ ├── stencil.config.js
│ ├── .editorconfig
│ ├── src
│ ├── components
│ │ └── phodit-header
│ │ │ ├── phodit-header.css
│ │ │ ├── phodit-header.tsx
│ │ │ └── phodit-header.spec.ts
│ ├── index.html
│ └── components.d.ts
│ ├── .gitignore
│ ├── tsconfig.json
│ ├── LICENSE
│ ├── .github
│ ├── ISSUE_TEMPLATE.md
│ └── CONTRIBUTING.md
│ ├── package-lock.json
│ ├── package.json
│ └── readme.md
├── _config.yml
├── assets
├── hot-words.txt
├── imgs
│ ├── phodit.sketch
│ └── icons
│ │ ├── mac
│ │ └── icon.icns
│ │ ├── png
│ │ ├── 16x16.png
│ │ ├── 24x24.png
│ │ ├── 32x32.png
│ │ ├── 48x48.png
│ │ ├── 64x64.png
│ │ ├── 128x128.png
│ │ ├── 256x256.png
│ │ ├── 512x512.png
│ │ └── 1024x1024.png
│ │ └── win
│ │ └── icon.ico
├── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
├── css
│ ├── themes
│ │ ├── dark-theme.css
│ │ ├── monokai-sublime.css
│ │ ├── monokai-bright.css
│ │ └── default.css
│ ├── styles.css
│ └── normalize.css
├── libs
│ └── clipboard.min.js
└── simplemde
│ └── simplemde.min.css
├── .adr.json
├── src
├── common
│ ├── interface
│ │ ├── IFileSave.ts
│ │ └── IFileOpen.ts
│ └── constants
│ │ └── event.constants.ts
├── native
│ ├── features
│ │ ├── git
│ │ │ ├── index.ts
│ │ │ ├── spawn-git.ts
│ │ │ └── status.ts
│ │ └── pandoc
│ │ │ ├── spawn-pandoc.ts
│ │ │ ├── index.ts
│ │ │ ├── html.ts
│ │ │ ├── word.ts
│ │ │ ├── slide.ts
│ │ │ └── pdf.ts
│ ├── ui
│ │ ├── menu.dock.ts
│ │ ├── touch-bar.ts
│ │ └── menu.ts
│ ├── main.ts
│ ├── pages
│ │ ├── html.page.ts
│ │ ├── help.page.ts
│ │ └── silde.page.ts
│ ├── support
│ │ └── file-support.ts
│ ├── phodit-ipc.ts
│ └── phodit.ts
└── render
│ ├── support
│ ├── event.util.ts
│ ├── file.utils.ts
│ └── markdown.utils.ts
│ ├── key.event.ts
│ ├── worker
│ └── suggest.worker.ts
│ ├── plugins
│ └── terminal.ts
│ ├── menu.right.ts
│ └── renderer.ts
├── .gitmodules
├── .editorconfig
├── tsconfig.json
├── tslint.json
├── .travis.yml
├── .gitignore
├── LICENSE
├── test
├── input-test.js
├── webview-test.js
├── command-test.js
├── global-setup.js
├── basic-test.js
└── application-test.js
├── views
├── slide.html
├── index.html
└── help.html
├── README.md
├── package.json
└── CHANGELOG.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | v15.2.1
2 |
--------------------------------------------------------------------------------
/docs/spike/analyses.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | www.phodit.com
2 |
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | www.phodit.com
--------------------------------------------------------------------------------
/components/interact/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | remote_theme: phodal/mifa-jekyll
2 |
--------------------------------------------------------------------------------
/assets/hot-words.txt:
--------------------------------------------------------------------------------
1 | 微前端
2 | 微前端架构
3 | 微服务
4 | DevOps
5 |
--------------------------------------------------------------------------------
/docs/spike/export.md:
--------------------------------------------------------------------------------
1 | 文件导出
2 | ===
3 |
4 | pandoc
5 | ---
6 |
--------------------------------------------------------------------------------
/.adr.json:
--------------------------------------------------------------------------------
1 | {"language":"zh-cn","path":"docs/adr/","prefix":"","digits":4}
--------------------------------------------------------------------------------
/docs/spike/ui.md:
--------------------------------------------------------------------------------
1 | UI
2 | ===
3 |
4 | https://pqx.github.io/react-ui-tree/
5 |
--------------------------------------------------------------------------------
/docs/phodit.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/docs/phodit.jpg
--------------------------------------------------------------------------------
/docs/spike/plugins.md:
--------------------------------------------------------------------------------
1 | 插件篇
2 | ===
3 |
4 | 考虑:
5 |
6 | https://github.com/ternjs/tern
7 |
--------------------------------------------------------------------------------
/assets/imgs/phodit.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/phodit.sketch
--------------------------------------------------------------------------------
/docs/spike/auto.md:
--------------------------------------------------------------------------------
1 | 自动更新
2 | ===
3 |
4 | [官方更新文档](https://electronjs.org/docs/tutorial/updates)
5 |
--------------------------------------------------------------------------------
/docs/spike/sexy.md:
--------------------------------------------------------------------------------
1 | 经验
2 | ===
3 |
4 | https://blog.dcpos.ch/how-to-make-your-electron-app-sexy
5 |
--------------------------------------------------------------------------------
/assets/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/docs/spike/webview.md:
--------------------------------------------------------------------------------
1 | 嵌入 WebView
2 | ===
3 |
4 | https://electronjs.org/docs/api/webview-tag
5 |
6 |
7 |
--------------------------------------------------------------------------------
/assets/imgs/icons/mac/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/icons/mac/icon.icns
--------------------------------------------------------------------------------
/assets/imgs/icons/png/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/icons/png/16x16.png
--------------------------------------------------------------------------------
/assets/imgs/icons/png/24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/icons/png/24x24.png
--------------------------------------------------------------------------------
/assets/imgs/icons/png/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/icons/png/32x32.png
--------------------------------------------------------------------------------
/assets/imgs/icons/png/48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/icons/png/48x48.png
--------------------------------------------------------------------------------
/assets/imgs/icons/png/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/icons/png/64x64.png
--------------------------------------------------------------------------------
/assets/imgs/icons/win/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/icons/win/icon.ico
--------------------------------------------------------------------------------
/docs/spike/icon.md:
--------------------------------------------------------------------------------
1 | 图标生成
2 | ===
3 |
4 | https://github.com/manojsinghnegiwd/electron-icon-generator
5 |
6 |
--------------------------------------------------------------------------------
/assets/imgs/icons/png/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/icons/png/128x128.png
--------------------------------------------------------------------------------
/assets/imgs/icons/png/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/icons/png/256x256.png
--------------------------------------------------------------------------------
/assets/imgs/icons/png/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/icons/png/512x512.png
--------------------------------------------------------------------------------
/components/interact/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/src/common/interface/IFileSave.ts:
--------------------------------------------------------------------------------
1 | export interface IFileSave {
2 | isTempFile: boolean;
3 | data: any;
4 | }
5 |
--------------------------------------------------------------------------------
/assets/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/assets/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/assets/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/assets/imgs/icons/png/1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/imgs/icons/png/1024x1024.png
--------------------------------------------------------------------------------
/components/interact/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/components/interact/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/components/interact/src/favicon.ico
--------------------------------------------------------------------------------
/assets/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/assets/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/components/tree-view/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/phodit/HEAD/components/tree-view/public/favicon.ico
--------------------------------------------------------------------------------
/docs/spike/terminal.md:
--------------------------------------------------------------------------------
1 | Terminal
2 | ===
3 |
4 | [https://github.com/xtermjs/xterm.js/](https://github.com/xtermjs/xterm.js/)
5 |
--------------------------------------------------------------------------------
/src/native/features/git/index.ts:
--------------------------------------------------------------------------------
1 | import gitStatus from "./status";
2 |
3 | export const git = {
4 | status: gitStatus,
5 | };
6 |
--------------------------------------------------------------------------------
/src/common/interface/IFileOpen.ts:
--------------------------------------------------------------------------------
1 | export interface IFileOpen {
2 | file: string;
3 | isTempFile: boolean;
4 | data: any;
5 | }
6 |
--------------------------------------------------------------------------------
/components/tree-view/README.md:
--------------------------------------------------------------------------------
1 | # Phodit UI Tree
2 |
3 | ## Setup
4 |
5 | ```
6 | yarn install
7 | ```
8 |
9 | ```
10 | yarn build
11 | ```
12 |
--------------------------------------------------------------------------------
/docs/spike/documents.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [Build cross-platform desktop apps with Electron ](https://github.com/feross/electron-workshop/blob/master/README.md)
4 |
--------------------------------------------------------------------------------
/docs/spike/git.md:
--------------------------------------------------------------------------------
1 | Git 相关资源
2 | ===
3 |
4 | nodegit 需要跨平台编译,不考虑
5 |
6 | 考虑使用 [git-it-electron](https://github.com/jlord/git-it-electron) 类似的方式来实现。
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/native/ui/menu.dock.ts:
--------------------------------------------------------------------------------
1 | export const dockMenu = [
2 | {
3 | label: "New Window",
4 | click() {
5 | console.log("New Window");
6 | },
7 | }
8 | ];
9 |
--------------------------------------------------------------------------------
/docs/spike/cli.md:
--------------------------------------------------------------------------------
1 | 命令行
2 | ===
3 |
4 |
5 | https://github.com/akameco/electron-terminal-open
6 |
7 |
8 | https://binary-studio.com/2017/09/21/implementing-cli-electron-apps/
9 |
--------------------------------------------------------------------------------
/docs/spike/browser.md:
--------------------------------------------------------------------------------
1 | 浏览器
2 | ===
3 |
4 | 返回上一页
5 | ---
6 |
7 | 默认打开方式
8 | ---
9 |
10 | https://stackoverflow.com/questions/31749625/make-a-link-from-electron-open-in-browser
11 |
--------------------------------------------------------------------------------
/src/native/main.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import Phodit from "./phodit";
3 |
4 | function initMain() {
5 | const phodit = new Phodit();
6 | phodit.init();
7 | }
8 |
9 | initMain();
10 |
--------------------------------------------------------------------------------
/docs/spike/slides.md:
--------------------------------------------------------------------------------
1 | Slides 功能
2 | ===
3 |
4 | https://github.com/hakimel/reveal.js/
5 |
6 | https://github.com/webpro/reveal-md
7 |
8 |
9 | 相关编辑器:
10 |
11 | https://github.com/sunya9/reveal-editor
12 |
--------------------------------------------------------------------------------
/docs/spike/python.md:
--------------------------------------------------------------------------------
1 | 嵌入 Python
2 | ===
3 |
4 | 在 Electron 中嵌入 Python 环境:
5 |
6 | https://github.com/fyears/electron-python-example
7 |
8 | https://www.fyears.org/2017/02/electron-as-gui-of-python-apps-updated.html
9 |
--------------------------------------------------------------------------------
/docs/adr/0003-支持批注.md:
--------------------------------------------------------------------------------
1 | # 3. 支持批注
2 |
3 | 日期: 2018-07-23
4 |
5 | ## 状态
6 |
7 | 2018-07-23 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0008-另存为功能.md:
--------------------------------------------------------------------------------
1 | # 8. 另存为功能
2 |
3 | 日期: 2018-07-25
4 |
5 | ## 状态
6 |
7 | 2018-07-25 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/src/native/features/git/spawn-git.ts:
--------------------------------------------------------------------------------
1 | const exec = require("child_process").exec;
2 |
3 | export default function spawnGit(command: any, options: any, callback: any) {
4 | exec("git " + command, options, callback);
5 | }
6 |
--------------------------------------------------------------------------------
/components/tree-view/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render(, document.getElementById('tree-view'));
7 |
--------------------------------------------------------------------------------
/docs/adr/0016-引入操作日志.md:
--------------------------------------------------------------------------------
1 | # 16. 引入操作日志
2 |
3 | 日期: 2018-08-15
4 |
5 | ## 状态
6 |
7 | 2018-08-15 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/src/native/features/pandoc/spawn-pandoc.ts:
--------------------------------------------------------------------------------
1 | import {exec} from "child_process";
2 |
3 | export default function spawnPandoc(command: any, options: any, callback: any) {
4 | exec("pandoc " + command, options, callback);
5 | }
6 |
--------------------------------------------------------------------------------
/docs/adr/0009-搜索引擎或者是数据库.md:
--------------------------------------------------------------------------------
1 | # 9. 搜索引擎或者是数据库
2 |
3 | 日期: 2018-07-27
4 |
5 | ## 状态
6 |
7 | 2018-07-27 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0010-全文搜索文本支持.md:
--------------------------------------------------------------------------------
1 | # 10. 全文搜索文本支持
2 |
3 | 日期: 2018-07-27
4 |
5 | ## 状态
6 |
7 | 2018-07-27 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0015-可配置的搜索菜单.md:
--------------------------------------------------------------------------------
1 | # 15. 可配置的搜索菜单
2 |
3 | 日期: 2018-08-03
4 |
5 | ## 状态
6 |
7 | 2018-08-03 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0017-支持记住文件的行号.md:
--------------------------------------------------------------------------------
1 | # 17. 支持记住文件的行号
2 |
3 | 日期: 2018-08-22
4 |
5 | ## 状态
6 |
7 | 2018-08-22 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0019-支持选择本地图片.md:
--------------------------------------------------------------------------------
1 | # 19. 支持选择本地图片
2 |
3 | 日期: 2018-08-23
4 |
5 | ## 状态
6 |
7 | 2018-08-23 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0031-屏保或定时休息提醒.md:
--------------------------------------------------------------------------------
1 | # 31. 屏保或定时休息提醒
2 |
3 | 日期: 2019-01-17
4 |
5 | ## 状态
6 |
7 | 2019-01-17 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0025-添加复制、粘贴图片功能.md:
--------------------------------------------------------------------------------
1 | # 25. 添加复制、粘贴图片功能
2 |
3 | 日期: 2018-10-31
4 |
5 | ## 状态
6 |
7 | 2018-10-31 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0032-touchbar-支持.md:
--------------------------------------------------------------------------------
1 | # 32. Touchbar 支持
2 |
3 | 日期: 2019-01-28
4 |
5 | ## 状态
6 |
7 | 2019-01-28 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/spike/file-save.md:
--------------------------------------------------------------------------------
1 | 存储策略
2 | ===
3 |
4 | 临时文件存储
5 | ---
6 |
7 | https://github.com/bruce/node-temp
8 |
9 |
10 |
11 | https://github.com/eligrey/FileSaver.js/
12 |
13 | https://github.com/electron/electron/issues/2721
14 |
--------------------------------------------------------------------------------
/docs/adr/0006-新增复制图片下载到本地的功能.md:
--------------------------------------------------------------------------------
1 | # 6. 新增复制图片下载到本地的功能
2 |
3 | 日期: 2018-07-24
4 |
5 | ## 状态
6 |
7 | 2018-07-24 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0024-添加不同的显示字体切换.md:
--------------------------------------------------------------------------------
1 | # 24. 添加不同的显示字体切换
2 |
3 | 日期: 2018-09-26
4 |
5 | ## 状态
6 |
7 | 2018-09-26 提议
8 |
9 | ## 背景
10 |
11 | 长时间写作时,容易对字体产生疲劳
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0029-[feat]-添加审校功能.md:
--------------------------------------------------------------------------------
1 | # 29. [FEAT] 添加审校功能
2 |
3 | 日期: 2019-01-11
4 |
5 | ## 状态
6 |
7 | 2019-01-11 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0018-支持内容中的待办事项列表-todo.md:
--------------------------------------------------------------------------------
1 | # 18. 支持内容中的待办事项列表 todo
2 |
3 | 日期: 2018-08-22
4 |
5 | ## 状态
6 |
7 | 2018-08-22 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0005-段落折叠功能.md:
--------------------------------------------------------------------------------
1 | # 5. 段落折叠功能
2 |
3 | 日期: 2018-07-24
4 |
5 | ## 状态
6 |
7 | 2018-07-24 提议
8 |
9 | 2019-01-15 通过
10 |
11 | ## 背景
12 |
13 | 在这里补充上下文...
14 |
15 | ## 决策
16 |
17 | 在这里补充上决策信息...
18 |
19 | ## 后果
20 |
21 | 在这里记录结果...
22 |
--------------------------------------------------------------------------------
/docs/adr/0028-[bug]-修改重命名文件后的保存问题.md:
--------------------------------------------------------------------------------
1 | # 28. [BUG] 修改重命名文件后的保存问题
2 |
3 | 日期: 2018-12-28
4 |
5 | ## 状态
6 |
7 | 2018-12-28 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0030-进度条:根据行数和当前行数来显示百分比.md:
--------------------------------------------------------------------------------
1 | # 30. 进度条:根据行数和当前行数来显示百分比
2 |
3 | 日期: 2019-01-14
4 |
5 | ## 状态
6 |
7 | 2019-01-14 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0033-multiple-instances.md:
--------------------------------------------------------------------------------
1 | # 33. multiple instances
2 |
3 | 日期: 2021-08-27
4 |
5 | ## 状态
6 |
7 | 2021-08-27 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/src/native/features/pandoc/index.ts:
--------------------------------------------------------------------------------
1 | import html from "./html";
2 | import word from "./word";
3 | import slide from "./slide";
4 | import pdf from "./pdf";
5 |
6 | export const pandoc = {
7 | html,
8 | word,
9 | slide,
10 | pdf
11 | };
12 |
--------------------------------------------------------------------------------
/docs/adr/0020-submline-text-类似的管口滚动.md:
--------------------------------------------------------------------------------
1 | # 20. Submline Text 类似的管口滚动
2 |
3 | 日期: 2018-08-24
4 |
5 | ## 状态
6 |
7 | 2018-08-24 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0014-实现重命名文件等对话框.md:
--------------------------------------------------------------------------------
1 | # 14. 实现重命名文件等对话框
2 |
3 | 日期: 2018-08-01
4 |
5 | ## 状态
6 |
7 | 2018-08-01 提议
8 |
9 | 2018-08-22 完成
10 |
11 | ## 背景
12 |
13 | 在这里补充上下文...
14 |
15 | ## 决策
16 |
17 | 在这里补充上决策信息...
18 |
19 | ## 后果
20 |
21 | 在这里记录结果...
22 |
--------------------------------------------------------------------------------
/docs/adr/0027-粘贴内容到编辑器时转为-markdown-格式.md:
--------------------------------------------------------------------------------
1 | # 27. 粘贴内容到编辑器时,转为 markdown 格式
2 |
3 | 日期: 2018-12-07
4 |
5 | ## 状态
6 |
7 | 2018-12-07 提议
8 |
9 | ## 背景
10 |
11 | 在这里补充上下文...
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0004-统计打字速度及显示活跃时间.md:
--------------------------------------------------------------------------------
1 | # 4. 统计打字速度及显示活跃时间
2 |
3 | 日期: 2018-07-23
4 |
5 | ## 状态
6 |
7 | 2018-07-23 提议
8 |
9 | 2018-09-10 已弃用
10 |
11 | ## 背景
12 |
13 | 在这里补充上下文...
14 |
15 | ## 决策
16 |
17 | 在这里补充上决策信息...
18 |
19 | ## 后果
20 |
21 | 在这里记录结果...
22 |
--------------------------------------------------------------------------------
/docs/adr/0022-[tabs]-支持.md:
--------------------------------------------------------------------------------
1 | # 22. [Tabs] 支持
2 |
3 | 日期: 2018-09-06
4 |
5 | ## 状态
6 |
7 | 2018-09-06 提议
8 |
9 | ## 背景
10 |
11 | https://github.com/adamschwartz/chrome-tabs
12 |
13 | ## 决策
14 |
15 | 在这里补充上决策信息...
16 |
17 | ## 后果
18 |
19 | 在这里记录结果...
20 |
--------------------------------------------------------------------------------
/docs/adr/0001-系统进行-discover-设计.md:
--------------------------------------------------------------------------------
1 | # 1. 系统进行 Discover 设计
2 |
3 | 日期: 2018-07-16
4 |
5 | ## 状态
6 |
7 | 2018-07-16 提议
8 |
9 | 2018-09-10 完成
10 |
11 | ## 背景
12 |
13 | 在这里补充上下文...
14 |
15 | ## 决策
16 |
17 | 在这里补充上决策信息...
18 |
19 | ## 后果
20 |
21 | 在这里记录结果...
22 |
--------------------------------------------------------------------------------
/docs/adr/0023-[pdf]-支持编译文章成-pdf.md:
--------------------------------------------------------------------------------
1 | # 23. [PDF] 支持编译文章成 PDF
2 |
3 | 日期: 2018-09-10
4 |
5 | ## 状态
6 |
7 | 2018-09-10 提议
8 |
9 | 2019-01-15 完成
10 |
11 | ## 背景
12 |
13 | 在这里补充上下文...
14 |
15 | ## 决策
16 |
17 | 在这里补充上决策信息...
18 |
19 | ## 后果
20 |
21 | 在这里记录结果...
22 |
--------------------------------------------------------------------------------
/docs/adr/0013-使用-toast-来实现简单的提醒功能.md:
--------------------------------------------------------------------------------
1 | # 13. 使用 Toast 来实现简单的提醒功能
2 |
3 | 日期: 2018-08-01
4 |
5 | ## 状态
6 |
7 | 2018-08-01 提议
8 |
9 | 2018-08-22 完成
10 |
11 | ## 背景
12 |
13 | 在这里补充上下文...
14 |
15 | ## 决策
16 |
17 | 在这里补充上决策信息...
18 |
19 | ## 后果
20 |
21 | 在这里记录结果...
22 |
--------------------------------------------------------------------------------
/docs/spike/editor.md:
--------------------------------------------------------------------------------
1 | CodeMirror
2 | ===
3 |
4 | 自动完成
5 |
6 | https://stackoverflow.com/questions/41953077/codemirror-editor-show-hint-after-specific-key-pattern-like
7 |
8 | Ace 编辑器
9 | ===
10 |
11 | [https://github.com/ajaxorg/ace/](https://github.com/ajaxorg/ace/)
12 |
--------------------------------------------------------------------------------
/components/interact/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "target": "es2015",
6 | "types": []
7 | },
8 | "exclude": [
9 | "src/test.ts",
10 | "**/*.spec.ts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/components/interact/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "nlp"]
2 | path = nlp
3 | url = https://github.com/phodal/phodit-nlp
4 | [submodule "editor"]
5 | path = editor
6 | url = https://github.com/phodal/phodit-mde
7 | [submodule "views/reveal.js"]
8 | path = views/reveal.js
9 | url = https://github.com/hakimel/reveal.js/
10 |
--------------------------------------------------------------------------------
/docs/adr/0007-autocomplete->-冒号后自动换行并加-item-标识符.md:
--------------------------------------------------------------------------------
1 | # 7. AutoComplete -> 冒号后自动换行并加 item 标识符
2 |
3 | 日期: 2018-07-24
4 |
5 | ## 状态
6 |
7 | 2018-07-24 提议
8 |
9 | 2019-01-15 完成
10 |
11 | ## 背景
12 |
13 | 在这里补充上下文...
14 |
15 | ## 决策
16 |
17 | 在这里补充上决策信息...
18 |
19 | ## 后果
20 |
21 | 在这里记录结果...
22 |
--------------------------------------------------------------------------------
/components/interact/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/components/header/stencil.config.js:
--------------------------------------------------------------------------------
1 | exports.config = {
2 | namespace: 'phodit-header',
3 | outputTargets:[
4 | {
5 | type: 'dist'
6 | },
7 | {
8 | type: 'dist-custom-elements-bundle',
9 | },
10 | {
11 | type: 'www',
12 | serviceWorker: false
13 | }
14 | ]
15 | };
16 |
--------------------------------------------------------------------------------
/docs/spike/open-files.md:
--------------------------------------------------------------------------------
1 | 打开窗口
2 | ===
3 |
4 | [使用 Electron 调用系统对话框](https://segmentfault.com/a/1190000008530168)
5 |
6 | 树形结构
7 | ---
8 |
9 | https://codeburst.io/practical-recursion-implementing-a-file-tree-view-in-react-electron-af62e7b46d26
10 |
11 |
12 | 代码:https://github.com/mperitz/react-filetree-electron
13 |
--------------------------------------------------------------------------------
/docs/adr/0026-添加粘贴图片到目录的支持.md:
--------------------------------------------------------------------------------
1 | # 26. 添加粘贴图片到目录的支持
2 |
3 | 日期: 2018-12-07
4 |
5 | ## 状态
6 |
7 | 2018-12-07 提议
8 |
9 | 2018-12-10 已弃用
10 |
11 | ## 背景
12 |
13 | 当前的 Electron 并不支持复制、粘贴图片
14 |
15 | https://electronjs.org/docs/api/clipboard
16 |
17 | ## 决策
18 |
19 | 在这里补充上决策信息...
20 |
21 | ## 后果
22 |
23 | 在这里记录结果...
24 |
--------------------------------------------------------------------------------
/docs/spike/mindmap.md:
--------------------------------------------------------------------------------
1 | 思维导图
2 | ===
3 |
4 | Atom 插件:https://atom.io/packages/markdown-mindmap
5 |
6 | 百度脑图:
7 |
8 | https://github.com/fex-team/kityminder-core
9 |
10 | https://github.com/fex-team/kityminder-editor
11 |
12 | 库
13 | ---
14 |
15 | Markmap
16 |
17 | https://github.com/dundalek/markmap
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/docs/adr/0011-监测到文件变化自动更新目录.md:
--------------------------------------------------------------------------------
1 | # 11. 监测到文件变化,自动更新目录
2 |
3 | 日期: 2018-07-29
4 |
5 | ## 状态
6 |
7 | 2018-07-29 提议
8 |
9 | 2018-09-10 通过
10 |
11 | 2018-09-10 完成
12 |
13 | ## 背景
14 |
15 | 在这里补充上下文...
16 |
17 | https://github.com/bevry/watchr
18 |
19 | ## 决策
20 |
21 | 在这里补充上决策信息...
22 |
23 | ## 后果
24 |
25 | 在这里记录结果...
26 |
--------------------------------------------------------------------------------
/docs/adr/0012-使用-pandoc-来转换-slide.md:
--------------------------------------------------------------------------------
1 | # 12. 使用 pandoc 来转换 slide
2 |
3 | 日期: 2018-07-30
4 |
5 | ## 状态
6 |
7 | 2018-07-30 提议
8 |
9 | 2018-08-22 完成
10 |
11 | ## 背景
12 |
13 | 在这里补充上下文...
14 |
15 | ## 决策
16 |
17 | 将 reveal.js 和生成的 index.html 文件放置到 tmp 目录中,再读取。
18 |
19 | 每次更新后,替换掉旧的文件。
20 |
21 | ## 后果
22 |
23 | 在这里记录结果...
24 |
--------------------------------------------------------------------------------
/components/interact/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/components/header/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 2
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | insert_final_newline = false
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/components/tree-view/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | React App
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/components/header/src/components/phodit-header/phodit-header.css:
--------------------------------------------------------------------------------
1 | #phodit-editor {
2 | width: 100%;
3 | height: 20px;
4 | background: #fdfdfd;
5 | border-top: 1px solid #ddd;
6 | border-bottom: 1px solid #ddd;
7 | }
8 |
9 | .close-button {
10 | float: right;
11 | right: 10px;
12 | position: relative;
13 | line-height: 20px;
14 | }
15 |
--------------------------------------------------------------------------------
/src/native/features/git/status.ts:
--------------------------------------------------------------------------------
1 | import spawnGit from "./spawn-git";
2 |
3 | export default function gitStatus(path: string) {
4 | spawnGit("status --porcelain", {cwd: path}, function(err: any, stdout: any, stdrr: any) {
5 | if (err) {
6 | return;
7 | }
8 | // console.log(stdout.trim());
9 |
10 | return stdout.trim();
11 | });
12 | }
13 |
--------------------------------------------------------------------------------
/docs/adr/0021-自动发布系统-dsl.md:
--------------------------------------------------------------------------------
1 | # 21. 自动发布系统 DSL
2 |
3 | 日期: 2018-09-04
4 |
5 | ## 状态
6 |
7 | 2018-09-04 提议
8 |
9 | ## 背景
10 |
11 | 保存 cookies
12 |
13 | https://blog.csdn.net/changhuzhao/article/details/78773279
14 |
15 | 官方 cookie 文档
16 |
17 | https://electronjs.org/docs/api/cookies
18 |
19 | ## 决策
20 |
21 | 在这里补充上决策信息...
22 |
23 | ## 后果
24 |
25 | 在这里记录结果...
26 |
--------------------------------------------------------------------------------
/components/interact/src/app/interact-bar.component.html:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/docs/adr/0002-中文-nlp-相关功能.md:
--------------------------------------------------------------------------------
1 | # 2. 中文 NLP 相关功能
2 |
3 | 日期: 2018-07-20
4 |
5 | ## 状态
6 |
7 | 2018-07-20 提议
8 |
9 | 2019-01-15 完成
10 |
11 | ## 背景
12 |
13 | 希望在写作的时候,能自动提醒作者(即我)找到对应文章的链接,并做一些相关的自动匹配
14 |
15 | Editor 能支持以下的功能:
16 |
17 | - 查询、遍历先前博客的内容,并给出相关的文章
18 | - 自动寻找文章的关键词
19 | - 能支持直接寻找外部链接,如输入 Wiki 时,能查询
20 |
21 | ## 决策
22 |
23 | ## 后果
24 |
25 | 在这里记录结果...
26 |
--------------------------------------------------------------------------------
/components/header/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 | www/
3 |
4 | *~
5 | *.sw[mnpcod]
6 | *.log
7 | *.lock
8 | *.tmp
9 | *.tmp.*
10 | log.txt
11 | *.sublime-project
12 | *.sublime-workspace
13 |
14 | .stencil/
15 | .idea/
16 | .vscode/
17 | .sass-cache/
18 | .versions/
19 | node_modules/
20 | $RECYCLE.BIN/
21 |
22 | .DS_Store
23 | Thumbs.db
24 | UserInterfaceState.xcuserstate
25 | .env
26 |
--------------------------------------------------------------------------------
/src/native/features/pandoc/html.ts:
--------------------------------------------------------------------------------
1 | import spawnPandoc from "./spawn-pandoc";
2 |
3 | export default function html(path: string) {
4 | spawnPandoc(`-s ${path} -t html5 -o index.html`, {cwd: path}, function(err: any, stdout: any, stdrr: any) {
5 | if (err) {
6 | return;
7 | }
8 | console.log(stdout.trim());
9 |
10 | return stdout.trim();
11 | });
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/docs/spike/multiple-windows.md:
--------------------------------------------------------------------------------
1 | 多窗口示例
2 | ===
3 |
4 | [https://github.com/akabekobeko/examples-electron/blob/master/multiple-windows/src/js/main/App.js](https://github.com/akabekobeko/examples-electron/blob/master/multiple-windows/src/js/main/App.js)
5 |
6 | 打开新窗口示例
7 | ---
8 |
9 | https://stackoverflow.com/questions/42044898/open-a-new-window-to-display-a-html-file-using-a-menu-click-electron
10 |
--------------------------------------------------------------------------------
/components/interact/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 |
3 | describe('workspace-project App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to interact!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/components/interact/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "module": "commonjs",
6 | "types": [
7 | "jasmine",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "test.ts",
13 | "polyfills.ts"
14 | ],
15 | "include": [
16 | "**/*.spec.ts",
17 | "**/*.d.ts"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/components/tree-view/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/src/render/support/event.util.ts:
--------------------------------------------------------------------------------
1 | export function createEvent(name: string, data: any) {
2 | let event: CustomEvent;
3 | if (typeof data === "string") {
4 | event = new CustomEvent(name, {
5 | detail: data,
6 | });
7 | } else {
8 | event = new CustomEvent(name, {
9 | detail: JSON.stringify(data),
10 | });
11 | }
12 |
13 | window.document.dispatchEvent(event);
14 | }
15 |
--------------------------------------------------------------------------------
/components/interact/src/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/components/interact/src/browserslist:
--------------------------------------------------------------------------------
1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed
5 | > 0.5%
6 | last 2 versions
7 | Firefox ESR
8 | not dead
9 | # IE 9-11
--------------------------------------------------------------------------------
/docs/spike/search-server.md:
--------------------------------------------------------------------------------
1 | 搜索功能
2 | ===
3 |
4 | ## Cluster
5 |
6 | http://nodejs.cn/api/cluster.html
7 |
8 | ## Express
9 |
10 | https://github.com/frankhale/electron-with-express
11 |
12 | ## MultiThreading
13 |
14 | 方式 -> 使用 WebWorkers:
15 |
16 | https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
17 |
18 | WebWorker 相关库:
19 |
20 | https://github.com/GoogleChromeLabs/comlink
21 |
22 |
23 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "noImplicitAny": true,
5 | "sourceMap": true,
6 | "outDir": "dist",
7 | "baseUrl": ".",
8 | "target": "es6",
9 | "paths": {
10 | "*": [
11 | "node_modules/*"
12 | ]
13 | },
14 | "lib": [
15 | "es2018",
16 | "dom"
17 | ]
18 | },
19 | "include": [
20 | "src/**/*"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/components/interact/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.log(err));
13 |
--------------------------------------------------------------------------------
/src/native/features/pandoc/word.ts:
--------------------------------------------------------------------------------
1 | import spawnPandoc from "./spawn-pandoc";
2 | import {shell} from "electron";
3 |
4 | export default function word(path: string) {
5 | const newPath = path.replace(/\.md/, '.docx');
6 | spawnPandoc(`-s ${path} -o ${newPath}`, {}, function(err: any, stdout: any, stdrr: any) {
7 | if (err) {
8 | console.error(err);
9 | return;
10 | }
11 |
12 | shell.openPath(newPath).then(() => {})
13 | return stdout.trim();
14 | });
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/components/interact/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "moduleResolution": "node",
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "target": "es2015",
12 | "typeRoots": [
13 | "node_modules/@types"
14 | ],
15 | "lib": [
16 | "es2017",
17 | "dom"
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/docs/spike/chinese-improve.md:
--------------------------------------------------------------------------------
1 | 中文增强
2 | ===
3 |
4 | 1. 字数统计
5 | 2. 中文分词和拼写检查
6 |
7 |
8 | 拼写检测相关
9 | ---
10 |
11 | https://segmentfault.com/a/1190000008233147
12 |
13 | https://github.com/topics/spell
14 |
15 | 相关库
16 | ---
17 |
18 | 语言检测
19 |
20 | https://github.com/wooorm/franc
21 |
22 | Hunspell 的 Node.js 绑定:
23 |
24 | https://github.com/Wulf/nodehun
25 |
26 | Hunspell 兼容的拼写检查:
27 |
28 | https://github.com/wooorm/nspell
29 |
30 | Hunspell 字典
31 |
32 | https://github.com/wooorm/dictionaries
33 |
--------------------------------------------------------------------------------
/components/header/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Stencil Component Starter
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/render/key.event.ts:
--------------------------------------------------------------------------------
1 | document.onkeydown = (event: any) => {
2 | event = event || window.event;
3 | let isEscape = false;
4 | if ("key" in event) {
5 | isEscape = (event.key === "Escape" || event.key === "Esc");
6 | } else {
7 | isEscape = (event.keyCode === 27);
8 | }
9 | if (isEscape) {
10 | document.querySelector("interact-bar").setAttribute("style", "display: none;");
11 |
12 | document.getElementById("terminal-section").setAttribute("style", "display: none;");
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint:recommended",
3 | "rules": {
4 | "max-line-length": {
5 | "options": [
6 | 120
7 | ]
8 | },
9 | "new-parens": true,
10 | "no-arg": true,
11 | "no-var-requires": false,
12 | "no-bitwise": true,
13 | "no-console": false,
14 | "object-literal-sort-keys": false,
15 | "no-conditional-assignment": true,
16 | "no-consecutive-blank-lines": false
17 | },
18 | "jsRules": {
19 | "max-line-length": {
20 | "options": [
21 | 120
22 | ]
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/native/features/pandoc/slide.ts:
--------------------------------------------------------------------------------
1 | import spawnPandoc from "./spawn-pandoc";
2 | const tmp = require("tmp");
3 |
4 | export default function slide(path: string) {
5 | const tmpobj = tmp.fileSync();
6 | const newPath = tmpobj.name;
7 |
8 | return new Promise(function (resolve, reject) {
9 | spawnPandoc(` -t revealjs -s ${path} -o ${newPath} -V theme=simple`, {}, function (err: any, stdout: any, stdrr: any) {
10 | if (err) {
11 | reject(err);
12 | return;
13 | }
14 |
15 | stdout.trim();
16 | resolve(tmpobj.name);
17 | });
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/docs/spike/storage.md:
--------------------------------------------------------------------------------
1 | 存储
2 | ===
3 |
4 | 官方:
5 |
6 | [JSON-Storage](https://github.com/electron-userland/electron-json-storage)
7 |
8 | 其它:
9 |
10 | - [electron-settings](https://github.com/nathanbuchar/electron-settings)
11 | - [electron-store](https://github.com/sindresorhus/electron-store)
12 | - [electron-storage](https://github.com/Cocycles/electron-storage)
13 |
14 |
15 | SQLite
16 | ---
17 |
18 | [electron-boilerplate-sqlite](https://github.com/sjmelia/electron-boilerplate-sqlite)
19 |
20 |
21 |
22 | 客户端存储
23 | ---
24 |
25 | [store.js](https://github.com/marcuswestin/store.js)
26 |
--------------------------------------------------------------------------------
/components/header/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "allowUnreachableCode": false,
5 | "declaration": false,
6 | "experimentalDecorators": true,
7 | "lib": [
8 | "dom",
9 | "es2015"
10 | ],
11 | "moduleResolution": "node",
12 | "module": "esnext",
13 | "target": "es2017",
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "jsx": "react",
17 | "jsxFactory": "h"
18 | },
19 | "include": [
20 | "src",
21 | "types/jsx.d.ts"
22 | ],
23 | "exclude": [
24 | "node_modules"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/components/interact/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Interact
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
22 |
23 |
--------------------------------------------------------------------------------
/src/native/pages/html.page.ts:
--------------------------------------------------------------------------------
1 | export function openHtmlPage(BrowserWindow: any, filePath: any) {
2 | let htmlPage: any = null;
3 |
4 | function buildHtmlPage() {
5 | if (htmlPage) {
6 | htmlPage.focus();
7 | return;
8 | }
9 |
10 | htmlPage = new BrowserWindow({
11 | height: 768,
12 | width: 1024,
13 | title: "网页",
14 | backgroundColor: "#fff",
15 | });
16 |
17 | htmlPage.on("closed", () => {
18 | htmlPage = null;
19 | });
20 |
21 | htmlPage.loadFile(filePath);
22 | htmlPage.once("ready-to-show", () => htmlPage.show());
23 | // htmlPage.webContents.openDevTools();
24 | }
25 |
26 | buildHtmlPage();
27 | }
28 |
--------------------------------------------------------------------------------
/components/interact/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 |
--------------------------------------------------------------------------------
/src/native/features/pandoc/pdf.ts:
--------------------------------------------------------------------------------
1 | import spawnPandoc from "./spawn-pandoc";
2 | import { shell } from "electron";
3 |
4 | export default function pdf(path: string) {
5 | const pdfPath = path.replace(/\.md/, '.pdf');
6 |
7 | const additional = `--pdf-engine=xelatex -V -VCJKoptions=BoldFont="Hei" -VCJKmainfont="Hei"`;
8 | spawnPandoc(`-s ${path} -o ${pdfPath} ${additional}`, {}, (err: any, stdout: any, stdrr: any) => {
9 | if (err) {
10 | console.error(err);
11 | return;
12 | }
13 |
14 | console.info(stdout);
15 | console.info(stdrr);
16 |
17 | // tslint:disable-next-line:no-empty
18 | shell.openPath(pdfPath).then(() => {
19 | })
20 | return stdout.trim();
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/components/interact/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import {BrowserModule} from '@angular/platform-browser';
2 | import {Injector, NgModule} from '@angular/core';
3 |
4 | import {InteractBar} from './interact-bar.component';
5 | import {createCustomElement} from "@angular/elements";
6 | import {FormsModule} from "@angular/forms";
7 |
8 | @NgModule({
9 | declarations: [InteractBar],
10 | imports: [BrowserModule, FormsModule],
11 | entryComponents: [InteractBar]
12 | })
13 | export class AppModule {
14 | constructor(private injector: Injector) {
15 | const interactBar = createCustomElement(InteractBar, {injector});
16 | customElements.define('interact-bar', interactBar);
17 | }
18 |
19 | ngDoBootstrap() {
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/components/interact/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * In development mode, to ignore zone related error stack frames such as
11 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
12 | * import the following file, but please comment it out in production mode
13 | * because it will have performance impact when throw error
14 | */
15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
16 |
--------------------------------------------------------------------------------
/docs/spike/spell-check.md:
--------------------------------------------------------------------------------
1 | 中文拼写检查
2 | ===
3 |
4 | CodeMirrorSpellChecker
5 |
6 | https://github.com/sparksuite/codemirror-spell-checker/blob/master/src/js/spell-checker.js
7 |
8 | 字典库:
9 |
10 | [http://download.huzheng.org/zh_CN/](http://download.huzheng.org/zh_CN/)
11 |
12 |
13 | Node.js 版本:
14 |
15 | https://github.com/Wulf/nodehun
16 |
17 | [Hunspell 词干提取器](https://www.elastic.co/guide/cn/elasticsearch/guide/current/hunspell.html)
18 |
19 |
20 | 中文分词:
21 |
22 | https://github.com/yanyiwu/nodejieba
23 |
24 | 敏感词:
25 |
26 | https://github.com/aojiaotage/text-censor
27 |
28 | Language Tool
29 | ---
30 |
31 | https://www.languagetool.org/download/
32 |
33 | 在线 API:
34 |
35 | http://wiki.languagetool.org/public-http-api
36 |
--------------------------------------------------------------------------------
/src/native/pages/help.page.ts:
--------------------------------------------------------------------------------
1 | import * as path from "path";
2 |
3 | export function createHelpPage(BrowserWindow: any) {
4 | let helpWindow: any = null;
5 |
6 | function openAboutWindow() {
7 | if (helpWindow) {
8 | helpWindow.focus();
9 | return;
10 | }
11 |
12 | helpWindow = new BrowserWindow({
13 | height: 800,
14 | width: 600,
15 | title: "Markdown 帮助",
16 | backgroundColor: "#ddd",
17 | });
18 |
19 | helpWindow.on("closed", () => {
20 | helpWindow = null;
21 | });
22 |
23 | helpWindow.loadFile(path.join(__dirname, "../../../views/help.html"));
24 |
25 | helpWindow.once("ready-to-show", () => helpWindow.show());
26 | }
27 |
28 | openAboutWindow();
29 | }
30 |
--------------------------------------------------------------------------------
/components/interact/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | os: osx
2 | osx_image: xcode10.2
3 | #dist: bionic
4 | #os: linux
5 | language: node_js
6 | node_js:
7 | - '15'
8 |
9 | cache:
10 | directories:
11 | - node_modules
12 | - "$HOME/.electron"
13 | - "$HOME/.cache"
14 |
15 | addons:
16 | apt:
17 | packages:
18 | - libgnome-keyring-dev
19 | - icnsutils
20 | - xvfb
21 |
22 | install:
23 | - npm install -g xvfb-maybe
24 | - npm install -g yarn
25 | - npm install
26 |
27 | services:
28 | - xvfb
29 |
30 | before_script:
31 | - export DISPLAY=':99.0'
32 | - Xvfb :99 -screen 0 1024x600x24 > /dev/null 2>&1 &
33 | - sleep 3 # give xvfb some time to start
34 |
35 | script:
36 | - PROGRESS="--no-progress" npm run build:native
37 | - PROGRESS="--no-progress" npm run test
38 |
--------------------------------------------------------------------------------
/components/tree-view/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tree-view",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "classnames": "^2.2.6",
7 | "react": "^16.4.1",
8 | "react-dom": "^16.4.1",
9 | "react-scripts": "1.1.4",
10 | "react-ui-tree": "https://github.com/phodal/react-ui-tree"
11 | },
12 | "scripts": {
13 | "start": "react-scripts start && yarn copyfile",
14 | "build": "react-scripts build --watch && yarn copyfile",
15 | "copyfile": "mkdir -p ../../dist/component/tree-view && cp build/static/js/*.js ../../dist/component/tree-view/tree-view.js && cp build/static/css/*.css ../../dist/component/tree-view/tree-view.css",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 |
13 | # Directory for instrumented libs generated by jscoverage/JSCover
14 | lib-cov
15 |
16 | # Coverage directory used by tools like istanbul
17 | coverage
18 |
19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
20 | .grunt
21 |
22 | # node-waf configuration
23 | .lock-wscript
24 |
25 | # Compiled binary addons (http://nodejs.org/api/addons.html)
26 | build/Release
27 |
28 | # Dependency directory
29 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
30 | node_modules
31 | Phodit-darwin-x64/
32 | componets/**/dist
33 | assets/data
34 | logo.png
35 | *.tgz
36 | build
37 | .idea
38 | .DS_Store
39 |
--------------------------------------------------------------------------------
/src/native/support/file-support.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "fs";
2 | import * as path from "path";
3 | const storage = require("electron-json-storage");
4 |
5 | export function dirTree(filename: string) {
6 | let stats;
7 | try {
8 | stats = fs.lstatSync(filename);
9 | } catch (e) {
10 | storage.remove("storage.last.path");
11 | return;
12 | }
13 | const info: any = {
14 | filename,
15 | module: path.basename(filename),
16 | };
17 |
18 | if (stats.isDirectory()) {
19 | info.collapsed = true;
20 | info.children = fs.readdirSync(filename).filter((child: string) => {
21 | return child !== ".git" && child !== ".DS_Store" && child !== ".idea";
22 | }).map((child) => dirTree(filename + "/" + child));
23 | } else {
24 | info.leaf = true;
25 | }
26 |
27 | return info;
28 | }
29 |
--------------------------------------------------------------------------------
/components/interact/src/app/interact-bar.component.css:
--------------------------------------------------------------------------------
1 | .interact-bar {
2 | background: #5e6772;
3 | width: 100%;
4 | max-width: 100%;
5 | height: 64px;
6 | font-size: 12px;
7 | display: inline-block;
8 | }
9 |
10 | .interact-bar .form-group {
11 | display: flex;
12 | }
13 |
14 | .interact-bar label {
15 | color: #fff;
16 | line-height: 64px;
17 | padding-left: 10px;
18 | padding-right: 10px;
19 | width: 120px;
20 | }
21 |
22 | .interact-bar input {
23 | width: calc(100% - 120px);
24 | height: 48px;
25 | margin-top: 10px;
26 | font-size: 16px;
27 | line-height: 64px;
28 | display: inline-block;
29 | font-weight: 400;
30 | color: #5e6772;
31 | background: #fff;
32 | border: 0;
33 | border-radius: 3px;
34 | outline: 0;
35 | text-indent: 4px;
36 | transition: all .3s ease-in-out;
37 | }
38 |
--------------------------------------------------------------------------------
/components/interact/src/app/interact-bar.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, EventEmitter, Input, Output, ViewEncapsulation} from '@angular/core';
2 |
3 | @Component({
4 | // tslint:disable-next-line:component-selector
5 | selector: 'interact-bar',
6 | templateUrl: './interact-bar.component.html',
7 | styleUrls: ['./interact-bar.component.css'],
8 | encapsulation: ViewEncapsulation.Emulated
9 | })
10 | // tslint:disable-next-line:component-class-suffix
11 | export class InteractBar {
12 | @Input() set filename(value: string) {
13 | this.renameModel.name = value;
14 | }
15 |
16 | @Output() action = new EventEmitter();
17 | renameModel = {
18 | name: this.filename
19 | };
20 |
21 | onSubmit() {
22 | if (!this.renameModel.name) {
23 | return;
24 | }
25 | this.action.emit(this.renameModel.name);
26 | }
27 |
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/native/ui/touch-bar.ts:
--------------------------------------------------------------------------------
1 | import {BrowserWindow} from "electron";
2 |
3 | const {TouchBar} = require('electron');
4 |
5 | import {EventConstants} from '../../common/constants/event.constants';
6 |
7 | const {TouchBarButton, TouchBarSpacer} = TouchBar;
8 |
9 | export default function buildTouchBar(mainWindow: BrowserWindow) {
10 | let dark = false;
11 | const theme = new TouchBarButton({
12 | label: '🎰 Theme',
13 | backgroundColor: '#384452',
14 | click: () => {
15 | mainWindow.webContents.send(EventConstants.PHODIT.TOGGLE_THEME, {});
16 | dark = !dark;
17 | if(dark) {
18 | theme.backgroundColor = '#384452';
19 | } else {
20 | theme.backgroundColor = '#fff';
21 | }
22 | }
23 | });
24 |
25 | return new TouchBar({
26 | items: [
27 | theme,
28 | new TouchBarSpacer({size: 'large'}),
29 | ]
30 | });
31 | }
32 |
--------------------------------------------------------------------------------
/components/interact/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './src/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: require('path').join(__dirname, './tsconfig.e2e.json')
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
--------------------------------------------------------------------------------
/src/render/worker/suggest.worker.ts:
--------------------------------------------------------------------------------
1 | const blogpostData = require("../../assets/data/output.json");
2 |
3 | const lunr = require("lunr");
4 | const dataWithIndex: any[] = [];
5 | let lunrIdx: any;
6 |
7 | lunrIdx = lunr(function() {
8 | this.field("title", {boost: 10});
9 | this.field("content");
10 |
11 | for (const item of blogpostData) {
12 | this.add(item);
13 | dataWithIndex[item.id] = item;
14 | }
15 | });
16 |
17 | export class SuggestWorker {
18 | public search(arg: string) {
19 | const searchResults = lunrIdx.search(arg);
20 | const response = [];
21 |
22 | for (const result of searchResults) {
23 | const blogpost = dataWithIndex[result.ref];
24 | response.push({
25 | text: `[${blogpost.title}](https://www.phodal.com/blog/${blogpost.slug})`,
26 | displayText: blogpost.title,
27 | });
28 | }
29 |
30 | return response;
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/components/header/src/components/phodit-header/phodit-header.tsx:
--------------------------------------------------------------------------------
1 | import {Component, State, h} from '@stencil/core';
2 |
3 | @Component({
4 | tag: 'phodit-header',
5 | styleUrl: 'phodit-header.css'
6 | })
7 | export class PhoditHeader {
8 | @State() showCloseHeader = false;
9 |
10 | componentDidLoad() {
11 | var that = this;
12 | window.document.addEventListener('phodit.editor.git.commit', function () {
13 | that.showCloseHeader = true;
14 | })
15 | }
16 |
17 | handleClick() {
18 | var event = new CustomEvent("phodit.editor.hidden.terminal", {});
19 | window.document.dispatchEvent(event);
20 | }
21 |
22 | render() {
23 | if (this.showCloseHeader) {
24 | return (
25 |
26 |
27 |
28 |
);
29 | }
30 |
31 | return ();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/assets/css/themes/dark-theme.css:
--------------------------------------------------------------------------------
1 | /* #22272e */
2 |
3 |
4 | [data-theme^="dark"] .CodeMirror,
5 | [data-theme^="dark"] #input .editor-statusbar,
6 | [data-theme^="dark"] #input .editor-toolbar {
7 | background: #22272e;
8 | }
9 |
10 | #input .CodeMirror.cm-s-django .cm-link {
11 | color: #539bf5;
12 | }
13 |
14 | #input .CodeMirror.cm-s-django,
15 | #input .CodeMirror.cm-s-django .cm-variable-2,
16 | .CodeMirror.cm-s-django .cm-header {
17 | color: #adbac7;
18 | }
19 |
20 | #input .CodeMirror.cm-s-django .cm-quote {
21 | color: #768390;
22 | }
23 |
24 | #input .cm-s-django .cm-header-1 {
25 | font-size: 200%;
26 | line-height: 200%;
27 | }
28 |
29 | /* todo: add more font size */
30 | #input .cm-s-django .cm-header-2 {
31 | font-size: 160%;
32 | line-height: 160%;
33 | }
34 |
35 | #input .cm-s-django .cm-header-3 {
36 | font-size: 125%;
37 | line-height: 125%;
38 | }
39 |
40 | #input .cm-s-django .cm-header-4 {
41 | font-size: 110%;
42 | line-height: 110%;
43 | }
44 |
--------------------------------------------------------------------------------
/src/render/support/file.utils.ts:
--------------------------------------------------------------------------------
1 | export function getFileName(path: string) {
2 | if (!path) {
3 | return path;
4 | }
5 |
6 | const splitArray = path.split("/");
7 | return splitArray[splitArray.length - 1];
8 | }
9 |
10 | export function getCodeMirrorMode(file: string) {
11 | file = getFileName(file);
12 |
13 | if (file.toLowerCase() === "makefile") {
14 | return "cmake";
15 | }
16 |
17 | let format = "gfm";
18 | if (file.endsWith(".css")) {
19 | format = "css";
20 | }
21 | if (file.endsWith(".js")) {
22 | format = "js";
23 | }
24 | if (file.endsWith(".html")) {
25 | format = "xml";
26 | }
27 | if (file.endsWith(".tex")) {
28 | format = "tex";
29 | }
30 | if (file.endsWith(".tex")) {
31 | format = "textile";
32 | }
33 | if (file.endsWith(".xml")) {
34 | format = "xml";
35 | }
36 | if (file.endsWith(".yml")) {
37 | format = "yaml";
38 | }
39 | if (file.endsWith(".md")) {
40 | format = "multiplex";
41 | }
42 | return format;
43 | }
44 |
--------------------------------------------------------------------------------
/docs/spike/shortcuts.md:
--------------------------------------------------------------------------------
1 | 快捷键
2 | ===
3 |
4 | https://electronjs.org/docs/tutorial/keyboard-shortcuts
5 |
6 |
7 | 菜单
8 | ---
9 |
10 | ```javascript
11 | const {Menu, MenuItem} = require('electron')
12 | const menu = new Menu()
13 |
14 | menu.append(new MenuItem({
15 | label: 'Print',
16 | accelerator: 'CmdOrCtrl+P',
17 | click: () => { console.log('time to print stuff') }
18 | }))
19 | ```
20 |
21 | 全局
22 | ---
23 |
24 | ```
25 | const {app, globalShortcut} = require('electron')
26 |
27 | app.on('ready', () => {
28 | globalShortcut.register('CommandOrControl+X', () => {
29 | console.log('CommandOrControl+X is pressed')
30 | })
31 | })
32 | ```
33 |
34 | BrowserWindow
35 | ---
36 |
37 | [Mousetrap](https://github.com/ccampbell/mousetrap)
38 |
39 | ```javascript
40 | // gmail 风格序列
41 | Mousetrap.bind('g i', () => { console.log('go to inbox') })
42 | Mousetrap.bind('* a', () => { console.log('select all') })
43 |
44 | // konami 代码
45 | Mousetrap.bind('up up down down left right left right b a enter', () => {
46 | console.log('konami code')
47 | })
48 | ```
49 |
50 |
--------------------------------------------------------------------------------
/components/interact/src/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: ['html', 'lcovonly'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false
30 | });
31 | };
--------------------------------------------------------------------------------
/components/header/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Ionic
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 |
--------------------------------------------------------------------------------
/components/interact/README.md:
--------------------------------------------------------------------------------
1 | # Interact
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.8.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018~2019 Phodal Huang
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 |
--------------------------------------------------------------------------------
/docs/spike/communication.md:
--------------------------------------------------------------------------------
1 | Electron 通讯
2 | ---
3 |
4 | [Electron 文档](https://electronjs.org/docs/api/ipc-main)
5 |
6 | ## [发送消息](https://electronjs.org/docs/api/ipc-main#%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF)
7 |
8 | 当然也有可能从主进程向渲染进程发送消息,查阅webContents.send 获取更多信息。
9 |
10 | * 发送消息时,事件名称为`channel`。
11 | * 回复同步信息时,需要设置`event.returnValue`。
12 | * 将异步消息发送回发件人,需要使用`event.sender.send(...)`。
13 |
14 | 下面是在渲染和主进程之间发送和处理消息的一个例子:
15 |
16 | ```javascript
17 | // 在主进程中.
18 | const {ipcMain} = require('electron')
19 | ipcMain.on('asynchronous-message', (event, arg) => {
20 | console.log(arg) // prints "ping"
21 | event.sender.send('asynchronous-reply', 'pong')
22 | })
23 |
24 | ipcMain.on('synchronous-message', (event, arg) => {
25 | console.log(arg) // prints "ping"
26 | event.returnValue = 'pong'
27 | })
28 | ```
29 |
30 | ```javascript
31 | //在渲染器进程 (网页) 中。
32 | const {ipcRenderer} = require('electron')
33 | console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong"
34 |
35 | ipcRenderer.on('asynchronous-reply', (event, arg) => {
36 | console.log(arg) // prints "pong"
37 | })
38 | ipcRenderer.send('asynchronous-message', 'ping')
39 | ```
40 |
--------------------------------------------------------------------------------
/docs/spike/nlp.md:
--------------------------------------------------------------------------------
1 | 短语预测
2 | ===
3 |
4 | https://github.com/talyssonoc/jsT9
5 |
6 |
7 | 高亮
8 | ---
9 |
10 | https://github.com/julmot/mark.js
11 |
12 | 文章智能感知
13 | ---
14 |
15 | Trie
16 |
17 | https://gist.github.com/tpae/72e1c54471e88b689f85ad2b3940a8f0
18 |
19 | https://github.com/pthurlow/triejs
20 |
21 | https://johnresig.com/blog/javascript-trie-performance-analysis/
22 |
23 | 中文 nlp
24 | ---
25 |
26 | https://www.zhihu.com/question/19929473
27 |
28 | Python 库:
29 |
30 | https://github.com/huyingxi/Synonyms
31 |
32 | Node.js 版本:
33 |
34 | https://github.com/Samurais/node-synonyms
35 |
36 | OpenNLP 中文:
37 |
38 | https://github.com/mbejda/Node-OpenNLP
39 |
40 | Search Engine
41 | ---
42 |
43 | https://github.com/olivernn/lunr.js/
44 |
45 | lunr.js 不支持中文
46 |
47 | 修改版本:
48 |
49 | https://github.com/codepiano/lunr.js
50 |
51 | https://github.com/weixsong/elasticlunr.js
52 |
53 |
54 | 数据库
55 | ---
56 |
57 | https://github.com/louischatriot/nedb
58 |
59 | https://github.com/techfort/LokiJS
60 |
61 | 自动联想
62 | ---
63 |
64 | https://blog.csdn.net/wendingzhulu/article/details/38168039
65 |
66 | 同义词
67 | ---
68 |
69 | https://github.com/codemirror/CodeMirror/blob/master/demo/complete.html
70 |
--------------------------------------------------------------------------------
/components/header/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **Resources:**
2 | Before submitting an issue, please consult our [docs](https://stenciljs.com/).
3 |
4 | **Stencil version:** (run `npm list @stencil/core` from a terminal/cmd prompt and paste output below):
5 |
6 | ```
7 | insert the output from npm list @stencil/core here
8 | ```
9 |
10 | **I'm submitting a ...** (check one with "x")
11 | [ ] bug report
12 | [ ] feature request
13 | [ ] support request => Please do not submit support requests here, use one of these channels: https://forum.ionicframework.com/ or https://stencil-worldwide.slack.com
14 |
15 | **Current behavior:**
16 |
17 |
18 | **Expected behavior:**
19 |
20 |
21 | **Steps to reproduce:**
22 |
24 |
25 | **Related code:**
26 |
27 | ```
28 | insert any relevant code here
29 | ```
30 |
31 | **Other information:**
32 |
33 |
--------------------------------------------------------------------------------
/components/header/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phodit-header",
3 | "version": "0.0.1",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "phodit-header",
9 | "version": "0.0.1",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "@stencil/core": "^2.7.0"
13 | }
14 | },
15 | "node_modules/@stencil/core": {
16 | "version": "2.7.0",
17 | "resolved": "https://repo.huaweicloud.com/repository/npm/@stencil/core/-/core-2.7.0.tgz",
18 | "integrity": "sha512-5TyOQQzAkvKACRCvjOF4Uq2T527T8WJ9u+EEwtoBA88b7PFjrgI1wbEFAkYGJLghvKi/ED+arV+NiH3ZnbVKXg==",
19 | "dev": true,
20 | "license": "MIT",
21 | "bin": {
22 | "stencil": "bin/stencil"
23 | },
24 | "engines": {
25 | "node": ">=12.10.0",
26 | "npm": ">=6.0.0"
27 | }
28 | }
29 | },
30 | "dependencies": {
31 | "@stencil/core": {
32 | "version": "2.7.0",
33 | "resolved": "https://repo.huaweicloud.com/repository/npm/@stencil/core/-/core-2.7.0.tgz",
34 | "integrity": "sha512-5TyOQQzAkvKACRCvjOF4Uq2T527T8WJ9u+EEwtoBA88b7PFjrgI1wbEFAkYGJLghvKi/ED+arV+NiH3ZnbVKXg==",
35 | "dev": true
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/components/header/src/components.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /* tslint:disable */
3 | /**
4 | * This is an autogenerated file created by the Stencil compiler.
5 | * It contains typing information for all components that exist in this project.
6 | */
7 | import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
8 | export namespace Components {
9 | interface PhoditHeader {
10 | }
11 | }
12 | declare global {
13 | interface HTMLPhoditHeaderElement extends Components.PhoditHeader, HTMLStencilElement {
14 | }
15 | var HTMLPhoditHeaderElement: {
16 | prototype: HTMLPhoditHeaderElement;
17 | new (): HTMLPhoditHeaderElement;
18 | };
19 | interface HTMLElementTagNameMap {
20 | "phodit-header": HTMLPhoditHeaderElement;
21 | }
22 | }
23 | declare namespace LocalJSX {
24 | interface PhoditHeader {
25 | }
26 | interface IntrinsicElements {
27 | "phodit-header": PhoditHeader;
28 | }
29 | }
30 | export { LocalJSX as JSX };
31 | declare module "@stencil/core" {
32 | export namespace JSX {
33 | interface IntrinsicElements {
34 | "phodit-header": LocalJSX.PhoditHeader & JSXBase.HTMLAttributes;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/test/input-test.js:
--------------------------------------------------------------------------------
1 | const helpers = require('./global-setup');
2 | const path = require('path');
3 | const fs = require('fs');
4 |
5 | const describe = global.describe;
6 | const it = global.it;
7 | const beforeEach = global.beforeEach;
8 | const afterEach = global.afterEach;
9 | const expect = require('chai').expect;
10 |
11 | describe('input test', function () {
12 | beforeEach(function () {
13 | return helpers
14 | .startApplication({
15 | args: [path.join(__dirname, '..')],
16 | })
17 | .then(function (startedApp) {
18 | app = startedApp;
19 | });
20 | });
21 |
22 | afterEach(function () {
23 | return helpers.stopApplication(app);
24 | });
25 |
26 | describe('webContents.sendInputEvent', function () {
27 | xit('triggers a keypress DOM event', async function () {
28 | await app.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A' });
29 | const elem = await app.client.$('.keypress-count');
30 | console.log(elem);
31 | let text = await elem.getText();
32 | expect(text).to.equal('A');
33 | await app.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'B' });
34 | text = await elem.getText();
35 | expect(text).to.equal('B');
36 | });
37 | });
38 | });
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/render/plugins/terminal.ts:
--------------------------------------------------------------------------------
1 | const os = require("os");
2 | const pty = require("node-pty");
3 | const Terminal = require("xterm").Terminal;
4 | const FitAddon = require('xterm-addon-fit').FitAddon;
5 | const WebLinksAddon = require('xterm-addon-web-links').WebLinksAddon;
6 |
7 | export function createTerminal(path?: string) {
8 | // Initialize node-pty with an appropriate shell
9 | const shell = process.env[os.platform() === "win32" ? "COMSPEC" : "SHELL"];
10 | const ptyProcess = pty.spawn(shell, [], {
11 | name: 'xterm-color',
12 | cols: 80,
13 | rows: 15,
14 | cwd: path || process.cwd(),
15 | env: process.env
16 | });
17 |
18 | // Initialize xterm.js and attach it to the DOM
19 | const xterm = new Terminal({
20 | cols: 80,
21 | rows: 15,
22 | cursorBlink: true,
23 | });
24 | const fitaddon = new FitAddon();
25 | xterm.loadAddon(fitaddon);
26 | xterm.loadAddon(new WebLinksAddon());
27 |
28 | xterm.open(document.getElementById("terminal"));
29 | fitaddon.fit();
30 |
31 | // Setup communication between xterm.js and node-pty
32 | xterm.onData((data: any) => {
33 | ptyProcess.write(data);
34 | });
35 |
36 | ptyProcess.on("data", (data: any) => {
37 | xterm.write(data);
38 | });
39 |
40 | ptyProcess.write("git status \n");
41 | }
42 |
--------------------------------------------------------------------------------
/test/webview-test.js:
--------------------------------------------------------------------------------
1 | const helpers = require('./global-setup');
2 | const path = require('path');
3 | const { expect } = require('chai');
4 |
5 | const describe = global.describe;
6 | const it = global.it;
7 | const beforeEach = global.beforeEach;
8 | const afterEach = global.afterEach;
9 |
10 | describe(' tags', function () {
11 | helpers.setupTimeout(this);
12 |
13 | let app = null;
14 |
15 | beforeEach(function () {
16 | return helpers
17 | .startApplication({
18 | args: [path.join(__dirname, '..')]
19 | })
20 | .then(function (startedApp) {
21 | app = startedApp;
22 | });
23 | });
24 |
25 | afterEach(function () {
26 | return helpers.stopApplication(app);
27 | });
28 |
29 | it('allows the web view to be accessed', async function () {
30 | // waiting for windowHandles ensures waitUntilWindowLoaded doesn't access a nil webContents.
31 | // TODO: this issue should be fixed by waitUntilWindowLoaded instead of this workaround.
32 | await app.client.waitUntilWindowLoaded();
33 | const elem = await app.client.$('body .editor-statusbar');
34 | // const text = await elem.getText();
35 | // expect(text).to.equal('web view');
36 | app.webContents.getTitle().should.eventually.equal('Web View');
37 | });
38 | });
39 |
40 |
--------------------------------------------------------------------------------
/assets/css/themes/monokai-sublime.css:
--------------------------------------------------------------------------------
1 | .cm-s-django {
2 | font-size: 1em;
3 | line-height: 1.5em;
4 | font-family: inconsolata, monospace;
5 | letter-spacing: 0.3px;
6 | word-spacing: 1px;
7 | background: #0B2F20;
8 | color: #F8F8F8;
9 | }
10 | .cm-s-django .CodeMirror-lines {
11 | padding: 8px 0;
12 | }
13 | .cm-s-django .CodeMirror-gutters {
14 | box-shadow: 1px 0 2px 0 rgba(0, 0, 0, 0.5);
15 | -webkit-box-shadow: 1px 0 2px 0 rgba(0, 0, 0, 0.5);
16 | background-color: #0B2F20;
17 | padding-right: 10px;
18 | z-index: 3;
19 | border: none;
20 | }
21 | .cm-s-django div.CodeMirror-cursor {
22 | border-left: 3px solid #F8F8F8;
23 | }
24 | .cm-s-django .CodeMirror-activeline-background {
25 | background: #0000004A;
26 | }
27 | .cm-s-django .CodeMirror-selected {
28 | background: #245032;
29 | }
30 | .cm-s-django .cm-comment {
31 | font-style: italic;
32 | color: #245032;
33 | }
34 | .cm-s-django .cm-keyword {
35 | color: #96DD3B;
36 | }
37 | .cm-s-django .cm-string {
38 | color: #91BB9E;
39 | }
40 | .cm-s-django .cm-property {
41 | color: #FFB454;
42 | }
43 | .cm-s-django .cm-atom {
44 | color: #FFB454;
45 | }
46 | .cm-s-django .cm-number {
47 | color: #FFB454;
48 | }
49 | .cm-s-django .cm-operator {
50 | color: #96DD3B;
51 | }
52 | .cm-s-django .CodeMirror-linenumber {
53 | color: italic;
54 | }
55 |
--------------------------------------------------------------------------------
/test/command-test.js:
--------------------------------------------------------------------------------
1 | const helpers = require('./global-setup');
2 | const path = require('path');
3 | const fs = require('fs');
4 |
5 | const describe = global.describe;
6 | const it = global.it;
7 | const beforeEach = global.beforeEach;
8 | const afterEach = global.afterEach;
9 | const expect = require('chai').expect;
10 |
11 | describe('ipc command test', function () {
12 | beforeEach(function () {
13 | return helpers
14 | .startApplication({
15 | args: [path.join(__dirname, '..')],
16 | })
17 | .then(function (startedApp) {
18 | app = startedApp;
19 | });
20 | });
21 |
22 | afterEach(function () {
23 | return helpers.stopApplication(app);
24 | });
25 |
26 | describe('electron.ipcRenderer.send', function () {
27 | xit('sends the message to the main process', async function () {
28 | let ipcCount = await app.electron.remote.getGlobal('ipcEventCount');
29 | expect(ipcCount).to.equal(0);
30 | await app.electron.ipcRenderer.send('ipc-event', 123);
31 | ipcCount = await app.electron.remote.getGlobal('ipcEventCount');
32 | expect(ipcCount).to.equal(123);
33 | await app.electron.ipcRenderer.send('ipc-event', 456);
34 | ipcCount = await app.electron.remote.getGlobal('ipcEventCount');
35 | expect(ipcCount).to.equal(579);
36 | });
37 | });
38 | });
39 |
40 |
41 |
--------------------------------------------------------------------------------
/components/header/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phodit-header",
3 | "version": "0.0.1",
4 | "description": "Stencil Component Starter",
5 | "module": "dist/custom-elements/index.js",
6 | "main": "dist/index.cjs.js",
7 | "types": "dist/types/components.d.ts",
8 | "collection": "dist/collection/collection-manifest.json",
9 | "files": [
10 | "dist/"
11 | ],
12 | "scripts": {
13 | "build": "stencil build && mkdir -p ../../dist/component/header && cp -a dist/ ../../dist/component/header",
14 | "start": "stencil build --dev --watch --serve",
15 | "test": "jest",
16 | "test.watch": "jest --watch"
17 | },
18 | "dependencies": {},
19 | "devDependencies": {
20 | "@stencil/core": "^2.7.0"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/ionic-team/stencil-component-starter.git"
25 | },
26 | "author": "Ionic Team",
27 | "license": "MIT",
28 | "bugs": {
29 | "url": "https://github.com/ionic-team/stencil"
30 | },
31 | "homepage": "https://github.com/ionic-team/stencil",
32 | "jest": {
33 | "transform": {
34 | "^.+\\.(ts|tsx)$": "/node_modules/@stencil/core/testing/jest.preprocessor.js"
35 | },
36 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(tsx?|jsx?)$",
37 | "moduleFileExtensions": [
38 | "ts",
39 | "tsx",
40 | "js",
41 | "json",
42 | "jsx"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/test/global-setup.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 | const chai = require('chai');
3 | const chaiAsPromised = require('chai-as-promised');
4 | const chaiRoughly = require('chai-roughly');
5 | const Application = require('spectron').Application;
6 |
7 | const path = require('path');
8 |
9 | global.before(function () {
10 | chai.should();
11 | chai.use(chaiAsPromised);
12 | chai.use(chaiRoughly);
13 | });
14 |
15 | exports.getElectronPath = function () {
16 | let electronPath = path.join(
17 | __dirname,
18 | '..',
19 | 'node_modules',
20 | '.bin',
21 | 'electron'
22 | );
23 | if (process.platform === 'win32') electronPath += '.cmd';
24 | return electronPath;
25 | };
26 |
27 | exports.setupTimeout = function (test) {
28 | if (process.env.CI) {
29 | test.timeout(30000);
30 | } else {
31 | test.timeout(10000);
32 | }
33 | };
34 |
35 | exports.startApplication = function (options) {
36 | options.path = exports.getElectronPath();
37 | if (process.env.CI) options.startTimeout = 30000;
38 |
39 | const app = new Application(options);
40 | return app.start().then(function () {
41 | assert.strictEqual(app.isRunning(), true);
42 | chaiAsPromised.transferPromiseness = app.transferPromiseness;
43 | return app;
44 | });
45 | };
46 |
47 | exports.stopApplication = async function (app) {
48 | if (!app || !app.isRunning()) return;
49 |
50 | await app.stop();
51 | assert.strictEqual(app.isRunning(), false);
52 | };
53 |
--------------------------------------------------------------------------------
/components/header/src/components/phodit-header/phodit-header.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestWindow } from '@stencil/core/testing';
2 | import { PhoditHeader } from './phodit-header';
3 |
4 | describe('my-component', () => {
5 | it('should build', () => {
6 | expect(new PhoditHeader()).toBeTruthy();
7 | });
8 |
9 | describe('rendering', () => {
10 | let element: any;
11 | let testWindow: TestWindow;
12 | beforeEach(async () => {
13 | testWindow = new TestWindow();
14 | element = await testWindow.load({
15 | components: [PhoditHeader],
16 | html: ''
17 | });
18 | });
19 |
20 | it('should work without parameters', () => {
21 | expect(element.textContent.trim()).toEqual('Hello, World! I\'m');
22 | });
23 |
24 | it('should work with a first name', async () => {
25 | element.first = 'Peter';
26 | await testWindow.flush();
27 | expect(element.textContent.trim()).toEqual('Hello, World! I\'m Peter');
28 | });
29 |
30 | it('should work with a last name', async () => {
31 | element.last = 'Parker';
32 | await testWindow.flush();
33 | expect(element.textContent.trim()).toEqual('Hello, World! I\'m Parker');
34 | });
35 |
36 | it('should work with both a first and a last name', async () => {
37 | element.first = 'Peter';
38 | element.last = 'Parker';
39 | await testWindow.flush();
40 | expect(element.textContent.trim()).toEqual('Hello, World! I\'m Peter Parker');
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/src/native/pages/silde.page.ts:
--------------------------------------------------------------------------------
1 | import * as path from "path";
2 | import {pandoc} from "../features/pandoc";
3 | import * as fs from "fs";
4 | import {ipcMain} from "electron";
5 |
6 | export function createSlidePage(BrowserWindow: any, arg: any) {
7 | let slideWindow: any = null;
8 |
9 | function createSlidePage(data: any) {
10 | if (slideWindow) {
11 | slideWindow.focus();
12 | return;
13 | }
14 |
15 | slideWindow = new BrowserWindow({
16 | height: 1440,
17 | width: 960,
18 | title: "Slide",
19 | fullscreen: true,
20 | });
21 |
22 | slideWindow.on("closed", () => {
23 | slideWindow = null;
24 | });
25 |
26 |
27 | slideWindow.loadFile(path.join(__dirname, "../../../views/slide.html"));
28 |
29 | slideWindow.once("ready-to-show", () => slideWindow.show());
30 | slideWindow.webContents.on("did-finish-load", () => {
31 |
32 | });
33 |
34 | slideWindow.openDevTools();
35 |
36 | if (!slideWindow.webContents) {
37 | createSlidePage(data);
38 | }
39 |
40 | ipcMain.on("phodit.slide.ready", () => {
41 | if (slideWindow && slideWindow.webContents) {
42 | slideWindow.webContents.send("phodit.slide.send.content", data);
43 | }
44 | });
45 | }
46 |
47 | pandoc.slide(arg.file).then((filePath: any) => {
48 | fs.readFile(filePath, "utf-8", (err, data) => {
49 | if (err) {
50 | console.log("An error ocurred reading the file :" + err.message);
51 | return;
52 | }
53 |
54 | createSlidePage(data);
55 | });
56 | });
57 | }
58 |
--------------------------------------------------------------------------------
/docs/adr/README.md:
--------------------------------------------------------------------------------
1 | # 架构决策记录
2 |
3 | * [1. 系统进行-discover-设计](0001-系统进行-discover-设计.md)
4 | * [2. 中文-nlp-相关功能](0002-中文-nlp-相关功能.md)
5 | * [3. 支持批注](0003-支持批注.md)
6 | * [4. 统计打字速度及显示活跃时间](0004-统计打字速度及显示活跃时间.md)
7 | * [5. 段落折叠功能](0005-段落折叠功能.md)
8 | * [6. 新增复制图片下载到本地的功能](0006-新增复制图片下载到本地的功能.md)
9 | * [7. autocomplete->-冒号后自动换行并加-item-标识符](0007-autocomplete->-冒号后自动换行并加-item-标识符.md)
10 | * [8. 另存为功能](0008-另存为功能.md)
11 | * [9. 搜索引擎或者是数据库](0009-搜索引擎或者是数据库.md)
12 | * [10. 全文搜索文本支持](0010-全文搜索文本支持.md)
13 | * [11. 监测到文件变化自动更新目录](0011-监测到文件变化自动更新目录.md)
14 | * [12. 使用-pandoc-来转换-slide](0012-使用-pandoc-来转换-slide.md)
15 | * [13. 使用-toast-来实现简单的提醒功能](0013-使用-toast-来实现简单的提醒功能.md)
16 | * [14. 实现重命名文件等对话框](0014-实现重命名文件等对话框.md)
17 | * [15. 可配置的搜索菜单](0015-可配置的搜索菜单.md)
18 | * [16. 引入操作日志](0016-引入操作日志.md)
19 | * [17. 支持记住文件的行号](0017-支持记住文件的行号.md)
20 | * [18. 支持内容中的待办事项列表-todo](0018-支持内容中的待办事项列表-todo.md)
21 | * [19. 支持选择本地图片](0019-支持选择本地图片.md)
22 | * [20. submline-text-类似的管口滚动](0020-submline-text-类似的管口滚动.md)
23 | * [21. 自动发布系统-dsl](0021-自动发布系统-dsl.md)
24 | * [22. [tabs]-支持](0022-[tabs]-支持.md)
25 | * [23. [pdf]-支持编译文章成-pdf](0023-[pdf]-支持编译文章成-pdf.md)
26 | * [24. 添加不同的显示字体切换](0024-添加不同的显示字体切换.md)
27 | * [25. 添加复制、粘贴图片功能](0025-添加复制、粘贴图片功能.md)
28 | * [26. 添加粘贴图片到目录的支持](0026-添加粘贴图片到目录的支持.md)
29 | * [27. 粘贴内容到编辑器时转为-markdown-格式](0027-粘贴内容到编辑器时转为-markdown-格式.md)
30 | * [28. [bug]-修改重命名文件后的保存问题](0028-[bug]-修改重命名文件后的保存问题.md)
31 | * [29. [feat]-添加审校功能](0029-[feat]-添加审校功能.md)
32 | * [30. 进度条:根据行数和当前行数来显示百分比](0030-进度条:根据行数和当前行数来显示百分比.md)
33 | * [31. 屏保或定时休息提醒](0031-屏保或定时休息提醒.md)
34 | * [32. touchbar-支持](0032-touchbar-支持.md)
35 | * [33. multiple-instances](0033-multiple-instances.md)
36 |
--------------------------------------------------------------------------------
/components/interact/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "interact",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build --prod --output-hashing=none && yarn package",
8 | "package": "mkdir -p ../../dist/component/interact && cat dist/interact/{runtime,polyfills,main}-es2015.js > ../../dist/component/interact/interact.js",
9 | "test": "ng test",
10 | "lint": "ng lint",
11 | "e2e": "ng e2e"
12 | },
13 | "private": true,
14 | "dependencies": {
15 | "@angular/animations": "~11.0.1",
16 | "@angular/common": "~11.0.1",
17 | "@angular/compiler": "~11.0.1",
18 | "@angular/elements": "~11.0.1",
19 | "@angular/core": "~11.0.1",
20 | "@angular/forms": "~11.0.1",
21 | "@angular/platform-browser": "~11.0.1",
22 | "@angular/platform-browser-dynamic": "~11.0.1",
23 | "@angular/router": "~11.0.1",
24 | "rxjs": "~6.6.0",
25 | "tslib": "^2.0.0",
26 | "zone.js": "~0.10.2"
27 | },
28 | "devDependencies": {
29 | "@angular-devkit/build-angular": "~0.1100.2",
30 | "@angular/cli": "~11.0.2",
31 | "@angular/compiler-cli": "~11.0.1",
32 | "@types/jasmine": "~3.6.0",
33 | "@types/node": "^12.11.1",
34 | "codelyzer": "^6.0.0",
35 | "jasmine-core": "~3.6.0",
36 | "jasmine-spec-reporter": "~5.0.0",
37 | "karma": "~5.1.0",
38 | "karma-chrome-launcher": "~3.1.0",
39 | "karma-coverage": "~2.0.3",
40 | "karma-jasmine": "~4.0.0",
41 | "karma-jasmine-html-reporter": "^1.5.0",
42 | "protractor": "~7.0.0",
43 | "ts-node": "~8.3.0",
44 | "tslint": "~6.1.0",
45 | "typescript": "~4.0.2"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/components/tree-view/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | text-align: left;
6 | }
7 |
8 | .f-no-select, .m-tree {
9 | -webkit-user-select: none;
10 | -khtml-user-select: none;
11 | -moz-user-select: none;
12 | -ms-user-select: none;
13 | user-select: none
14 | }
15 |
16 | .m-tree {
17 | position: relative;
18 | overflow: hidden
19 | }
20 |
21 | .m-draggable {
22 | position: absolute;
23 | opacity: .8;
24 | -webkit-user-select: none;
25 | -khtml-user-select: none;
26 | -moz-user-select: none;
27 | -ms-user-select: none;
28 | user-select: none
29 | }
30 |
31 | .m-node.placeholder > * {
32 | visibility: hidden
33 | }
34 |
35 | .m-node.placeholder {
36 | border: 1px dashed #ccc
37 | }
38 |
39 | .m-node .inner {
40 | position: relative;
41 | cursor: pointer;
42 | padding-left: 10px
43 | }
44 |
45 | .m-node .collapse {
46 | position: absolute;
47 | left: 0;
48 | cursor: pointer
49 | }
50 |
51 | .m-node .caret-right:before {
52 | content: "\25B8"
53 | }
54 |
55 | .m-node .caret-down:before {
56 | content: "\25BE"
57 | }
58 |
59 | .tree {
60 | position: fixed;
61 | top: 0;
62 | left: 0;
63 | bottom: 0;
64 | overflow-x: hidden;
65 | overflow-y: auto;
66 | background-color: #21252b
67 | }
68 |
69 | .m-node.placeholder {
70 | border: 1px dashed #1385e5
71 | }
72 |
73 | .m-node .inner {
74 | color: #9da5b4;
75 | font-size: 12px;
76 | }
77 |
78 | .m-node .node {
79 | display: inline-block;
80 | width: 100%;
81 | padding: 2px 3px;
82 | text-align: left;
83 | }
84 |
85 | .m-node .node.is-active {
86 | background-color: #31363f
87 | }
88 |
--------------------------------------------------------------------------------
/assets/css/themes/monokai-bright.css:
--------------------------------------------------------------------------------
1 | .cm-s-monokai-bright {
2 | font-size: 1em;
3 | line-height: 1.5em;
4 | font-family: inconsolata, monospace;
5 | letter-spacing: 0.3px;
6 | word-spacing: 1px;
7 | background: #272822;
8 | color: #F8F8F2;
9 | }
10 |
11 | .cm-s-monokai-bright .CodeMirror-lines {
12 | padding: 8px 0;
13 | }
14 |
15 | .cm-s-monokai-bright .CodeMirror-gutters {
16 | box-shadow: 1px 0 2px 0 rgba(0, 0, 0, 0.5);
17 | -webkit-box-shadow: 1px 0 2px 0 rgba(0, 0, 0, 0.5);
18 | background-color: #272822;
19 | padding-right: 10px;
20 | z-index: 3;
21 | border: none;
22 | }
23 |
24 | .cm-s-monokai-bright div.CodeMirror-cursor {
25 | border-left: 3px solid #F8F8F2;
26 | }
27 |
28 | .cm-s-monokai-bright .CodeMirror-activeline-background {
29 | background: #3E3D32;
30 | }
31 |
32 | .cm-s-monokai-bright .CodeMirror-selected {
33 | background: #9D550F;
34 | }
35 |
36 | .cm-s-monokai-bright .cm-comment {
37 | color: #75715E;
38 | }
39 |
40 | .cm-s-monokai-bright .cm-string {
41 | color: #E6DB74;
42 | }
43 |
44 | .cm-s-monokai-bright .cm-number {
45 | color: #66D9EF;
46 | }
47 |
48 | .cm-s-monokai-bright .cm-atom {
49 | color: #66D9EF;
50 | }
51 |
52 | .cm-s-monokai-bright .cm-keyword {
53 | color: #F92672;
54 | }
55 |
56 | .cm-s-monokai-bright .cm-variable {
57 | color: #A6E22E;
58 | }
59 |
60 | .cm-s-monokai-bright .cm-def {
61 | font-style: italic;
62 | color: #FD971F;
63 | }
64 |
65 | .cm-s-monokai-bright .cm-variable-2 {
66 | color: #F92672;
67 | }
68 |
69 | .cm-s-monokai-bright .cm-property {
70 | color: #66D9EF;
71 | }
72 |
73 | .cm-s-monokai-bright .cm-operator {
74 | color: #F92672;
75 | }
76 |
77 | .cm-s-monokai-bright .CodeMirror-linenumber {
78 | color: #75715E;
79 | }
80 |
--------------------------------------------------------------------------------
/test/basic-test.js:
--------------------------------------------------------------------------------
1 | // Test for examples included in README.md
2 | const helpers = require('./global-setup');
3 | const path = require('path');
4 | const {expect} = require('chai');
5 |
6 | const describe = global.describe;
7 | const it = global.it;
8 | const beforeEach = global.beforeEach;
9 | const afterEach = global.afterEach;
10 |
11 | describe('example application launch', function () {
12 | helpers.setupTimeout(this);
13 |
14 | let app = null;
15 |
16 | beforeEach(function () {
17 | return helpers
18 | .startApplication({
19 | args: [path.join(__dirname, '..')]
20 | })
21 | .then(function (startedApp) {
22 | app = startedApp;
23 | });
24 | });
25 |
26 | afterEach(function () {
27 | return helpers.stopApplication(app);
28 | });
29 |
30 | it('opens a window', async function () {
31 | await app.client.waitUntilWindowLoaded();
32 | app.browserWindow.focus();
33 | const windowCount = await app.client.getWindowCount();
34 | expect(windowCount).to.equal(1);
35 | const isMinimized = await app.browserWindow.isMinimized();
36 | expect(isMinimized).to.equal(false);
37 | const isDevOpen = await app.browserWindow.isDevToolsOpened();
38 | expect(isDevOpen).to.equal(false);
39 | const isVisible = await app.browserWindow.isVisible();
40 | expect(isVisible).to.equal(true);
41 | const isFocused = await app.browserWindow.isFocused();
42 | expect(isFocused).to.equal(true);
43 | app.browserWindow
44 | .getBounds()
45 | .should.eventually.have.property('width')
46 | .and.be.above(0);
47 | app.browserWindow
48 | .getBounds()
49 | .should.eventually.have.property('height')
50 | .and.be.above(0);
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/src/common/constants/event.constants.ts:
--------------------------------------------------------------------------------
1 | export const EventConstants = {
2 | TEMP_FILE_STATUS: "phodit.temp.file.status",
3 | PHODIT: {
4 | LOADED: "phodit.lifecycle.load",
5 | OPEN_PATH: "phodit.open.path",
6 | RELOAD_PATH: "phodit.reload.path",
7 | SAVE_FILE: "phodit.save.file",
8 | OPEN_ONE_FILE: "phodit.open.one-file",
9 | OPEN_FILE: "phodit.open.file",
10 | SUGGEST_SEND: "phodit.suggest.send",
11 | SUGGEST_TO_EDITOR: "phodit.editor.suggest.receive",
12 | GET_SUGGEST: "phodit.suggest.get",
13 | FULL_SCREEN: "phodit.fullscreen",
14 | UN_FULL_SCREEN: "phodit.unfullscreen",
15 | OPEN_GUIDE: "phodit.open.guide",
16 | SHOW_SLIDES: "phodit.show.echoesworks",
17 | SHOW_WORD: "phodit.show.word",
18 | SHOW_PDF: "phodit.show.pdf",
19 | GIT_STATUS: "phodit.git.status",
20 | OPEN_SYSTEM_PATH: "phodit.system.open.path",
21 | TOGGLE_THEME: "phodit.theme.toggle",
22 | SET_THEME: "phodit.theme.set",
23 | },
24 | CLIENT: {
25 | SAVE_FILE: "client.save.file",
26 | GET_SUGGEST: "phodit.editor.suggest.get",
27 | FULL_SCREEN: "phodit.editor.fullscreen",
28 | UN_FULL_SCREEN: "phodit.editor.unfullscreen",
29 | SHOW_WORD: "phodit.editor.show.word",
30 | SHOW_PDF: "phodit.editor.show.pdf",
31 | SHOW_TERMINAL: "phodit.editor.git.commit",
32 | HIDDEN_TERMINAL: "phodit.editor.hidden.terminal",
33 | OPEN_GUIDE: "phodit.editor.open.guide",
34 | TREE_OPEN: "tree.pub.open",
35 | FILE_MENU_CLICK: "tree.right.click",
36 | SHOW_SLIDES: "phodit.editor.show.slides",
37 | SEND_MARKDOWN: "phodit.editor.send.result",
38 | GET_RENDERER_MARKDOWN: "phodit.editor.get.result",
39 | SHOW_SIDE: "phodit.editor.showside",
40 | HIDDEN_SIDE: "phodit.editor.hiddenside",
41 | TOGGLE_THEME: "phodit.editor.theme.toggle",
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/assets/css/themes/default.css:
--------------------------------------------------------------------------------
1 |
2 | .CodeMirror .cm-header {
3 | color: #44475a;
4 | }
5 |
6 | .CodeMirror .cm-quote {
7 | color: #090;
8 | }
9 |
10 | .cm-negative {
11 | color: #d44;
12 | }
13 |
14 | .cm-positive {
15 | color: #292;
16 | }
17 |
18 | .cm-header, .cm-strong {
19 | font-weight: bold;
20 | }
21 |
22 | .cm-em {
23 | font-style: italic;
24 | }
25 |
26 | .cm-link {
27 | text-decoration: underline;
28 | }
29 |
30 | .cm-strikethrough {
31 | text-decoration: line-through;
32 | }
33 |
34 | .CodeMirror .cm-keyword {
35 | color: #708;
36 | }
37 |
38 | .CodeMirror .cm-atom {
39 | color: #219;
40 | }
41 |
42 | .CodeMirror .cm-number {
43 | color: #164;
44 | }
45 |
46 | .CodeMirror .cm-def {
47 | color: #00f;
48 | }
49 |
50 | .CodeMirror .cm-variable,
51 | .CodeMirror .cm-punctuation,
52 | .CodeMirror .cm-property,
53 | .CodeMirror .cm-operator {
54 | }
55 |
56 | .CodeMirror .cm-variable-2 {
57 | color: #05a;
58 | }
59 |
60 | .CodeMirror .cm-variable-3, .CodeMirror .cm-type {
61 | color: #085;
62 | }
63 |
64 | .CodeMirror .cm-comment {
65 | color: #a50;
66 | }
67 |
68 | .CodeMirror .cm-string {
69 | color: #a11;
70 | }
71 |
72 | .CodeMirror .cm-string-2 {
73 | color: #f50;
74 | }
75 |
76 | .CodeMirror .cm-meta {
77 | color: #555;
78 | }
79 |
80 | .CodeMirror .cm-qualifier {
81 | color: #555;
82 | }
83 |
84 | .CodeMirror .cm-builtin {
85 | color: #30a;
86 | }
87 |
88 | .CodeMirror .cm-bracket {
89 | color: #997;
90 | }
91 |
92 | .CodeMirror .cm-tag {
93 | color: #170;
94 | }
95 |
96 | .CodeMirror .cm-attribute {
97 | color: #00c;
98 | }
99 |
100 | .CodeMirror .cm-hr {
101 | color: #999;
102 | }
103 |
104 | .CodeMirror .cm-link {
105 | color: #00c;
106 | }
107 |
108 | .CodeMirror .cm-error {
109 | color: #f00;
110 | }
111 |
112 | .cm-invalidchar {
113 | color: #f00;
114 | }
115 |
--------------------------------------------------------------------------------
/views/slide.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | README
8 |
9 |
10 |
11 |
12 |
18 |
19 |
20 |
27 |
28 |
29 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/render/support/markdown.utils.ts:
--------------------------------------------------------------------------------
1 | const marked = require("marked");
2 |
3 | class MarkdownImprove {
4 | private file: string;
5 | private marked: any;
6 |
7 | // tslint:disable-next-line:no-shadowed-variable
8 | constructor(file: string, marked?: any) {
9 | this.file = file;
10 | this.marked = marked;
11 | }
12 |
13 | public fixedImagePath(text: any) {
14 | const imgRegex = /!\[[^\]]+\]\(([^)]+)\)/;
15 | const imgRegexGlobal = /!\[[^\]]+\]\(([^)]+)\)/gi;
16 | const that = this;
17 | const matches = text.match(imgRegexGlobal);
18 | if (matches && matches.length > 0) {
19 | matches.forEach((image: any) => {
20 | const originImage = image;
21 | if (imgRegex.test(image)) {
22 | const result = imgRegex.exec(image);
23 | const newFilePath = getFileRelativePath(that.file, result[1]);
24 | image = image.replace(/\(([^)]+)\)/, "(" + newFilePath + ")");
25 | text = text.replace(originImage, image);
26 | }
27 | });
28 | }
29 | return text;
30 | }
31 | }
32 |
33 | export function markdownRender(text: string, file: string) {
34 | // Options list
35 | // https://marked.js.org/#/USING_ADVANCED.md
36 | const renderer = new marked.Renderer();
37 | renderer.listitem = (item: any) => {
38 | return '' + item + "
\n";
39 | };
40 |
41 | marked.setOptions({
42 | renderer,
43 | highlight(code: string) {
44 | return require("highlight.js").highlightAuto(code).value;
45 | },
46 | footnote: true,
47 | pedantic: false,
48 | gfm: true,
49 | tables: true,
50 | breaks: false,
51 | sanitize: false,
52 | smartLists: true,
53 | smartypants: false,
54 | xhtml: false,
55 | });
56 |
57 | if (file) {
58 | const markdownImprove = new MarkdownImprove(file);
59 | text = markdownImprove.fixedImagePath(text);
60 | }
61 | return marked(text);
62 | }
63 |
64 | export function removeLastDirectoryPartOf(path: string) {
65 | if (!path) {
66 | return path;
67 | }
68 |
69 | const splitArray = path.split("/");
70 | splitArray.pop();
71 | return (splitArray.join("/"));
72 | }
73 |
74 | function getFileRelativePath(path: string, imgFilePath: string) {
75 | return `${removeLastDirectoryPartOf(path)}/${imgFilePath}`;
76 | }
77 |
--------------------------------------------------------------------------------
/components/tree-view/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tree-view",
3 | "version": "0.1.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "classnames": {
8 | "version": "2.2.6",
9 | "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
10 | "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
11 | },
12 | "js-tokens": {
13 | "version": "4.0.0",
14 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
15 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
16 | },
17 | "js-tree": {
18 | "version": "2.0.1",
19 | "resolved": "https://registry.npmjs.org/js-tree/-/js-tree-2.0.1.tgz",
20 | "integrity": "sha512-otG8ZDEQP9PW+80nyXcrsJYB0Mj6vMy673u0/oYqyOZhIBrniOnR3pyL9D0FIDzpZGCaiGbspgkBHZeJinR1PQ=="
21 | },
22 | "loose-envify": {
23 | "version": "1.4.0",
24 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
25 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
26 | "requires": {
27 | "js-tokens": "4.0.0"
28 | }
29 | },
30 | "object-assign": {
31 | "version": "4.1.1",
32 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
33 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
34 | },
35 | "prop-types": {
36 | "version": "15.6.2",
37 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
38 | "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
39 | "requires": {
40 | "loose-envify": "1.4.0",
41 | "object-assign": "4.1.1"
42 | }
43 | },
44 | "react-ui-tree": {
45 | "version": "4.0.0",
46 | "resolved": "https://registry.npmjs.org/react-ui-tree/-/react-ui-tree-4.0.0.tgz",
47 | "integrity": "sha512-2y649h0psahtYb4NW5KK/zcwnzH0Di4a0PPzsorNyHzLGr5fXGB4MyubMcZ1BGD5nZFIATalvCeSJ/YDvIriVw==",
48 | "requires": {
49 | "classnames": "2.2.6",
50 | "js-tree": "2.0.1",
51 | "prop-types": "15.6.2"
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Phodit - 私人定制的 Markdown 编辑器
2 |
3 | [](https://travis-ci.org/phodal/phodit)  [](https://github.com/phodal/markdown-improve)
4 |
5 | > 一个基于 Electron 的私人定制的 Markdown 编辑器
6 |
7 |
8 |
9 |
10 |
11 | Screenshots
12 |
13 | 
14 |
15 | Features
16 | ---
17 |
18 | - **标题折叠**
19 | - 支持所见所得模式
20 | - 编写时图片
21 | - 语法高亮
22 | - 集成 Terminal
23 | - markdown 实时预览
24 | - 工程管理
25 | - 支持使用 Git 来编写 markdown 项目
26 | - 支持全屏
27 | - 语法高亮
28 | - 编辑器主题切换
29 | - 导出支持(pdf、docx)
30 | - 右键选择搜索: Google, Baidu, WIKI, Zhihu, Github
31 | - 微信公众号排版支持
32 | - 支持 Slide 编写(by reveal.js)
33 | - `.md` 和 `.markdown` 文件关联打开
34 |
35 | Tech Stack
36 | ---
37 |
38 | Phodit 是一个使用**微前端架构**开发的 Electron 应用:
39 |
40 | - React.js -> TreeView
41 | - Angular -> Rename box
42 | - Stencil.js + Web Components -> Terminal Header
43 |
44 | 相应的技术栈有:
45 |
46 | - SimpleMDE + CodeMirror -> Editor
47 | - xterm -> Terminal
48 | - marked -> Markdown Parser
49 | - highlight.js -> Code Highlight
50 | - Reveal.js -> Slide
51 |
52 | ### Setup
53 |
54 | requirements: ``node.js``
55 |
56 | Submodule
57 |
58 | ```
59 | git submodule init
60 | git submodule update
61 | ```
62 |
63 | ```
64 | npm install --python=python2.7
65 | ```
66 |
67 | ```
68 | yarn install
69 | yarn build:app
70 | ```
71 |
72 | #### Components
73 |
74 | Name | Path | Stacks
75 | -----------|------------------------|--------
76 | editor | ./editor | SimpleMDE
77 | header | ./component/header | Stencil.js
78 | interact | ./component/interact | Angular
79 | tree-view | ./component/tree-view | React
80 |
81 | Setup && build
82 |
83 | ```
84 | yarn install
85 | yarn build
86 | ```
87 |
88 | ## License
89 |
90 | [](https://ideas.phodal.com/)
91 |
92 | © 2018~2021 A [Phodal Huang](https://www.phodal.com)'s [Idea](https://github.com/phodal/ideas). This code is distributed under the MIT license. See `LICENSE` in this directory.
93 |
--------------------------------------------------------------------------------
/src/native/phodit-ipc.ts:
--------------------------------------------------------------------------------
1 | import { BrowserWindow, ipcMain, nativeTheme } from "electron";
2 | import { EventConstants } from "../common/constants/event.constants";
3 | import { IFileSave } from "../common/interface/IFileSave";
4 | import { pandoc } from "./features/pandoc";
5 | import { createSlidePage } from "./pages/silde.page";
6 | import Phodit from "./phodit";
7 |
8 | export default class PhoditIpc {
9 | private app: Phodit;
10 |
11 | constructor(app: Phodit) {
12 | this.app = app;
13 | }
14 |
15 | startListener() {
16 | ipcMain.on(EventConstants.PHODIT.OPEN_FILE, (event: any, arg: any) => {
17 | this.app.openFile(arg);
18 | });
19 |
20 | ipcMain.on(EventConstants.PHODIT.SAVE_FILE, (event: any, arg: IFileSave) => {
21 | this.app.saveFile(arg.data, arg.isTempFile);
22 | });
23 |
24 | ipcMain.on(EventConstants.PHODIT.OPEN_GUIDE, (event: any, arg: any) => {
25 | this.app.openAboutPage();
26 | });
27 |
28 | ipcMain.on(EventConstants.PHODIT.SHOW_SLIDES, (event: any, arg: any) => {
29 | createSlidePage(BrowserWindow, arg);
30 | });
31 |
32 | ipcMain.on(EventConstants.PHODIT.FULL_SCREEN, (event: any, arg: any) => {
33 | this.app.mainWindow.setFullScreen(true);
34 | this.app.mainWindow.maximize();
35 | });
36 |
37 | ipcMain.on(EventConstants.PHODIT.UN_FULL_SCREEN, (event: any, arg: any) => {
38 | this.app.mainWindow.setFullScreen(false);
39 | this.app.mainWindow.unmaximize();
40 | });
41 |
42 | ipcMain.on(EventConstants.PHODIT.RELOAD_PATH, (event: any, arg: any) => {
43 | this.app.reloadPath();
44 | });
45 |
46 | ipcMain.on(EventConstants.PHODIT.SET_THEME, (event: any, arg: any) => {
47 | nativeTheme.themeSource = arg.mode;
48 | return nativeTheme.shouldUseDarkColors
49 | });
50 |
51 | ipcMain.on(EventConstants.PHODIT.TOGGLE_THEME, (event: any, arg: any) => {
52 | if (nativeTheme.shouldUseDarkColors) {
53 | nativeTheme.themeSource = 'light'
54 | } else {
55 | nativeTheme.themeSource = 'dark'
56 | }
57 |
58 | return nativeTheme.shouldUseDarkColors
59 | });
60 |
61 | ipcMain.on(EventConstants.PHODIT.SHOW_WORD, (event: any, arg: any) => {
62 | pandoc.word(arg);
63 | });
64 |
65 | ipcMain.on(EventConstants.PHODIT.SHOW_PDF, (event: any, arg: any) => {
66 | pandoc.pdf(arg);
67 | });
68 |
69 | ipcMain.on(EventConstants.PHODIT.OPEN_SYSTEM_PATH, (event: any, arg: any) => {
70 | require("electron").shell.showItemInFolder(arg);
71 | });
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Phodit
6 |
7 |
8 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
--------------------------------------------------------------------------------
/test/application-test.js:
--------------------------------------------------------------------------------
1 | const helpers = require('./global-setup');
2 | const path = require('path');
3 | const fs = require('fs');
4 | const temp = require('temp').track();
5 |
6 | const describe = global.describe;
7 | const it = global.it;
8 | const beforeEach = global.beforeEach;
9 | const afterEach = global.afterEach;
10 | const expect = require('chai').expect;
11 |
12 | describe('application loading', function () {
13 | helpers.setupTimeout(this);
14 | let tempPath = null;
15 | let app = null;
16 |
17 | beforeEach(function () {
18 | tempPath = temp.mkdirSync('spectron-temp-dir-');
19 |
20 | return helpers
21 | .startApplication({
22 | args: [path.join(__dirname, '..')],
23 | })
24 | .then(function (startedApp) {
25 | app = startedApp;
26 | });
27 | });
28 |
29 | afterEach(function () {
30 | return helpers.stopApplication(app);
31 | });
32 |
33 | it('launches the application', async function () {
34 | app.browserWindow.getBounds().should.eventually.roughly(5).deep.equal({
35 | x: 25,
36 | y: 35,
37 | width: 200,
38 | height: 100
39 | });
40 | app.client.waitUntilTextExists('html', 'Hello');
41 | app.client.getTitle().should.eventually.equal('Test');
42 | });
43 |
44 | describe('browserWindow.capturePage', function () {
45 | it('returns a Buffer screenshot of the given rectangle', async function () {
46 | const buffer = await app.browserWindow.capturePage({
47 | x: 0,
48 | y: 0,
49 | width: 10,
50 | height: 10
51 | })
52 |
53 | expect(buffer).to.be.an.instanceof(Buffer);
54 | expect(buffer.length).to.be.above(0);
55 | });
56 |
57 | it('returns a Buffer screenshot of the entire page when no rectangle is specified', async function () {
58 | const buffer = await app.browserWindow.capturePage();
59 | expect(buffer).to.be.an.instanceof(Buffer);
60 | expect(buffer.length).to.be.above(0);
61 | });
62 | });
63 |
64 | describe('webContents.savePage', function () {
65 | it('saves the page to the specified path', function () {
66 | const filePath = path.join(tempPath, 'page.html');
67 | return app.webContents
68 | .savePage(filePath, 'HTMLComplete')
69 | .then(function () {
70 | const html = fs.readFileSync(filePath, 'utf8');
71 | expect(html).to.contain('Phodit');
72 | expect(html).to.contain('Phodit');
73 | });
74 | });
75 |
76 | it('throws an error when the specified path is invalid', async function () {
77 | await expect(
78 | app.webContents.savePage(tempPath, 'MHTMLfds')
79 | ).to.be.rejectedWith(Error);
80 | });
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/views/help.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Markdown Guide
6 |
7 |
8 |
9 |
32 |
33 |
34 |
35 |
36 |
37 |
Markdown Guide
38 |
Emphasis
39 |
**bold**
40 | *italics*
41 | ~~strikethrough~~
42 |
Headers
43 |
# Big header
44 | ## Medium header
45 | ### Small header
46 | #### Tiny header
47 |
Lists
48 |
* Generic list item
49 | * Generic list item
50 | * Generic list item
51 |
52 | 1. Numbered list item
53 | 2. Numbered list item
54 | 3. Numbered list item
55 |
Links
56 |
[Text to display](http://www.example.com)
57 |
Quotes
58 |
> This is a quote.
59 | > It can span multiple lines!
60 |
Images
61 | Need to upload an image? Imgur has a great interface.
62 |
63 |
64 |

65 |
Tables
66 |
| Column 1 | Column 2 | Column 3 |
67 | | -------- | -------- | -------- |
68 | | John | Doe | Male |
69 | | Mary | Smith | Female |
70 |
71 |
72 |
Footnote
73 |
PHP[^1] Markdown Extra [^2] 是这样的。
74 |
75 | [^1]: 脚注1
76 | [^2]: 脚注2
77 |
78 |
79 |
Or without aligning the columns...
80 |
| Column 1 | Column 2 | Column 3 |
81 | | -------- | -------- | -------- |
82 | | John | Doe | Male |
83 | | Mary | Smith | Female |
84 |
85 |
Displaying code
86 |
`var example = "hello!";`
87 |
88 |
Or spanning multiple lines...
89 |
90 |
```
91 | var example = "hello!";
92 | alert(example);
93 | ```
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/src/native/ui/menu.ts:
--------------------------------------------------------------------------------
1 | export function buildMenu(app: any, funcs: any) {
2 | const template: any[] = [
3 | {
4 | label: "File",
5 | submenu: [
6 | {
7 | label: "Open...",
8 | accelerator: "CmdOrCtrl+O",
9 | click() {
10 | funcs.open();
11 | },
12 | },
13 | {
14 | label: "New...",
15 | accelerator: "CmdOrCtrl+N",
16 | click() {
17 | funcs.newFile();
18 | },
19 | },
20 | {
21 | label: "Save...",
22 | accelerator: "CmdOrCtrl+S",
23 | click() {
24 | funcs.saveFileSignal();
25 | },
26 | },
27 | {
28 | label: "Debug...",
29 | accelerator: "CmdOrCtrl+Alt+I",
30 | click() {
31 | funcs.debug();
32 | },
33 | },
34 | {
35 | label: "Reload...",
36 | accelerator: "CmdOrCtrl+R",
37 | click() {
38 | funcs.reload();
39 | },
40 | },
41 | ],
42 | },
43 | {
44 | label: "Edit",
45 | submenu: [
46 | {role: "undo"},
47 | {role: "redo"},
48 | {type: "separator"},
49 | {role: "cut"},
50 | {role: "copy"},
51 | {role: "paste"},
52 | {role: "pasteandmatchstyle"},
53 | {role: "delete"},
54 | {role: "selectall"},
55 | ],
56 | },
57 | {
58 | label: "View",
59 | submenu: [
60 | {role: "reload"},
61 | {role: "forcereload"},
62 | {role: "toggledevtools"},
63 | {type: "separator"},
64 | {role: "resetzoom"},
65 | {role: "zoomin"},
66 | {role: "zoomout"},
67 | {type: "separator"},
68 | {role: "togglefullscreen"},
69 | ],
70 | },
71 | {
72 | role: "window",
73 | submenu: [
74 | {role: "minimize"},
75 | {role: "close"},
76 | ],
77 | },
78 | {
79 | role: "help",
80 | submenu: [
81 | {
82 | label: "More",
83 | click() {
84 | funcs.openAboutPage();
85 | },
86 | },
87 | ],
88 | },
89 | ];
90 |
91 | if (process.platform === "darwin") {
92 | template.unshift({
93 | label: app.getName(),
94 | submenu: [
95 | {role: "about"},
96 | {type: "separator"},
97 | {role: "hide"},
98 | {role: "hideothers"},
99 | {role: "unhide"},
100 | {type: "separator"},
101 | {role: "quit"},
102 | ],
103 | });
104 |
105 | // Edit menu
106 | (template[1].submenu as any[]).push(
107 | {type: "separator"},
108 | {
109 | label: "Speech",
110 | submenu: [
111 | {role: "startspeaking"},
112 | {role: "stopspeaking"},
113 | ],
114 | },
115 | );
116 |
117 | // Window menu
118 | template[3].submenu = [
119 | {role: "close"},
120 | {role: "minimize"},
121 | {role: "zoom"},
122 | {type: "separator"},
123 | {role: "front"},
124 | ];
125 | }
126 |
127 | return template;
128 | }
129 |
--------------------------------------------------------------------------------
/components/tree-view/src/App.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import cx from 'classnames';
3 | import Tree from "react-ui-tree";
4 |
5 | class App extends Component {
6 | state = {
7 | path: null,
8 | active: null,
9 | tree: {}
10 | };
11 |
12 | diffTree(originTree, newTree) {
13 | if (originTree.hasOwnProperty('collapsed')) {
14 | if (newTree.module === originTree.module) {
15 | newTree.collapsed = originTree.collapsed;
16 | }
17 |
18 | if (originTree.children && newTree.children) {
19 | for (let originIndex in originTree.children) {
20 | for (let newIndex in newTree.children) {
21 | if (originTree.children[originIndex].module === newTree.children[newIndex].module) {
22 | newTree.children[newIndex].collapsed = originTree.children[originIndex].collapsed;
23 | }
24 |
25 | this.diffTree(originTree.children, newTree.children);
26 | }
27 | }
28 | }
29 | }
30 |
31 | return newTree;
32 | }
33 |
34 | componentDidMount() {
35 | let that = this;
36 | window.document.addEventListener('phodit.tree.open', function(data) {
37 | let parsedData = JSON.parse(data.detail);
38 |
39 | that.setState({
40 | path: parsedData.path,
41 | tree: parsedData.tree
42 | });
43 |
44 | that.getTreeData();
45 | });
46 | }
47 |
48 | renderNode = node => {
49 | return (
50 |
53 | {node.module}
54 |
55 | );
56 | };
57 |
58 | onClickNode = node => {
59 | this.setState({
60 | active: node
61 | });
62 |
63 | if (!node.leaf) {
64 | return;
65 | }
66 |
67 | const event = new CustomEvent('tree.pub.open', {
68 | detail: JSON.stringify(node)
69 | });
70 | window.document.dispatchEvent(event);
71 | };
72 |
73 | onContextMenuClick = node => {
74 | const event = new CustomEvent('tree.right.click', {
75 | detail: JSON.stringify(node)
76 | });
77 | window.document.dispatchEvent(event);
78 | };
79 |
80 | handleChange = tree => {
81 | this.setState({
82 | tree: tree
83 | });
84 | this.storageTreeData()
85 | };
86 |
87 | storageTreeData() {
88 | localStorage.setItem(this.state.path, JSON.stringify(this.state.tree));
89 | }
90 |
91 | getTreeData() {
92 | const data = localStorage.getItem(this.state.path);
93 | if (!data) return;
94 |
95 | try {
96 | const parsedData = JSON.parse(data);
97 | let diffedTree = this.diffTree(parsedData, this.state.tree);
98 |
99 | this.setState({
100 | tree: diffedTree
101 | });
102 | } catch (e) {
103 | console.log(e);
104 | }
105 | }
106 |
107 | render() {
108 | return (
109 |
120 | );
121 | }
122 | }
123 |
124 | export default App;
125 |
--------------------------------------------------------------------------------
/components/interact/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs/Rx"
22 | ],
23 | "import-spacing": true,
24 | "indent": [
25 | true,
26 | "spaces"
27 | ],
28 | "interface-over-type-literal": true,
29 | "label-position": true,
30 | "max-line-length": [
31 | true,
32 | 140
33 | ],
34 | "member-access": false,
35 | "member-ordering": [
36 | true,
37 | {
38 | "order": [
39 | "static-field",
40 | "instance-field",
41 | "static-method",
42 | "instance-method"
43 | ]
44 | }
45 | ],
46 | "no-arg": true,
47 | "no-bitwise": true,
48 | "no-console": [
49 | true,
50 | "debug",
51 | "info",
52 | "time",
53 | "timeEnd",
54 | "trace"
55 | ],
56 | "no-construct": true,
57 | "no-debugger": true,
58 | "no-duplicate-super": true,
59 | "no-empty": false,
60 | "no-empty-interface": true,
61 | "no-eval": true,
62 | "no-inferrable-types": [
63 | true,
64 | "ignore-params"
65 | ],
66 | "no-misused-new": true,
67 | "no-non-null-assertion": true,
68 | "no-shadowed-variable": true,
69 | "no-string-literal": false,
70 | "no-string-throw": true,
71 | "no-switch-case-fall-through": true,
72 | "no-trailing-whitespace": true,
73 | "no-unnecessary-initializer": true,
74 | "no-unused-expression": true,
75 | "no-use-before-declare": true,
76 | "no-var-keyword": true,
77 | "object-literal-sort-keys": false,
78 | "one-line": [
79 | true,
80 | "check-open-brace",
81 | "check-catch",
82 | "check-else",
83 | "check-whitespace"
84 | ],
85 | "prefer-const": true,
86 | "quotemark": [
87 | true,
88 | "single"
89 | ],
90 | "radix": true,
91 | "semicolon": [
92 | true,
93 | "always"
94 | ],
95 | "triple-equals": [
96 | true,
97 | "allow-null-check"
98 | ],
99 | "typedef-whitespace": [
100 | true,
101 | {
102 | "call-signature": "nospace",
103 | "index-signature": "nospace",
104 | "parameter": "nospace",
105 | "property-declaration": "nospace",
106 | "variable-declaration": "nospace"
107 | }
108 | ],
109 | "unified-signatures": true,
110 | "variable-name": false,
111 | "whitespace": [
112 | true,
113 | "check-branch",
114 | "check-decl",
115 | "check-operator",
116 | "check-separator",
117 | "check-type"
118 | ],
119 | "no-output-on-prefix": true,
120 | "use-input-property-decorator": true,
121 | "use-output-property-decorator": true,
122 | "use-host-property-decorator": true,
123 | "no-input-rename": true,
124 | "no-output-rename": true,
125 | "use-life-cycle-interface": true,
126 | "use-pipe-transform-interface": true,
127 | "component-class-suffix": true,
128 | "directive-class-suffix": true
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/components/interact/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** IE10 and IE11 requires the following for the Reflect API. */
41 | // import 'core-js/es6/reflect';
42 |
43 |
44 | /** Evergreen browsers require these. **/
45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
46 | // import 'core-js/es7/reflect';
47 |
48 |
49 | /**
50 | * Web Animations `@angular/platform-browser/animations`
51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
53 | **/
54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
55 |
56 | /**
57 | * By default, zone.js will patch all possible macroTask and DomEvents
58 | * user can disable parts of macroTask/DomEvents patch by setting following flags
59 | */
60 |
61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
64 |
65 | /*
66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
68 | */
69 | // (window as any).__Zone_enable_cross_context_check = true;
70 |
71 | /***************************************************************************************************
72 | * Zone JS is required by default for Angular itself.
73 | */
74 | import 'zone.js/dist/zone'; // Included with Angular CLI.
75 |
76 |
77 |
78 | /***************************************************************************************************
79 | * APPLICATION IMPORTS
80 | */
81 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phodit",
3 | "version": "1.1.0",
4 | "description": "Phodal's new Markdown Editor",
5 | "main": "dist/native/main.js",
6 | "scripts": {
7 | "build:native": "tsc && cp -a views dist",
8 | "build:packager": "electron-packager --ignore=components --ignore=editor/node_modules --ignore=nlp . Phodit --platform=darwin --icon=assets/imgs/icons/mac/icon.icns --overwrite",
9 | "build:app": "npm run build:native && electron-builder",
10 | "watch": "tsc -w",
11 | "lint": "tslint -c tslint.json -p tsconfig.json --fix",
12 | "start": "yarn build:native && electron ./dist/native/main.js",
13 | "postinstall": "yarn electron-rebuild -p -t \"dev,prod,optional\" && yarn electron-builder install-app-deps",
14 | "rebuild": "electron-rebuild -f -w node-pty",
15 | "test": "xvfb-maybe mocha"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/phodal/phodit.git"
20 | },
21 | "author": "Phodal HUANG",
22 | "license": "MIT",
23 | "bugs": {
24 | "url": "https://github.com/phodal/phodit/issues"
25 | },
26 | "homepage": "https://github.com/phodal/phodit#readme",
27 | "devDependencies": {
28 | "@types/clipboard": "^2.0.1",
29 | "@types/highlightjs": "^10.1.0",
30 | "@types/node": "12.12.6",
31 | "@types/yargs": "15.0.0",
32 | "assert": "^2.0.0",
33 | "chai": "^4.2.0",
34 | "chai-as-promised": "^7.1.1",
35 | "chai-roughly": "^1.0.0",
36 | "devtron": "^1.4.0",
37 | "electron": "^11.0.2",
38 | "electron-builder": "^22.9.1",
39 | "electron-packager": "^15.1.0",
40 | "electron-rebuild": "^2.3.5",
41 | "mocha": "^8.2.1",
42 | "node-gyp": "^7.1.2",
43 | "spectron": "^12.0.0",
44 | "temp": "^0.9.1",
45 | "tslint": "^6.1.2",
46 | "typescript": "^3.9.5",
47 | "xvfb-maybe": "^0.2.1",
48 | "yargs-parser": "^20.2.4"
49 | },
50 | "build": {
51 | "appId": "com.phodal.phodit",
52 | "productName": "Phodit",
53 | "asar": "false",
54 | "copyright": "Copyright © 2018 ~ 2021 ${author} (https://ww.phodal.com)",
55 | "fileAssociations": [
56 | {
57 | "ext": "md",
58 | "name": "Phodit",
59 | "role": "Editor"
60 | },
61 | {
62 | "ext": "markdown",
63 | "name": "Phodit",
64 | "role": "Editor"
65 | }
66 | ],
67 | "files": [
68 | "!components/**",
69 | "!editor/**",
70 | "!views/reveal.js/test**",
71 | "!.git/**",
72 | "!.editorconfig"
73 | ],
74 | "directories": {
75 | "output": "build"
76 | },
77 | "mac": {
78 | "icon": "assets/imgs/icons/mac/icon.icns",
79 | "category": "public.app-category.developer-tools"
80 | },
81 | "win": {
82 | "icon": "assets/imgs/icons/win/icon.ico"
83 | },
84 | "nsis": {
85 | "createDesktopShortcut": "always"
86 | },
87 | "dmg": {
88 | "contents": [
89 | {
90 | "x": 110,
91 | "y": 150
92 | },
93 | {
94 | "x": 240,
95 | "y": 150,
96 | "type": "link",
97 | "path": "/Applications"
98 | }
99 | ]
100 | },
101 | "linux": {
102 | "icon": "assets/imgs/icons/png/1024x1024.png",
103 | "category": "Development"
104 | }
105 | },
106 | "dependencies": {
107 | "chokidar": "^3.4.0",
108 | "electron-json-storage": "^4.1.8",
109 | "electron-store": "^6.0.1",
110 | "electron-window-state": "^5.0.3",
111 | "highlight.js": "latest",
112 | "marked": "^1.1.0",
113 | "node-pty": "0.10.1",
114 | "sweetalert": "^2.1.2",
115 | "tmp": "^0.2.1",
116 | "winston": "^3.3.1",
117 | "xterm": "4.13.0",
118 | "xterm-addon-fit": "^0.5.0",
119 | "xterm-addon-web-links": "^0.5.0-beta.6"
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/components/header/readme.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Stencil Component Starter
4 |
5 | This is a starter project for building a standalone Web Component using Stencil.
6 |
7 | Stencil is also great for building entire apps. For that, use the [stencil-app-starter](https://github.com/ionic-team/stencil-app-starter) instead.
8 |
9 | # Stencil
10 |
11 | Stencil is a compiler for building fast web apps using Web Components.
12 |
13 | Stencil combines the best concepts of the most popular frontend frameworks into a compile-time rather than run-time tool. Stencil takes TypeScript, JSX, a tiny virtual DOM layer, efficient one-way data binding, an asynchronous rendering pipeline (similar to React Fiber), and lazy-loading out of the box, and generates 100% standards-based Web Components that run in any browser supporting the Custom Elements v1 spec.
14 |
15 | Stencil components are just Web Components, so they work in any major framework or with no framework at all.
16 |
17 | ## Getting Started
18 |
19 | To start building a new web component using Stencil, clone this repo to a new directory:
20 |
21 | ```bash
22 | git clone https://github.com/ionic-team/stencil-component-starter.git my-component
23 | cd my-component
24 | git remote rm origin
25 | ```
26 |
27 | and run:
28 |
29 | ```bash
30 | npm install
31 | npm start
32 | ```
33 |
34 | To build the component for production, run:
35 |
36 | ```bash
37 | npm run build
38 | ```
39 |
40 | To run the unit tests for the components, run:
41 |
42 | ```bash
43 | npm test
44 | ```
45 |
46 | Need help? Check out our docs [here](https://stenciljs.com/docs/my-first-component).
47 |
48 |
49 | ## Naming Components
50 |
51 | When creating new component tags, we recommend _not_ using `stencil` in the component name (ex: ``). This is because the generated component has little to nothing to do with Stencil; it's just a web component!
52 |
53 | Instead, use a prefix that fits your company or any name for a group of related components. For example, all of the Ionic generated web components use the prefix `ion`.
54 |
55 |
56 | ## Using this component
57 |
58 | ### Script tag
59 |
60 | - [Publish to NPM](https://docs.npmjs.com/getting-started/publishing-npm-packages)
61 | - Put a script tag similar to this `` in the head of your index.html
62 | - Then you can use the element anywhere in your template, JSX, html etc
63 |
64 | ### Node Modules
65 | - Run `npm install my-component --save`
66 | - Put a script tag similar to this `` in the head of your index.html
67 | - Then you can use the element anywhere in your template, JSX, html etc
68 |
69 | ### In a stencil-starter app
70 | - Run `npm install my-component --save`
71 | - Add an import to the npm packages `import my-component;`
72 | - Then you can use the element anywhere in your template, JSX, html etc
73 |
--------------------------------------------------------------------------------
/components/interact/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "interact": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "prefix": "app",
11 | "schematics": {},
12 | "architect": {
13 | "build": {
14 | "builder": "@angular-devkit/build-angular:browser",
15 | "options": {
16 | "outputPath": "dist/interact",
17 | "index": "src/index.html",
18 | "main": "src/main.ts",
19 | "polyfills": "src/polyfills.ts",
20 | "tsConfig": "src/tsconfig.app.json",
21 | "assets": [
22 | "src/favicon.ico",
23 | "src/assets"
24 | ],
25 | "styles": [
26 | "src/styles.css"
27 | ],
28 | "scripts": []
29 | },
30 | "configurations": {
31 | "production": {
32 | "fileReplacements": [
33 | {
34 | "replace": "src/environments/environment.ts",
35 | "with": "src/environments/environment.prod.ts"
36 | }
37 | ],
38 | "optimization": true,
39 | "outputHashing": "all",
40 | "sourceMap": false,
41 | "extractCss": true,
42 | "namedChunks": false,
43 | "aot": true,
44 | "extractLicenses": true,
45 | "vendorChunk": false,
46 | "buildOptimizer": true
47 | }
48 | }
49 | },
50 | "serve": {
51 | "builder": "@angular-devkit/build-angular:dev-server",
52 | "options": {
53 | "browserTarget": "interact:build"
54 | },
55 | "configurations": {
56 | "production": {
57 | "browserTarget": "interact:build:production"
58 | }
59 | }
60 | },
61 | "extract-i18n": {
62 | "builder": "@angular-devkit/build-angular:extract-i18n",
63 | "options": {
64 | "browserTarget": "interact:build"
65 | }
66 | },
67 | "test": {
68 | "builder": "@angular-devkit/build-angular:karma",
69 | "options": {
70 | "main": "src/test.ts",
71 | "polyfills": "src/polyfills.ts",
72 | "tsConfig": "src/tsconfig.spec.json",
73 | "karmaConfig": "src/karma.conf.js",
74 | "styles": [
75 | "src/styles.css"
76 | ],
77 | "scripts": [],
78 | "assets": [
79 | "src/favicon.ico",
80 | "src/assets"
81 | ]
82 | }
83 | },
84 | "lint": {
85 | "builder": "@angular-devkit/build-angular:tslint",
86 | "options": {
87 | "tsConfig": [
88 | "src/tsconfig.app.json",
89 | "src/tsconfig.spec.json"
90 | ],
91 | "exclude": [
92 | "**/node_modules/**"
93 | ]
94 | }
95 | }
96 | }
97 | },
98 | "interact-e2e": {
99 | "root": "e2e/",
100 | "projectType": "application",
101 | "architect": {
102 | "e2e": {
103 | "builder": "@angular-devkit/build-angular:protractor",
104 | "options": {
105 | "protractorConfig": "e2e/protractor.conf.js",
106 | "devServerTarget": "interact:serve"
107 | },
108 | "configurations": {
109 | "production": {
110 | "devServerTarget": "interact:serve:production"
111 | }
112 | }
113 | },
114 | "lint": {
115 | "builder": "@angular-devkit/build-angular:tslint",
116 | "options": {
117 | "tsConfig": "e2e/tsconfig.e2e.json",
118 | "exclude": [
119 | "**/node_modules/**"
120 | ]
121 | }
122 | }
123 | }
124 | }
125 | },
126 | "defaultProject": "interact",
127 | "cli": {
128 | "analytics": false
129 | }
130 | }
--------------------------------------------------------------------------------
/src/render/menu.right.ts:
--------------------------------------------------------------------------------
1 | const {ipcRenderer, remote} = require('electron');
2 | const { Menu, MenuItem } = remote;
3 | import * as fs from "fs";
4 | import {EventConstants} from "../common/constants/event.constants";
5 |
6 | let node: any = null;
7 |
8 | const globalStore = {
9 | eventTarget: {},
10 | };
11 |
12 | function createEditorMenu(menu: Electron.Menu) {
13 | menu.append(new MenuItem({
14 | label: "Google", click() {
15 | const text = (globalStore.eventTarget as any).innerText;
16 | require("electron").shell.openExternal(`https://www.google.com/search?q=${text}`);
17 | },
18 | }));
19 |
20 | menu.append(new MenuItem({
21 | label: "Baidu", click() {
22 | const text = (globalStore.eventTarget as any).innerText;
23 | require("electron").shell.openExternal(`https://www.baidu.com/s?wd=${text}`);
24 | },
25 | }));
26 |
27 | menu.append(new MenuItem({
28 | label: "GitHub", click() {
29 | const text = (globalStore.eventTarget as any).innerText;
30 | require("electron").shell.openExternal(`https://github.com/search?q=${text}&ref=opensearch`);
31 | },
32 | }));
33 |
34 | menu.append(new MenuItem({
35 | label: "Wiki", click() {
36 | const text = (globalStore.eventTarget as any).innerText;
37 | require("electron").shell.openExternal(`https://zh.wikipedia.org/wiki/Special:Search?search=${text}`);
38 | },
39 | }));
40 |
41 | menu.append(new MenuItem({
42 | label: "Phodal", click() {
43 | const text = (globalStore.eventTarget as any).innerText;
44 | require("electron").shell.openExternal(`http://www.phodal.com/search/?q=${text}`);
45 | },
46 | }));
47 | }
48 |
49 | function createFileMenu(menu: Electron.Menu) {
50 | menu.append(new MenuItem({
51 | label: "Rename", click() {
52 | const filePath = node.filename;
53 | if (!filePath) {
54 | return;
55 | }
56 | const bar = document.querySelector("interact-bar");
57 | const folderPath = filePath.replace(/[^\/]*$/, "");
58 |
59 | bar.setAttribute("filename", filePath.split("/").pop());
60 | bar.setAttribute("style", "display: block;");
61 | bar.addEventListener("action", (event: any) => {
62 | const newPath = folderPath + event.detail;
63 | console.log(`Rename file from: ${filePath} to ${newPath}`);
64 | fs.rename(filePath, newPath, (err) => {
65 | if (err) {
66 | console.log("ERROR: " + err);
67 | }
68 | });
69 | bar.setAttribute("style", "display: none;");
70 | });
71 |
72 | node = null;
73 | },
74 | }));
75 |
76 | menu.append(new MenuItem({
77 | label: "New File", click() {
78 | const filePath = node.filename;
79 | if (!filePath) {
80 | return;
81 | }
82 | const bar = document.querySelector("interact-bar");
83 |
84 | bar.setAttribute("filename", "");
85 | bar.setAttribute("style", "display: block;");
86 | bar.addEventListener("action", (event: any) => {
87 | const newPath = `${filePath}/${event.detail}`;
88 | console.log(`New file from: ${newPath}`);
89 | fs.writeFile(newPath, "", (err) => {
90 | if (err) {
91 | console.log("ERROR: " + err);
92 | }
93 | });
94 | bar.setAttribute("style", "display: none;");
95 | });
96 |
97 | node = null;
98 | },
99 | }));
100 |
101 | menu.append(new MenuItem({
102 | label: "Open In Folder", click() {
103 | ipcRenderer.send("phodit.system.open.path", node.filename);
104 | },
105 | }));
106 |
107 | if (node.hasOwnProperty("collapsed")) {
108 | return;
109 | }
110 |
111 | menu.append(new MenuItem({
112 | label: "Delete", click() {
113 | console.log("Delete", node.filename);
114 | fs.unlink(node.filename, (err: any) => {
115 | if (err) {
116 | return console.log(err);
117 | }
118 | ipcRenderer.send(EventConstants.PHODIT.RELOAD_PATH);
119 |
120 | node = null;
121 | });
122 | },
123 | }));
124 | }
125 |
126 | // FileMenu Click
127 | window.document.addEventListener(EventConstants.CLIENT.FILE_MENU_CLICK, (data: any) => {
128 | const nodeInfo = JSON.parse(data.detail);
129 | node = nodeInfo;
130 | });
131 |
132 | window.addEventListener("contextmenu", (event: any) => {
133 | event.preventDefault();
134 | globalStore.eventTarget = event.target;
135 | const menu = new Menu();
136 |
137 | if (event.target.className === "node" || event.target.className === "node is-active") {
138 | createFileMenu(menu);
139 | } else {
140 | createEditorMenu(menu);
141 | }
142 |
143 | menu.popup({window: remote.getCurrentWindow()});
144 | }, false);
145 |
--------------------------------------------------------------------------------
/components/header/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for your interest in contributing to the Stencil Component Starter! :tada:
4 |
5 |
6 | ## Contributing Etiquette
7 |
8 | Please see our [Contributor Code of Conduct](https://github.com/ionic-team/stencil/blob/master/CODE_OF_CONDUCT.md) for information on our rules of conduct.
9 |
10 |
11 | ## Creating an Issue
12 |
13 | * If you have a question about using Stencil, please ask in the [Stencil Worldwide Slack](https://join.slack.com/t/stencil-worldwide/shared_invite/enQtMjQ2MzkyMTY0MTk0LTQ4ODgzYjFjNjdkNDY3YWVhMmNlMTljMWQxNTM3Yjg0ZTIyZTM1MmU2YWE5YzNjNzE1MmQ3ZTk2NjQ1YzM5ZDM group.
14 |
15 | * It is required that you clearly describe the steps necessary to reproduce the issue you are running into. Although we would love to help our users as much as possible, diagnosing issues without clear reproduction steps is extremely time-consuming and simply not sustainable.
16 |
17 | * The issue list of this repository is exclusively for bug reports and feature requests. Non-conforming issues will be closed immediately.
18 |
19 | * Issues with no clear steps to reproduce will not be triaged. If an issue is labeled with "needs reply" and receives no further replies from the author of the issue for more than 5 days, it will be closed.
20 |
21 | * If you think you have found a bug, or have a new feature idea, please start by making sure it hasn't already been [reported](https://github.com/ionic-team/stencil/issues?utf8=%E2%9C%93&q=is%3Aissue). You can search through existing issues to see if there is a similar one reported. Include closed issues as it may have been closed with a solution.
22 |
23 | * Next, [create a new issue](https://github.com/ionic-team/stencil-component-starter/issues/new) that thoroughly explains the problem. Please fill out the populated issue form before submitting the issue.
24 |
25 |
26 | ## Creating a Pull Request
27 |
28 | * We appreciate you taking the time to contribute! Before submitting a pull request, we ask that you please [create an issue](#creating-an-issue) that explains the bug or feature request and let us know that you plan on creating a pull request for it. If an issue already exists, please comment on that issue letting us know you would like to submit a pull request for it. This helps us to keep track of the pull request and make sure there isn't duplicated effort.
29 |
30 | * Looking for an issue to fix? Make sure to look through our issues with the [help wanted](https://github.com/ionic-team/stencil-component-starter/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label!
31 |
32 | ### Setup
33 |
34 | 1. Fork the repo.
35 | 2. Clone your fork.
36 | 3. Make a branch for your change.
37 | 4. Run `npm install` (make sure you have [node](https://nodejs.org/en/) and [npm](http://blog.npmjs.org/post/85484771375/how-to-install-npm) installed first)
38 |
39 |
40 | #### Updates
41 |
42 | 1. Unit test. Unit test. Unit test. Please take a look at how other unit tests are written, and you can't write too many tests.
43 | 2. If there is a `*.spec.ts` file located in the components folder, update it to include a test for your change, if needed. If this file doesn't exist, please notify us.
44 | 3. Run `npm run test` or `npm run test.watch` to make sure all tests are working, regardless if a test was added.
45 |
46 |
47 | ## Commit Message Format
48 |
49 | We have very precise rules over how our git commit messages should be formatted. This leads to readable messages that are easy to follow when looking through the project history. We also use the git commit messages to generate our changelog. (Ok you got us, it's basically Angular's commit message format).
50 |
51 | `type(scope): subject`
52 |
53 | #### Type
54 | Must be one of the following:
55 |
56 | * **feat**: A new feature
57 | * **fix**: A bug fix
58 | * **docs**: Documentation only changes
59 | * **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
60 | * **refactor**: A code change that neither fixes a bug nor adds a feature
61 | * **perf**: A code change that improves performance
62 | * **test**: Adding missing tests
63 | * **chore**: Changes to the build process or auxiliary tools and libraries such as documentation generation
64 |
65 | #### Scope
66 | The scope can be anything specifying place of the commit change. For example `renderer`, `compiler`, etc.
67 |
68 | #### Subject
69 | The subject contains succinct description of the change:
70 |
71 | * use the imperative, present tense: "change" not "changed" nor "changes"
72 | * do not capitalize first letter
73 | * do not place a period `.` at the end
74 | * entire length of the commit message must not go over 50 characters
75 | * describe what the commit does, not what issue it relates to or fixes
76 | * **be brief, yet descriptive** - we should have a good understanding of what the commit does by reading the subject
77 |
78 |
79 | #### Adding Documentation
80 |
81 | Please see the [stencil-site](https://github.com/ionic-team/stencil-site) repo to update documentation.
82 |
83 |
84 | ## License
85 |
86 | By contributing your code to the ionic-team/stencil GitHub Repository, you agree to license your contribution under the MIT license.
87 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [1.1.0](https://github.com/phodal/phodit/compare/v1.0.0...v1.1.0) (2021-07-19)
2 |
3 |
4 |
5 | # [1.0.0](https://github.com/phodal/phodit/compare/v0.3.0...v1.0.0) (2020-11-26)
6 |
7 |
8 | ### Bug Fixes
9 |
10 | * fix mode issues ([ea39f1d](https://github.com/phodal/phodit/commit/ea39f1d37abeeaf827a5b568e29161b707a85102))
11 |
12 |
13 | ### Features
14 |
15 | * add image preview in articles ([f412be1](https://github.com/phodal/phodit/commit/f412be100adc1230157b14515c3aa4fdc96eff4c))
16 | * make nvm versions ([bf15da4](https://github.com/phodal/phodit/commit/bf15da4c239d64136d7e09371976109e86abbec0))
17 |
18 |
19 |
20 | # [0.3.0](https://github.com/phodal/phodit/compare/0.2.0...v0.3.0) (2020-11-23)
21 |
22 |
23 | ### Bug Fixes
24 |
25 | * align api change ([4cf3c73](https://github.com/phodal/phodit/commit/4cf3c73a2b821e6830ea8186bbe9b0e4aeee3a30))
26 | * fix application issue ([c663dd2](https://github.com/phodal/phodit/commit/c663dd2704993c661564eb9ad3ac5d962137b80a))
27 | * fix catch error issue ([1cf32ea](https://github.com/phodal/phodit/commit/1cf32eaedb0264873938d9be26c74b7927b37bf0))
28 | * fix file associations error issues ([29e7b10](https://github.com/phodal/phodit/commit/29e7b1054a1fe198816d3574eb444376ca8ee240))
29 | * fix icons ([ae570b3](https://github.com/phodal/phodit/commit/ae570b34730af7058c4eb643d072ed78f44f2fb7))
30 | * fix last versions issues ([c16a1b3](https://github.com/phodal/phodit/commit/c16a1b39e13875ac889cfcd1d060f878167491e2))
31 | * fix lint issues ([6deb045](https://github.com/phodal/phodit/commit/6deb045b79e481528e475b9acd7401dc314b3e61))
32 | * fix lost install ([6b92c66](https://github.com/phodal/phodit/commit/6b92c66eebb250c103fc5e3200863dc25d115462))
33 | * fix menu issues ([a92f800](https://github.com/phodal/phodit/commit/a92f800ec0f2482fc84b608e6284d39ccb4feac3))
34 | * fix rename interact bar error issues ([6e2b46f](https://github.com/phodal/phodit/commit/6e2b46f5ec0e0265db983c21e553ce352cdf55b6))
35 | * fix test scripts ([c73d386](https://github.com/phodal/phodit/commit/c73d3867aeee01674e490511f3d8949d7948f1f0))
36 | * fix themes issues ([5028d5b](https://github.com/phodal/phodit/commit/5028d5b531000adfd7139be0caac5863345cee67))
37 | * fix typoo ([9905eeb](https://github.com/phodal/phodit/commit/9905eeb05d12d51f146246ed2683e357fcbf040c))
38 | * make it works ([b9bf33f](https://github.com/phodal/phodit/commit/b9bf33f404040b0c32652f2149bf168ec445701a))
39 | * try to fix highlight issues ([394cf23](https://github.com/phodal/phodit/commit/394cf2306681995d138333e2a079ec6a66e7746b))
40 | * upgrade moach version ([4687d90](https://github.com/phodal/phodit/commit/4687d90afd220768799437397829616166e6faf6))
41 |
42 |
43 | ### Features
44 |
45 | * add html verify for content ([b3d3515](https://github.com/phodal/phodit/commit/b3d3515e7a8d2e4ad05db85240c622d506c48c11))
46 | * add link search addon ([b5b70b1](https://github.com/phodal/phodit/commit/b5b70b1d8e5a3f0c78504dc319e5c7be7e819d95))
47 | * add new file support ([4e7d60a](https://github.com/phodal/phodit/commit/4e7d60abd785ebdbd988b10faf00ccb714cfe70d))
48 | * add touchbar toggle theme support ([2da2f13](https://github.com/phodal/phodit/commit/2da2f13fdc4bad6f5ed972389da4586e386af261))
49 | * fix code mirror not focus issue ([415b60e](https://github.com/phodal/phodit/commit/415b60e52aa3f76d0ba9bb7c8b664e7d30738370))
50 | * import spectron travis config ([b93ace7](https://github.com/phodal/phodit/commit/b93ace7c8c4b1ff7969d1d7005ae98f544b8953c))
51 | * make highlight works ([9d0e91a](https://github.com/phodal/phodit/commit/9d0e91ac53072b799929bd3a440ba71489e3110b))
52 | * make loading to show ([4b8088b](https://github.com/phodal/phodit/commit/4b8088b6cb02cc35ae3ef20065b178015df883de))
53 | * make tray works ([e9dcb25](https://github.com/phodal/phodit/commit/e9dcb256accb9d719bf9e7b1967498a925f55c7e))
54 | * try to add input test ([5eaaac2](https://github.com/phodal/phodit/commit/5eaaac2f731df9d805b2dc3dd973c2637cf9a249))
55 | * try to add touchbar ([8c3d588](https://github.com/phodal/phodit/commit/8c3d58818ee9412cef20c54f889f2b26c9fe59d2))
56 | * try to add tray ([1003a59](https://github.com/phodal/phodit/commit/1003a59f457467698444af144fea9a4c62e81f73))
57 | * upgrade electron & node-pty version ([548828b](https://github.com/phodal/phodit/commit/548828b13a624d75007fe9d46ad6006ae9e37d00))
58 | * upgrade termanil ([87e5693](https://github.com/phodal/phodit/commit/87e5693a79cc7b2815260f6a7a807e78f507e423))
59 | * upgrade theme api ([f872d87](https://github.com/phodal/phodit/commit/f872d87444cd9b92ea8a679801a1728dafbe1157))
60 |
61 |
62 |
63 | # [0.1.0](https://github.com/phodal/phodit/compare/0.0.4...0.1.0) (2019-03-11)
64 |
65 |
66 | ### Bug Fixes
67 |
68 | * add clipboard types ([fd37253](https://github.com/phodal/phodit/commit/fd37253268fc33b94ad3d5d19b9572c25ab10756))
69 | * add lost deps ([3dfbb76](https://github.com/phodal/phodit/commit/3dfbb7601cd1047f788ba34be1bf5be18d9bf223))
70 | * fix checkstyle ([fd8db56](https://github.com/phodal/phodit/commit/fd8db56ee5490186f6767317ac26b4a02a7062d9))
71 | * fix cli typo ([430be69](https://github.com/phodal/phodit/commit/430be695d16145c90ad3e0fccdc940c55be34798))
72 | * fix limit issue ([e5b5cea](https://github.com/phodal/phodit/commit/e5b5ceaca0c3306ad86c093dec8525ebba2379e8))
73 | * fix type issue ([54ace91](https://github.com/phodal/phodit/commit/54ace91a1fc8a5bc78034e78b6eeecabbd0f2807))
74 |
75 |
76 | ### Features
77 |
78 | * [dark mode] add basic backgroun ([6d0e6c5](https://github.com/phodal/phodit/commit/6d0e6c5806c0e434180d6cde4ae40725ab8e501e))
79 | * [dark] add basic color ([6691fca](https://github.com/phodal/phodit/commit/6691fcacb2bed66837a006922fc32c856f2be554))
80 | * [themes] get theme from system ([286c6a0](https://github.com/phodal/phodit/commit/286c6a0f427a119adf1f2eded05ae4074f0281be))
81 | * add change themes icon ([fdb01f9](https://github.com/phodal/phodit/commit/fdb01f97f277c0963a56289df2122a425d32d7b5))
82 | * add file assosciation support ([94fd295](https://github.com/phodal/phodit/commit/94fd295c307b32518f41e72574678d951b522f2b))
83 | * add some build config ([a59b7b4](https://github.com/phodal/phodit/commit/a59b7b4663a04fc1c001603b193c41f039ecc6b0))
84 | * add toggle themes ([529580a](https://github.com/phodal/phodit/commit/529580a33eed9bd6601ec6cd5503e6eafb580046))
85 | * change to simplemde event ([93db32b](https://github.com/phodal/phodit/commit/93db32b70cdcb48836825742a5b19ffbc894631c))
86 | * fix limit ([a4fd7ec](https://github.com/phodal/phodit/commit/a4fd7ec2c01e36d45909f6fc6b068017832c8f34))
87 | * thinking in copy & paste ([11efcf4](https://github.com/phodal/phodit/commit/11efcf45c5dc7ef9dfab86bdb800bb891a8d5f5e))
88 | * try to add some model ([3fa1ef7](https://github.com/phodal/phodit/commit/3fa1ef79c6a17f212512d5236ffc191b1a463ffc))
89 | * update deps ([7fbd88a](https://github.com/phodal/phodit/commit/7fbd88acba3886c1f1945615b1a060a0fc71f5cd))
90 | * update ss & toc ([8709cf1](https://github.com/phodal/phodit/commit/8709cf13d7b4d694aa7789aba9285b0dcf57a886))
91 |
92 |
93 |
94 | ## [0.0.4](https://github.com/phodal/phodit/compare/v0.0.3...0.0.4) (2019-01-10)
95 |
96 |
97 | ### Reverts
98 |
99 | * Revert "[T] update deps" ([4825a9a](https://github.com/phodal/phodit/commit/4825a9a5bfe5a7dd77d8a64bef92c50b92f31d62))
100 | * Revert "[T] resize for codE" ([32add77](https://github.com/phodal/phodit/commit/32add7764b8c536e529210e67d0c7061dc7c6821))
101 |
102 |
103 |
104 | ## [0.0.3](https://github.com/phodal/phodit/compare/v0.0.2...v0.0.3) (2018-09-02)
105 |
106 |
107 |
108 | ## [0.0.2](https://github.com/phodal/phodit/compare/v0.0.0...v0.0.2) (2018-08-14)
109 |
110 |
111 |
112 | # 0.0.0 (2018-07-26)
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/assets/css/styles.css:
--------------------------------------------------------------------------------
1 | ::selection {
2 | background: transparent;
3 | }
4 |
5 | #input .CodeMirror-code pre.CodeMirror-line {
6 | font-size: 16px;
7 | font-weight: 500;
8 |
9 | font-family: "Source Code Variable", "Noto Serif SC", "PingFangTC-light", serif;
10 | }
11 |
12 | #input .cm-s-django .cm-header-1,
13 | #input .cm-s-django .cm-header-2,
14 | #input .cm-s-django .cm-header-3,
15 | #input .cm-s-easymde .cm-header-1,
16 | #input .cm-s-easymde .cm-header-2,
17 | #input .cm-s-easymde .cm-header-3 {
18 | font-family: "Noto Serif SC", serif;
19 | }
20 |
21 | #input .cm-s-easymde .cm-link {
22 | color: #1abc9c;
23 | }
24 |
25 | #input .CodeMirror .cm-variable-2 {
26 | color: #040507;
27 | font-weight: bold;
28 | font-family: "FZNewShuSong_GB18030-Z10", serif;
29 | }
30 |
31 | #input .CodeMirror .cm-variable-3 {
32 | color: #0a5438;
33 | font-size: 95%;
34 | }
35 |
36 |
37 | .CodeMirror div.CodeMirror-cursor {
38 | border-left: 2px solid #0b85f3;
39 | }
40 |
41 | .header {
42 | height: 22px;
43 | width: 100%;
44 | background: #fff;
45 | position: fixed;
46 | top: 0;
47 | left: 0;
48 | z-index: 1;
49 | }
50 |
51 | .far {
52 | height: 22px;
53 | width: 22px;
54 | }
55 |
56 | #phodit-editor {
57 | height: 100%;
58 | overflow-y: scroll;
59 | margin: 0 auto;
60 | position: relative;
61 | overflow-x: hidden;
62 | }
63 |
64 | #phodit-editor.row {
65 | width: 100%;
66 | height: 100vh;
67 | overflow: hidden;
68 | }
69 |
70 | .row {
71 | position: absolute;
72 | }
73 |
74 | .row .column {
75 | padding: 0;
76 | margin: 0;
77 | }
78 |
79 | textarea#input {
80 | border-radius: 0;
81 | padding: 1.5em;
82 | font-size: 14px;
83 | resize: horizontal; /* user can resize vertically, but width is fixed */
84 | }
85 |
86 | #input {
87 | width: 100vw;
88 | height: calc(100vh - 22px);
89 | }
90 |
91 | #tree-view .tree,
92 | #tree-view {
93 | background: #384452;
94 | }
95 |
96 | #tree-view .m-node .inner {
97 | color: #d1d8df;
98 | }
99 |
100 | #input .editor-toolbar a,
101 | #input .editor-toolbar button {
102 | color: #384452 !important;
103 | }
104 |
105 | #input-section {
106 | height: calc(100vh - 22px);
107 | max-height: calc(100vh - 22px);
108 | overflow-y: scroll
109 | }
110 |
111 | .CodeMirror-dialog.CodeMirror-dialog-top {
112 | z-index: 12;
113 | position: absolute;
114 | bottom: 30px;
115 | width: 100vw;
116 | border-top: 1px solid #d1d8df;
117 | background: #fff;
118 | }
119 |
120 | #input .ace_search_form,
121 | #input .ace_replace_form {
122 | border: none;
123 | border-radius: 0;
124 | }
125 |
126 | #input .ace_search_form {
127 | padding-right: 2em;
128 | }
129 |
130 | #input .ace_search.right {
131 | bottom: 0;
132 | top: inherit;
133 | width: 100%;
134 | max-width: 100%;
135 | border-radius: 0;
136 | border: 1px solid #d1d8df;
137 | background-color: #fdfdfd;
138 | }
139 |
140 | #output {
141 | padding: 1em;
142 | height: 100%;
143 | max-height: 100%;
144 | overflow-y: scroll;
145 | }
146 |
147 | #input .editor-toolbar {
148 | position: fixed;
149 | opacity: 1;
150 | background: #fdfdfd;
151 | z-index: 2;
152 | width: 100%;
153 | border: none;
154 | border-radius: 0;
155 | border-bottom: 1px solid #d1d8df;
156 | padding: 0 5px;
157 | top: -1px;
158 | }
159 |
160 | #input .editor-toolbar.fullscreen {
161 | padding-top: 10px;
162 | }
163 |
164 | #input .CodeMirror.CodeMirror-fullscreen {
165 | top: 49px;
166 | }
167 |
168 | #input .editor-toolbar.fullscreen:after,
169 | #input .editor-toolbar:before,
170 | #input .editor-toolbar:after {
171 | margin: 0;
172 | }
173 |
174 | #input .CodeMirror {
175 | font-size: 14px;
176 | top: 29px;
177 | position: relative;
178 | height: calc(100% - 30px);
179 | border-radius: 0;
180 | padding: 5px;
181 | }
182 |
183 | #input .editor-statusbar {
184 | padding: 2px 10px;
185 | width: 100%;
186 | left: 0;
187 | bottom: 0;
188 | position: fixed;
189 | background: #fdfdfd;
190 | border-top: 1px solid #d1d8df;
191 | height: 22px;
192 | z-index: 9;
193 | }
194 |
195 | #input .editor-statusbar span.cursor {
196 | float: left;
197 | margin-left: 0;
198 | min-width: auto;
199 | }
200 |
201 | #tree-view .tree {
202 | width: 100%;
203 | padding-left: 10px;
204 | }
205 |
206 | #input .editor-preview-side {
207 | top: 33px;
208 | }
209 |
210 | #input.full-screen .editor-preview-side.editor-preview-active-side {
211 | top: 49px;
212 | border-left: 1px solid #d1d8df;
213 | }
214 |
215 | #input .editor-preview-side.editor-preview-active-side {
216 | top: 32px;
217 | border-left: none;
218 | }
219 |
220 | #input .editor-preview-side pre > code {
221 | font-size: 12px;
222 | padding: 0;
223 | }
224 |
225 | #terminal-section {
226 | position: absolute;
227 | width: 100%;
228 | right: 0;
229 | bottom: 21px;
230 | z-index: 9;
231 | }
232 |
233 | p {
234 | font-size: 14px;
235 | margin-bottom: 15px;
236 | margin-top: 15px;
237 | }
238 |
239 | h1,
240 | h2,
241 | h3,
242 | h4,
243 | h5,
244 | h6 {
245 | margin-top: 3rem;
246 | margin-bottom: 3rem;
247 | }
248 |
249 | h1 {
250 | font-size: 32px;
251 | }
252 |
253 | h2 {
254 | font-size: 28px;
255 | }
256 |
257 | h3 {
258 | font-size: 24px;
259 | }
260 |
261 | h4 {
262 | font-size: 20px;
263 | }
264 |
265 | h5 {
266 | font-size: 15px;
267 | }
268 |
269 | pre.prettyprint {
270 | border: none;
271 | border-left: 0.3rem solid #1abc9c !important;
272 | }
273 |
274 | figure {
275 | margin: 0;
276 | }
277 |
278 | blockquote, dl, figure, form, ol, p, pre, table, ul {
279 | margin-bottom: 16px;
280 | }
281 |
282 | li p {
283 | display: inline-block;
284 | margin: 0;
285 | }
286 |
287 | interact-bar {
288 | background: #d1d8df;
289 | z-index: 999;
290 | position: fixed;
291 | width: 100%;
292 | bottom: 22px;
293 | left: 0;
294 | }
295 |
296 | .CodeMirror-line > span > .cm-quote:first-child {
297 | border-left: 0.3rem solid #1abc9c;
298 | margin-left: 0;
299 | margin-right: 0;
300 | padding: 1rem 1.5rem;
301 | }
302 |
303 | /* Turn on custom 8px wide scrollbar */
304 | ::-webkit-scrollbar {
305 | width: 8px; /* 1px wider than Lion. */
306 | /* This is more usable for users trying to click it. */
307 | background-color: rgba(0, 0, 0, 0);
308 | -webkit-border-radius: 100px;
309 | }
310 |
311 | /* hover effect for both scrollbar area, and scrollbar 'thumb' */
312 | ::-webkit-scrollbar:hover {
313 | background-color: rgba(0, 0, 0, 0.09);
314 | }
315 |
316 | /* The scrollbar 'thumb' ...that marque oval shape in a scrollbar */
317 | ::-webkit-scrollbar-thumb:vertical {
318 | /* This is the EXACT color of Mac OS scrollbars.
319 | Yes, I pulled out digital color meter */
320 | background: rgba(0, 0, 0, 0.5);
321 | -webkit-border-radius: 100px;
322 | }
323 |
324 | ::-webkit-scrollbar-thumb:vertical:active {
325 | background: rgba(0, 0, 0, 0.61); /* Some darker color when you click it */
326 | -webkit-border-radius: 100px;
327 | }
328 |
329 | #input .CodeMirror {
330 | color: #384452;
331 | }
332 |
333 | .CodeMirror pre {
334 | font-size: 12px;
335 | }
336 |
337 | .editor-preview pre > code {
338 | white-space: pre-wrap;
339 | }
340 |
341 | .CodeMirror-foldmarker {
342 | color: blue;
343 | text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
344 | font-family: arial;
345 | line-height: .3;
346 | cursor: pointer;
347 | }
348 |
349 | .CodeMirror-foldgutter {
350 | width: .7em;
351 | }
352 |
353 | .CodeMirror-foldgutter-open,
354 | .CodeMirror-foldgutter-folded {
355 | cursor: pointer;
356 | }
357 |
358 | .CodeMirror-foldgutter-open:after {
359 | content: "\25BE";
360 | }
361 |
362 | .CodeMirror-foldgutter-folded:after {
363 | content: "\25B8";
364 | }
365 |
366 | [data-theme^="light"],
367 | [data-theme] [data-theme^="light"] {
368 | --main-color: #000;
369 | --background-color: #fff;
370 | --box-color: lightgrey;
371 | }
372 |
373 | [data-theme^="dark"],
374 | [data-theme] [data-theme^="dark"],
375 | [data-theme="light-dark-sidebar"] nav {
376 | --blue-color: #1da1f2;
377 | --main-color: #fff;
378 | --background-color: #000;
379 | --sidebar-color: #333;
380 | --box-color: grey;
381 | }
382 |
383 | [data-theme^="dark"] .editor-toolbar i.separator {
384 | border-left: none;
385 | }
386 |
387 | .column .CodeMirror-widget {
388 | font-size: 24px;
389 | }
390 |
391 | .column .CodeMirror-foldmarker {
392 | color: #b086ff;
393 | text-shadow: 1px 0px 1px #CCCCCC, 0px 1px 1px #EEEEEE, 2px 1px 1px #CCCCCC, 1px 2px 1px #EEEEEE, 3px 2px 1px #CCCCCC, 2px 3px 1px #EEEEEE, 4px 3px 1px #CCCCCC, 3px 4px 1px #EEEEEE, 5px 4px 1px #CCCCCC, 4px 5px 1px #EEEEEE, 6px 5px 1px #CCCCCC, 5px 6px 1px #EEEEEE, 7px 6px 1px #CCCCCC;
394 | top: -2px;
395 | left: 4px;
396 | position: relative;
397 | }
398 |
--------------------------------------------------------------------------------
/assets/css/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */
2 |
3 | /**
4 | * 1. Set default font family to sans-serif.
5 | * 2. Prevent iOS text size adjust after orientation change, without disabling
6 | * user zoom.
7 | */
8 |
9 | html {
10 | font-family: sans-serif; /* 1 */
11 | -ms-text-size-adjust: 100%; /* 2 */
12 | -webkit-text-size-adjust: 100%; /* 2 */
13 | }
14 |
15 | /**
16 | * Remove default margin.
17 | */
18 |
19 | body {
20 | margin: 0;
21 | }
22 |
23 | /* HTML5 display definitions
24 | ========================================================================== */
25 |
26 | /**
27 | * Correct `block` display not defined for any HTML5 element in IE 8/9.
28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11
29 | * and Firefox.
30 | * Correct `block` display not defined for `main` in IE 11.
31 | */
32 |
33 | article,
34 | aside,
35 | details,
36 | figcaption,
37 | figure,
38 | footer,
39 | header,
40 | hgroup,
41 | main,
42 | menu,
43 | nav,
44 | section,
45 | summary {
46 | display: block;
47 | }
48 |
49 | /**
50 | * 1. Correct `inline-block` display not defined in IE 8/9.
51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
52 | */
53 |
54 | audio,
55 | canvas,
56 | progress,
57 | video {
58 | display: inline-block; /* 1 */
59 | vertical-align: baseline; /* 2 */
60 | }
61 |
62 | /**
63 | * Prevent modern browsers from displaying `audio` without controls.
64 | * Remove excess height in iOS 5 devices.
65 | */
66 |
67 | audio:not([controls]) {
68 | display: none;
69 | height: 0;
70 | }
71 |
72 | /**
73 | * Address `[hidden]` styling not present in IE 8/9/10.
74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
75 | */
76 |
77 | [hidden],
78 | template {
79 | display: none;
80 | }
81 |
82 | /* Links
83 | ========================================================================== */
84 |
85 | /**
86 | * Remove the gray background color from active links in IE 10.
87 | */
88 |
89 | a {
90 | background-color: transparent;
91 | }
92 |
93 | /**
94 | * Improve readability when focused and also mouse hovered in all browsers.
95 | */
96 |
97 | a:active,
98 | a:hover {
99 | outline: 0;
100 | }
101 |
102 | /* Text-level semantics
103 | ========================================================================== */
104 |
105 | /**
106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
107 | */
108 |
109 | abbr[title] {
110 | border-bottom: 1px dotted;
111 | }
112 |
113 | /**
114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
115 | */
116 |
117 | b,
118 | strong {
119 | font-weight: bold;
120 | }
121 |
122 | /**
123 | * Address styling not present in Safari and Chrome.
124 | */
125 |
126 | dfn {
127 | font-style: italic;
128 | }
129 |
130 | /**
131 | * Address variable `h1` font-size and margin within `section` and `article`
132 | * contexts in Firefox 4+, Safari, and Chrome.
133 | */
134 |
135 | h1 {
136 | font-size: 2em;
137 | margin: 0.67em 0;
138 | }
139 |
140 | /**
141 | * Address styling not present in IE 8/9.
142 | */
143 |
144 | mark {
145 | background: #ff0;
146 | color: #000;
147 | }
148 |
149 | /**
150 | * Address inconsistent and variable font size in all browsers.
151 | */
152 |
153 | small {
154 | font-size: 80%;
155 | }
156 |
157 | /**
158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers.
159 | */
160 |
161 | sub,
162 | sup {
163 | font-size: 75%;
164 | line-height: 0;
165 | position: relative;
166 | vertical-align: baseline;
167 | }
168 |
169 | sup {
170 | top: -0.5em;
171 | }
172 |
173 | sub {
174 | bottom: -0.25em;
175 | }
176 |
177 | /* Embedded content
178 | ========================================================================== */
179 |
180 | /**
181 | * Remove border when inside `a` element in IE 8/9/10.
182 | */
183 |
184 | img {
185 | border: 0;
186 | }
187 |
188 | /**
189 | * Correct overflow not hidden in IE 9/10/11.
190 | */
191 |
192 | svg:not(:root) {
193 | overflow: hidden;
194 | }
195 |
196 | /* Grouping content
197 | ========================================================================== */
198 |
199 | /**
200 | * Address margin not present in IE 8/9 and Safari.
201 | */
202 |
203 | figure {
204 | margin: 1em 40px;
205 | }
206 |
207 | /**
208 | * Address differences between Firefox and other browsers.
209 | */
210 |
211 | hr {
212 | -moz-box-sizing: content-box;
213 | box-sizing: content-box;
214 | height: 0;
215 | }
216 |
217 | /**
218 | * Contain overflow in all browsers.
219 | */
220 |
221 | pre {
222 | overflow: auto;
223 | }
224 |
225 | /**
226 | * Address odd `em`-unit font size rendering in all browsers.
227 | */
228 |
229 | code,
230 | kbd,
231 | pre,
232 | samp {
233 | font-family: monospace, monospace;
234 | font-size: 1em;
235 | }
236 |
237 | /* Forms
238 | ========================================================================== */
239 |
240 | /**
241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited
242 | * styling of `select`, unless a `border` property is set.
243 | */
244 |
245 | /**
246 | * 1. Correct color not being inherited.
247 | * Known issue: affects color of disabled elements.
248 | * 2. Correct font properties not being inherited.
249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
250 | */
251 |
252 | button,
253 | input,
254 | optgroup,
255 | select,
256 | textarea {
257 | color: inherit; /* 1 */
258 | font: inherit; /* 2 */
259 | margin: 0; /* 3 */
260 | }
261 |
262 | /**
263 | * Address `overflow` set to `hidden` in IE 8/9/10/11.
264 | */
265 |
266 | button {
267 | overflow: visible;
268 | }
269 |
270 | /**
271 | * Address inconsistent `text-transform` inheritance for `button` and `select`.
272 | * All other form control elements do not inherit `text-transform` values.
273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
274 | * Correct `select` style inheritance in Firefox.
275 | */
276 |
277 | button,
278 | select {
279 | text-transform: none;
280 | }
281 |
282 | /**
283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
284 | * and `video` controls.
285 | * 2. Correct inability to style clickable `input` types in iOS.
286 | * 3. Improve usability and consistency of cursor style between image-type
287 | * `input` and others.
288 | */
289 |
290 | button,
291 | html input[type="button"], /* 1 */
292 | input[type="reset"],
293 | input[type="submit"] {
294 | -webkit-appearance: button; /* 2 */
295 | cursor: pointer; /* 3 */
296 | }
297 |
298 | /**
299 | * Re-set default cursor for disabled elements.
300 | */
301 |
302 | button[disabled],
303 | html input[disabled] {
304 | cursor: default;
305 | }
306 |
307 | /**
308 | * Remove inner padding and border in Firefox 4+.
309 | */
310 |
311 | button::-moz-focus-inner,
312 | input::-moz-focus-inner {
313 | border: 0;
314 | padding: 0;
315 | }
316 |
317 | /**
318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in
319 | * the UA stylesheet.
320 | */
321 |
322 | input {
323 | line-height: normal;
324 | }
325 |
326 | /**
327 | * It's recommended that you don't attempt to style these elements.
328 | * Firefox's implementation doesn't respect box-sizing, padding, or width.
329 | *
330 | * 1. Address box sizing set to `content-box` in IE 8/9/10.
331 | * 2. Remove excess padding in IE 8/9/10.
332 | */
333 |
334 | input[type="checkbox"],
335 | input[type="radio"] {
336 | box-sizing: border-box; /* 1 */
337 | padding: 0; /* 2 */
338 | }
339 |
340 | /**
341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain
342 | * `font-size` values of the `input`, it causes the cursor style of the
343 | * decrement button to change from `default` to `text`.
344 | */
345 |
346 | input[type="number"]::-webkit-inner-spin-button,
347 | input[type="number"]::-webkit-outer-spin-button {
348 | height: auto;
349 | }
350 |
351 | /**
352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome
354 | * (include `-moz` to future-proof).
355 | */
356 |
357 | input[type="search"] {
358 | -webkit-appearance: textfield; /* 1 */
359 | -moz-box-sizing: content-box;
360 | -webkit-box-sizing: content-box; /* 2 */
361 | box-sizing: content-box;
362 | }
363 |
364 | /**
365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X.
366 | * Safari (but not Chrome) clips the cancel button when the search input has
367 | * padding (and `textfield` appearance).
368 | */
369 |
370 | input[type="search"]::-webkit-search-cancel-button,
371 | input[type="search"]::-webkit-search-decoration {
372 | -webkit-appearance: none;
373 | }
374 |
375 | /**
376 | * Define consistent border, margin, and padding.
377 | */
378 |
379 | fieldset {
380 | border: 1px solid #c0c0c0;
381 | margin: 0 2px;
382 | padding: 0.35em 0.625em 0.75em;
383 | }
384 |
385 | /**
386 | * 1. Correct `color` not being inherited in IE 8/9/10/11.
387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
388 | */
389 |
390 | legend {
391 | border: 0; /* 1 */
392 | padding: 0; /* 2 */
393 | }
394 |
395 | /**
396 | * Remove default vertical scrollbar in IE 8/9/10/11.
397 | */
398 |
399 | textarea {
400 | overflow: auto;
401 | }
402 |
403 | /**
404 | * Don't inherit the `font-weight` (applied by a rule above).
405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
406 | */
407 |
408 | optgroup {
409 | font-weight: bold;
410 | }
411 |
412 | /* Tables
413 | ========================================================================== */
414 |
415 | /**
416 | * Remove most spacing between table cells.
417 | */
418 |
419 | table {
420 | border-collapse: collapse;
421 | border-spacing: 0;
422 | }
423 |
424 | td,
425 | th {
426 | padding: 0;
427 | }
428 |
--------------------------------------------------------------------------------
/src/native/phodit.ts:
--------------------------------------------------------------------------------
1 | import {app, BrowserWindow, dialog, Menu, nativeImage, OpenDialogReturnValue, shell, Tray} from "electron";
2 | import * as fs from "fs";
3 | import * as path from "path";
4 | import {EventConstants} from "../common/constants/event.constants";
5 | import {createHelpPage} from "./pages/help.page";
6 | import {openHtmlPage} from "./pages/html.page";
7 | import PhoditIpc from "./phodit-ipc";
8 | import {dirTree} from "./support/file-support";
9 | import {buildMenu} from "./ui/menu";
10 | import {dockMenu} from "./ui/menu.dock";
11 | import buildTouchBar from "./ui/touch-bar";
12 | import windowStateKeeper = require("electron-window-state");
13 |
14 | const tmp = require("tmp");
15 | const chokidar = require("chokidar");
16 |
17 | const storage = require("electron-json-storage");
18 |
19 | export default class Phodit {
20 | mainWindow: Electron.BrowserWindow;
21 | currentFile: string;
22 | tray: Tray = null;
23 | dir: any;
24 | private storage: any;
25 |
26 | constructor() {
27 | this.storage = storage;
28 | }
29 |
30 | init() {
31 | app.dock.setMenu(Menu.buildFromTemplate(dockMenu));
32 | app.allowRendererProcessReuse = false;
33 |
34 | app.on("ready", this.onAppReady.bind(this));
35 |
36 | app.on("window-all-closed", () => {
37 | const isMacOS = process.platform === "darwin";
38 | if (!isMacOS) {
39 | app.quit();
40 | }
41 | });
42 |
43 | app.on("activate", () => {
44 | if (this.mainWindow === null) {
45 | this.onAppReady();
46 | }
47 | });
48 |
49 | app.on("open-file", (event, arg) => {
50 | this.openFile(arg);
51 | });
52 |
53 | // TODO: add protocol support
54 | app.setAsDefaultProtocolClient("md");
55 | app.setAsDefaultProtocolClient("markdown");
56 |
57 | const phoditIpc = new PhoditIpc(this);
58 | phoditIpc.startListener();
59 | }
60 |
61 | onAppReady() {
62 | const that = this;
63 |
64 | const mainWindowState = windowStateKeeper({
65 | defaultHeight: 800,
66 | defaultWidth: 1000,
67 | });
68 |
69 | const image = nativeImage.createFromPath(__dirname + '../../assets/imgs/icons/mac/icon.icns');
70 | image.setTemplateImage(true);
71 |
72 | this.mainWindow = new BrowserWindow({
73 | x: mainWindowState.x,
74 | y: mainWindowState.y,
75 | width: mainWindowState.width,
76 | height: mainWindowState.height,
77 | icon: image,
78 | show: false,
79 | backgroundColor: "#fff",
80 | webPreferences: {
81 | nodeIntegration: true,
82 | nodeIntegrationInWorker: true,
83 | enableRemoteModule: true,
84 | // offscreen: true
85 | },
86 | });
87 |
88 | this.setTray();
89 |
90 | console.info("main config dir:", storage.getDataPath());
91 |
92 | mainWindowState.manage(this.mainWindow);
93 | this.mainWindow.loadFile(path.join(__dirname, "../../views/index.html"));
94 | this.mainWindow.setTouchBar(buildTouchBar(this.mainWindow));
95 |
96 | this.mainWindow.on("closed", () => {
97 | this.mainWindow = null;
98 | });
99 |
100 | this.mainWindow.setDocumentEdited(true);
101 | this.mainWindow.webContents.on("did-finish-load", () => {
102 | setTimeout(() => {
103 | this.initEditor(that);
104 | }, 500);
105 | });
106 | this.mainWindow.webContents.on("will-navigate", (event: any, url) => {
107 | if (url !== this.mainWindow.webContents.getURL()) {
108 | event.preventDefault();
109 | const win = new BrowserWindow({show: false});
110 | win.loadURL(url);
111 | win.show();
112 | event.newGuest = win;
113 | }
114 | });
115 |
116 | const menu = Menu.buildFromTemplate(buildMenu(app, that));
117 | Menu.setApplicationMenu(menu);
118 | }
119 |
120 | private initEditor(that: this) {
121 | storage.get("storage.last.file", (error: any, data: any) => {
122 | if (error) throw error;
123 | if (data && data.file) {
124 | that.openFile(data.file);
125 | }
126 | });
127 |
128 | storage.get("storage.last.path", (error: any, data: any) => {
129 | if (error) throw error;
130 | if (data && data.file) {
131 | that.openPath(data.file);
132 | }
133 | });
134 |
135 | this.mainWindow.webContents.setFrameRate(30);
136 | this.mainWindow.webContents.send(EventConstants.PHODIT.LOADED);
137 |
138 | this.mainWindow.show();
139 | this.mainWindow.focus();
140 | }
141 |
142 | private setTray() {
143 | const iconPath = path.join(__dirname, '../../assets/imgs/icons/png/16x16.png');
144 | this.tray = new Tray(nativeImage.createFromPath(iconPath));
145 | const contextMenu = Menu.buildFromTemplate([
146 | {label: 'Item1', type: 'radio'},
147 | {label: 'Exit', type: 'radio', role: "quit" },
148 | ]);
149 | this.tray.setToolTip('Phodit');
150 | this.tray.setContextMenu(contextMenu);
151 | }
152 |
153 | openFile(willLoadFile: string, isTempFile: boolean = false) {
154 | const imageRegex = /\.(jpe?g|png|gif|bmp|ico)$/i;
155 | const htmlRegex = /\.(html)$/i;
156 | const wordRegex = /\.(doc?x)$/i;
157 |
158 | if (imageRegex.test(willLoadFile)) {
159 | return this.mainWindow.previewFile(willLoadFile);
160 | } else if (htmlRegex.test(willLoadFile)) {
161 | return openHtmlPage(BrowserWindow, willLoadFile);
162 | } else if (wordRegex.test(willLoadFile)) {
163 | return shell.openPath(willLoadFile).then(() => {
164 | // do something
165 | });
166 | }
167 |
168 | if (this.mainWindow && !isTempFile) {
169 | const fileName = path.basename(willLoadFile);
170 | this.mainWindow.setTitle(fileName);
171 | this.mainWindow.setRepresentedFilename(willLoadFile);
172 | } else {
173 | this.checkWindow();
174 | storage.remove("storage.last.path");
175 | this.mainWindow.setTitle("Untitled");
176 | }
177 |
178 | storage.set("storage.last.file", {file: willLoadFile});
179 | this.currentFile = willLoadFile;
180 | fs.readFile(willLoadFile, "utf-8", (err, data) => {
181 | if (err) {
182 | console.log("An error ocurred reading the file :" + err.message);
183 | return;
184 | }
185 |
186 | app.addRecentDocument(willLoadFile);
187 |
188 | this.mainWindow.webContents.send("phodit.open.one-file", {
189 | data,
190 | isTempFile,
191 | file: willLoadFile,
192 | });
193 | });
194 | }
195 |
196 | openPath(pathName: any, isWatch = false) {
197 | this.checkWindow();
198 |
199 | storage.set("storage.last.path", {file: pathName});
200 | // storage.remove("storage.last.file");
201 |
202 | let dirFiles: any[] = [];
203 | if (!isWatch) {
204 | chokidar
205 | .watch(pathName, {ignored: /(^|[\/\\])\../})
206 | .on("unlink", () => {
207 | this.reloadPath(true);
208 | })
209 | .on("add", () => {
210 | this.reloadPath(true);
211 | });
212 | }
213 |
214 | fs.readdir(pathName, (err, files) => {
215 | try {
216 | dirFiles = dirTree(pathName);
217 | } catch (e) {
218 | storage.remove("storage.last.path");
219 | console.log(e);
220 | }
221 |
222 | this.mainWindow.webContents.send(EventConstants.PHODIT.OPEN_PATH, {
223 | path: pathName,
224 | tree: dirFiles,
225 | });
226 | });
227 | }
228 |
229 | open() {
230 | this.dir = dialog.showOpenDialog(this.mainWindow, {
231 | filters: [
232 | {name: "Markdown ", extensions: ["markdown", "md", "txt"]},
233 | {name: "All Files", extensions: ["*"]},
234 | ],
235 | properties: ["openFile", "openDirectory", "multiSelections"],
236 | }).then((dialogValue: OpenDialogReturnValue) => {
237 | const fileNames = dialogValue.filePaths;
238 | if (!fileNames) {
239 | return;
240 | }
241 |
242 | if (fileNames.length === 1 && fs.lstatSync(fileNames[0]).isFile()) {
243 | this.openFile(fileNames[0]);
244 | }
245 |
246 | if (fileNames.length === 1 && fs.lstatSync(fileNames[0]).isDirectory()) {
247 | this.openPath(fileNames[0]);
248 | }
249 | });
250 | }
251 |
252 | saveFileSignal() {
253 | if (this.mainWindow.webContents) {
254 | this.mainWindow.webContents.send(EventConstants.CLIENT.SAVE_FILE);
255 | } else {
256 | dialog.showErrorBox("error", "not open file");
257 | }
258 | }
259 |
260 | saveFile(data: any, isTempFile: boolean) {
261 | if (!this.currentFile) {
262 | return;
263 | }
264 | if (!isTempFile && !this.currentFile.endsWith(".tmp")) {
265 | fs.writeFileSync(this.currentFile, data);
266 | } else {
267 | dialog.showSaveDialog(this.mainWindow, {}).then((filename: any) => {
268 | isTempFile = false;
269 |
270 | this.mainWindow.webContents.send(EventConstants.TEMP_FILE_STATUS, {
271 | isTempFile: false,
272 | });
273 | this.currentFile = filename;
274 | this.mainWindow.setTitle(filename);
275 | fs.writeFileSync(filename, data);
276 | });
277 | }
278 | }
279 |
280 | newFile() {
281 | tmp.file((err: any, lastPath: any, fd: any, cleanupCallback: any) => {
282 | if (err) {
283 | throw err;
284 | }
285 |
286 | this.checkWindow();
287 |
288 | storage.set("storage.last.file", {file: lastPath});
289 | this.openFile(lastPath, true);
290 | });
291 | }
292 |
293 | debug() {
294 | this.mainWindow.webContents.openDevTools();
295 | }
296 |
297 | reload() {
298 | this.mainWindow.webContents.reload();
299 | }
300 |
301 | openAboutPage() {
302 | createHelpPage(BrowserWindow);
303 | }
304 |
305 | reloadPath(isWatch = false) {
306 | storage.get("storage.last.path", (error: any, data: any) => {
307 | if (error) {
308 | throw error;
309 | }
310 |
311 | if (data && data.file) {
312 | this.openPath(data.file, isWatch);
313 | }
314 | });
315 | }
316 |
317 | checkWindow() {
318 | if (!this.mainWindow) {
319 | return this.onAppReady();
320 | }
321 | if (this.mainWindow && !this.mainWindow.webContents) {
322 | return this.onAppReady();
323 | }
324 | }
325 |
326 | }
327 |
--------------------------------------------------------------------------------
/assets/libs/clipboard.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * clipboard.js v2.0.0
3 | * https://zenorocha.github.io/clipboard.js
4 | *
5 | * Licensed MIT © Zeno Rocha
6 | */
7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=3)}([function(t,e,n){var o,r,i;!function(a,c){r=[t,n(7)],o=c,void 0!==(i="function"==typeof o?o.apply(e,r):o)&&(t.exports=i)}(0,function(t,e){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(t){return t&&t.__esModule?t:{default:t}}(e),r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,o.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,o.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=a})},function(t,e,n){function o(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!c.string(e))throw new TypeError("Second argument must be a String");if(!c.fn(n))throw new TypeError("Third argument must be a Function");if(c.node(t))return r(t,e,n);if(c.nodeList(t))return i(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function r(t,e,n){return t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function i(t,e,n){return Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function a(t,e,n){return u(document.body,t,e,n)}var c=n(6),u=n(5);t.exports=o},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function o(){r.off(t,o),e.apply(n,arguments)}var r=this;return o._=e,this.on(t,o,n)},emit:function(t){var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;for(o;o0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===d(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,f.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new l.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return u("action",t)}},{key:"defaultTarget",value:function(t){var e=u("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return u("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}(s.default);t.exports=p})},function(t,e){function n(t,e){for(;t&&t.nodeType!==o;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}var o=9;if("undefined"!=typeof Element&&!Element.prototype.matches){var r=Element.prototype;r.matches=r.matchesSelector||r.mozMatchesSelector||r.msMatchesSelector||r.oMatchesSelector||r.webkitMatchesSelector}t.exports=n},function(t,e,n){function o(t,e,n,o,r){var a=i.apply(this,arguments);return t.addEventListener(n,a,r),{destroy:function(){t.removeEventListener(n,a,r)}}}function r(t,e,n,r,i){return"function"==typeof t.addEventListener?o.apply(null,arguments):"function"==typeof n?o.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return o(t,e,n,r,i)}))}function i(t,e,n,o){return function(n){n.delegateTarget=a(n.target,e),n.delegateTarget&&o.call(t,n)}}var a=n(4);t.exports=r},function(t,e){e.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},e.nodeList=function(t){var n=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in t&&(0===t.length||e.node(t[0]))},e.string=function(t){return"string"==typeof t||t instanceof String},e.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e){function n(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}t.exports=n}])});
--------------------------------------------------------------------------------
/assets/simplemde/simplemde.min.css:
--------------------------------------------------------------------------------
1 | /**
2 | * simplemde v1.11.2
3 | * Copyright Next Step Webs, Inc.
4 | * @link https://github.com/NextStepWebs/simplemde-markdown-editor
5 | * @license MIT
6 | */
7 | .CodeMirror{color:#000}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-ruler{border-left:1px solid #ccc;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta,.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-invalidchar,.cm-s-default .cm-error{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:0;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:0 0!important;border:none!important;-webkit-user-select:none;-moz-user-select:none;user-select:none}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:none;font-variant-ligatures:none}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors,div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected,.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.CodeMirror{height:auto;min-height:300px;border:1px solid #ddd;border-bottom-left-radius:4px;border-bottom-right-radius:4px;padding:10px;font:inherit;z-index:1}.CodeMirror-scroll{min-height:300px}.CodeMirror-fullscreen{background:#fff;position:fixed!important;top:50px;left:0;right:0;bottom:0;height:auto;z-index:9}.CodeMirror-sided{width:50%!important}.editor-toolbar{position:relative;opacity:.6;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;padding:0 10px;border-top:1px solid #bbb;border-left:1px solid #bbb;border-right:1px solid #bbb;border-top-left-radius:4px;border-top-right-radius:4px}.editor-toolbar:after,.editor-toolbar:before{display:block;content:' ';height:1px}.editor-toolbar:before{margin-bottom:8px}.editor-toolbar:after{margin-top:8px}.editor-toolbar:hover,.editor-wrapper input.title:focus,.editor-wrapper input.title:hover{opacity:.8}.editor-toolbar.fullscreen{width:100%;height:50px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;padding-top:10px;padding-bottom:10px;box-sizing:border-box;background:#fff;border:0;position:fixed;top:0;left:0;opacity:1;z-index:9}.editor-toolbar.fullscreen::before{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,1)),color-stop(100%,rgba(255,255,255,0)));background:-webkit-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-o-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:linear-gradient(to right,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);position:fixed;top:0;left:0;margin:0;padding:0}.editor-toolbar.fullscreen::after{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,0)),color-stop(100%,rgba(255,255,255,1)));background:-webkit-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-o-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:linear-gradient(to right,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);position:fixed;top:0;right:0;margin:0;padding:0}.editor-toolbar a{display:inline-block;text-align:center;text-decoration:none!important;color:#2c3e50!important;width:30px;height:30px;margin:0;border:1px solid transparent;border-radius:3px;cursor:pointer}.editor-toolbar a.active,.editor-toolbar a:hover{background:#fcfcfc;border-color:#95a5a6}.editor-toolbar a:before{line-height:30px}.editor-toolbar i.separator{display:inline-block;width:0;border-left:1px solid #d9d9d9;border-right:1px solid #fff;color:transparent;text-indent:-10px;margin:0 6px}.editor-toolbar a.fa-header-x:after{font-family:Arial,"Helvetica Neue",Helvetica,sans-serif;font-size:65%;vertical-align:text-bottom;position:relative;top:2px}.editor-toolbar a.fa-header-1:after{content:"1"}.editor-toolbar a.fa-header-2:after{content:"2"}.editor-toolbar a.fa-header-3:after{content:"3"}.editor-toolbar a.fa-header-bigger:after{content:"▲"}.editor-toolbar a.fa-header-smaller:after{content:"▼"}.editor-toolbar.disabled-for-preview a:not(.no-disable){pointer-events:none;background:#fff;border-color:transparent;text-shadow:inherit}@media only screen and (max-width:700px){.editor-toolbar a.no-mobile{display:none}}.editor-statusbar{padding:8px 10px;font-size:12px;color:#959694;text-align:right}.editor-statusbar span{display:inline-block;min-width:4em;margin-left:1em}.editor-preview,.editor-preview-side{padding:10px;background:#fafafa;overflow:auto;display:none;box-sizing:border-box}.editor-statusbar .lines:before{content:'lines: '}.editor-statusbar .words:before{content:'words: '}.editor-statusbar .characters:before{content:'characters: '}.editor-preview{position:absolute;width:100%;height:100%;top:0;left:0;z-index:7}.editor-preview-side{position:fixed;bottom:0;width:50%;top:50px;right:0;z-index:9;border:1px solid #ddd}.editor-preview-active,.editor-preview-active-side{display:block}.editor-preview-side>p,.editor-preview>p{margin-top:0}.editor-preview pre,.editor-preview-side pre{background:#eee;margin-bottom:10px}.editor-preview table td,.editor-preview table th,.editor-preview-side table td,.editor-preview-side table th{border:1px solid #ddd;padding:5px}.CodeMirror .CodeMirror-code .cm-tag{color:#63a35c}.CodeMirror .CodeMirror-code .cm-attribute{color:#795da3}.CodeMirror .CodeMirror-code .cm-string{color:#183691}.CodeMirror .CodeMirror-selected{background:#d9d9d9}.CodeMirror .CodeMirror-code .cm-header-1{font-size:200%;line-height:200%}.CodeMirror .CodeMirror-code .cm-header-2{font-size:160%;line-height:160%}.CodeMirror .CodeMirror-code .cm-header-3{font-size:125%;line-height:125%}.CodeMirror .CodeMirror-code .cm-header-4{font-size:110%;line-height:110%}.CodeMirror .CodeMirror-code .cm-comment{background:rgba(0,0,0,.05);border-radius:2px}.CodeMirror .CodeMirror-code .cm-link{color:#7f8c8d}.CodeMirror .CodeMirror-code .cm-url{color:#aab2b3}.CodeMirror .CodeMirror-code .cm-strikethrough{text-decoration:line-through}.CodeMirror .CodeMirror-placeholder{opacity:.5}.CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word){background:rgba(255,0,0,.15)}
--------------------------------------------------------------------------------
/src/render/renderer.ts:
--------------------------------------------------------------------------------
1 | import { EventConstants } from "../common/constants/event.constants";
2 | import { IFileOpen } from "../common/interface/IFileOpen";
3 | import "./key.event";
4 | import "./menu.right";
5 | import { createTerminal } from "./plugins/terminal";
6 | import { createEvent } from "./support/event.util";
7 | import { getCodeMirrorMode } from "./support/file.utils";
8 | import { markdownRender, removeLastDirectoryPartOf } from "./support/markdown.utils";
9 |
10 | const {nativeTheme, ipcRenderer} = require("electron");
11 |
12 | const swal = require("sweetalert");
13 |
14 | declare global {
15 | // tslint:disable-next-line
16 | interface Window {
17 | easymde: any;
18 | }
19 | }
20 |
21 | class ClientUI {
22 | public state = {
23 | isShowTerminal: false,
24 | hasCreateTerminal: false,
25 | currentFile: "",
26 | isCurrentFileTemp: false,
27 | isOneFile: false,
28 | isPath: false,
29 | };
30 |
31 | public easymde = new (window as any).EasyMDE({
32 | spellChecker: false,
33 | autosave: {
34 | enabled: true,
35 | uniqueId: "phodit",
36 | delay: 1000,
37 | },
38 | promptTexts: {
39 | link: "link",
40 | image: "image",
41 | },
42 | minHeight: "500px",
43 | maxHeight: "100%",
44 | autoDownloadFontAwesome: false,
45 | syncSideBySidePreviewScroll: false,
46 | renderingConfig: {
47 | singleLineBreaks: false,
48 | codeSyntaxHighlighting: true
49 | },
50 | element: document.getElementById("input-section"),
51 | });
52 |
53 | public init() {
54 | (window as any).easymde = this.easymde;
55 | // @ts-ignore
56 | const clipboard = new ClipboardJS(".wechat-button");
57 | this.easymde.codemirror.focus()
58 |
59 | this.easymde.codemirror.on('optionChange', () => {
60 | this.renderElements();
61 | })
62 | this.easymde.codemirror.on('cursorActivity', () => {
63 | this.renderElements();
64 | })
65 | this.easymde.codemirror.on('viewportChange', () => {
66 | this.renderElements();
67 | })
68 |
69 | clipboard.on("success", (event: any) => {
70 | swal({
71 | title: "Copy Success", icon: "success", dangerMode: true,
72 | buttons: {
73 | confirm: {text: "OK"},
74 | },
75 | });
76 | event.clearSelection();
77 | });
78 | }
79 |
80 | private renderElements() {
81 | this.easymde.codemirror.execCommand('markdownRenderImages');
82 | }
83 |
84 | public updatePos(currentFile: string) {
85 | const lastPos = localStorage.getItem("line_" + currentFile);
86 | if (lastPos) {
87 | const parsedPos = JSON.parse(lastPos);
88 | this.easymde.codemirror.setCursor(parsedPos.line, parsedPos.ch);
89 | }
90 | }
91 |
92 | public bindEvent() {
93 | const that = this;
94 | // 打开帮助
95 | window.document.addEventListener(EventConstants.CLIENT.OPEN_GUIDE, (data) => {
96 | ipcRenderer.send(EventConstants.PHODIT.OPEN_GUIDE, this.easymde.value());
97 | });
98 |
99 | // 全屏
100 | window.document.addEventListener(EventConstants.CLIENT.FULL_SCREEN, (data) => {
101 | document.getElementById("input").classList.add("full-screen");
102 | ipcRenderer.send(EventConstants.PHODIT.FULL_SCREEN);
103 | });
104 |
105 | // 取消全屏
106 | window.document.addEventListener(EventConstants.CLIENT.UN_FULL_SCREEN, (data) => {
107 | document.getElementById("input").classList.remove("full-screen");
108 | ipcRenderer.send(EventConstants.PHODIT.UN_FULL_SCREEN);
109 | });
110 |
111 | // Terminal show
112 | window.document.addEventListener(EventConstants.CLIENT.SHOW_TERMINAL, () => {
113 | if (!this.state.hasCreateTerminal) {
114 | createTerminal(removeLastDirectoryPartOf(this.state.currentFile));
115 | this.state.hasCreateTerminal = true;
116 | }
117 |
118 | this.state.isShowTerminal = !this.state.isShowTerminal;
119 | if (this.state.isShowTerminal) {
120 | document.getElementById("terminal-section").setAttribute("style", "display: block;");
121 | } else {
122 | document.getElementById("terminal-section").setAttribute("style", "display: none;");
123 | }
124 | });
125 |
126 | // Terminal hidden
127 | window.document.addEventListener(EventConstants.CLIENT.HIDDEN_TERMINAL, () => {
128 | document.getElementById("terminal-section").setAttribute("style", "display: none;");
129 | });
130 |
131 | // Toggle Themes
132 | window.document.addEventListener(EventConstants.CLIENT.TOGGLE_THEME, () => {
133 | ipcRenderer.send(EventConstants.PHODIT.TOGGLE_THEME);
134 | this.toggleTheme(that.easymde.codemirror);
135 | });
136 |
137 | // ShowSlides
138 | window.document.addEventListener(EventConstants.CLIENT.SHOW_SLIDES, () => {
139 | if (!this.state.currentFile) {
140 | return;
141 | }
142 | ipcRenderer.send(EventConstants.PHODIT.SHOW_SLIDES, {
143 | isTempFile: this.state.isCurrentFileTemp,
144 | file: this.state.currentFile,
145 | data: this.easymde.value(),
146 | });
147 | });
148 |
149 | // 隐藏 SIDE
150 | window.document.addEventListener(EventConstants.CLIENT.HIDDEN_SIDE, () => {
151 | document.getElementById("tree-view").setAttribute("style", "display: none;");
152 | document.querySelector(".wechat-button").setAttribute("data-clipboard-target", ".editor-preview-side");
153 | });
154 |
155 | // 展示 SIDE
156 | window.document.addEventListener(EventConstants.CLIENT.SHOW_SIDE, () => {
157 | document.querySelector(".wechat-button").removeAttribute("data-clipboard-target");
158 | if (this.state.isPath) {
159 | document.getElementById("tree-view").setAttribute("style", "display: block;");
160 | }
161 | });
162 |
163 | // 发起获取自动完成请求
164 | window.document.addEventListener(EventConstants.CLIENT.GET_SUGGEST, (data: any) => {
165 | ipcRenderer.send(EventConstants.PHODIT.GET_SUGGEST, data.detail);
166 | });
167 |
168 | // 打开左侧树型文件
169 | window.document.addEventListener(EventConstants.CLIENT.TREE_OPEN, (event: any) => {
170 | const file = JSON.parse(event.detail).filename;
171 | this.state.currentFile = file;
172 | this.state.isOneFile = true;
173 |
174 | ipcRenderer.send(EventConstants.PHODIT.SAVE_FILE, {
175 | isTempFile: this.state.isCurrentFileTemp,
176 | data: this.easymde.value(),
177 | });
178 |
179 | ipcRenderer.send(EventConstants.PHODIT.OPEN_FILE, file);
180 | });
181 |
182 | // 返回 Markdown 渲染结果
183 | window.document.addEventListener(EventConstants.CLIENT.SEND_MARKDOWN, (event: any) => {
184 | const data = markdownRender(event.detail, this.state.currentFile);
185 | createEvent(EventConstants.CLIENT.GET_RENDERER_MARKDOWN, data);
186 | });
187 |
188 | // Pandoc 转换
189 | window.document.addEventListener(EventConstants.CLIENT.SHOW_WORD, (event: any) => {
190 | swal({
191 | title: "Open File", text: "Are you want to Open File", icon: "info", dangerMode: true,
192 | buttons: {
193 | cancel: {text: "Cancel", visible: true},
194 | confirm: {text: "OK"},
195 | },
196 | }).then((willDelete: any) => {
197 | if (willDelete) {
198 | ipcRenderer.send(EventConstants.PHODIT.SHOW_WORD, this.state.currentFile);
199 | }
200 | });
201 | });
202 |
203 | // Pandoc 转换
204 | window.document.addEventListener(EventConstants.CLIENT.SHOW_PDF, (event: any) => {
205 | swal({
206 | title: "Open File", text: "Are you want to Open File", icon: "info", dangerMode: true,
207 | buttons: {
208 | cancel: {text: "Cancel", visible: true},
209 | confirm: {text: "OK"},
210 | },
211 | }).then((willDelete: any) => {
212 | if (willDelete) {
213 | ipcRenderer.send(EventConstants.PHODIT.SHOW_PDF, this.state.currentFile);
214 | }
215 | });
216 | });
217 |
218 | // 返回获取自动完成请求
219 | ipcRenderer.on(EventConstants.PHODIT.SUGGEST_SEND, (event: any, arg: any) => {
220 | createEvent(EventConstants.PHODIT.SUGGEST_TO_EDITOR, arg);
221 | });
222 |
223 | // 返回获取自动完成请求
224 | ipcRenderer.on(EventConstants.PHODIT.TOGGLE_THEME, (event: any, arg: any) => {
225 | createEvent(EventConstants.CLIENT.TOGGLE_THEME, arg);
226 | });
227 |
228 | // 打开文件
229 | ipcRenderer.on(EventConstants.PHODIT.OPEN_ONE_FILE, (event: any, arg: IFileOpen) => {
230 | this.state.currentFile = arg.file;
231 | this.state.isOneFile = false;
232 | this.state.isCurrentFileTemp = arg.isTempFile;
233 | this.easymde.codemirror.setOption("mode", getCodeMirrorMode(this.state.currentFile));
234 | this.easymde.value(arg.data);
235 | this.updatePos(arg.file);
236 |
237 | localStorage.setItem("currentFile", arg.file);
238 | });
239 |
240 | // 保存文件
241 | ipcRenderer.on(EventConstants.CLIENT.SAVE_FILE, () => {
242 | ipcRenderer.send(EventConstants.PHODIT.SAVE_FILE, {
243 | isTempFile: this.state.isCurrentFileTemp,
244 | data: this.easymde.value(),
245 | });
246 | });
247 |
248 | // 打开某一目录
249 | ipcRenderer.on(EventConstants.PHODIT.OPEN_PATH, (event: any, arg: any) => {
250 | this.state.isPath = true;
251 | document.getElementById("tree-view").setAttribute("style", "display: block");
252 | (window as any).rootPath = arg.path + '/';
253 | localStorage.setItem("currentPath", arg.path + '/');
254 |
255 | createEvent("phodit.tree.open", {
256 | path: arg.path,
257 | tree: arg.tree,
258 | });
259 | });
260 |
261 | // 改变临时文件的状态
262 | ipcRenderer.on(EventConstants.TEMP_FILE_STATUS, (event: any, arg: any) => {
263 | this.state.isCurrentFileTemp = arg.isTempFile;
264 | });
265 | }
266 |
267 | public initTheme() {
268 | if (window.localStorage.os_theme === "dark") {
269 | ipcRenderer.send(EventConstants.PHODIT.SET_THEME, {mode: 'dark'})
270 | this.easymde.codemirror.setOption('theme', 'django');
271 | } else {
272 | ipcRenderer.send(EventConstants.PHODIT.SET_THEME, {mode: 'light'})
273 | this.easymde.codemirror.setOption('theme', 'easymde');
274 | }
275 |
276 | (window as any).__setTheme();
277 | }
278 |
279 | public toggleTheme(codemirror: any) {
280 | if (window.localStorage.os_theme === "dark") {
281 | window.localStorage.os_theme = "light";
282 | codemirror.setOption('theme', 'easymde');
283 | } else {
284 | window.localStorage.os_theme = "dark";
285 | codemirror.setOption('theme', 'django');
286 | }
287 |
288 | (window as any).__setTheme();
289 | }
290 | }
291 |
292 | const client = new ClientUI();
293 | client.init();
294 | client.bindEvent();
295 |
296 | client.initTheme();
297 |
--------------------------------------------------------------------------------