├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── public ├── electron.js ├── favicon.ico ├── images │ └── icons │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-384x384.png │ │ ├── icon-512x512.png │ │ ├── icon-72x72.png │ │ └── icon-96x96.png ├── index.html ├── manifest.json └── refresh │ └── index.html ├── resources └── icon.png ├── scripts └── notarize.js ├── src ├── App.css ├── App.js ├── addons │ ├── calculator │ │ ├── addon.json │ │ ├── index.js │ │ └── style.css │ ├── conversion │ │ ├── addon.json │ │ ├── index.js │ │ └── style.css │ ├── links │ │ ├── addon.json │ │ ├── index.js │ │ └── style.css │ └── write-good │ │ ├── addon.json │ │ ├── index.js │ │ └── style.css ├── assets │ ├── icons │ │ ├── mac │ │ │ └── icon.icns │ │ ├── png │ │ │ ├── 1024x1024.png │ │ │ ├── 128x128.png │ │ │ ├── 16x16.png │ │ │ ├── 24x24.png │ │ │ ├── 256x256.png │ │ │ ├── 32x32.png │ │ │ ├── 48x48.png │ │ │ ├── 512x512.png │ │ │ └── 64x64.png │ │ └── win │ │ │ └── icon.ico │ ├── memo_desktop.svg │ ├── memo_desktop_png.png │ ├── memo_logo_left.svg │ ├── memo_logo_left_white.svg │ ├── memo_logo_old.svg │ ├── memo_logo_right.svg │ ├── memo_mobile.svg │ ├── memo_mobile_png.png │ ├── memo_mobile_stage.svg │ └── memo_store_1.svg ├── components │ ├── AppBar │ │ ├── index.js │ │ └── style.css │ ├── Cover │ │ ├── index.js │ │ └── style.css │ ├── Handy │ │ ├── index.js │ │ └── style.css │ ├── Line │ │ ├── index.js │ │ └── style.css │ ├── Loading │ │ ├── index.js │ │ └── style.css │ ├── Login │ │ ├── index.js │ │ └── style.css │ ├── Title │ │ ├── index.js │ │ └── style.css │ └── Toolbar │ │ ├── attachment_icon.md │ │ ├── index.js │ │ └── style.css ├── electron.js ├── entitlements.mac.inherit.plist ├── fonts │ ├── font.css │ ├── rubik-medium-cyrillic.woff2 │ ├── rubik-medium-hebrew.woff2 │ ├── rubik-medium-latin-ext.woff2 │ ├── rubik-medium.woff2 │ ├── rubik-regular-cyrillic.woff2 │ ├── rubik-regular-hebrew.woff2 │ ├── rubik-regular-latin-ext.woff2 │ └── rubik-regular.woff2 ├── icon │ ├── LICENSE │ ├── README.md │ ├── github.svg │ ├── icon-announcement.svg │ ├── icon-archive.svg │ ├── icon-arrow-down.svg │ ├── icon-arrow-left.svg │ ├── icon-arrow-right.svg │ ├── icon-arrow-up.svg │ ├── icon-at-symbol.svg │ ├── icon-book.svg │ ├── icon-bookmark.svg │ ├── icon-briefcase.svg │ ├── icon-browser.svg │ ├── icon-building.svg │ ├── icon-calander.svg │ ├── icon-call-incoming.svg │ ├── icon-call-outgoing.svg │ ├── icon-call.svg │ ├── icon-camera.svg │ ├── icon-cart.svg │ ├── icon-chat.svg │ ├── icon-check-circle.svg │ ├── icon-cheveron-down.svg │ ├── icon-cheveron-left.svg │ ├── icon-cheveron-right.svg │ ├── icon-cheveron-up.svg │ ├── icon-clip.svg │ ├── icon-clipboard.svg │ ├── icon-clock.svg │ ├── icon-code.svg │ ├── icon-cog.svg │ ├── icon-comment.svg │ ├── icon-compass.svg │ ├── icon-currency-dollar.svg │ ├── icon-dashboard.svg │ ├── icon-desktop.svg │ ├── icon-discount.svg │ ├── icon-download.svg │ ├── icon-duplicate.svg │ ├── icon-edit.svg │ ├── icon-emotion-happy.svg │ ├── icon-emotion-sad.svg │ ├── icon-exclamation.svg │ ├── icon-external-link.svg │ ├── icon-file-blank.svg │ ├── icon-file-minus.svg │ ├── icon-file-plus.svg │ ├── icon-file.svg │ ├── icon-film.svg │ ├── icon-filter.svg │ ├── icon-flag.svg │ ├── icon-folder-minus.svg │ ├── icon-folder-plus.svg │ ├── icon-folder.svg │ ├── icon-globe.svg │ ├── icon-graph-bar.svg │ ├── icon-grid.svg │ ├── icon-group.svg │ ├── icon-hashtag.svg │ ├── icon-heart.svg │ ├── icon-help.svg │ ├── icon-home.svg │ ├── icon-image.svg │ ├── icon-inbox.svg │ ├── icon-information.svg │ ├── icon-key.svg │ ├── icon-link.svg │ ├── icon-location.svg │ ├── icon-lock-closed.svg │ ├── icon-lock-open.svg │ ├── icon-mail.svg │ ├── icon-map.svg │ ├── icon-menu.svg │ ├── icon-microphone.svg │ ├── icon-minus-circle.svg │ ├── icon-minus-square.svg │ ├── icon-minus.svg │ ├── icon-mobile.svg │ ├── icon-moon.svg │ ├── icon-more-horiz.svg │ ├── icon-music.svg │ ├── icon-news.svg │ ├── icon-notification.svg │ ├── icon-plus-circle.svg │ ├── icon-plus-square.svg │ ├── icon-plus.svg │ ├── icon-print.svg │ ├── icon-puzzle.svg │ ├── icon-refresh.svg │ ├── icon-repeat.svg │ ├── icon-rocket.svg │ ├── icon-search.svg │ ├── icon-server.svg │ ├── icon-speaker.svg │ ├── icon-star.svg │ ├── icon-store.svg │ ├── icon-tablet.svg │ ├── icon-tag.svg │ ├── icon-thumb-down.svg │ ├── icon-thumb-up.svg │ ├── icon-trash.svg │ ├── icon-trending-down.svg │ ├── icon-trending-up.svg │ ├── icon-trophy.svg │ ├── icon-upload.svg │ ├── icon-user-check.svg │ ├── icon-user-minus.svg │ ├── icon-user-plus.svg │ ├── icon-user.svg │ ├── icon-video.svg │ ├── icon-view.svg │ ├── icon-x-circle.svg │ ├── icon-x-square.svg │ ├── icon-x.svg │ ├── icon-zoom-in.svg │ ├── icon-zoom-out.svg │ └── preview.png ├── index.css ├── index.js ├── js │ ├── api.js │ ├── cursorPosition.js │ ├── date.js │ ├── event.js │ ├── files.js │ ├── localdb.js │ ├── makeid.js │ ├── markdown.js │ └── octokit.js ├── serviceWorker.js ├── tabs │ ├── Addons │ │ ├── index.js │ │ └── style.css │ ├── Archives │ │ ├── index.js │ │ └── style.css │ ├── Push │ │ ├── index.js │ │ └── style.css │ ├── Search │ │ ├── index.js │ │ └── style.css │ ├── Settings │ │ ├── index.js │ │ └── style.css │ ├── Sheets │ │ ├── index.js │ │ └── style.css │ ├── index.js │ └── style.css └── win.css └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | # testing 8 | /coverage 9 | 10 | # production 11 | /build 12 | /dist 13 | /release-builds 14 | 15 | # misc 16 | .DS_Store 17 | .env 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | api 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | --- 2 | path: "/changelog/" 3 | title: "Changelog" 4 | --- 5 | 6 | ## Beta Versions 7 | 8 | ### v0.6.3 9 | - Fix: GitHub login with parameters deprecation warnings 10 | - Make `font-weight` for some bold text not that bold (600 -> 500) 11 | - [@hannahhhmilan](https://github.com/hannahhhmilan) joins Memo as a co-maker 🎉 12 | 13 | ### v0.6.1 14 | - Better my account section on Settings 15 | - Add Your Data link on username dropdown 16 | - Add a quote from Kobe Bryant, RIP 17 | 18 | ### v0.5.14 19 | - Fix Linux build toolbar bug 20 | 21 | ## Pre-Beta Versions 22 | 23 | No changelog was recorded for pre-beta versions 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Memo is an opensource browser and desktop app that allows you to take smarter notes with GitHub Gists. Own your own private data, store it with Github's gists and access it from anywhere, anytime. 4 | 5 | Check out Memo App: [https://usememo.com](https://usememo.com). 6 | 7 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 8 | 9 | ## Available Scripts 10 | 11 | In the project directory, you can run: 12 | 13 | ### `npm start` 14 | 15 | Runs the app in the development mode.
16 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 17 | 18 | The page will reload if you make edits.
19 | You will also see any lint errors in the console. 20 | 21 | ### `npm test` 22 | 23 | Launches the test runner in the interactive watch mode.
24 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 25 | 26 | ### `npm run build` 27 | 28 | Builds the app for production to the `build` folder.
29 | It correctly bundles React in production mode and optimizes the build for the best performance. 30 | 31 | The build is minified and the filenames include the hashes.
32 | Your app is ready to be deployed! 33 | 34 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 35 | 36 | ### `npm run app` 37 | 38 | Creates and starts the electron app for your local OS.
39 | This is intended for development tests. Also you can toggle `dev` constant in `src/electron.js` file to change between production and development. 40 | 41 | ### `npm run package` 42 | 43 | Try to package electron app for all OS.
44 | Make sure you `dev` constant in `src/electron.js` file is set to false. 45 | 46 | ### `npm run push` 47 | 48 | Take a new build and upload this new build to amazon s3 server.
49 | This will work only if you have proper access and credentials. 50 | 51 | ## Learn More 52 | 53 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 54 | 55 | To learn React, check out the [React documentation](https://reactjs.org/). 56 | 57 | ## Contributing 58 | 59 | Memo is currently in beta, it needs your help but not fully documented, or might have big systematic changes. But pull requests and bug fixes are still welcome. 60 | 61 | Additionally, you might want to make an addon. We will publish a dedicated page or tutorial for how to do this. But you can go to `src/addons` and duplicate one of the addons to use as a template for yours. 62 | 63 | Overall, please be respectful and inclusive, keep in mind that this app does not come with any warranty or liability. 64 | 65 | 66 | Memo - Plain & Smart Notes - Take smarter notes with GitHub Gists, anywhere. | Product Hunt Embed 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memo", 3 | "productName": "Memo", 4 | "description": "Take smarter notes with GitHub gists.", 5 | "version": "1.0.3", 6 | "author": "Burak Tokak (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 | API.event.emit("toggle", "addons")}> 68 | 69 | 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 | API.event.emit("toggle", "addons")}> 57 | 58 | 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 | API.event.emit("toggle", "addons")}> 50 | 51 | 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 | 3 | 4 | memo_desktop 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /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 | 3 | 4 | memo_logo_left 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/assets/memo_logo_left_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | memo_logo_left 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/assets/memo_logo_old.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | memo_logo_old 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/assets/memo_logo_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | memo_logo_right 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/assets/memo_mobile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | memo_mobile 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /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 | 3 | 4 | memo_mobile_stage 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /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 |
37 | 38 |
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 |
75 |
76 |
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 |
65 | 66 |
this.setState({loginButtonText: "Just a second..."})}> 67 | GitHub Logo 68 | {this.state.loginButtonText} 69 |
70 |
71 | {!this.props.forceLogout && 72 | 73 | } 74 |
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 | ![](preview.png) 6 | -------------------------------------------------------------------------------- /src/icon/github.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 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 | 2 | 3 | 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 | 77 | 78 | 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 |
83 | 89 |
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 | --------------------------------------------------------------------------------