1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-23 23:33:01 +02:00

Compare commits

..

10 Commits

Author SHA1 Message Date
Laurent Cozic
afd63aa3c8 test 2022-01-08 12:49:45 +00:00
Laurent Cozic
04c3c218b9 test 2022-01-08 12:39:24 +00:00
Laurent Cozic
088ae44c63 test 2022-01-08 12:37:56 +00:00
Laurent Cozic
4aa9339fbb test 2022-01-08 12:36:15 +00:00
Laurent Cozic
7edcbc5c27 test 2022-01-08 12:33:40 +00:00
Laurent Cozic
f3650097a0 test 2022-01-08 12:22:44 +00:00
Laurent Cozic
6a4326e2db test 2022-01-08 12:21:27 +00:00
Laurent Cozic
50dc656f65 Doc: Move faq entry to correct document 2022-01-07 18:12:47 +00:00
Laurent Cozic
b36cf46a06 Plugin Repo: Improved error message when plugin "publish" directory is missing 2022-01-07 18:12:47 +00:00
Roman Musin
1781334374 Doc: A few more GSoC ideas (#5959) 2022-01-06 16:42:13 -05:00
38 changed files with 124 additions and 5729 deletions

View File

@@ -52,8 +52,6 @@ packages/app-mobile/lib/rnInjectedJs/
packages/app-mobile/locales
packages/app-mobile/node_modules
packages/app-mobile/pluginAssets/
packages/electron-process-manager/dist/
packages/electron-process-manager/src/ui/
packages/fork-*
packages/htmlpack/dist/
packages/lib/assets/

View File

@@ -1,10 +1,5 @@
#!/bin/bash
# We want the script to stop as soon as an error is found. Otherwise if there's
# an error during `yarn install` for example, it's also going to throw millions
# of errors in test units, which makes debugging difficult.
set -e
# =============================================================================
# Setup environment variables
# =============================================================================

25
.github/stale.yml vendored
View File

@@ -1,25 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 30
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "good first issue"
- "upstream"
- "backlog"
- "high"
- "medium"
- "spec"
- "cannot reproduce"
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs.
You may comment on the issue and I will leave it open.
Thank you for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.
only: issues

View File

@@ -0,0 +1,22 @@
name: 'Close stale issues'
on:
schedule:
- cron: '0 16 * * *'
permissions:
issues: write
jobs:
ProcessStaleIssues:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
# Use this to do a dry run from a pull request
# debug-only: true
stale-issue-message: "Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may comment on the issue and I will leave it open. Thank you for your contributions."
days-before-stale: 30
days-before-close: 7
exempt-issue-labels: 'good first issue,upstream,backlog,high,medium,spec,cannot reproduce'
stale-issue-label: 'stale'
close-issue-message: 'Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, feel free to create a new issue with up-to-date information.'
# Don't process pull requests at all
days-before-pr-stale: -1

View File

@@ -9,7 +9,6 @@ const path = require('path');
const { dirname } = require('@joplin/lib/path-utils');
const fs = require('fs-extra');
const { ipcMain } = require('electron');
const { openProcessManager } = require('@joplin/electron-process-manager');
interface RendererProcessQuitReply {
canClose: boolean;
@@ -42,10 +41,6 @@ export default class ElectronAppWrapper {
this.initialCallbackUrl_ = initialCallbackUrl;
}
public openProcessManager() {
openProcessManager(require('@electron/remote/main'));
}
electronApp() {
return this.electronApp_;
}

View File

@@ -30,10 +30,6 @@ export class Bridge {
return !this.electronApp().electronApp().isPackaged;
}
public openProcessManager() {
this.electronApp().openProcessManager();
}
// The build directory contains additional external files that are going to
// be packaged by Electron Builder. This is for files that need to be
// accessed outside of the Electron app (for example the application icon).

View File

@@ -726,13 +726,6 @@ function useMenu(props: Props) {
});
},
},
{
id: 'help:toggleTaskList',
label: _('Open plugin task list'),
click: () => {
bridge().openProcessManager();
},
},
menuItemDic.toggleSafeMode,
menuItemDic.openProfileDirectory,

View File

@@ -134,10 +134,9 @@
"7zip-bin-win": "^2.1.1"
},
"dependencies": {
"@electron/remote": "2.0.1",
"@electron/remote": "^2.0.1",
"@fortawesome/fontawesome-free": "^5.13.0",
"@joeattardi/emoji-button": "^4.6.0",
"@joplin/electron-process-manager": "workspace:^",
"@joplin/lib": "~2.7",
"@joplin/renderer": "~2.7",
"async-mutex": "^0.1.3",

View File

@@ -1,4 +0,0 @@
{
"presets" : ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 KiB

View File

@@ -1 +0,0 @@
dist/

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<content url="file://$MODULE_DIR$/../electron-process-reporter" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -1,81 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="JSX" />
</component>
<component name="MarkdownProjectSettings" wasCopied="true">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.5" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" emojiShortcuts="0" emojiImages="0">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="HTML_DEEP_PARSER" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="ADMONITION_EXT" value="false" />
<option name="ATTRIBUTES_EXT" value="false" />
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="ENUMERATED_REFERENCES_EXT" value="false" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="NO_TEXT_ATTRIBUTES" value="false" />
<option name="PARSE_HTML_ANCHOR_ID" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
<cssUriHistory />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/electron-process-manager.iml" filepath="$PROJECT_DIR$/.idea/electron-process-manager.iml" />
</modules>
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -1,107 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b935834e-d3d3-420f-991d-e49e232dd71b" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileColors">
<fileColor scope="Non-Project Files (Material Default)" color="2E3C43" />
<fileColor scope="Non-Project Files (Material Darker)" color="323232" />
<fileColor scope="Non-Project Files (Material Lighter)" color="eae8e8" />
<fileColor scope="Non-Project Files (Material Palenight)" color="2f2e43" />
</component>
<component name="Git.Settings">
<option name="PREVIOUS_COMMIT_AUTHORS">
<list>
<option value="Kris Dages &lt;krisdages@git.whiteboxsoftware.net&gt;" />
</list>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GitSEFilterConfiguration">
<file-type-list>
<filtered-out-file-type name="LOCAL_BRANCH" />
<filtered-out-file-type name="REMOTE_BRANCH" />
<filtered-out-file-type name="TAG" />
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
</file-type-list>
</component>
<component name="ProjectId" id="1hYhEkXEjWlOUW5DO64AadCwogW" />
<component name="ProjectViewState">
<option name="autoscrollFromSource" value="true" />
<option name="autoscrollToSource" value="true" />
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/../electron-process-reporter" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.detected.package.tslint" value="true" />
<property name="node.js.selected.package.eslint" value="(autodetect)" />
<property name="node.js.selected.package.tslint" value="(autodetect)" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
<property name="nodejs_package_manager_path" value="yarn" />
<property name="settings.editor.selected.configurable" value="web-ide.project.structure" />
<property name="ts.external.directory.path" value="$APPLICATION_HOME_DIR$/plugins/JavaScriptLanguage/jsLanguageServicesImpl/external" />
<property name="vue.rearranger.settings.migration" value="true" />
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="b935834e-d3d3-420f-991d-e49e232dd71b" name="Default Changelist" comment="" />
<created>1600193824206</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1600193824206</updated>
<workItem from="1600193825399" duration="438000" />
<workItem from="1606177958157" duration="419000" />
<workItem from="1616558380418" duration="1481000" />
<workItem from="1627318845586" duration="3411000" />
<workItem from="1627323366882" duration="72000" />
<workItem from="1627489266598" duration="1192000" />
</task>
<task id="LOCAL-00001" summary="(fix) Add `enableRemoteModule: true` to ProcessManagerWindow webPreferences&#10;&#10;Fixes broken UI in Electron 10">
<created>1600193995342</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1600193995342</updated>
</task>
<task id="LOCAL-00002" summary="(breaking) Use `@electron/remote` instead of deprecated `remote` module&#10;&#10;Updated min electron version to 10.&#10;Bump version to 2.0.0">
<created>1627322100905</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1627322100905</updated>
</task>
<task id="LOCAL-00003" summary="Update README.md for fork">
<created>1627490446165</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1627490446165</updated>
</task>
<option name="localTasksCounter" value="4" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="(fix) Add `enableRemoteModule: true` to ProcessManagerWindow webPreferences&#10;&#10;Fixes broken UI in Electron 10" />
<MESSAGE value="(breaking) Use `@electron/remote` instead of deprecated `remote` module&#10;&#10;Updated min electron version to 10.&#10;Bump version to 2.0.0" />
<MESSAGE value="Update README.md for fork" />
<option name="LAST_COMMIT_MESSAGE" value="Update README.md for fork" />
</component>
</project>

View File

@@ -1,92 +0,0 @@
# Process Manager UI for Electron Apps
* * *
2022-01-06: Forked from https://github.com/krisdages/electron-process-manager
* * *
## Fork using @electron/remote instead of builtin remote module
* Minimum electron version is `10`
* [@electron/remote](https://github.com/electron/remote) is a peerDependency. It needs to be initialized in the main process. Follow the instructions in the link.
## Original 1.0 Readme
This package provides a process manager UI for Electron applications.
It opens a window displaying a table of every processes run by the Electron application with information (type, URL for `webContents`, memory..).
[![npm version](https://badge.fury.io/js/electron-process-manager.svg)](https://badge.fury.io/js/electron-process-manager)
![screenshot](https://github.com/getstation/electron-process-manager/raw/master/.github/screenshots/window.png)
~~:warning: For `@electron>=3.0.0, <7.x`, use version `0.7.1` of this package.
For versions `>=7.x`, use latest.~~
It can be useful to debug performance of an app with several `webview`.
It's inspired from Chrome's task manager.
## Features
- [ ] Memory reporting
- [ ] Link memory data to web-contents (for electron >=1.7.1)
- [x] Kill a process from the UI
- [x] Open developer tools for a given process
- [x] CPU metrics
- [x] Sort by columns
⚠️ Unfortunately, memory info are no longer available in Electron>=4 (see [electron/electron#16179](https://github.com/electron/electron/issues/16179))
## Installation
```bash
$ npm install electron-process-manager
```
## Usage
```js
const { openProcessManager } = require('electron-process-manager');
openProcessManager();
```
## Options
`openProcessManager` function can take options in paramters
#### options.defaultSorting
**defaultSorting.how**: `'ascending' | 'descending'`
**defaultSorting.path**:
| Field name | path |
|--------------------|----------------------------|
| Pid | 'pid' |
| WebContents Domain | 'webContents.0.URLDomain' |
| Process Type | 'webContents.0.type' |
| Private Memory | 'memory.privateBytes' |
| Shared Memory | 'memory.sharedBytes' |
| Working Set Size | 'memory.workingSetSize' |
| % CPU | 'cpu.percentCPUUsage' |
| Idle Wake Ups /s | 'cpu.idleWakeupsPerSecond' |
| WebContents Id | 'webContents.0.id' |
| WebContents Type | 'webContents.0.type' |
| WebContents URL | 'webContents.0.URL' |
example:
```js
const { openProcessManager } = require('electron-process-manager');
openProcessManager({ how: 'descending', path: 'cpu.percentCPUUsage' });
```
## Future
- Add physical memory (noted as "Memory" in Chrome's task manager)
- Add networks metrics
Pull requests welcome :)
## License
MIT License

View File

@@ -1,37 +0,0 @@
{
"name": "@joplin/electron-process-manager",
"version": "2.0.1",
"description": "Process manager UI for Electron applications - Fork with support for @electron/remote",
"main": "src/index.js",
"private": true,
"scripts": {
"build": "webpack"
},
"author": "",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.0",
"bluebird": "^3.7.1",
"bluebird-extra": "^2.0.0",
"electron": "^10.4.7",
"electron-default-menu": "1.0.1",
"filesize": "^5.0.3",
"format-number": "^3.0.0",
"object-path": "^0.11.4",
"prop-types": "^15.5.10",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"spectron": "^12.0.0",
"webpack": "^2.5.1"
},
"peerDependencies": {
"electron": ">= 10"
},
"dependencies": {
"electron-process-reporter": "^1.4.0"
}
}

View File

@@ -1,21 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Process Manager</title>
<link rel="stylesheet" href="vendor/photon.css">
<style>
.process-table {
/*margin: 10px;*/
}
.process-table-container {
overflow-x: scroll;
flex: 2;
}
</style>
</head>
<body>
<div id="app" class="window"></div>
<script src="dist/ui-bundle.js"></script>
</body>
</html>

View File

@@ -1,64 +0,0 @@
const { EventEmitter } = require('events');
const process = require('process');
const { webContents } = require('electron');
const ProcessManagerWindow = require('./ProcessManagerWindow.js');
const defaultOptions = { defaultSorting: { path: null, how: null } };
class ProcessManager extends EventEmitter {
constructor() {
super();
// legacy
this.openProcessManager = this.open.bind(this);
}
// in case this isn't already done in the app.
//
// No longer needed because caller should setup electron/remote
//
// initializeElectronRemote() {
// return require('@electron/remote/main').initialize();
// }
// We pass the electron/remote/main instance to the manager to ensure it's
// using the same as the main application.
//
// When using a peer dependency it seems the package ends up using its own
// instance, which doesn't work.
open(electronRemote, options = defaultOptions) {
if (this.window) {
this.window.focus();
}
this.window = new ProcessManagerWindow(electronRemote);
this.window.defaultSorting = options.defaultSorting || {};
this.window.showWhenReady();
this.window.on('kill-process', pid => this.killProcess(pid));
this.window.on('open-dev-tools', webContentsId => this.openDevTools(webContentsId));
this.window.on('closed', () => this.window = null);
this.emit('open-window', this.window);
return this.window;
}
killProcess(pid) {
this.emit('will-kill-process', pid, this.window);
process.kill(pid);
this.emit('killed-process', pid, this.window);
}
openDevTools(webContentsId) {
this.emit('will-open-dev-tools', webContentsId, this.window);
const wc = webContents.fromId(webContentsId);
wc.openDevTools({ mode: 'detach' });
this.emit('did-open-dev-tools', webContentsId, this.window);
}
}
module.exports = ProcessManager;

View File

@@ -1,78 +0,0 @@
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const { onExtendedProcessMetrics } = require('electron-process-reporter');
class ProcessManagerWindow extends BrowserWindow {
constructor(electronRemote, options) {
const winOptions = Object.assign({
width: 800,
height: 300,
useContentSize: true,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInSubFrames: true,
nodeIntegrationInWorker: true,
webviewTag: true,
enableRemoteModule: true,
contextIsolation: false,
},
}, options || {});
super(winOptions);
this.options = options;
this.attachProcessReporter();
const indexHtml = `file://${path.join(__dirname, '..', 'process-manager.html')}`;
this.loadURL(indexHtml);
console.info('IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', electronRemote);
electronRemote.enable(this.webContents);
setTimeout(() => {
this.openDevTools();
}, 5000);
}
showWhenReady() {
this.once('ready-to-show', () => {
this.show();
});
}
sendStatsReport(reportData) {
if (!this.webContents) return;
this.webContents.send('process-manager:data', reportData);
}
openDevTools() {
this.webContents.openDevTools();
}
attachProcessReporter() {
this.subscription = onExtendedProcessMetrics(app)
.subscribe(report => this.sendStatsReport(report));
ipcMain.on('process-manager:kill-process', (e, pid) => {
// ignore if not for us
if (!this || this.isDestroyed()) return;
if (e.sender !== this.webContents) return;
this.emit('kill-process', pid);
});
ipcMain.on('process-manager:open-dev-tools', (e, webContentsId) => {
// ignore if not for us
if (!this || this.isDestroyed()) return;
if (e.sender !== this.webContents) return;
this.emit('open-dev-tools', webContentsId);
});
this.on('closed', () => {
if (this.subscription) this.subscription.unsubscribe();
});
}
}
module.exports = ProcessManagerWindow;

View File

@@ -1,4 +0,0 @@
const ProcessManager = require('./ProcessManager.js');
// singleton
module.exports = new ProcessManager();

View File

@@ -1,106 +0,0 @@
import React from 'react';
import { ipcRenderer } from 'electron';
import objectPath from 'object-path';
import ProcessTable from './ProcessTable';
import ToolBar from './ToolBar';
export default class ProcessManager extends React.Component {
constructor(props) {
super(props);
this.state = {
processData: null,
selectedPid: null,
sorting: {
path: null,
how: null
}
};
}
UNSAFE_componentWillMount() {
// TODO: disabled for now - the remote package would need to be passed to this script somehow.
//
// this.setState({ sorting: remote.getCurrentWindow().defaultSorting });
ipcRenderer.on('process-manager:data', (_, data) => {
this.setState({ processData: data });
})
}
canKill() {
if (!this.state.selectedPid) return false;
const pids = this.state.processData.map(p => p.pid);
// verify that select pid is in list of processes
return pids.indexOf(this.state.selectedPid) !== -1;
}
canOpenDevTool() {
return this.canKill() && this.getWebContentsIdForSelectedProcess() !== null;
}
getWebContentsIdForSelectedProcess() {
const { processData, selectedPid } = this.state;
if (!selectedPid) return null;
const process = processData.find(p => p.pid === selectedPid);
if (!process || !process.webContents || process.webContents.length === 0) return null;
return process.webContents[0].id;
}
handleKillProcess() {
const pid = this.state.selectedPid;
if (!pid) return;
ipcRenderer.send('process-manager:kill-process', pid);
}
handleOpenDevTool() {
const webContentsId = this.getWebContentsIdForSelectedProcess();
ipcRenderer.send('process-manager:open-dev-tools', webContentsId);
}
getProcessData() {
const { processData, sorting } = this.state;
if (!sorting.path || !sorting.how) return processData;
return processData.sort((p1, p2) => {
const p1Metric = objectPath.get(p1, sorting.path);
const p2Metric = objectPath.get(p2, sorting.path);
if (p1Metric === p2Metric) return 0;
const comp = p1Metric < p2Metric ? -1 : 1;
return sorting.how == 'ascending' ? comp : -comp;
});
}
render () {
const { processData } = this.state;
if (!processData) return (<span>No data</span>);
return (
<div className="window">
<header className="toolbar toolbar-header">
<ToolBar
disableKill={!this.canKill()}
onKillClick={this.handleKillProcess.bind(this)}
disabelOpenDevTool={!this.canOpenDevTool()}
onOpenDevToolClick={this.handleOpenDevTool.bind(this)}
/>
</header>
<div className="process-table-container">
<ProcessTable
processData={this.getProcessData()}
selectedPid={this.state.selectedPid}
sorting={this.state.sorting}
onSortingChange={sorting => this.setState({ sorting })}
onSelectedPidChange={pid => this.setState({ selectedPid: pid })}
/>
</div>
</div>
)
}
}

View File

@@ -1,75 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import filesize from 'filesize';
import format from 'format-number';
const KB = 1024;
const formatPercentage = format({
round: 1,
padRight: 1
});
export default class ProcessRow extends React.Component {
static propTypes = {
pid: PropTypes.number,
type: PropTypes.string,
memory: PropTypes.shape({
peakWorkingSetSize: PropTypes.number,
workingSetSize: PropTypes.number
}),
cpu: PropTypes.shape({
percentCPUUsage: PropTypes.number,
idleWakeupsPerSecond: PropTypes.number
}),
webContents: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number,
type: PropTypes.string,
URL: PropTypes.string,
URLDomain: PropTypes.string
})),
selected: PropTypes.bool,
onSelect: PropTypes.func
}
render() {
const { webContents, memory } = this.props;
if (!webContents || webContents.length === 0) {
return (
<tr
className={this.props.selected ? 'selected': ''}
onClick={this.props.onSelect}
>
<td>{this.props.pid}</td>
<td></td>
<td>{this.props.type}</td>
<td>{memory ? filesize(memory.workingSetSize*KB) : 'N/A'}</td>
<td>{formatPercentage(this.props.cpu.percentCPUUsage)}</td>
<td>{this.props.cpu.idleWakeupsPerSecond}</td>
<td></td>
<td></td>
<td></td>
</tr>
)
} else {
// FIX ME: we consider we have only have 1 webContents per process
const wc = webContents[0];
return (
<tr
className={this.props.selected ? 'selected': ''}
onClick={this.props.onSelect}
>
<td>{this.props.pid}</td>
<td>{wc.URLDomain}</td>
<td>{this.props.type}</td>
<td>{memory ? filesize(memory.workingSetSize*KB) : 'N/A'}</td>
<td>{formatPercentage(this.props.cpu.percentCPUUsage)}</td>
<td>{this.props.cpu.idleWakeupsPerSecond}</td>
<td>{wc.id}</td>
<td>{wc.type}</td>
<td>{wc.URL}</td>
</tr>
)
}
}
}

View File

@@ -1,94 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import ProcessRow from './ProcessRow';
import ProcessTableHeader from './ProcessTableHeader';
export default class ProcessTable extends React.Component {
static propTypes = {
processData: PropTypes.arrayOf(PropTypes.object),
selectedPid: PropTypes.number,
sorting: PropTypes.PropTypes.shape({
path: PropTypes.string,
how: PropTypes.string
}),
onSortingChange: PropTypes.func,
onSelectedPidChange: PropTypes.func
}
render() {
return (
<table className="process-table table-striped">
<thead>
<tr>
<ProcessTableHeader
path='pid'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>Pid</ProcessTableHeader>
<ProcessTableHeader
path='webContents.0.URLDomain'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>WebContents Domain</ProcessTableHeader>
<ProcessTableHeader
path='webContents.0.type'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>Process Type</ProcessTableHeader>
<ProcessTableHeader
path='memory.workingSetSize'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>Working Set Size</ProcessTableHeader>
<ProcessTableHeader
path='cpu.percentCPUUsage'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>% CPU</ProcessTableHeader>
<ProcessTableHeader
path='cpu.idleWakeupsPerSecond'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>Idle Wake Ups /s</ProcessTableHeader>
<ProcessTableHeader
path='webContents.0.id'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>WebContents Id</ProcessTableHeader>
<ProcessTableHeader
path='webContents.0.type'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>WebContents Type</ProcessTableHeader>
<ProcessTableHeader
path='webContents.0.URL'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>WebContents URL</ProcessTableHeader>
</tr>
</thead>
<tbody>
{
this.props.processData.map(p =>
<ProcessRow
key={p.pid}
{...p}
onSelect={() => this.props.onSelectedPidChange(p.pid)}
selected={this.props.selectedPid === p.pid}
/>
)
}
</tbody>
</table>
)
}
}

View File

@@ -1,59 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
export default class ProcessTableHeader extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
static propTypes = {
children: PropTypes.node,
path: PropTypes.string.isRequired,
sorting: PropTypes.PropTypes.shape({
path: PropTypes.string,
how: PropTypes.string
}),
onSortingChange: PropTypes.func
}
getSortCharacter() {
if (!this.sortHow) return (
<span>&nbsp;</span>
);
return this.sortHow == 'ascending' ? '👆' : '👇'
}
get sortHow() {
if (!this.props.sorting) return null;
if (this.props.sorting.path == this.props.path){
return this.props.sorting.how;
}
return null;
}
handleClick() {
let nextSortHow = null;
if(this.sortHow === null) {
nextSortHow = 'ascending';
} else if (this.sortHow === 'ascending') {
nextSortHow = 'descending';
} else {
nextSortHow = null;
}
this.props.onSortingChange({
path: this.props.path,
how: nextSortHow
});
}
render() {
return (
<th onClick={this.handleClick}>
{this.props.children}
&nbsp;
{this.getSortCharacter()}
</th>
)
}
}

View File

@@ -1,35 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
export default class ToolBar extends React.Component {
static propTypes = {
onKillClick: PropTypes.func,
disableKill: PropTypes.bool,
onOpenDevToolClick: PropTypes.func,
disabelOpenDevTool: PropTypes.bool
}
render() {
return (
<div className="toolbar-actions">
<div className="btn-group">
<button
className="btn btn-default"
disabled={this.props.disableKill}
onClick={this.props.onKillClick}
>
End process
</button>
<button
className="btn btn-default"
disabled={this.props.disabelOpenDevTool}
onClick={this.props.onOpenDevToolClick}
>
Open Dev Tool
</button>
</div>
</div>
)
}
}

View File

@@ -1,6 +0,0 @@
import React from 'react';
import { render } from 'react-dom';
import ProcessManager from './ProcessManager';
render(<ProcessManager/>, document.getElementById('app'));

View File

@@ -1,30 +0,0 @@
const Application = require('spectron').Application;
const { join } = require('path');
const assert = require('assert');
const app = new Application({
env: { TEST_PROCESS_MANAGER: 1 },
path: require(join(__dirname, '../node_modules/electron')),
args: [join(__dirname, '../example/main.js')],
});
(async () => {
try {
await app.start();
await app.client.waitUntilWindowLoaded();
await app.electron.ipcRenderer.send('open-process-manager');
// This looks to be incorrect signature for assert.
// assert(app.client.getWindowCount(), 2);
// There are 2 webviews on the index page. They are included in windowCount, so it's 4, not 2.
assert.equal(await app.client.getWindowCount(), 4);
await app.client.switchWindow(/process-manager\.html/);
await (await app.client.$('#app .process-table')).waitForDisplayed({ timeout: 60000 });
await app.stop();
} catch (error) {
console.error('Test failed', error);
if (app && app.isRunning()) {
await app.stop();
process.exit(1);
} else { process.exit(1); }
}
})();

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +0,0 @@
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
const webpack = require('webpack');
const path = require('path');
const BUILD_DIR = path.resolve(__dirname, 'dist');
const config = {
entry: path.resolve(__dirname, 'src/ui/index.js'),
// mode: 'development',
devtool: 'eval-source-map',
output: {
path: BUILD_DIR,
filename: 'ui-bundle.js',
},
module: {
loaders: [
{
test: /\.(jsx|js)?$/,
loader: 'babel-loader',
include: path.resolve(__dirname, 'src/ui'),
},
],
},
target: 'electron-renderer',
};
module.exports = config;

View File

@@ -51,6 +51,8 @@ async function extractPluginFilesFromPackage(existingManifests: any, workDir: st
const pluginDir = resolveRelativePathWithinDir(workDir, 'node_modules', packageName, 'publish');
if (!(await fs.pathExists(pluginDir))) throw new Error(`Could not find publish directory at ${pluginDir}`);
const files = await fs.readdir(pluginDir);
const manifestFilePath = path.resolve(pluginDir, files.find((f: any) => path.extname(f) === '.json'));
const pluginFilePath = path.resolve(pluginDir, files.find((f: any) => path.extname(f) === '.jpl'));

View File

@@ -20,6 +20,10 @@ To switch between yearly and monthly payments, or to change from a Basic to Pro
Note that if you downgrade from Pro to Basic, new limitations will apply so for example you may have to delete some notes so that your account is below the required limit.
## Can my subscription be refunded?
We offer a 14 days trial when the subscription starts so that you can evaluate the service and potentially change your mind - if you cancel during that period you will not be charged. After 14 days your card will be charged and it will not be possible to issue a refund.
## How can I cancel my account?
Click on the [Profile button](#how-can-i-change-my-details), then scroll down and click on "Manage subscription".

View File

@@ -147,7 +147,7 @@ export function execCommand(command: string, options: any = null): Promise<strin
});
}
export function resolveRelativePathWithinDir(baseDir: string, ...relativePath: string[]) {
export function resolveRelativePathWithinDir(baseDir: string, ...relativePath: string[]): string {
const path = require('path');
const resolvedBaseDir = path.resolve(baseDir);
const resolvedPath = path.resolve(baseDir, ...relativePath);

View File

@@ -21,7 +21,3 @@ If you exceed the storage space, you will not be able to upload new notes. You m
We offer a 50% Education Discount for students and teachers. To claim it, please [contact us](mailto:support@joplincloud.com) from your university or school email address. You will then receive a URL you can use to subscribe to Joplin Cloud while benefiting from the 50% discount. This is valid for a whole year and can be renewed for as long as you are in education by contacting us again.
We may also offer bulk discounts for companies, associations and nonprofit organisations. Please [contact us](mailto:support@joplincloud.com) for more details.
## Can I get a refund for my subscription?
Unless you were accidentally charged due to an error, your subscription cannot be refunded. You can cancel it at any time however, which will stop any further charges. We also give a 14 days trial when the subscription starts so that you can evaluate the service and potentially change your mind - if you cancel during that period you will not be charged.

View File

@@ -1,6 +1,6 @@
# GSoC 2022 Ideas
2022 is Joplin second round at Google Summer of Code. Detailed information on how to get involved and apply are given in the [general Summer of Code introduction](https://joplinapp.org/gsoc2022/index/)
2022 is Joplin third round at Google Summer of Code. Detailed information on how to get involved and apply are given in the [general Summer of Code introduction](https://joplinapp.org/gsoc2022/index/)
**These are all proposals! We are open to new ideas you might have!!** Do you have an awesome idea you want to work on with Joplin but that is not among the ideas below? That's cool. We love that! But please do us a favour: Get in touch with a mentor early on and make sure your project is realistic and within the scope of Joplin. Just make sure your idea is within this year's theme:
@@ -81,6 +81,46 @@ Difficulty level: High
Skills Required: Typescript, Javascript, CSS, HTML, Markdown rendering. You will also need to learn about TinyMCE if you're not already familiar with it.
## 7. Improve PDF export
Joplin uses Chrome's built-in print to PDF function which is very limited. This can be improved by using a 3rd party library to convert notes to PDF. Applies to desktop and CLI versions.
Potential benefits:
* Export multiple notes as a single PDF
* Embedding attachments (see https://github.com/laurent22/joplin/issues/5943)
* Delay export until the note is fully rendered (https://discourse.joplinapp.org/t/ability-to-delay-pdf-export-to-allow-plugins-to-render/22159)
Difficulty level: Medium
Skills Required: Typescript, Javascript.
## 8. Replace built-in PDF renderer with a library
Just like with export, Joplin relies on the built-in PDF renderer to show PDF attachments. Replacing it with a 3rd-party library has a number of advantages:
* Joplin can preserve PDF viewer state when a note is re-rendered. For instance currently after opening and closing settings, PDF are reset to the 1st page.
* It may be possible to link to a specific page or even a location within a PDF document.
* Annotate PDF documents from Joplin
Difficulty level: Medium
Skills Required: Typescript, Javascript.
## 9. Rebuild file system sync on Android
A recent update broke file system synchronization on Android, as applications are now required to use a new API to access storage. Currently there are no libraries that would proxy this API for React Native. If we want to get file system sync working again it has to be written from scratch.
Difficulty level: High
Skills Required: Android, Java/Kotlin, Typescript.
## 10. Tablet layout
On wide screens devices like tables Joplin could use a different layout, e.g. with note list always showing, or have both editor and viewer visible at the same time.
Difficulty Level: Medium
Skills Required: React, Typescript, CSS.
# More info
- Make sure you read the [Joplin Google Summer of Code Introduction](https://joplinapp.org/gsoc2022/index/)

2432
yarn.lock

File diff suppressed because it is too large Load Diff