(https://buraktokak.com/)",
7 | "copyright": "GPLv3",
8 | "license": "GPLv3",
9 | "private": true,
10 | "main": "src/electron.js",
11 | "dependencies": {
12 | "@octokit/rest": "^16.35.0",
13 | "file-saver": "^2.0.2",
14 | "jsstore": "^3.4.4",
15 | "react": "^16.8.6",
16 | "react-dom": "^16.8.6",
17 | "react-ga": "^2.5.7",
18 | "react-scripts": "2.1.8",
19 | "write-good": "^1.0.1"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject",
26 | "push": "react-scripts build && aws s3 rm s3://app.usememo.com --recursive && aws s3 cp build s3://app.usememo.com --recursive && aws s3 cp build s3://app.usememo.com/ --recursive --exclude * --include precache*.js --include service-worker.js --metadata-directive REPLACE --cache-control max-age=0,no-cache,no-store,must-revalidate --content-type application/javascript --acl public-read",
27 | "icon": "./node_modules/.bin/electron-icon-maker --input=./src/assets/memo_desktop_png.png --output=./src/assets",
28 | "app": "electron .",
29 | "app-mac": "electron-packager . --overwrite --platform=darwin --arch=x64 --icon=src/assets/icons/mac/icon.icns --prune=true --out=release-builds",
30 | "app-win": "electron-packager . --overwrite --asar --platform=win32 --arch=x64 --icon=src/assets/icons/win/icon.ico --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"Memo App\"",
31 | "app-lin": "electron-packager . --overwrite --platform=linux --arch=x64 --icon=src/assets/icons/png/1024x1024.png --prune=true --out=release-builds",
32 | "package": "yarn build && electron-builder build --publish never",
33 | "package-all": "electron-builder build -mwl",
34 | "package-mac": "electron-builder build --mac osx-sign=true",
35 | "package-ci": "yarn build && electron-builder --publish always",
36 | "package-linux": "electron-builder build --linux",
37 | "package-win": "electron-builder build --win --x64"
38 | },
39 | "homepage": "https://app.usememo.com",
40 | "eslintConfig": {
41 | "extends": "react-app"
42 | },
43 | "browserslist": [
44 | ">0.2%",
45 | "not dead",
46 | "not ie <= 11",
47 | "not op_mini all"
48 | ],
49 | "devDependencies": {
50 | "electron": "^7.1.7",
51 | "electron-notarize": "^0.2.1"
52 | },
53 | "build": {
54 | "afterSign": "scripts/notarize.js",
55 | "appId": "com.usememo.app",
56 | "files": [
57 | "src/electron.js",
58 | "package.json"
59 | ],
60 | "directories": {
61 | "buildResources": "resources"
62 | },
63 | "dmg": {
64 | "sign": false,
65 | "contents": [
66 | {
67 | "x": 130,
68 | "y": 220
69 | },
70 | {
71 | "x": 410,
72 | "y": 220,
73 | "type": "link",
74 | "path": "/Applications"
75 | }
76 | ]
77 | },
78 | "mac": {
79 | "hardenedRuntime": true,
80 | "gatekeeperAssess": false,
81 | "entitlements": "./src/entitlements.mac.inherit.plist",
82 | "entitlementsInherit": "./src/entitlements.mac.inherit.plist",
83 | "target": [
84 | "dmg",
85 | "zip"
86 | ]
87 | },
88 | "win": {
89 | "target": [
90 | "nsis",
91 | "msi"
92 | ]
93 | },
94 | "linux": {
95 | "target": [
96 | "deb",
97 | "rpm",
98 | "AppImage"
99 | ],
100 | "category": "Productivity"
101 | },
102 | "publish": {
103 | "provider": "github",
104 | "owner": "btk",
105 | "repo": "memo",
106 | "private": true
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/public/electron.js:
--------------------------------------------------------------------------------
1 | // Modules to control application life and create native browser window
2 | const {app, BrowserWindow, shell} = require('electron')
3 |
4 | // Keep a global reference of the window object, if you don't, the window will
5 | // be closed automatically when the JavaScript object is garbage collected.
6 | let mainWindow
7 |
8 | function createWindow () {
9 | // Create the browser window.
10 | mainWindow = new BrowserWindow({
11 | width: 1000,
12 | height: 600,
13 | titleBarStyle: "hiddenInset",
14 | frame: false,
15 | webPreferences: {
16 | nodeIntegration: true
17 | }
18 | })
19 | /*
20 | const remote = window.require('electron').remote;
21 | let win = remote.getCurrentWindow();
22 |
23 | win.webContents.session.clearCache(function(){
24 | //some callback.
25 | });
26 | */
27 |
28 | mainWindow.setMenu(null)
29 |
30 | const dev = false;
31 |
32 | if(dev){
33 | mainWindow.loadURL('http://localhost:3000/')
34 | mainWindow.webContents.openDevTools();
35 | }else{
36 | // and load the index.html of the app.
37 | mainWindow.loadURL('https://app.usememo.com/')
38 | }
39 |
40 | mainWindow.webContents.on('new-window', function(event, url){
41 | event.preventDefault();
42 | shell.openItem(url);
43 | });
44 | // Open the DevTools.
45 | // mainWindow.webContents.openDevTools()
46 |
47 | // Emitted when the window is closed.
48 | mainWindow.on('closed', function () {
49 | // Dereference the window object, usually you would store windows
50 | // in an array if your app supports multi windows, this is the time
51 | // when you should delete the corresponding element.
52 | mainWindow = null
53 | })
54 | }
55 |
56 | // This method will be called when Electron has finished
57 | // initialization and is ready to create browser windows.
58 | // Some APIs can only be used after this event occurs.
59 | app.on('ready', createWindow)
60 |
61 | // Quit when all windows are closed.
62 | app.on('window-all-closed', function () {
63 | // On macOS it is common for applications and their menu bar
64 | // to stay active until the user quits explicitly with Cmd + Q
65 | if (process.platform !== 'darwin') app.quit()
66 | })
67 |
68 | app.on('activate', function () {
69 | // On macOS it's common to re-create a window in the app when the
70 | // dock icon is clicked and there are no other windows open.
71 | if (mainWindow === null) createWindow()
72 | })
73 |
74 | // In this file you can include the rest of your app's specific main process
75 | // code. You can also put them in separate files and require them here.
76 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/public/favicon.ico
--------------------------------------------------------------------------------
/public/images/icons/icon-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/public/images/icons/icon-128x128.png
--------------------------------------------------------------------------------
/public/images/icons/icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/public/images/icons/icon-144x144.png
--------------------------------------------------------------------------------
/public/images/icons/icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/public/images/icons/icon-152x152.png
--------------------------------------------------------------------------------
/public/images/icons/icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/public/images/icons/icon-192x192.png
--------------------------------------------------------------------------------
/public/images/icons/icon-384x384.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/public/images/icons/icon-384x384.png
--------------------------------------------------------------------------------
/public/images/icons/icon-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/public/images/icons/icon-512x512.png
--------------------------------------------------------------------------------
/public/images/icons/icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/public/images/icons/icon-72x72.png
--------------------------------------------------------------------------------
/public/images/icons/icon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/public/images/icons/icon-96x96.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
15 |
16 |
25 | Memo - Take Smarter Notes
26 |
35 |
36 |
37 |
38 |
39 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Memo - Take Smarter Notes",
3 | "short_name": "Memo",
4 | "version": 102,
5 | "theme_color": "#565e65",
6 | "background_color": "#ffffff",
7 | "display": "standalone",
8 | "Scope": "/",
9 | "start_url": "/",
10 | "icons": [
11 | {
12 | "src": "images/icons/icon-72x72.png",
13 | "sizes": "72x72",
14 | "type": "image/png"
15 | },
16 | {
17 | "src": "images/icons/icon-96x96.png",
18 | "sizes": "96x96",
19 | "type": "image/png"
20 | },
21 | {
22 | "src": "images/icons/icon-128x128.png",
23 | "sizes": "128x128",
24 | "type": "image/png"
25 | },
26 | {
27 | "src": "images/icons/icon-144x144.png",
28 | "sizes": "144x144",
29 | "type": "image/png"
30 | },
31 | {
32 | "src": "images/icons/icon-152x152.png",
33 | "sizes": "152x152",
34 | "type": "image/png"
35 | },
36 | {
37 | "src": "images/icons/icon-192x192.png",
38 | "sizes": "192x192",
39 | "type": "image/png"
40 | },
41 | {
42 | "src": "images/icons/icon-384x384.png",
43 | "sizes": "384x384",
44 | "type": "image/png"
45 | },
46 | {
47 | "src": "images/icons/icon-512x512.png",
48 | "sizes": "512x512",
49 | "type": "image/png"
50 | }
51 | ],
52 | "splash_pages": null
53 | }
54 |
--------------------------------------------------------------------------------
/public/refresh/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Memo - Take Smarter Notes
5 |
6 |
7 | Auth Refreshed
8 |
9 |
10 |
--------------------------------------------------------------------------------
/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/resources/icon.png
--------------------------------------------------------------------------------
/scripts/notarize.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | const { notarize } = require('electron-notarize');
3 |
4 | exports.default = async function notarizing(context) {
5 | const { electronPlatformName, appOutDir } = context;
6 | if (electronPlatformName !== 'darwin') {
7 | return;
8 | }
9 |
10 | const appName = context.packager.appInfo.productFilename;
11 |
12 | return await notarize({
13 | appBundleId: 'com.usememo.app',
14 | appPath: `${appOutDir}/${appName}.app`,
15 | appleId: process.env.APPLEID,
16 | appleIdPassword: process.env.APPLEIDPASS,
17 | });
18 | };
19 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .AppHolder {
2 | width: 100%;
3 | height: 100%;
4 | overflow: hidden;
5 | display: flex;
6 | flex-direction: row;
7 | }
8 |
9 | .Note {
10 | overflow: hidden;
11 | height: 100vh;
12 | width: 100vw;
13 | background: #fff;
14 | display: flex;
15 | flex-direction: column;
16 | position: relative;
17 | }
18 |
19 | .darkmode .Note {
20 | background: #252525;
21 | }
22 |
23 | .Content {
24 | width: calc(100% - 50px);
25 | height: 100%;
26 | top: 10px;
27 | transition: 100ms top, 100ms opacity;
28 | transition-timing-function: linear;
29 | position: relative;
30 | overflow-y: scroll;
31 | overflow-x: hidden;
32 | -webkit-overflow-scrolling: touch;
33 | background: #fff;
34 | opacity: 0;
35 | }
36 |
37 | .ContentLoaded {
38 | top: 0px;
39 | opacity: 1;
40 | }
41 |
42 | .darkmode .Content {
43 | background: #252525;
44 | }
45 |
46 | .Identifier {
47 | padding: 10px 20px;
48 | color: #787878;
49 | font-size: 0.85em;
50 | }
51 |
52 | .darkmode .Identifier {
53 | color: #aaa;
54 | }
55 |
56 | .spacer {
57 | height: 60vh;
58 | }
59 | .rightSpacer {
60 | width: 15px;
61 | height: 100vh;
62 | }
63 |
64 | #dummy {
65 | background: red;
66 | margin: 0px;
67 | padding: 0px;
68 | height: 0px;
69 | overflow: hidden;
70 | }
71 |
72 | #dummy textarea {
73 | height: 0px !important;
74 | }
75 |
--------------------------------------------------------------------------------
/src/addons/calculator/addon.json:
--------------------------------------------------------------------------------
1 | {
2 | "slug": "calculator",
3 | "display": "Calculator",
4 | "description": "Calculator addon calculates equations marked as (x+y*z)= on the current line of your memo."
5 | }
6 |
--------------------------------------------------------------------------------
/src/addons/calculator/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 | import API from '../../js/api';
4 |
5 | const addon = require("./addon.json");
6 |
7 | function evil(fn) {
8 | try{
9 | return new Function('return ' + fn)();
10 | } catch(err){
11 | return "NaN";
12 | }
13 | }
14 |
15 | class App extends Component {
16 | state = {
17 | text: ""
18 | }
19 |
20 | componentDidMount(){
21 | API.event.on("lineFocused", this.lineFocusedAction);
22 | API.event.on("lineChanged", this.lineChangedAction);
23 | }
24 |
25 | componentWillUnmount(){
26 | API.event.removeListener("lineFocused", this.lineFocusedAction);
27 | API.event.removeListener("lineChanged", this.lineFocusedAction);
28 | }
29 |
30 | lineFocusedAction = (line) => {
31 | this.setState({
32 | text: line.text,
33 | lineId: line.lineId,
34 | index: line.index
35 | });
36 | }
37 |
38 | lineChangedAction = (text) => {
39 | this.setState({text});
40 | }
41 |
42 |
43 | renderCalculated(text){
44 | text = text.replace(/\) =/g, ")=");
45 | if(text.includes(")=")){
46 | let calculatables = [];
47 | text.split(")=").forEach(calculatable => {
48 | if(calculatable.includes("(")){
49 | calculatables.push(calculatable.split("(").slice(1).join("("));
50 | }
51 | })
52 | return calculatables.map((calcable, i) => {
53 | return [{i+1}] = {evil(calcable)}
54 | });
55 | }else{
56 | return null;
57 | }
58 | }
59 |
60 | render() {
61 | if(this.state.text){
62 | let includes = this.state.text.replace(") =", ")=").includes(")=");
63 | if(includes){
64 | return (
65 | <>
66 |
67 |
70 |
{addon.display}
71 |
{this.renderCalculated(this.state.text)}
72 |
73 | >
74 | );
75 | }else{
76 | return null;
77 | }
78 | }else{
79 | return null;
80 | }
81 | }
82 | }
83 |
84 | export default App;
85 |
--------------------------------------------------------------------------------
/src/addons/calculator/style.css:
--------------------------------------------------------------------------------
1 | .AddonItem {
2 | color: #555;
3 | position: relative;
4 | }
5 |
6 | .AddonItem h5 {
7 | font-size: 0.75rem;
8 | font-weight: 500;
9 | color: #444;
10 | text-transform: uppercase;
11 | margin-bottom: 3px;
12 | }
13 |
14 | .AddonItem p {
15 | font-size: 0.85em;
16 | }
17 |
18 | .AddonItem .AddonConfigure {
19 | position: absolute;
20 | right: 0px;
21 | top: -3px;
22 | cursor: pointer;
23 | opacity: 0;
24 | transform: rotate(0deg);
25 | transition: 300ms all;
26 | transition-delay: 200ms;
27 | }
28 |
29 | .AddonItem .AddonConfigure path {
30 | fill: #222;
31 | }
32 | .darkmode .AddonItem .AddonConfigure path {
33 | fill: #fff;
34 | }
35 |
36 | .AddonItem:hover .AddonConfigure {
37 | opacity: 0.8;
38 | transform: rotate(180deg);
39 | }
40 |
41 | .darkmode .AddonItem h5 {
42 | font-size: 0.75rem;
43 | font-weight: 500;
44 | color: #aaa;
45 | text-transform: uppercase;
46 | margin-bottom: 3px;
47 | }
48 |
49 | .calculatedNode {
50 | background: #e3d3f9;
51 | padding: 5px;
52 | border-radius: 5px;
53 | border: 1px solid #dac9ea;
54 | margin-bottom: 5px;
55 | font-size: 0.9em;
56 | color: #8d5f92;
57 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
58 | monospace;
59 | letter-spacing: -0.5px;
60 | }
61 |
62 | .darkmode .calculatedNode{
63 | background: #26162d;
64 | border: 1px solid #562671;
65 | color: #87498e;
66 | }
67 |
--------------------------------------------------------------------------------
/src/addons/conversion/addon.json:
--------------------------------------------------------------------------------
1 | {
2 | "slug": "conversion",
3 | "display": "Conversion",
4 | "description": "Conversion addon helps you by convert the currency value on your current memo line."
5 | }
6 |
--------------------------------------------------------------------------------
/src/addons/conversion/style.css:
--------------------------------------------------------------------------------
1 | .AddonItem {
2 | color: #555;
3 | position: relative;
4 | }
5 |
6 | .AddonItem h5 {
7 | font-size: 0.75rem;
8 | font-weight: 500;
9 | color: #444;
10 | text-transform: uppercase;
11 | margin-bottom: 3px;
12 | }
13 |
14 | .AddonItem > div {
15 | font-size: 0.85em;
16 | }
17 |
18 | .AddonItem .AddonConfigure {
19 | position: absolute;
20 | right: 0px;
21 | top: -3px;
22 | cursor: pointer;
23 | opacity: 0;
24 | transform: rotate(0deg);
25 | transition: 300ms all;
26 | transition-delay: 200ms;
27 | }
28 |
29 | .AddonItem .AddonConfigure path {
30 | fill: #222;
31 | }
32 | .darkmode .AddonItem .AddonConfigure path {
33 | fill: #fff;
34 | }
35 |
36 | .AddonItem:hover .AddonConfigure {
37 | opacity: 0.8;
38 | transform: rotate(180deg);
39 | }
40 |
41 | .darkmode .AddonItem h5 {
42 | font-size: 0.75rem;
43 | font-weight: 500;
44 | color: #aaa;
45 | text-transform: uppercase;
46 | margin-bottom: 3px;
47 | }
48 |
49 | .conversionNode {
50 | background: #dbf9d3;
51 | padding: 5px;
52 | border-radius: 5px;
53 | border: 1px solid #cfe8c9;
54 | margin-bottom: 5px;
55 | font-size: 0.9em;
56 | color: #2b8414;
57 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
58 | monospace;
59 | letter-spacing: -0.5px;
60 | }
61 |
62 | .darkmode .conversionNode{
63 | background: #183525;
64 | border: 1px solid #105400;
65 | color: #479832;
66 | }
67 |
--------------------------------------------------------------------------------
/src/addons/links/addon.json:
--------------------------------------------------------------------------------
1 | {
2 | "slug": "links",
3 | "display": "Links",
4 | "description": "Helps you preview and list clickable links and images in the current line of your memo."
5 | }
6 |
--------------------------------------------------------------------------------
/src/addons/links/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 | import API from '../../js/api';
4 |
5 | const addon = require("./addon.json");
6 |
7 | class App extends Component {
8 | state = {
9 | text: ""
10 | }
11 |
12 | componentDidMount(){
13 | API.event.on("lineFocused", this.lineFocusedAction);
14 | API.event.on("lineChanged", this.lineChangedAction);
15 | }
16 |
17 | componentWillUnmount(){
18 | API.event.removeListener("lineFocused", this.lineFocusedAction);
19 | API.event.removeListener("lineChanged", this.lineFocusedAction);
20 | }
21 |
22 | lineFocusedAction = (line) => {
23 | this.setState({
24 | text: line.text,
25 | lineId: line.lineId,
26 | index: line.index
27 | });
28 | }
29 |
30 | lineChangedAction = (text) => {
31 | this.setState({text});
32 | }
33 |
34 | renderLinks(text){
35 | let links = text.match(/http[^\s]*/g);
36 | return links.map((link, i) => {
37 | if(link.endsWith(".png") || link.endsWith(".jpg") || link.endsWith(".gif") || link.endsWith(".svg") || link.endsWith(".JPG") || link.endsWith(".jpeg") || link.endsWith(".JPEG")){
38 | return
39 | }else{
40 | if(link.includes("//")){
41 | return Link from {link.split("//")[1].split("/")[0]}
42 | }else{
43 | return null;
44 | }
45 | }
46 | });
47 | }
48 |
49 | render() {
50 | if(this.state.text){
51 | let includes = this.state.text.includes("http");
52 | if(includes){
53 | return (
54 | <>
55 |
56 |
59 |
{addon.display}
60 |
{this.renderLinks(this.state.text)}
61 |
62 | >
63 | );
64 | }else{
65 | return null;
66 | }
67 | }else{
68 | return null;
69 | }
70 | }
71 | }
72 |
73 | export default App;
74 |
--------------------------------------------------------------------------------
/src/addons/links/style.css:
--------------------------------------------------------------------------------
1 | .AddonItem {
2 | color: #555;
3 | position: relative;
4 | }
5 |
6 | .AddonItem h5 {
7 | font-size: 0.75rem;
8 | font-weight: 500;
9 | color: #444;
10 | text-transform: uppercase;
11 | margin-bottom: 3px;
12 | }
13 |
14 | .AddonItem a {
15 | font-size: 0.95em;
16 | margin-top: 5px;
17 | }
18 |
19 | .AddonItem .AddonConfigure {
20 | position: absolute;
21 | right: 0px;
22 | top: -3px;
23 | cursor: pointer;
24 | opacity: 0;
25 | transform: rotate(0deg);
26 | transition: 300ms all;
27 | transition-delay: 200ms;
28 | }
29 |
30 | .AddonItem .AddonConfigure path {
31 | fill: #222;
32 | }
33 | .darkmode .AddonItem .AddonConfigure path {
34 | fill: #fff;
35 | }
36 |
37 | .AddonItem:hover .AddonConfigure {
38 | opacity: 0.8;
39 | transform: rotate(180deg);
40 | }
41 |
42 | .darkmode .AddonItem h5 {
43 | font-size: 0.75rem;
44 | font-weight: 500;
45 | color: #aaa;
46 | text-transform: uppercase;
47 | margin-bottom: 3px;
48 | }
49 |
50 |
51 | .imageCarrier {
52 | width: 194px;
53 | max-height: 120px;
54 | display: flex;
55 | justify-content: center;
56 | align-items: center;
57 | border-radius: 5px;
58 | overflow: hidden;
59 | margin-bottom: 10px;
60 | opacity: 0.8;
61 | transition: 200ms all;
62 | line-height: 0px;
63 | }
64 |
65 | .imageCarrier:hover {
66 | opacity: 1;
67 | }
68 |
69 | .linkCarrier {
70 | padding: 5px 7px;
71 | border-radius: 5px;
72 | color: #40858c;
73 | border: 1px solid #adcfe8;
74 | display: block;
75 | background: #cae3ef;
76 | margin-bottom: 5px;
77 | }
78 |
79 | .linkCarrier b {
80 | font-weight: 500;
81 | }
82 |
83 | .darkmode .linkCarrier {
84 | color: #b1c5b2;
85 | border: 1px solid #3b753d;
86 | display: block;
87 | background: #243e24;
88 | }
89 |
--------------------------------------------------------------------------------
/src/addons/write-good/addon.json:
--------------------------------------------------------------------------------
1 | {
2 | "slug": "write-good",
3 | "display": "Write-Good",
4 | "description": "Write-Good is an addon based on an open-source tool that helps you write more readable paragraphs with suggestions."
5 | }
6 |
--------------------------------------------------------------------------------
/src/addons/write-good/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 | import API from '../../js/api';
4 |
5 | import writeGood from "write-good";
6 |
7 | const THROTTLE_LIMIT = 5; // fire for every 5 changes
8 |
9 | class App extends Component {
10 | state = {
11 | text: ""
12 | }
13 |
14 | componentDidMount(){
15 | this.throttleCounter = 0;
16 | API.event.on("lineFocused", this.lineFocusedAction);
17 | API.event.on("lineChanged", this.lineChangedAction);
18 | }
19 |
20 | componentWillUnmount(){
21 | API.event.removeListener("lineFocused", this.lineFocusedAction);
22 | API.event.removeListener("lineChanged", this.lineFocusedAction);
23 | }
24 |
25 | lineFocusedAction = (line) => {
26 | this.setState({
27 | text: line.text,
28 | lineId: line.lineId,
29 | index: line.index
30 | });
31 | }
32 |
33 | lineChangedAction = (text) => {
34 | if(this.throttleCounter === THROTTLE_LIMIT){
35 | this.setState({text});
36 | this.throttleCounter = 0;
37 | }
38 | this.throttleCounter++;
39 | }
40 |
41 |
42 | render() {
43 | if(this.state.text){
44 | let writeGoodSuggestions = writeGood(this.state.text);
45 | if(writeGoodSuggestions.length){
46 | return (
47 | <>
48 |
49 |
52 |
Write-Good
53 |
{writeGoodSuggestions.map((s, i) => {
54 | return (
{s.reason}
)
55 | })}
56 |
57 | >
58 | );
59 | }else{
60 | return null;
61 | }
62 | }else{
63 | return null;
64 | }
65 | }
66 | }
67 |
68 | export default App;
69 |
--------------------------------------------------------------------------------
/src/addons/write-good/style.css:
--------------------------------------------------------------------------------
1 | .AddonItem {
2 | color: #555;
3 | position: relative;
4 | }
5 |
6 | .AddonItem h5 {
7 | font-size: 0.75rem;
8 | font-weight: 500;
9 | color: #444;
10 | text-transform: uppercase;
11 | margin-bottom: 3px;
12 | }
13 |
14 | .darkmode .AddonItem h5 {
15 | font-size: 0.75rem;
16 | font-weight: 500;
17 | color: #aaa;
18 | text-transform: uppercase;
19 | margin-bottom: 3px;
20 | }
21 |
22 | .AddonItem > div {
23 | font-size: 0.85em;
24 | margin-top: 10px;
25 | }
26 |
27 | .AddonItem .AddonConfigure {
28 | position: absolute;
29 | right: 0px;
30 | top: -3px;
31 | cursor: pointer;
32 | opacity: 0;
33 | transform: rotate(0deg);
34 | transition: 300ms all;
35 | transition-delay: 200ms;
36 | }
37 |
38 | .AddonItem .AddonConfigure path {
39 | fill: #222;
40 | }
41 | .darkmode .AddonItem .AddonConfigure path {
42 | fill: #fff;
43 | }
44 |
45 | .AddonItem:hover .AddonConfigure {
46 | opacity: 0.8;
47 | transform: rotate(180deg);
48 | }
49 |
50 | .writeGoodAdvice {
51 | background: #fff3d1;
52 | padding: 5px;
53 | border-radius: 5px;
54 | border: 1px solid #ffe395;
55 | margin-bottom: 5px;
56 | color: #8a712a;
57 | }
58 |
59 | .darkmode .writeGoodAdvice {
60 | background: #1f343c;
61 | border: 1px solid #19506d;
62 | color: #a3bfd0;
63 | }
64 |
--------------------------------------------------------------------------------
/src/assets/icons/mac/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/icons/mac/icon.icns
--------------------------------------------------------------------------------
/src/assets/icons/png/1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/icons/png/1024x1024.png
--------------------------------------------------------------------------------
/src/assets/icons/png/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/icons/png/128x128.png
--------------------------------------------------------------------------------
/src/assets/icons/png/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/icons/png/16x16.png
--------------------------------------------------------------------------------
/src/assets/icons/png/24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/icons/png/24x24.png
--------------------------------------------------------------------------------
/src/assets/icons/png/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/icons/png/256x256.png
--------------------------------------------------------------------------------
/src/assets/icons/png/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/icons/png/32x32.png
--------------------------------------------------------------------------------
/src/assets/icons/png/48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/icons/png/48x48.png
--------------------------------------------------------------------------------
/src/assets/icons/png/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/icons/png/512x512.png
--------------------------------------------------------------------------------
/src/assets/icons/png/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/icons/png/64x64.png
--------------------------------------------------------------------------------
/src/assets/icons/win/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/icons/win/icon.ico
--------------------------------------------------------------------------------
/src/assets/memo_desktop.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/memo_desktop_png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/memo_desktop_png.png
--------------------------------------------------------------------------------
/src/assets/memo_logo_left.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/memo_logo_left_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
19 |
--------------------------------------------------------------------------------
/src/assets/memo_logo_old.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/memo_logo_right.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/memo_mobile.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/memo_mobile_png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/assets/memo_mobile_png.png
--------------------------------------------------------------------------------
/src/assets/memo_mobile_stage.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/components/AppBar/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 |
4 |
5 | class App extends Component {
6 |
7 | state = {
8 | maximized: false
9 | }
10 |
11 | renderWindowsButtons(){
12 | const remote = window.require('electron').remote;
13 | let win = remote.getCurrentWindow();
14 |
15 | return (
16 |
17 |
win.minimize()}>
18 |
19 |
20 | {!this.state.maximized &&
21 |
{ this.setState({maximized: true}); win.maximize() }}>
22 |
23 |
24 | }
25 | {this.state.maximized &&
26 |
{ this.setState({maximized: false}); win.unmaximize() }}>
27 |
28 |
29 | }
30 |
win.close()}>
31 |
32 |
33 |
34 | );
35 | }
36 |
37 | render() {
38 | if(window && window.process && window.process.type) {
39 | if(this.props.spacer){
40 | return ();
41 | }else {
42 | return (
43 |
44 |

45 | {window.process.platform != 'darwin' && this.renderWindowsButtons()}
46 |
47 | );
48 | }
49 | }else{
50 | return null;
51 | }
52 | }
53 | }
54 |
55 | export default App;
56 |
--------------------------------------------------------------------------------
/src/components/AppBar/style.css:
--------------------------------------------------------------------------------
1 |
2 | .AppTitle {
3 | width: 100%;
4 | height: 36px;
5 | -webkit-app-region: drag;
6 | background: linear-gradient(rgba(245,245,245, 1), rgba(255,255,255, 1));
7 | border-bottom: 1px solid #fafafa;
8 | position: absolute;
9 | top: 0px;
10 | left: 0px;
11 | z-index: 99;
12 | }
13 |
14 | .AppTitle:active {
15 | background: linear-gradient(rgba(245,245,245, 1), rgba(245,245,245, 1));
16 | }
17 |
18 | .darkmode .AppTitle {
19 | background: linear-gradient(rgba(23,23,23, 1), rgba(37,37,37, 1));
20 | border-bottom-color: rgba(30,30,30, 1);
21 | }
22 |
23 | .darkmode .AppTitle:active {
24 | background: linear-gradient(rgba(23,23,23, 1), rgba(23,23,23, 1));
25 | }
26 |
27 | .AppTitle img {
28 | -webkit-app-region: drag;
29 | height: 20px;
30 | padding: 8px 20px;
31 | }
32 |
33 | .AppTitle.AppTitleOSX img {
34 | display: block;
35 | margin: auto;
36 | }
37 |
38 | .AppTitleSpacer {
39 | width: 100%;
40 | height: 36px;
41 | }
42 |
43 | #window-controls {
44 | display: grid;
45 | grid-template-columns: repeat(3, 46px);
46 | position: absolute;
47 | top: 0;
48 | right: 0;
49 | height: 100%;
50 | font-family: "Segoe MDL2 Assets";
51 | font-size: 10px;
52 | -webkit-app-region: no-drag;
53 | }
54 |
55 | #window-controls .wbutton {
56 | grid-row: 1 / span 1;
57 | display: flex;
58 | justify-content: center;
59 | align-items: center;
60 | width: 100%;
61 | height: 100%;
62 | user-select: none;
63 | cursor: default;
64 | -webkit-app-region: no-drag;
65 | }
66 |
67 | .darkmode #window-controls .wbutton {
68 | color: #fff;
69 | }
70 |
71 | #window-controls #min-button {
72 | grid-column: 1;
73 | }
74 | #window-controls #max-button, #window-controls #restore-button {
75 | grid-column: 2;
76 | }
77 | #window-controls #close-button {
78 | grid-column: 3;
79 | }
80 | #window-controls .wbutton:hover {
81 | background: linear-gradient(rgba(245,245,245, 1), rgba(245,245,245, 1));
82 | }
83 | .darkmode #window-controls .wbutton:hover {
84 | background: linear-gradient(rgba(30,30,30, 1), rgba(30,30,30, 1));
85 | }
86 | #window-controls .wbutton:active {
87 | background: rgba(255,255,255,0.2);
88 | }
89 |
90 | #close-button:hover {
91 | background: #E81123 !important;
92 | color: #fff;
93 | }
94 | #close-button:active {
95 | background: #f1707a !important;
96 | color: #000;
97 | }
98 |
99 | .darkmode #close-button:active {
100 | background: #800000 !important;
101 | color: #fff;
102 | }
103 |
--------------------------------------------------------------------------------
/src/components/Cover/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 |
4 | import API from '../../js/api'
5 |
6 | class App extends Component {
7 |
8 | state = {
9 | headText: "Drop to Import your notes",
10 | smallText: "Only plaintext is supported!"
11 | }
12 |
13 | componentDidMount(){
14 | API.event.on("importStarted", fileName => {
15 | this.setState({
16 | headText: "Processing...",
17 | smallText: "Please wait while processing " + fileName + "..."
18 | })
19 | })
20 |
21 | API.event.on("importEnded", () => {
22 | setTimeout(() => {
23 | this.setState({
24 | headText: "Drop to Import your notes",
25 | smallText: "Only plaintext is supported!"
26 | })
27 | }, 200);
28 | })
29 | }
30 |
31 | render() {
32 | return (
33 | <>
34 |
35 |
36 |
39 |
{this.state.headText}
40 |
{this.state.smallText}
41 |
42 |
43 | >
44 | );
45 | }
46 | }
47 |
48 | export default App;
49 |
--------------------------------------------------------------------------------
/src/components/Cover/style.css:
--------------------------------------------------------------------------------
1 | .cover {
2 | width: 100vw;
3 | height: 100vh;
4 | background: rgba(255,255,255,0.8);
5 | position: fixed;
6 | top: 0px;
7 | left: 0px;
8 | z-index: 9999;
9 | visibility: hidden;
10 | opacity: 0;
11 | transition: 200ms;
12 | transition-delay: 100ms opacity;
13 | }
14 |
15 | .darkmode .cover {
16 | background: rgba(0,0,0,0.8);
17 | }
18 |
19 | .coverActive {
20 | visibility: visible;
21 | opacity: 1;
22 | }
23 |
24 | .coverInner {
25 | margin: 10px;
26 | border: 5px dashed #ccc;
27 | border-radius: 20px;
28 | width: calc(100% - 30px);
29 | height: calc(100% - 30px);
30 | display: flex;
31 | justify-content: center;
32 | align-items: center;
33 | flex-direction: column;
34 | background: rgba(255,255,255,0.6);
35 | }
36 |
37 | .darkmode .coverInner {
38 | background: rgba(0,0,0,0.6);
39 | color: #ddd;
40 | }
41 |
42 | .coverInner svg {
43 | fill: #242938;
44 | }
45 |
46 | .darkmode .coverInner svg {
47 | fill: #ffffff;
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/Handy/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 | import API from '../../js/api';
4 |
5 | import WriteGood from '../../addons/write-good/';
6 | import Conversion from '../../addons/conversion/';
7 | import Calculator from '../../addons/calculator/';
8 | import Links from '../../addons/links/';
9 |
10 | class App extends Component {
11 | state = {
12 | addons: API.getData("addons") != null ? API.getData("addons") : API.defaultAddons,
13 | position: 0
14 | }
15 |
16 | componentDidMount(){
17 |
18 | API.event.on("lineFocused", (line) => {
19 | this.setState({
20 | position: line.position
21 | });
22 | });
23 |
24 | API.event.on("sheet", () => {
25 | this.setState({ position: 0 });
26 | });
27 |
28 | API.event.on("addonsUpdated", (addons) => {
29 | this.setState({ addons });
30 | });
31 |
32 | }
33 |
34 |
35 | render() {
36 | let addons = this.state.addons;
37 |
38 | return (
39 | <>
40 |
41 |
42 | {addons.includes("|write-good|") && }
43 | {addons.includes("|conversion|") && }
44 | {addons.includes("|calculator|") && }
45 | {addons.includes("|links|") && }
46 |
47 |
48 | >
49 | );
50 | }
51 | }
52 |
53 | export default App;
54 |
--------------------------------------------------------------------------------
/src/components/Handy/style.css:
--------------------------------------------------------------------------------
1 | .Handy {
2 | width: 225px;
3 | position: absolute;
4 | right: 0px;
5 | padding: 10px 0px;
6 | }
7 |
8 | .HandyInner {
9 | padding: 0px 15px;
10 | margin-top: 5px;
11 | font-size: 1em;
12 | }
13 |
14 | .HandyInnerApp {
15 | position: relative;
16 | top: -36px;
17 | }
18 |
19 | div.HandyInner > div:nth-child(1) > h5 {
20 | padding-top: 0px;
21 | margin-top: 0px;
22 |
23 | }
24 |
25 | @media (max-width: 820px) {
26 | .Handy {
27 | width: calc(100% - 50px);
28 | position: fixed;
29 | right: auto;
30 | top: auto !important;
31 | bottom: 0px;
32 | height: auto;
33 | left: 0px;
34 | background: #fff;
35 | overflow-y: scroll;
36 | max-height: 150px;
37 | border-top: 1px solid #fafafa;
38 | }
39 |
40 | .darkmode .Handy {
41 | background: #222;
42 | border-top-color: rgba(30,30,30, 1);
43 | }
44 |
45 | .HandyInnerApp {
46 | position: relative;
47 | top: 0px;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/Line/style.css:
--------------------------------------------------------------------------------
1 | .Line {
2 | line-height: 0px;
3 | }
4 |
5 | .Line textarea {
6 | overflow: hidden;
7 | color: #444;
8 | }
9 |
10 | .darkmode .Line textarea {
11 | overflow: hidden;
12 | color: #ddd;
13 | background: #252525;
14 | }
15 |
16 | .Line textarea.white {
17 | background: #fafafa;
18 | }
19 |
20 | .Line textarea.yellow {
21 | background: #f5f4be;
22 | }
23 |
24 | .Line textarea.purple {
25 | background: #efc8d9;
26 | }
27 |
28 | .Line textarea.green {
29 | background: #d0ecc8;
30 | }
31 |
32 | .Line textarea.blue {
33 | background: #d3eaf5;
34 | }
35 |
36 | .Line textarea:focus {
37 | background: #fafafa;
38 | }
39 |
40 | .darkmode .Line textarea:focus {
41 | background: #1e1e1e;
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/Loading/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 | import API from '../../js/api';
4 |
5 | const quoteRand = Math.floor(Math.random() * (4 - 0 + 1) ) + 0;
6 |
7 | class App extends Component {
8 |
9 | state = {
10 | spinning: true
11 | }
12 |
13 | componentDidMount(){
14 | API.event.on("loginButton", () => {
15 | this.setState({spinning: false});
16 | });
17 |
18 | API.event.on("checkingUpdates", () => {
19 | this.setState({statusText: "Checking for updates..."});
20 | });
21 |
22 | API.event.on("fetching", () => {
23 | this.setState({statusText: "Processing data from GitHub..."});
24 | });
25 |
26 | API.event.on("fetched", () => {
27 | this.setState({statusText: ""});
28 | });
29 |
30 |
31 | }
32 |
33 | renderQuote(){
34 | let quoteArray = [
35 | {
36 | person: "Muhammed Ali",
37 | quote: "Wake up everyday, expecting resistance, and push through it!"
38 | },
39 | {
40 | person: "Bruce Lee",
41 | quote: "There are no limits. There are only plateaus, and you must not stay there, you must go beyond them."
42 | },
43 | {
44 | person: "Walt Disney",
45 | quote: "When you believe in a thing, believe in it all the way, implicitly and unquestionable."
46 | },
47 | {
48 | person: "Nelson Mandela",
49 | quote: "It always seems impossible until it's done."
50 | },
51 | {
52 | person: "Kobe Bryant",
53 | quote: "If you’re afraid to fail, then you’re probably going to fail."
54 | }
55 | ];
56 |
57 | let quote = quoteArray[quoteRand];
58 |
59 | return (
60 |
61 |
{quote.quote}
62 |
63 |
{quote.person}
64 |
65 | );
66 | }
67 |
68 | render() {
69 | return (
70 |
71 | {this.props.quote &&
)
}
72 | {this.state.spinning &&
73 | <>
74 |
77 | >
78 | }
79 |
{this.props.children}
80 | {this.props.quote && this.renderQuote()}
81 | {this.state.statusText &&
{this.state.statusText}
}
82 |
83 | );
84 | }
85 | }
86 |
87 | export default App;
88 |
--------------------------------------------------------------------------------
/src/components/Loading/style.css:
--------------------------------------------------------------------------------
1 | .Loading {
2 | width: 100%;
3 | height: 100vh;
4 | background: #ffffff;
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | flex-direction: column;
9 | color: #222222;
10 | }
11 |
12 | .darkmode .Loading {
13 | background: #222222;
14 | color: #fff;
15 | }
16 |
17 | .loginStatusText {
18 | position: absolute;
19 | width: 100%;
20 | height: 35px;
21 | text-align: center;
22 | bottom: 0px;
23 | left: 0px;
24 | border-top: 1px solid #eee;
25 | background: #fafafa;
26 | line-height: 35px;
27 | color: #666;
28 | }
29 |
30 | .darkmode .loginStatusText {
31 | border-top: 1px solid #111;
32 | background: #151515;
33 | color: #ddd;
34 | }
35 |
36 | .spinner {
37 | background-image: linear-gradient(to right, #f1f1f1 , #dddddd);
38 | width: 28px;
39 | height: 28px;
40 | display: flex;
41 | justify-content: center;
42 | align-items: center;
43 | border-radius: 14px;
44 | animation: rotating 1s linear infinite;
45 | margin: 25px;
46 | }
47 |
48 | .darkmode .spinner {
49 | background-image: linear-gradient(to right, #888888 , #222222);
50 | }
51 |
52 | .spinnerHole {
53 | background-color: #fff;
54 | width: 18px;
55 | height: 18px;
56 | border-radius: 9px;
57 | }
58 |
59 | .darkmode .spinnerHole {
60 | background-color: #222;
61 | }
62 |
63 | .quote {
64 | width: 300px;
65 | position: relative;
66 | display: flex;
67 | justify-content: center;
68 | align-items: center;
69 | flex-direction: column;
70 | animation: quoteFadeIn 1000ms;
71 | }
72 |
73 | .quote p {
74 | color: #444;
75 | line-height: 1.5em;
76 | text-align: center;
77 | }
78 |
79 | .darkmode .quote p {
80 | color: #ccc;
81 | }
82 |
83 |
84 | .quote .hr {
85 | width: 20px;
86 | border-top: 2px solid #eee;
87 | margin-bottom: 15px;
88 | }
89 |
90 | .darkmode .quote .hr {
91 | border-top: 2px solid #777;
92 | }
93 |
94 | .quote span {
95 | font-weight: 500;
96 | font-size: 0.9em;
97 | color: #444;
98 | }
99 |
100 | .darkmode .quote span {
101 | color: #ccc;
102 | }
103 |
104 | .copyright {
105 | position: fixed;
106 | bottom: 20px;
107 | font-size: 0.8em;
108 | opacity: 0.5;
109 | }
110 |
111 |
112 | @keyframes rotating {
113 | from {
114 | transform: rotate(0deg);
115 | }
116 | to {
117 | transform: rotate(360deg);
118 | }
119 | }
120 |
121 | @keyframes quoteFadeIn {
122 | from {
123 | top: 15px;
124 | opacity: 0;
125 | }
126 | to {
127 | top: 0px;
128 | opacity: 1;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/components/Login/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 |
4 | import API from '../../js/api';
5 |
6 | let AUTH_URL = "https://github.com/login/oauth/authorize?client_id=d63ed284bfb2c8e7a5d4&scope=gist&redirect_uri=https://api.usememo.com/github/";
7 |
8 | if(API.development){
9 | AUTH_URL += "?development=true";
10 | }
11 |
12 | let isRefresh = AUTH_URL.includes("?") ? "&refresh=true" : "?refresh=true";
13 | let AUTH_URL_REFRESH = AUTH_URL + isRefresh;
14 |
15 | class App extends Component {
16 |
17 | state = {
18 | loginButtonText: "Login with GitHub"
19 | }
20 |
21 | componentDidMount(){
22 | if(this.props.forceLogout){
23 | console.log("forcing logout");
24 | setTimeout(() => {
25 | API.event.emit("loginButton");
26 | }, 1000);
27 | }
28 |
29 | if(window.navigator.userAgent.includes("Firefox")){
30 | setTimeout(() => {
31 | API.event.emit("loginButton");
32 | }, 1000);
33 | }
34 | }
35 |
36 | handleIframeLoad(e){
37 | let iframe = this.refs._authIframe;
38 | let isOnline = API.isOnline();
39 | if(iframe && isOnline){
40 | try{
41 | let iframeURL = (iframe.contentWindow||iframe.contentDocument).location.href;
42 | if(iframeURL){
43 | console.warn("Already authorized by GitHub!");
44 | }
45 | API.githubLogin();
46 | } catch(err){
47 | console.log(err);
48 | console.warn("User haven't given authorization to Memo app on GitHub yet!");
49 | API.githubLogin();
50 | setTimeout(() => {
51 | API.event.emit("loginButton");
52 | }, 2000);
53 | }
54 |
55 | }
56 | if(!isOnline){
57 | API.offlineLogin();
58 | }
59 | }
60 |
61 | render() {
62 | return (
63 | <>
64 |
75 | API.offlineLogin()}>
76 | Use Offline
77 |
78 |
79 | {this.props.forceLogout &&
80 | You might also need to sign off from GitHub to login with another Account.
81 | }
82 | >
83 | );
84 | }
85 | }
86 |
87 | export default App;
88 |
--------------------------------------------------------------------------------
/src/components/Login/style.css:
--------------------------------------------------------------------------------
1 | .Login {
2 | height: 48px;
3 | overflow: hidden;
4 | }
5 | .githubIframe {
6 | height: 0px;
7 | width: 0px;
8 | border: 0px;
9 | padding: 0px;
10 | margin: 0px;
11 | }
12 |
13 | .loginWithGithub {
14 | background: #2a2a2a;
15 | border-radius: 6px;
16 | padding: 10px 15px;
17 | color: #fff;
18 | font-weight: 500;
19 | display: flex;
20 | justify-content: center;
21 | align-items: center;
22 | position: relative;
23 | bottom: 0px;
24 | transition: 200ms all;
25 | margin-top: 3px;
26 | }
27 |
28 | .loginWithGithub:hover {
29 | bottom: 2px;
30 | background: #333;
31 | }
32 |
33 | .loginWithGithub span {
34 | text-decoration: none;
35 | }
36 |
37 | .loginWithGithub img {
38 | width: 25px;
39 | height: 25px;
40 | padding: 0px;
41 | margin: 0px;
42 | line-height: 0px;
43 | margin-right: 13px;
44 | }
45 |
46 | .localModeButton {
47 | border: 1px solid #eee;
48 | border-radius: 6px;
49 | padding: 10px 15px;
50 | font-weight: 500;
51 | display: flex;
52 | justify-content: center;
53 | align-items: center;
54 | margin-top: 8px;
55 | cursor: pointer;
56 | color: #777;
57 | box-shadow: rgba(30, 30, 30, 0.1) 0px 4px 5px -4px;
58 | margin-bottom: 5px;
59 | position: relative;
60 | bottom: 0px;
61 | transition: 200ms all;
62 | }
63 |
64 | .localModeButton:hover {
65 | color: #444;
66 | border-color: #76c3e8;
67 | box-shadow: rgba(30, 30, 30, 0.15) 0px 4px 10px -4px;
68 | bottom: 2px;
69 | }
70 |
71 | .darkmode .localModeButton {
72 | border: 1px solid #222;
73 | box-shadow: rgba(0, 0, 0, 0.3) 0px 4px 10px -4px;
74 | color: #aaa;
75 | }
76 |
77 | .darkmode .localModeButton:hover {
78 | box-shadow: rgba(0, 0, 0, 0.4) 0px 4px 10px -4px;
79 | color: #eee;
80 | }
81 |
--------------------------------------------------------------------------------
/src/components/Title/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 |
4 | import AppBar from '../AppBar'
5 |
6 | import API from '../../js/api';
7 | import Files from '../../js/files';
8 |
9 | class App extends Component {
10 |
11 | state = {
12 | text: this.props.children,
13 | archived: !(this.props.sheet.active === 1),
14 | archiveProgress: false
15 | }
16 |
17 | componentDidMount(){
18 | if(this.state.text === "Untitled Sheet"){
19 | setTimeout(() => {
20 | this.refs._title.focus();
21 | },10);
22 | }
23 | }
24 |
25 | componentWillReceiveProps(newProps){
26 | if(this.props.shouldFocused !== newProps.shouldFocused && newProps.shouldFocused){
27 | setTimeout(() => {
28 | this.refs._title.focus();
29 | },10);
30 | }
31 | }
32 |
33 | handleChange(e){
34 | if(e){
35 | this.setState({text: e.target.value});
36 | //.replace(/(\r\n|\n|\r)/gm,"")
37 | }
38 | }
39 |
40 | handleBlur(e){
41 | let text = e.target.value;
42 | if(text !== this.props.sheet.title){
43 | if(!text){
44 | text = "Untitled Sheet";
45 | }
46 | document.title = text + " | Memo";
47 | API.updateTitle(text, this.props.sheet.id);
48 | }
49 | }
50 |
51 | handleKeyDown(e){
52 | let selectionStart = this.refs._title.selectionStart;
53 | let selectionEnd = this.refs._title.selectionEnd;
54 |
55 | if(e.keyCode === 13 || (selectionStart === selectionStart && e.keyCode === 39 && selectionStart === this.state.text.length) || e.keyCode === 40){
56 | this.props.onTitleDown();
57 | e.preventDefault();
58 | return false;
59 | }
60 | }
61 |
62 | exportAction(){
63 | Files.exportFile(this.props.sheet.id).then(res => {
64 | console.log("File export started");
65 | });
66 | }
67 |
68 | archiveAction(){
69 | let currentStatus = this.state.archived;
70 | let toStatus = currentStatus;
71 | this.setState({
72 | archiveProgress: true
73 | })
74 |
75 | API.archiveUpdate(this.props.sheet.id, toStatus).then(() => {
76 | setTimeout(() => {
77 | this.setState({
78 | archived: !currentStatus,
79 | archiveProgress: false
80 | })
81 | }, 100);
82 | });
83 | }
84 |
85 | deleteAction(){
86 | API.deleteSheet(this.props.sheet.id);
87 | API.event.emit("sheet", "LAST_ACCESSED");
88 | }
89 |
90 | render() {
91 | return (
92 | <>
93 |
94 |
this.handleKeyDown(event)}
99 | onBlur={(event) => this.handleBlur(event)}
100 | onChange={(event) => this.handleChange(event)}/>
101 |
102 |
103 |
this.exportAction()}>
104 |
105 |
Export
106 |
107 |
this.archiveAction()}>
108 |
109 | {!this.state.archiveProgress &&
{this.state.archived ? "Unarchive" : "Archive"}}
110 | {this.state.archiveProgress &&
· · ·}
111 |
112 |
this.deleteAction()}>
113 |
114 |
Delete
115 |
116 |
117 |
118 | >
119 | );
120 | }
121 | }
122 |
123 | export default App;
124 |
--------------------------------------------------------------------------------
/src/components/Title/style.css:
--------------------------------------------------------------------------------
1 | .Title {
2 | position: relative;
3 | display: flex;
4 | flex-direction: column-reverse;
5 | }
6 | .Title input {
7 | position: relative;
8 | overflow: hidden;
9 | border: 0px;
10 | background: #fff;
11 | display: block;
12 | font-size: 1.3em;
13 | font-weight: 500;
14 | padding: 20px 20px;
15 | padding-top: 20px;
16 | margin-bottom: 0px;
17 | outline: none;
18 | width: calc(100% - 262px);
19 | padding-right: 240px;
20 | text-overflow: ellipsis;
21 | transition: 200ms width;
22 | }
23 |
24 | .TitleArchived input{
25 | width: calc(100% - 362px);
26 | padding-right: 340px;
27 | }
28 |
29 | .Title input:focus {
30 | background: #fafafa;
31 | }
32 |
33 | .darkmode .Title input {
34 | background: #252525;
35 | color: #fff;
36 | }
37 |
38 | .darkmode .Title input:focus {
39 | background: #1e1e1e;
40 | }
41 |
42 | .inSheetAction {
43 | height: 30px;
44 | background: #fafafa;
45 | display: flex;
46 | flex-direction: row;
47 | align-items: center;
48 | margin: 18px;
49 | position: absolute;
50 | right: 0px;
51 | top: 0px;
52 | border-radius: 15px;
53 | overflow: hidden;
54 | box-sizing: border-box;
55 | border: 1px solid #f1f1f1;
56 | }
57 |
58 | .darkmode .inSheetAction {
59 | border-color: #111;
60 | background: #151515;
61 | }
62 |
63 | .inSheetButton {
64 | padding: 0px 10px;
65 | height: 30px;
66 | line-height: 30px;
67 | font-size: 14px;
68 | background: green;
69 | cursor: pointer;
70 | border-left: 1px solid #eee;
71 | box-sizing: border-box;
72 | background: #fafafa;
73 | color: #666;
74 | justify-content: center;
75 | align-items: center;
76 | display: flex;
77 | }
78 |
79 | .darkmode .inSheetButton {
80 | background: #1e1e1e;
81 | border-color: #151515;
82 | }
83 |
84 | .inSheetButton span {
85 | margin-left: 5px;
86 | line-height: 24px;
87 | font-weight: 500;
88 | color: #444;
89 | position: relative;
90 | top: 1.5px;
91 | }
92 |
93 | .darkmode .inSheetButton span {
94 | color: #ccc;
95 | }
96 |
97 | .inSheetButton svg {
98 | width: 19px !important;
99 | height: 19px !important;
100 | fill: #444;
101 | line-height: 0px;
102 | }
103 |
104 | .darkmode .inSheetButton svg {
105 | fill: #ccc;
106 | }
107 |
108 | .inSheetButton:first-child {
109 | border-left: 0px;
110 | padding-left: 15px;
111 | }
112 |
113 | .inSheetButton:last-child {
114 | padding-right: 15px;
115 | }
116 |
117 | .inSheetButton:hover {
118 | background: #fff;
119 | }
120 |
121 | .darkmode .inSheetButton:hover {
122 | background: #000;
123 | }
124 |
125 | .inSheetButton:hover span {
126 | color: #000;
127 | }
128 |
129 | .darkmode .inSheetButton:hover span {
130 | color: #fff;
131 | }
132 |
133 | .inSheetButton:hover svg {
134 | fill: #000;
135 | }
136 |
137 | .darkmode .inSheetButton:hover svg {
138 | fill: #fff;
139 | }
140 |
141 | .archivedButton {
142 | padding-right: 15px;
143 | border-right: 0px !important;
144 | }
145 |
146 | .unarchivedButton span{
147 | color: #aaad00 !important;
148 | }
149 |
150 | .unarchivedButton svg {
151 | fill: #aaad00 !important;
152 | }
153 |
154 | .darkmode .unarchivedButton span{
155 | color: #d8d694 !important;
156 | }
157 |
158 | .darkmode .unarchivedButton svg {
159 | fill: #d8d694 !important;
160 | }
161 |
162 | .removeButton {
163 | max-width: 0px;
164 | overflow: hidden;
165 | visibility: hidden;
166 | transition: 200ms all;
167 | padding: 0px 0px;
168 | padding-right: 0px !important;
169 | }
170 |
171 |
172 | .removeButton svg {
173 | fill: #d23745 !important;
174 | }
175 |
176 | .removeButton span {
177 | color: #d23745 !important;
178 | }
179 |
180 | .darkmode .removeButton svg {
181 | fill: #fd6b78 !important;
182 | }
183 |
184 | .darkmode .removeButton span {
185 | color: #fd6b78 !important;
186 | }
187 |
188 | .removeButtonActive {
189 | max-width: 100px;
190 | visibility: visible;
191 | padding: 0px 10px;
192 | padding-right: 15px !important;
193 | }
194 |
195 | @media (max-width: 670px) {
196 | .inSheetAction {
197 | position: relative;
198 | margin-bottom: 0px;
199 | }
200 |
201 | .inSheetButton {
202 | width: 100%;
203 | max-width: none;
204 | }
205 |
206 | .removeButton {
207 | display: none;
208 | }
209 |
210 | .removeButtonActive {
211 | display: flex;
212 | }
213 |
214 | .Title input {
215 | width: calc(100% - 42px);
216 | padding-right: 20px;
217 | }
218 | }
219 |
220 | @media (max-width: 420px) {
221 | .inSheetButton span {
222 | display: none;
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/src/components/Toolbar/attachment_icon.md:
--------------------------------------------------------------------------------
1 |
2 | Event.emit("toggle", "attachments")}>
3 |
Attachments
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/Toolbar/style.css:
--------------------------------------------------------------------------------
1 | .Toolbar {
2 | width: 50px;
3 | border: 0px;
4 | height: 100%;
5 | background: #fff;
6 | display: flex;
7 | flex-direction: row;
8 | position: fixed;
9 | right: 0px;
10 | top: 0px;
11 | box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 5px 0px;
12 | z-index: 103;
13 | border-radius: 0px 0px 0px 0px;
14 | transition: 300ms width;
15 | user-select: none;
16 | -webkit-user-select: none;
17 | -webkit-app-region: no-drag !important;
18 | max-width: 100%;
19 | }
20 |
21 | .ToolbarWin {
22 | height: calc(100% - 36px);
23 | top: 36px;
24 | z-index: 99;
25 | }
26 |
27 | .darkmode .Toolbar {
28 | background: #222;
29 | box-shadow: rgba(0, 0, 0, 0.3) -2px 0px 16px -4px;
30 | }
31 |
32 | .Menu {
33 | width: 50px;
34 | height: 100%;
35 | display: flex;
36 | flex-direction: column;
37 | justify-content: space-between;
38 | -webkit-user-select: none;
39 | -moz-user-select: none;
40 | -ms-user-select: none;
41 | user-select: none;
42 |
43 | }
44 |
45 | .Menu .Top, .Menu .Bottom {
46 | display: flex;
47 | flex-direction: column;
48 | justify-content: flex-start;
49 | }
50 |
51 | .Item {
52 | position: relative;
53 | cursor: pointer;
54 | background: #fff;
55 | width: 24px;
56 | height: 24px;
57 | padding: 10px;
58 | margin: 3px;
59 | position: relative;
60 | transition: 200ms all;
61 | border-radius: 25px;
62 | }
63 |
64 | .Item .dot {
65 | position: absolute;
66 | right: 4px;
67 | bottom: 4px;
68 | width: 8px;
69 | height: 8px;
70 | border-radius: 4px;
71 | background-color: #76c3e8;
72 | }
73 |
74 | .ItemActive {
75 | background: #f1f1f1;
76 | }
77 |
78 | .darkmode .Item {
79 | background: #222;
80 | }
81 |
82 | .darkmode .ItemActive {
83 | background: #1b1b1b;
84 | }
85 |
86 | .Item.Search {
87 | width: 374px;
88 | background: transparent;
89 | }
90 |
91 | .Item svg {
92 | fill: #242938;
93 | }
94 |
95 | .darkmode .Item svg {
96 | fill: #ccc;
97 | }
98 |
99 | .Menu .Item .ToolTip {
100 | position: absolute;
101 | top: -1px;
102 | left: -90px;
103 | visibility: hidden;
104 | opacity: 0;
105 | width: 80px;
106 | height: 20px;
107 | margin: 15px 0px;
108 | background: rgba(0,0,0,0.8);
109 | text-align: center;
110 | transition: 100ms all;
111 | color: #fff;
112 | text-transform: uppercase;
113 | font-size: 0.60em;
114 | font-weight: 500;
115 | display: flex;
116 | justify-content: center;
117 | align-items: center;
118 | border-radius: 6px;
119 | transition-delay: 0ms;
120 | }
121 |
122 | .darkmode .Menu .Item .ToolTip {
123 | background: #fff;
124 | color: #444;
125 | }
126 |
127 | .Menu .Item .ToolTip:before {
128 | content:"\A";
129 | border-style: solid;
130 | border-width: 5px 0 5px 6px;
131 | border-color: transparent transparent transparent rgba(0,0,0,0.8);
132 | position: absolute;
133 | right: -6px;
134 | }
135 |
136 | .darkmode .Menu .Item .ToolTip:before {
137 | border-color: transparent transparent transparent rgba(255,255,255,1);
138 | }
139 |
140 | .Menu .Item img {
141 | opacity: 0.8;
142 | transition: 200ms all;
143 | }
144 |
145 | .Menu .Item:hover img {
146 | opacity: 1;
147 | }
148 |
149 | .Menu .Item:hover .ToolTip {
150 | opacity: 1;
151 | visibility: visible;
152 | left: -85px;
153 | transition-delay: 150ms;
154 | }
155 |
156 |
157 | .tabContent {
158 | width: 345px;
159 | height: 100%;
160 | overflow-y: scroll;
161 | -webkit-overflow-scrolling: touch;
162 | overflow-x: hidden;
163 | position: relative;
164 | transition: 200ms all;
165 | }
166 |
167 | .shadow {
168 | background: rgba(0,0,0,0.2);
169 | width: 100vw;
170 | height: 100vh;
171 | position: fixed;
172 | top: 0px;
173 | left: 0px;
174 | z-index: 98;
175 | visibility: visible;
176 | transition: 300ms all;
177 | }
178 |
179 | .shadowWin {
180 | z-index: 97;
181 | }
182 |
183 | .ToolBack {
184 | position: absolute;
185 | top: 0px;
186 | left: 0px;
187 | width: 100%;
188 | height: 44px;
189 | width: 50px;
190 | display: none;
191 | }
192 |
193 |
194 | @media (max-width: 420px) {
195 | .ToolBack {
196 | display: block;
197 | }
198 |
199 | .MenuOpen {
200 | position: relative;
201 | top: 44px;
202 | height: calc(100% - 44px);
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/src/electron.js:
--------------------------------------------------------------------------------
1 | // Modules to control application life and create native browser window
2 | const {app, BrowserWindow, shell} = require('electron')
3 |
4 | // Keep a global reference of the window object, if you don't, the window will
5 | // be closed automatically when the JavaScript object is garbage collected.
6 | let mainWindow
7 |
8 | function createWindow () {
9 | // Create the browser window.
10 | mainWindow = new BrowserWindow({
11 | width: 1000,
12 | height: 600,
13 | titleBarStyle: "hiddenInset",
14 | frame: false,
15 | webPreferences: {
16 | nodeIntegration: true
17 | }
18 | })
19 | /*
20 | const remote = window.require('electron').remote;
21 | let win = remote.getCurrentWindow();
22 |
23 | win.webContents.session.clearCache(function(){
24 | //some callback.
25 | });
26 | */
27 |
28 | mainWindow.setMenu(null)
29 |
30 | const dev = false;
31 |
32 | if(dev){
33 | mainWindow.loadURL('http://localhost:3000/')
34 | mainWindow.webContents.openDevTools();
35 | }else{
36 | // and load the index.html of the app.
37 | mainWindow.loadURL('https://app.usememo.com/')
38 | }
39 |
40 | mainWindow.webContents.on('new-window', function(event, url){
41 | event.preventDefault();
42 | shell.openItem(url);
43 | });
44 | // Open the DevTools.
45 | // mainWindow.webContents.openDevTools()
46 |
47 | // Emitted when the window is closed.
48 | mainWindow.on('closed', function () {
49 | // Dereference the window object, usually you would store windows
50 | // in an array if your app supports multi windows, this is the time
51 | // when you should delete the corresponding element.
52 | mainWindow = null
53 | })
54 | }
55 |
56 | // This method will be called when Electron has finished
57 | // initialization and is ready to create browser windows.
58 | // Some APIs can only be used after this event occurs.
59 | app.on('ready', createWindow)
60 |
61 | // Quit when all windows are closed.
62 | app.on('window-all-closed', function () {
63 | // On macOS it is common for applications and their menu bar
64 | // to stay active until the user quits explicitly with Cmd + Q
65 | if (process.platform !== 'darwin') app.quit()
66 | })
67 |
68 | app.on('activate', function () {
69 | // On macOS it's common to re-create a window in the app when the
70 | // dock icon is clicked and there are no other windows open.
71 | if (mainWindow === null) createWindow()
72 | })
73 |
74 | // In this file you can include the rest of your app's specific main process
75 | // code. You can also put them in separate files and require them here.
76 |
--------------------------------------------------------------------------------
/src/entitlements.mac.inherit.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.cs.allow-unsigned-executable-memory
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/fonts/font.css:
--------------------------------------------------------------------------------
1 | /* cyrillic */
2 | @font-face {
3 | font-family: 'Rubik';
4 | font-style: normal;
5 | font-weight: 400;
6 | font-display: swap;
7 | src: local('Rubik'), local('Rubik-Regular'), url(./rubik-regular-cyrillic.woff2) format('woff2');
8 | unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
9 | }
10 | /* hebrew */
11 | @font-face {
12 | font-family: 'Rubik';
13 | font-style: normal;
14 | font-weight: 400;
15 | font-display: swap;
16 | src: local('Rubik'), local('Rubik-Regular'), url(./rubik-regular-hebrew.woff2) format('woff2');
17 | unicode-range: U+0590-05FF, U+20AA, U+25CC, U+FB1D-FB4F;
18 | }
19 | /* latin-ext */
20 | @font-face {
21 | font-family: 'Rubik';
22 | font-style: normal;
23 | font-weight: 400;
24 | font-display: swap;
25 | src: local('Rubik'), local('Rubik-Regular'), url(./rubik-regular-latin-ext.woff2) format('woff2');
26 | unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
27 | }
28 | /* latin */
29 | @font-face {
30 | font-family: 'Rubik';
31 | font-style: normal;
32 | font-weight: 400;
33 | font-display: swap;
34 | src: local('Rubik'), local('Rubik-Regular'), url(./rubik-regular.woff2) format('woff2');
35 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
36 | }
37 | /* cyrillic */
38 | @font-face {
39 | font-family: 'Rubik';
40 | font-style: normal;
41 | font-weight: 500;
42 | font-display: swap;
43 | src: local('Rubik Medium'), local('Rubik-Medium'), url(./rubik-medium-cyrillic.woff2) format('woff2');
44 | unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
45 | }
46 | /* hebrew */
47 | @font-face {
48 | font-family: 'Rubik';
49 | font-style: normal;
50 | font-weight: 500;
51 | font-display: swap;
52 | src: local('Rubik Medium'), local('Rubik-Medium'), url(./rubik-medium-hebrew.woff2) format('woff2');
53 | unicode-range: U+0590-05FF, U+20AA, U+25CC, U+FB1D-FB4F;
54 | }
55 | /* latin-ext */
56 | @font-face {
57 | font-family: 'Rubik';
58 | font-style: normal;
59 | font-weight: 500;
60 | font-display: swap;
61 | src: local('Rubik Medium'), local('Rubik-Medium'), url(./rubik-medium-latin-ext.woff2) format('woff2');
62 | unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
63 | }
64 | /* latin */
65 | @font-face {
66 | font-family: 'Rubik';
67 | font-style: normal;
68 | font-weight: 500;
69 | font-display: swap;
70 | src: local('Rubik Medium'), local('Rubik-Medium'), url(./rubik-medium.woff2) format('woff2');
71 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
72 | }
73 |
--------------------------------------------------------------------------------
/src/fonts/rubik-medium-cyrillic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/fonts/rubik-medium-cyrillic.woff2
--------------------------------------------------------------------------------
/src/fonts/rubik-medium-hebrew.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/fonts/rubik-medium-hebrew.woff2
--------------------------------------------------------------------------------
/src/fonts/rubik-medium-latin-ext.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/fonts/rubik-medium-latin-ext.woff2
--------------------------------------------------------------------------------
/src/fonts/rubik-medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/fonts/rubik-medium.woff2
--------------------------------------------------------------------------------
/src/fonts/rubik-regular-cyrillic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/fonts/rubik-regular-cyrillic.woff2
--------------------------------------------------------------------------------
/src/fonts/rubik-regular-hebrew.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/fonts/rubik-regular-hebrew.woff2
--------------------------------------------------------------------------------
/src/fonts/rubik-regular-latin-ext.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/fonts/rubik-regular-latin-ext.woff2
--------------------------------------------------------------------------------
/src/fonts/rubik-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/fonts/rubik-regular.woff2
--------------------------------------------------------------------------------
/src/icon/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Steve Schoger
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 |
--------------------------------------------------------------------------------
/src/icon/README.md:
--------------------------------------------------------------------------------
1 | # Heroicons UI
2 |
3 | A set of 104 free premium SVG icons.
4 |
5 | 
6 |
--------------------------------------------------------------------------------
/src/icon/github.svg:
--------------------------------------------------------------------------------
1 |
39 |
--------------------------------------------------------------------------------
/src/icon/icon-announcement.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-archive.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-arrow-down.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-arrow-left.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-arrow-right.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-arrow-up.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-at-symbol.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-book.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-bookmark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-briefcase.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-browser.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-building.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-calander.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-call-incoming.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-call-outgoing.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-call.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-camera.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-cart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-chat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-check-circle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-cheveron-down.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-cheveron-left.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-cheveron-right.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-cheveron-up.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-clip.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-clipboard.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-clock.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icon/icon-code.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-cog.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-comment.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-compass.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-currency-dollar.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-dashboard.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-desktop.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-discount.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-download.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-duplicate.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-edit.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-emotion-happy.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-emotion-sad.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-exclamation.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-external-link.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-file-blank.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-file-minus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-file-plus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-film.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-filter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-flag.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-folder-minus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-folder-plus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-folder.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-graph-bar.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-grid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-group.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-hashtag.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-heart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-help.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-home.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-image.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-inbox.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-information.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-key.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-link.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-location.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-lock-closed.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-lock-open.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-mail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-map.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-menu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-microphone.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-minus-circle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-minus-square.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-minus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-mobile.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-moon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-more-horiz.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-music.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-news.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-notification.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-plus-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icon/icon-plus-square.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-plus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-print.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-puzzle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-refresh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-repeat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-rocket.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-search.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icon/icon-server.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-speaker.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-star.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-store.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-tablet.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-tag.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-thumb-down.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-thumb-up.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-trash.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-trending-down.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-trending-up.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-trophy.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-upload.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-user-check.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-user-minus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-user-plus.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-video.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-view.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-x-circle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-x-square.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-x.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-zoom-in.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/icon-zoom-out.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icon/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/btk/memo/614129c05fdc2bbea81720ad9a43d1eb2d22032f/src/icon/preview.png
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | -webkit-user-drag: none;
3 | user-drag: none;
4 | -webkit-app-region: no-drag;
5 | }
6 |
7 | body, html, textarea, input {
8 | font-family: "Rubik", -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
9 | }
10 |
11 | body {
12 | overflow: hidden !important;
13 | background: #fff;
14 | font-size: 16px;
15 | }
16 |
17 | body, input, textarea {
18 | margin: 0;
19 | padding: 0;
20 | font-size: 16px;
21 | color: #242938;
22 | }
23 |
24 | h1, h2, h3, h4, h5, h6 {
25 | font-weight: 500 !important;
26 | }
27 |
28 | code {
29 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
30 | monospace;
31 | }
32 |
33 | textarea {
34 | width: calc(100% - 252px);
35 | border: 0px;
36 | outline: 0px !important;
37 | resize: none !important;
38 | padding: 0px 20px;
39 | padding-top: 9px;
40 | padding-right: 230px;
41 | margin: 0px !important;
42 | line-height: 1.5em;
43 | color: #242938;
44 | }
45 |
46 | a {
47 | text-decoration: none;
48 | }
49 |
50 | @media (max-width: 820px) {
51 | textarea {
52 | width: calc(100% - 42px);
53 | padding-right: 20px;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import './win.css';
5 | import './fonts/font.css';
6 |
7 | import App from './App';
8 | import * as serviceWorker from './serviceWorker';
9 |
10 | ReactDOM.render(, document.getElementById('root'));
11 |
12 | // If you want your app to work offline and load faster, you can change
13 | // unregister() to register() below. Note this comes with some pitfalls.
14 | // Learn more about service workers: https://bit.ly/CRA-PWA
15 |
16 | serviceWorker.register();
17 |
--------------------------------------------------------------------------------
/src/js/cursorPosition.js:
--------------------------------------------------------------------------------
1 | export default function getCursorXY(input, selectionPoint){
2 | const {
3 | offsetLeft: inputX,
4 | offsetTop: inputY,
5 | } = input
6 | const div = document.createElement('div')
7 | const copyStyle = getComputedStyle(input)
8 | for (const prop of copyStyle) {
9 | div.style[prop] = copyStyle[prop]
10 | }
11 | const swap = '.'
12 | const inputValue = input.tagName === 'INPUT' ? input.value.replace(/ /g, swap) : input.value
13 | const textContent = inputValue.substr(0, selectionPoint)
14 | div.textContent = textContent
15 | if (input.tagName === 'TEXTAREA') div.style.height = 'auto'
16 | if (input.tagName === 'INPUT') div.style.width = 'auto'
17 | const span = document.createElement('span')
18 | span.textContent = inputValue.substr(selectionPoint) || '.'
19 | div.appendChild(span)
20 | document.body.appendChild(div)
21 | const { offsetLeft: spanX, offsetTop: spanY } = span
22 | document.body.removeChild(div)
23 | return {
24 | x: inputX + spanX,
25 | y: inputY + spanY,
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/js/date.js:
--------------------------------------------------------------------------------
1 | export default function date(length){
2 | var today = new Date();
3 | var dd = today.getDate();
4 | var mm = today.getMonth() + 1; //January is 0!
5 |
6 | var yyyy = today.getFullYear();
7 | if (dd < 10) {
8 | dd = '0' + dd;
9 | }
10 | if (mm < 10) {
11 | mm = '0' + mm;
12 | }
13 | return today = dd + '/' + mm + '/' + yyyy;
14 | }
15 |
--------------------------------------------------------------------------------
/src/js/event.js:
--------------------------------------------------------------------------------
1 | var EventEmitter = require('events').EventEmitter;
2 | EventEmitter.defaultMaxListeners = 40;
3 | let Event = new EventEmitter();
4 |
5 | export default Event;
6 |
--------------------------------------------------------------------------------
/src/js/files.js:
--------------------------------------------------------------------------------
1 | import API from './api';
2 | import LocalDB from './localdb';
3 | import Markdown from './markdown';
4 | import makeid from './makeid';
5 | import { saveAs } from 'file-saver';
6 |
7 | class Files {
8 | listenFileDrop(){
9 | let dropTimer = 0;
10 | let root = document.getElementById('root');
11 | let cover = document.getElementById('cover');
12 |
13 | root.ondragover = function() {
14 | clearTimeout(dropTimer);
15 | dropTimer = setTimeout(function() {
16 | cover.className = "cover";
17 | return false;
18 | }, 1000);
19 |
20 | if(cover.className != "cover coverActive"){
21 | cover.className = "cover coverActive";
22 | }
23 | return false;
24 | };
25 |
26 | cover.ondragleave = function() {
27 | clearTimeout(dropTimer);
28 | cover.className = "cover";
29 | return false;
30 | };
31 |
32 | root.ondragend = function() {
33 | clearTimeout(dropTimer);
34 | cover.className = "cover";
35 | return false;
36 | };
37 |
38 | root.ondrop = (e) => {
39 | clearTimeout(dropTimer);
40 | e.preventDefault();
41 | if(e.dataTransfer.files.length){
42 | var file = e.dataTransfer.files[0],
43 | reader = new FileReader();
44 |
45 | reader.onload = (event) => {
46 | API.event.emit("importStarted", file.name);
47 | if(file.name.endsWith(".txt") || file.name.endsWith(".md") || file.name.endsWith(".markdown")){
48 | this.importFile(file.name, file.lastModified, event.target.result).then(added => {
49 | API.event.emit("sheet", added);
50 | API.event.emit("importEnded");
51 | cover.className = "cover";
52 | });
53 | }else{
54 | alert("File should be a plaintext format (.txt, .md or .markdown) in order to import in Memo");
55 | cover.className = "cover";
56 | }
57 | };
58 | reader.readAsText(file);
59 | }
60 | return false;
61 | };
62 |
63 | }
64 |
65 | async importFile(fileName, unixTime, text){
66 |
67 | if(text[text.length - 1] == "\r" || text[text.length - 1] == "\n"){
68 | text = text.substr(0, text.length-1)
69 | }
70 |
71 | let fileParagraphs;
72 | // n+r is usually typed in a windows, n+n is default
73 | if(text.includes("\n\r")){
74 | fileParagraphs = text.split(/\n\r/);
75 | }else{
76 | fileParagraphs = text.split(/\n\n/);
77 | }
78 |
79 | await LocalDB.insert("sheet", {
80 | title: "📃 "+fileName,
81 | active: 1,
82 | created_at: Math.floor(unixTime / 1000),
83 | accessed_at: Math.round((new Date()).getTime() / 1000)
84 | });
85 |
86 | let newSheet = await LocalDB.select("sheet", null, {
87 | by: "id",
88 | type: "desc"
89 | }, 1);
90 |
91 | let sheetId = newSheet[0].id;
92 |
93 | let today = new Date();
94 | let lineDate = String(today.getDate()).padStart(2, '0') + "/" + String(today.getMonth() + 1).padStart(2, '0') + "/" + today.getFullYear();
95 |
96 | let linePos = 0;
97 | for (var j = 0; j < fileParagraphs.length; j++) {
98 | let p = fileParagraphs[j];
99 |
100 | if(p[p.length - 1] == "\r" || p[p.length - 1] == "\n"){
101 | p = p.substr(0, p.length-1)
102 | }
103 | if(p[0] == "\r" || p[0] == "\n"){
104 | p = p.substr(1)
105 | }
106 | if(p != ""){
107 | let lineKey = makeid(5);
108 | await LocalDB.insert("line", {
109 | sheet_id: sheetId,
110 | line_key: lineKey,
111 | date: lineDate,
112 | text: p,
113 | pos: linePos
114 | });
115 | linePos++;
116 | }
117 | }
118 | API.addToStaging(sheetId);
119 | return sheetId;
120 | }
121 |
122 | exportFile(sheetId) {
123 | try {
124 | var isFileSaverSupported = !!new Blob;
125 | } catch (e) {
126 | alert("Your device doesn't support file saver!");
127 | }
128 |
129 | return Markdown.getSheetMarkdown(sheetId, true).then((sheet) => {
130 | let markdownText = sheet.text;
131 |
132 | var blob = new Blob([markdownText], {
133 | type: "text/plain;charset=utf-8"
134 | });
135 |
136 | saveAs(blob, sheet.title + '.md');
137 | });
138 | }
139 |
140 | }
141 |
142 | const _files = new Files();
143 | export default _files;
144 |
--------------------------------------------------------------------------------
/src/js/localdb.js:
--------------------------------------------------------------------------------
1 | import * as JsStore from 'jsstore';
2 | import { IDataBase, DATA_TYPE, ITable } from 'jsstore';
3 | import * as JsStoreWorker from "jsstore/dist/jsstore.worker.commonjs2";
4 | window['JsStoreWorker'] = JsStoreWorker;
5 |
6 | const connection = new JsStore.Instance();
7 | const DB_NAME ='memo_db';
8 |
9 | class LocalDB {
10 |
11 | initDB(){
12 | try {
13 | const dataBase = this.getDataBaseSchema();
14 | return connection.initDb(dataBase);
15 | }
16 | catch (ex) {
17 | console.error(ex);
18 | }
19 | }
20 |
21 | getConnection(){
22 | return connection;
23 | }
24 |
25 | async insert(table, value){
26 | let noOfDataInserted = await connection.insert({
27 | into: table,
28 | values: [value]
29 | });
30 |
31 | if(noOfDataInserted > 0) {
32 | return true;
33 | }
34 | }
35 |
36 | async select(table, where, order, limit){
37 | let query = {
38 | from: table
39 | };
40 | if(where){
41 | query.where = where;
42 | }
43 | if(limit){
44 | query.limit = limit;
45 | }
46 | if(order){
47 | query.order = order;
48 | }
49 | let results = await connection.select(query);
50 |
51 | return results;
52 | }
53 |
54 | async delete(table, where, limit){
55 | let query = {
56 | from: table,
57 | where
58 | };
59 |
60 | if(limit){
61 | query.limit = limit;
62 | }
63 |
64 | let rowsDeleted = await connection.remove(query);
65 | return rowsDeleted;
66 | }
67 |
68 | async update(table, where, set, limit){
69 | let rowsUpdated = await connection.update({
70 | in: table,
71 | where,
72 | set,
73 | limit
74 | });
75 |
76 | return rowsUpdated;
77 | }
78 |
79 | async count(table, where){
80 | let rowsCounted = await connection.count({
81 | from: table,
82 | where
83 | });
84 |
85 | return rowsCounted;
86 | }
87 |
88 | async truncate(){
89 | let sheetsDeleted = await connection.remove({
90 | from: "sheet"
91 | });
92 |
93 | let linesDeleted = await connection.remove({
94 | from: "line"
95 | });
96 |
97 | return { sheetsDeleted, linesDeleted };
98 | }
99 |
100 | getDataBaseSchema() {
101 | const sheet = {
102 | name: 'sheet',
103 | columns: {
104 | id: { primaryKey: true, autoIncrement: true },
105 | title: { notNull: false, dataType: "string" },
106 | active: { notNull: true, dataType: "number" },
107 | created_at: { notNull: true, dataType: "number" },
108 | accessed_at: { notNull: true, dataType: "number" }
109 | }
110 | };
111 | const line = {
112 | name: 'line',
113 | columns: {
114 | id: { primaryKey: true, autoIncrement: true },
115 | sheet_id: { notNull: true, dataType: "number" },
116 | line_key: { notNull: true, dataType: "string" },
117 | date: { notNull: true, dataType: "string" },
118 | text: { notNull: false, dataType: "string" },
119 | pos: { notNull: true, dataType: "number" }
120 | }
121 | };
122 | const db = {
123 | name: DB_NAME,
124 | tables: [sheet, line]
125 | }
126 | return db;
127 | }
128 |
129 | }
130 |
131 | const _db = new LocalDB();
132 | export default _db;
133 |
--------------------------------------------------------------------------------
/src/js/makeid.js:
--------------------------------------------------------------------------------
1 | export default function makeid(length){
2 | var text = "";
3 | var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
4 |
5 | for(var i=0; i < length; i++)
6 | {
7 | text += possible.charAt(Math.floor(Math.random() * possible.length));
8 | }
9 |
10 | return text;
11 | }
12 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/tabs/Addons/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 | import API from '../../js/api';
4 |
5 | let addons = [
6 | require('../../addons/write-good/addon.json'),
7 | require('../../addons/conversion/addon.json'),
8 | require('../../addons/links/addon.json'),
9 | require('../../addons/calculator/addon.json')
10 | ]
11 |
12 |
13 | class App extends Component {
14 | state = {
15 | addons: API.getData("addons") != null ? API.getData("addons") : API.defaultAddons
16 | }
17 |
18 | toggleAddon(slug){
19 | let addons = this.state.addons;
20 | if(addons.includes("|" + slug + "|")){
21 | addons = addons.replace("|" + slug + "|", "");
22 | }else{
23 | addons = addons + "|" +slug+ "|";
24 | }
25 |
26 | API.event.emit("addonsUpdated", addons);
27 | this.setState({addons});
28 | API.updatePreference("addons", addons);
29 | }
30 |
31 | render() {
32 | return (
33 |
34 |
Addons
35 |
Discover and add tools to customise your note taking experience.
36 | {
37 | addons.map((addon, i) => {
38 | return (
39 |
this.toggleAddon(addon.slug)}>
43 |
44 |
{addon.display}
45 |
49 |
50 |
{addon.description}
51 |
52 |
53 | )
54 | })
55 | }
56 |
57 | );
58 | }
59 | }
60 |
61 | export default App;
62 |
--------------------------------------------------------------------------------
/src/tabs/Addons/style.css:
--------------------------------------------------------------------------------
1 | .AddonsTab {
2 |
3 | }
4 |
5 | .switch {
6 | position: absolute;
7 | display: inline-block;
8 | width: 30px;
9 | height: 17px;
10 | top: 15px;
11 | right: 20px;
12 | }
13 |
14 | .switch input {
15 | opacity: 0;
16 | width: 0;
17 | height: 0;
18 | }
19 |
20 | .slider {
21 | position: absolute;
22 | cursor: pointer;
23 | top: 0;
24 | left: 0;
25 | right: 0;
26 | bottom: 0;
27 | background-color: #ccc;
28 | -webkit-transition: .4s;
29 | transition: .4s;
30 | }
31 |
32 | .slider:before {
33 | position: absolute;
34 | content: "";
35 | height: 13px;
36 | width: 13px;
37 | left: 2px;
38 | bottom: 2px;
39 | background-color: white;
40 | -webkit-transition: .4s;
41 | transition: .4s;
42 | }
43 |
44 | input:checked + .slider {
45 | background-color: #2196F3;
46 | }
47 |
48 | input:focus + .slider {
49 | box-shadow: 0 0 1px #2196F3;
50 | }
51 |
52 | input:checked + .slider:before {
53 | -webkit-transform: translateX(13px);
54 | -ms-transform: translateX(13px);
55 | transform: translateX(13px);
56 | }
57 |
58 | /* Rounded sliders */
59 | .slider.round {
60 | border-radius: 17px;
61 | }
62 |
63 | .slider.round:before {
64 | border-radius: 50%;
65 | }
66 |
67 | .switchBlocker {
68 | background: transparent;
69 | width: 50px; height: 30px;
70 | position: absolute;
71 | z-index: 99;
72 | right: 0px;
73 | top: 0px;
74 | }
75 |
--------------------------------------------------------------------------------
/src/tabs/Archives/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 |
4 | import API from '../../js/api';
5 | import Loading from '../../components/Loading';
6 |
7 | class App extends Component {
8 | state = {
9 | sheets: [],
10 | noSheets: false
11 | }
12 |
13 | componentDidMount(){
14 | API.getSheets(0).then(sheets => {
15 | if(sheets.length){
16 | this.setState({sheets});
17 | }else{
18 | this.setState({noSheets: true});
19 | }
20 | });
21 | }
22 |
23 | renderSheets(sheets){
24 | if(sheets.length){
25 | return sheets.map(sheet => {
26 | let date = new Date(sheet.created_at * 1000);
27 | return (
28 | API.event.emit("sheet", sheet.id)}>
32 |
33 |
{sheet.title}
34 |
{sheet.first_line ? sheet.first_line.substr(0, 50).replace(/-/g, "") + "..." : "This sheet is as empty as it can be..."}
35 |
36 |
37 | {date.getDate() + "/" + date.getMonth() + "/" + date.getFullYear()}
38 | {sheet.first_line &&
39 | {sheet.line_count} Line{sheet.line_count != 1 ? "s": ""}
40 | }
41 | {!sheet.first_line &&
42 | Blank
43 | }
44 |
45 |
46 |
47 | );
48 | });
49 | }else{
50 | if(this.state.noSheets){
51 | return (You don't have any archived sheets!
);
52 | }else{
53 | return ();
54 | }
55 | }
56 | }
57 |
58 | render() {
59 | return (
60 |
61 |
Archives
62 |
See and manage your old and not-so-popular sheets.
63 |
64 | {this.renderSheets(this.state.sheets)}
65 |
66 |
67 | );
68 | }
69 | }
70 |
71 | export default App;
72 |
--------------------------------------------------------------------------------
/src/tabs/Archives/style.css:
--------------------------------------------------------------------------------
1 | .ArchivesTab {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/src/tabs/Push/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 | import API from '../../js/api';
4 | import Loading from '../../components/Loading';
5 |
6 | class App extends Component {
7 |
8 | state = {
9 | syncText: API.getData("staging") ? "Push Changes" : "No Local Changes",
10 | staged: API.getData("staging") ? API.getData("staging") : "",
11 | error: ""
12 | }
13 |
14 | componentDidMount(){
15 | API.event.on("sync", this.syncAction);
16 | API.event.on("syncError", this.syncErrorAction);
17 | }
18 |
19 | componentWillUnmount(){
20 | API.event.removeListener("sync", this.syncAction);
21 | API.event.removeListener("syncError", this.syncErrorAction);
22 | }
23 |
24 | syncAction = (status) => {
25 | if(status == "flushed"){
26 | this.setState({syncText: "No Local Changes", staged: API.getData("staging")});
27 | }else{
28 | this.setState({syncText: "Push Changes", staged: API.getData("staging")});
29 | }
30 | }
31 |
32 | syncErrorAction = (err) => {
33 | this.setState({syncText: "Error Occured!", error: String(err)});
34 | }
35 |
36 | renderChanges(staging){
37 | if(staging){
38 | let staged = staging.split(",");
39 | return staged.map((sheet, i) => {
40 | let sheetId = Number(sheet.replace(/\|/g, ""));
41 | return (
42 | API.event.emit("sheet", sheetId)}>
46 |
Changed sheet with id {sheetId}
47 |
48 | )
49 | })
50 | }else{
51 | return (Your local sheets are in sync with the origin.
);
52 | }
53 | }
54 |
55 | startSync(){
56 | if(this.state.staged){
57 | this.setState({syncText: "Syncing...", staged: ""});
58 | API.sync();
59 | }else{
60 | console.log("Already Synced");
61 | }
62 | }
63 |
64 | render() {
65 | return (
66 |
67 |
Push Changes
68 |
You can push changes to your Gist and sync your data.
69 |
70 |
71 | {API.isOnline() &&
72 |
this.startSync()}>
76 |
79 |
{this.state.syncText}
80 |
81 | }
82 | {(this.state.error != "") &&
83 |
{this.state.error}
Please try to login again by window.location.reload()}>restarting the app.
84 | }
85 | {!API.isOnline() &&
You are in offline mode.
Please connect to internet and window.location.reload()}>restart the app.
}
86 | {(API.isOnline() && (this.state.error == "")) && this.renderChanges(this.state.staged)}
87 |
88 |
89 | );
90 | }
91 | }
92 |
93 | export default App;
94 |
--------------------------------------------------------------------------------
/src/tabs/Push/style.css:
--------------------------------------------------------------------------------
1 | .PushTab {
2 |
3 | }
4 |
5 | div.pushButton {
6 | justify-content: center;
7 | background: #fff;
8 | border: 1px solid #76c3e8;
9 | color: #444;
10 | box-shadow: rgba(30, 30, 30, 0.1) 0px 4px 5px -4px;
11 | position: sticky;
12 | top: 10px;
13 | z-index: 100;
14 | display: flex;
15 | justify-content: center;
16 | flex-direction: row;
17 | align-items: center;
18 | height: 20px;
19 | }
20 |
21 | .darkmode div.pushButton {
22 | color: #fff;
23 | }
24 |
25 | div.pushButton span {
26 | line-height: 1.5em;
27 | font-weight: 500;
28 | }
29 | div.pushButton svg {
30 | margin-right: 5px;
31 | fill: #444;
32 | position: relative;
33 | bottom: 0.5px;
34 | }
35 | .darkmode div.pushButton svg {
36 | fill: #eee;
37 | }
38 |
39 | div.pushButton:hover {
40 | box-shadow: rgba(30, 30, 30, 0.15) 0px 4px 10px -4px;
41 | }
42 |
43 | .pushButton.passive {
44 | border: 1px solid rgba(0,0,0,0.1);
45 | background: rgba(0,0,0,0.05);
46 | }
47 |
48 | .pushButton.passive:hover {
49 | background: rgba(0,0,0,0.05);
50 | box-shadow: none;
51 | }
52 |
53 | .darkmode .pushButton.passive {
54 | border: 1px solid rgba(0,0,0,0.3);
55 | background: rgba(0,0,0,0.05);
56 | }
57 |
58 | .darkmode .pushButton.passive:hover {
59 | background: rgba(0,0,0,0.05);
60 | box-shadow: none;
61 | }
62 |
63 | .pushButton.passive svg {
64 | display: none;
65 | }
66 |
--------------------------------------------------------------------------------
/src/tabs/Search/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 | import API from '../../js/api';
4 | import Loading from '../../components/Loading';
5 |
6 | class App extends Component {
7 |
8 | state = {
9 | searchTerm: "",
10 | sheets: [],
11 | noSheets: false
12 | }
13 |
14 | componentDidMount(){
15 | setTimeout(() => {
16 | this.refs._searchInput.focus();
17 |
18 | }, 200);
19 | }
20 |
21 | handleChange(e){
22 | if(e){
23 | let searchTerm = e.target.value;
24 | this.setState({searchTerm});
25 | API.searchSheets(searchTerm).then(sheets => {
26 |
27 | let noSheets = (sheets.length === 0);
28 | this.setState({sheets, noSheets});
29 | })
30 | }
31 | }
32 |
33 | renderSheets(sheets){
34 | if(sheets.length){
35 | return sheets.map(sheet => {
36 | let date = new Date(sheet.created_at * 1000);
37 | return (
38 | API.event.emit("sheet", sheet.id)}>
42 |
43 |
{sheet.title}
44 |
{sheet.first_line ? sheet.first_line.substr(0, 50).replace(/-/g, "") + "..." : "This sheet is as empty as it can be..."}
45 |
46 |
47 | {date.getDate() + "/" + (date.getMonth() + 1) + "/" + date.getFullYear()}
48 | {sheet.first_line &&
49 | {sheet.line_count} Line{sheet.line_count != 1 ? "s": ""}
50 | }
51 | {!sheet.first_line &&
52 | Blank
53 | }
54 |
55 |
56 |
57 | );
58 | });
59 | }else{
60 | if(this.state.noSheets){
61 | return (No results with this term
);
62 | }else{
63 | return ();
64 | }
65 | }
66 | }
67 |
68 | render() {
69 | return (
70 |
71 |
72 |
Search
73 |
Search for a term in title or content of your notes.
74 |
75 |
this.handleChange(event)}/>
81 |
82 |
83 | { this.state.searchTerm && this.renderSheets(this.state.sheets)}
84 |
85 |
86 | );
87 | }
88 | }
89 |
90 | export default App;
91 |
--------------------------------------------------------------------------------
/src/tabs/Search/style.css:
--------------------------------------------------------------------------------
1 | .SearchTab {
2 | padding: 0px 10px;
3 | }
4 |
5 | .SearchResults {
6 | margin-top: 20px;
7 | }
8 |
9 | .searchInput {
10 | background: #fff;
11 | border: 1px solid #76c3e8;
12 | box-shadow: rgba(30, 30, 30, 0.1) 0px 4px 5px -4px;
13 | position: sticky;
14 | top: 10px;
15 | z-index: 100;
16 | width: calc(100% - 20px);
17 | border-radius: 3px;
18 | padding: 9px;
19 | outline: 0;
20 | }
21 |
22 | .darkmode .searchInput {
23 | background: #222;
24 | color: #fff;
25 | }
26 | .darkmode .searchInput:focus {
27 | background: #333;
28 | }
29 |
30 | .SearchHead {
31 | height: 85px;
32 | opacity: 1;
33 | transition: 200ms all;
34 | }
35 |
36 | .SearchHead h4 {
37 | transition: 200ms all;
38 | }
39 |
40 | .SearchHeadHidden {
41 | height: 0px;
42 | opacity: 0;
43 | }
44 | .SearchHeadHidden h4 {
45 | padding: 0px;
46 | margin: 0px;
47 | }
48 |
--------------------------------------------------------------------------------
/src/tabs/Settings/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 | import API from "../../js/api"
4 |
5 | class App extends Component {
6 |
7 | state = {
8 | theme: API.getTheme(),
9 | toggleUserOptions: false
10 | }
11 |
12 | changeTheme(changeTo){
13 | this.setState({theme: changeTo})
14 | API.event.emit("theme", changeTo);
15 | }
16 |
17 | changeCurrency(event){
18 | API.updatePreference("currency", event.target.value);
19 | }
20 |
21 | render() {
22 | let currencies = ["CAD", "HKD", "ISK", "PHP", "DKK", "HUF", "CZK", "GBP", "RON", "SEK", "IDR", "INR", "BRL", "RUB", "HRK", "JPY", "THB", "CHF", "EUR", "MYR", "BGN", "TRY", "CNY", "NOK", "NZD", "ZAR", "USD", "MXN", "SGD", "AUD", "ILS", "KRW", "PLN" ];
23 | let handle = API.user ? API.user.username ? API.user.username : API.user.email.split("@")[0] : API.anonymousUser.username;
24 |
25 | return (
26 |
27 |
Settings
28 |
Manage your preferences and access your account information.
29 |
Choose Your Side
30 |
31 |
this.changeTheme("dark")} className={this.state.theme == "dark" ? "darkTheme themeActive" : "darkTheme"}>
32 |
33 |
}/)
34 |
35 |
36 |
37 |
this.changeTheme("light")} className={this.state.theme == "light" ? "lightTheme themeActive" : "lightTheme"}>
38 |
39 |
}/)
40 |
41 |
42 |
43 |
44 |
Currency Preference
45 |
Choose your prefered currency that will be used for addons.
46 |
53 | <>
54 |
Revision History
55 |
In case of data loss, you can use your Gist revisions and see your change history.
56 | {API.user &&
57 |
58 | See Your Gist Revisions
59 |
60 | }
61 | >
62 | {!API.user &&
63 |
Only Available Online
64 | }
65 |
Memo App v{API.version} {API.version[0] == "0" && "Beta"}
66 |
67 | {API.isOnline() &&
68 |
69 |
70 |

71 |
72 |
{API.user.name ? API.user.name : handle[0].toUpperCase() + handle.substr(1)}
73 |
@{handle}
74 |
75 |
{
76 | let toggleUserOptions = this.state.toggleUserOptions;
77 | this.setState({toggleUserOptions: !toggleUserOptions});
78 | }}>
79 |
80 |
81 |
82 |
90 |
91 | }
92 | {!API.isOnline() &&
93 |
94 | You are in offline mode.
95 |
96 |
97 | You can login the app when you have internet.
98 | window.location.reload()}>Try to login?
99 |
100 |
101 | }
102 |
103 |
104 | );
105 | }
106 | }
107 |
108 | export default App;
109 |
--------------------------------------------------------------------------------
/src/tabs/Settings/style.css:
--------------------------------------------------------------------------------
1 | .SettingsTab {
2 | margin-bottom: 180px;
3 | }
4 |
5 | .SettingsTab h5 {
6 | font-size: 0.9em;
7 | margin-bottom: 8px;
8 | }
9 |
10 | .darkmode .SettingsTab h5 {
11 | color: #fff;
12 | }
13 |
14 | .prefItem.theme {
15 | display: flex;
16 | flex-direction: row;
17 | width: 100%;
18 | }
19 |
20 | .themeScreen {
21 | height: 75px;
22 | display: flex;
23 | justify-content: center;
24 | align-items: center;
25 | flex-direction: column;
26 | border-radius: 10px;
27 | margin: 3px;
28 | }
29 |
30 | .themeScreen img {
31 | height: 18px;
32 | }
33 |
34 | .themeScreenElement {
35 | width: 50px;
36 | height: 10px;
37 | margin-top: 10px;
38 | border-radius: 3px;
39 | }
40 |
41 | .darkTheme, .lightTheme {
42 | border: 2px solid transparent;
43 | cursor: pointer;
44 | flex: 1;
45 | }
46 |
47 | .darkTheme .themeScreen {
48 | background: #333;
49 | }
50 |
51 | .darkTheme .themeScreenElement {
52 | background: #222;
53 | }
54 |
55 | .lightTheme .themeScreen {
56 | background: #fff;
57 | }
58 |
59 | .lightTheme .themeScreenElement {
60 | background: #eee;
61 | }
62 |
63 | .themeActive {
64 | border: 2px solid #76c3e8;
65 | border-radius: 15px;
66 | }
67 |
68 | .label {
69 | background: rgba(255,255,255, 0);
70 | color: #444;
71 | text-align: center;
72 | padding: 7px 10px;
73 | border-radius: 10px;
74 | border: 2px solid #76c3e8;
75 | overflow: hidden;
76 | cursor: pointer;
77 | }
78 |
79 | .darkmode .label {
80 | background: rgba(255,255,255, 0.05);
81 | color: #fff;
82 | }
83 |
84 | .darkmode .label option {
85 | color: #444;
86 | }
87 |
88 | .version {
89 | opacity: 0.5;
90 | margin-top: 40px;
91 | font-size: 0.8em;
92 | text-align: center;
93 | }
94 |
95 | .darkmode .version {
96 | color: #ddd;
97 | }
98 |
99 | .myaccount {
100 | position: fixed;
101 | background: #fff;
102 | width: 100%;
103 | bottom: 0px;
104 | width: 310px;
105 | border-top: 1px solid #f1f1f1;
106 | }
107 |
108 | .darkmode .myaccount {
109 | background: #222222;
110 | border-top-color: #1a1a1a;
111 | }
112 |
113 | .toggleIcon {
114 | position: absolute;
115 | right: 15px;
116 | display: flex;
117 | border-radius: 15px;
118 | height: 30px;
119 | width: 30px;
120 | display: flex;
121 | justify-content: center;
122 | align-items: center;
123 | cursor: pointer;
124 | transition: 150ms transform;
125 | transform: rotate(0deg);
126 | }
127 |
128 | .toggleIconActive {
129 | background: #f1f1f1;
130 | transform: rotate(180deg);
131 | }
132 |
133 | .darkmode .toggleIconActive {
134 | background: #1b1b1b;
135 | }
136 |
137 | .toggleIcon svg {
138 | fill: #242938;
139 | }
140 |
141 | .darkmode .toggleIcon svg {
142 | fill: #ccc;
143 | }
144 |
145 | .subUser {
146 | padding: 2px;
147 | display: flex;
148 | flex-direction: row;
149 | margin-top: 10px;
150 | margin-bottom: 10px;
151 | align-items: center;
152 | }
153 |
154 | .userName {
155 | font-size: 0.9em;
156 | font-weight: 500;
157 | }
158 |
159 | .darkmode .userName {
160 | color: #fff;
161 | }
162 |
163 | .userHandle {
164 | font-size: 0.85em;
165 | opacity: 0.7;
166 | margin-top: 2px;
167 | }
168 |
169 | .darkmode .userHandle {
170 | color: #ddd;
171 | }
172 |
173 | .subUser img {
174 | border-radius: 20px;
175 | margin-right: 10px;
176 | opacity: 0.9;
177 | }
178 |
179 | .userOptions {
180 | overflow: hidden;
181 | transition: 150ms all;
182 | }
183 |
184 | .userOptions ul {
185 | list-style: none;
186 | padding: 0px;
187 | margin: 0px;
188 | margin-bottom: 10px;
189 | margin-left: 46px;
190 | }
191 |
192 | .userOptions a, .userOptions span {
193 | font-size: 0.9em;
194 | color: #555;
195 | padding: 5px;
196 | line-height: 1.6em;
197 | }
198 |
199 | .darkmode .userOptions a, .darkmode .userOptions span {
200 | color: #ddd;
201 | }
202 |
203 | .userOptions a:hover, .userOptions span:hover {
204 | color: #222;
205 | }
206 |
207 | .darkmode .userOptions a:hover, .darkmode .userOptions span:hover {
208 | color: #aaa;
209 | }
210 |
--------------------------------------------------------------------------------
/src/tabs/Sheets/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 |
4 | import API from '../../js/api';
5 | import Loading from '../../components/Loading';
6 |
7 | class App extends Component {
8 | state = {
9 | sheets: [],
10 | noSheets: false,
11 | archivedSheetCount: 0
12 | }
13 |
14 | componentDidMount(){
15 | API.getSheets(1).then(sheets => {
16 | if(sheets.length){
17 | this.setState({sheets});
18 | }else{
19 | this.setState({noSheets: true});
20 | }
21 | });
22 |
23 | API.getSheets(0, true).then(sheetCount => {
24 | this.setState({archivedSheetCount: sheetCount});
25 | });
26 | }
27 |
28 | renderSheets(sheets){
29 | if(sheets.length){
30 | return sheets.map(sheet => {
31 | let date = new Date(sheet.created_at * 1000);
32 | return (
33 | API.event.emit("sheet", sheet.id)}>
37 |
38 |
{sheet.title}
39 |
{sheet.first_line ? sheet.first_line.substr(0, 50).replace(/-/g, "") + "..." : "This sheet is as empty as it can be..."}
40 |
41 |
42 | {date.getDate() + "/" + (date.getMonth() + 1) + "/" + date.getFullYear()}
43 | {sheet.first_line &&
44 | {sheet.line_count} Line{sheet.line_count != 1 ? "s": ""}
45 | }
46 | {!sheet.first_line &&
47 | Blank
48 | }
49 |
50 |
51 |
52 | );
53 | });
54 | }else{
55 | if(this.state.noSheets){
56 | return (You don't have any active sheets, add a new one or check your archives!
);
57 | }else{
58 | return ();
59 | }
60 | }
61 | }
62 |
63 | render() {
64 | return (
65 |
66 |
Sheets
67 |
See and manage your most recently accessed sheets.
68 |
69 |
API.event.emit("sheet", "NEW_SHEET")}>
73 |
74 |
Create New Sheet
75 |
76 | {this.renderSheets(this.state.sheets)}
77 | {this.state.archivedSheetCount != 0 &&
78 |
API.event.emit("toggle", "archives")}>
83 |
You also have {this.state.archivedSheetCount} archived sheet{this.state.archivedSheetCount !== 1 ? "s" : ""}.
84 |
85 | }
86 |
87 |
88 | );
89 | }
90 | }
91 |
92 | export default App;
93 |
--------------------------------------------------------------------------------
/src/tabs/Sheets/style.css:
--------------------------------------------------------------------------------
1 | div.addNew {
2 | justify-content: center;
3 | background: #fff;
4 | border: 1px solid #76c3e8;
5 | color: #444;
6 | box-shadow: rgba(30, 30, 30, 0.1) 0px 4px 5px -4px;
7 | position: sticky;
8 | top: 10px;
9 | z-index: 100;
10 | display: flex;
11 | justify-content: center;
12 | flex-direction: row;
13 | align-items: center;
14 | height: 20px;
15 | }
16 |
17 | .darkmode div.addNew {
18 | color: #fff;
19 | }
20 |
21 | div.addNew span {
22 | line-height: 1.5em;
23 | font-weight: 500;
24 | }
25 | div.addNew svg {
26 | margin-right: 5px;
27 | fill: #444;
28 | position: relative;
29 | bottom: 0.5px;
30 | }
31 | .darkmode div.addNew svg {
32 | fill: #eee;
33 | }
34 |
35 | div.addNew:hover {
36 | box-shadow: rgba(30, 30, 30, 0.15) 0px 4px 10px -4px;
37 | }
38 |
--------------------------------------------------------------------------------
/src/tabs/index.js:
--------------------------------------------------------------------------------
1 | import Addons from './Addons';
2 | import Archives from './Archives';
3 | import Push from './Push';
4 | import Search from './Search';
5 | import Settings from './Settings';
6 | import Sheets from './Sheets';
7 |
8 | import './style.css';
9 |
10 | export { Addons, Archives, Search, Push, Settings, Sheets };
11 |
--------------------------------------------------------------------------------
/src/tabs/style.css:
--------------------------------------------------------------------------------
1 | .TabCarrier {
2 | padding: 0px 20px;
3 | width: 305px;
4 | }
5 |
6 | .TabCarrier h4 {
7 | font-size: 0.9rem;
8 | font-weight: 500;
9 | color: #444;
10 | text-transform: uppercase;
11 | margin-bottom: 0px;
12 | }
13 |
14 | .darkmode .TabCarrier h4 {
15 | color: #fff;
16 | }
17 |
18 | .TabCarrier .sub {
19 | font-size: 0.9rem;
20 | line-height: 1.4em;
21 | color: #444;
22 | }
23 |
24 | .darkmode .TabCarrier .sub {
25 | color: #ddd;
26 | }
27 |
28 | .sheetItem {
29 | cursor: pointer;
30 | position: relative;
31 | height: auto;
32 | display: flex;
33 | flex-direction: row;
34 | align-items: center;
35 | box-shadow: rgba(0, 0, 0, 0) 0px 0px 0px -4px;
36 | background: #f2f3f5;
37 | border-radius: 5px;
38 | transition: 150ms all;
39 | margin-bottom: 10px;
40 | padding: 10px 15px;
41 | }
42 |
43 | .darkmode .sheetItem {
44 | background: #272727;
45 | }
46 |
47 |
48 | .sheetItem:hover {
49 | box-shadow: rgba(30, 30, 30, 0.15) 0px 4px 10px -4px;
50 | background: #fff;
51 | }
52 |
53 | .darkmode .sheetItem:hover {
54 | box-shadow: rgba(0, 0, 0, 0.5) 0px 4px 10px -4px;
55 | background: #232323;
56 | }
57 |
58 | .sheetItem .sheetRight {
59 | display: flex;
60 | justify-content: center;
61 | align-items: flex-start;
62 | flex-direction: column;
63 | font-size: 1em;
64 | }
65 |
66 | .sheetItem .sheetRight span {
67 | line-height: 1.5em;
68 | font-weight: 500;
69 | }
70 |
71 |
72 | .darkmode .sheetItem .sheetRight span {
73 | color: #fff;
74 | }
75 |
76 |
77 | .sheetItem .sheetRight .subHolder {
78 | height: 25px;
79 | }
80 |
81 | .sheetItem .sheetRight sub {
82 | font-weight: normal;
83 | font-size: 0.7em;
84 | opacity: 0.6;
85 | margin-right: 5px;
86 | padding: 3px 10px;
87 | border-radius: 10px;
88 | position: relative;
89 | left: -5px;
90 | background: #fff;
91 | }
92 |
93 | .darkmode .sheetItem .sheetRight sub {
94 | background: #222;
95 | color: #fff;
96 | }
97 |
98 | .sheetItem img {
99 | width: 24px;
100 | height: 24px;
101 | margin-right: 4px;
102 | }
103 |
104 | .tabNotice {
105 | border: 2px dashed #eee;
106 | border-radius: 10px;
107 | padding: 20px;
108 | text-align: center;
109 | color: #666;
110 | line-height: 1.5em;
111 | }
112 |
113 | .darkmode .tabNotice {
114 | border: 2px dashed #333;
115 | color: #ccc;
116 | }
117 |
118 |
119 | @media (max-width: 420px) {
120 | .TabCarrier {
121 | width: calc(100vw - 88px);
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/win.css:
--------------------------------------------------------------------------------
1 | .win ::-webkit-scrollbar {width: 8px; height: 0; position: absolute; right: 0px; }
2 | .win ::-webkit-scrollbar-track {margin: 5px}
3 | .win ::-webkit-scrollbar-thumb {
4 | background: rgba(0,0,0,0.0);
5 | border-radius: 10px;
6 | cursor: pointer;
7 | }
8 |
9 | .win :hover::-webkit-scrollbar-thumb {background: rgba(0,0,0,0.35)}
10 | .win ::-webkit-scrollbar-thumb:hover {background: rgba(0,0,0,0.45)}
11 |
12 | .darkmode.win ::-webkit-scrollbar-thumb {
13 | background: rgba(255,255,255,0);
14 | border-radius: 10px;
15 | box-shadow: none;
16 | cursor: pointer;
17 | }
18 |
19 | .darkmode.win :hover::-webkit-scrollbar-thumb {
20 | background: rgba(255,255,255,0.1);
21 | box-shadow: rgba(0,0,0,0.3) 0 0 0 1px;
22 | }
23 |
24 |
25 | @media screen and (max-width: 480px) {
26 | .win ::-webkit-scrollbar {width: 5px;}
27 | }
28 |
--------------------------------------------------------------------------------