├── Dockerfile ├── LICENSE-CODE ├── README.md ├── app.py ├── editor-img.png ├── home-img.png ├── notes.db ├── notes └── Getting Started.md ├── requirements.txt ├── static ├── Icon │ ├── favicon.png │ └── icon-nobg.png ├── fonts │ ├── AGaramondPro-Italic.otf │ ├── Amiri-Regular.ttf │ ├── Biro Script Plus.ttf │ ├── Didot.ttc │ ├── Envy Code R.ttf │ ├── Europa.ttf │ ├── FuturaBkBt.otf │ ├── Garamond.otf │ ├── Hack-Bold.ttf │ ├── Hack-BoldItalic.ttf │ ├── Hack-Italic.ttf │ ├── Hack-Regular.ttf │ ├── Handwrittening-Regular.ttf │ ├── HankenGrotesk-Bold.otf │ ├── HankenGrotesk-SemiBold.otf │ ├── HelveticaNeueBold.otf │ ├── HomemadeApple.ttf │ ├── Janna.ttf │ ├── Lovtony Script.ttf │ ├── Merriweather-Regular.ttf │ ├── NYT │ │ ├── nyt-cheltenham-200-normal.woff │ │ ├── nyt-cheltenham-300-italic.woff │ │ ├── nyt-cheltenham-300-normal.woff │ │ ├── nyt-cheltenham-400-normal.woff │ │ ├── nyt-cheltenham-500-normal.woff │ │ ├── nyt-cheltenham-700-italic.woff │ │ ├── nyt-cheltenham-700-normal.woff │ │ ├── nyt-cheltenham-extra-condensed-bold-700-normal.woff │ │ ├── nyt-cheltenham-sh-400-italic.woff │ │ ├── nyt-cheltenham-sh-400-normal.woff │ │ ├── nyt-cheltenham-sh-700-normal.woff │ │ ├── nyt-franklin-300-italic.woff │ │ ├── nyt-franklin-300-normal.woff │ │ ├── nyt-franklin-500-italic.woff │ │ ├── nyt-franklin-500-normal.woff │ │ ├── nyt-franklin-700-italic.woff │ │ ├── nyt-franklin-700-normal.woff │ │ ├── nyt-karnak-display-130124-400-normal.woff │ │ ├── nyt-mag-sans-500-normal.woff │ │ ├── nyt-mag-sans-700-normal.woff │ │ ├── nyt-mag-serif-headline-700-normal.woff │ │ ├── nyt-mag-slab-700-normal.woff │ │ ├── nyt-stymie-500-normal.woff │ │ └── nyt-stymie-700-normal.woff │ ├── Nexa Light.otf │ ├── RodenBold.otf │ ├── RodenRegular.otf │ ├── ThipanydemoRegular.ttf │ ├── calibri-regular.ttf │ ├── couture-bld.otf │ ├── edit │ │ ├── Gilroy-Bold.ttf │ │ ├── Gilroy-Medium.ttf │ │ └── JosefinSans-Regular.ttf │ ├── leelawad.ttf │ ├── madani.ttf │ └── mathilde.otf ├── github-dark.min.css ├── highlight.min.js ├── img │ ├── cover.png │ ├── folder copy.jpg │ ├── folder-old.jpg │ ├── gradient-bg.jpg │ ├── grid.jpg │ ├── icon.png │ └── note-bg.jpg ├── parser-babel.js ├── parser-html.js ├── parser-markdown.js ├── script.js ├── standalone.js ├── styles.css ├── svg │ ├── .DS_Store │ ├── back.svg │ ├── bold.svg │ ├── bullet-list.svg │ ├── check.svg │ ├── checklist.svg │ ├── code.svg │ ├── delete.svg │ ├── dot.svg │ ├── dots.svg │ ├── download.svg │ ├── edit.svg │ ├── gear.svg │ ├── header-2-svgrepo-com.svg │ ├── header.svg │ ├── highlight.svg │ ├── image.svg │ ├── italic.svg │ ├── link.svg │ ├── num-list.svg │ ├── paragraph.svg │ ├── quote.svg │ ├── save-2.svg │ ├── save.svg │ ├── signature.svg │ ├── table.svg │ ├── title.svg │ ├── underline.svg │ └── video.svg └── tailwind.min.css └── templates ├── edit.html ├── edit_folder.html ├── index.html └── note.html /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | WORKDIR /app 3 | COPY . /app 4 | RUN apt-get update && apt-get install -y sqlite3 5 | RUN pip install --no-cache-dir flask flask-cors 6 | EXPOSE 5000 7 | CMD ["python", "app.py"] 8 | -------------------------------------------------------------------------------- /LICENSE-CODE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Memory 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Memory 2 | 3 | Memory is a simple, open-source note-taking application designed for minimalism and efficiency. The goal is to provide a straightforward interface for taking and organizing notes without unnecessary features that complicate the experience. 4 | 5 | ![Home](./home-img.png) 6 | 7 | ![Editor](./editor-img.png) 8 | 9 | ## Why Memory? 10 | 11 | When you think about it, almost anything on the web starts with a simple idea: an empty page that holds content. Yet, finding a simple, frictionless note-taking app has been a challenge. Over time, many apps have become bloated with features, making the act of writing down thoughts unnecessarily complex. 12 | 13 | Memory was created to solve this problem. It offers a lightweight and intuitive way to organize notes in simple folders, making it easy to record ideas while you work. 14 | 15 | ## Features 16 | 17 | - Simple UI with no unnecessary buttons—just press `` to submit. 18 | - Folder and note creation using ``: 19 | - **New Folder**: Input the folder name and press ``. 20 | - **New Note**: Input the title, folder, and subtitle, then press ``. 21 | - Built-in keyboard shortcuts for a faster workflow: 22 | - ``: Save edits. 23 | - ``: Bold selection. 24 | - ``: Italicize selection. 25 | - ``: Underline selection. 26 | - ``: Highlight selection. 27 | - Instant formatting for pasted URLs (YouTube, images, etc.). 28 | - Local media storage under `/notes`. 29 | 30 | ### Future Features 31 | 32 | - IPFS integration for secure note sharing. 33 | - Import & Export notes. 34 | - Folder and note reordering. 35 | - Code formatting & beautification. 36 | - One-click installation package for non-technical users. 37 | 38 | ## Installation 39 | 40 | To install and run Memory, ensure you have Python installed on your system. 41 | 42 | 1. Clone the repository: 43 | 44 | ```sh 45 | git clone https://github.com/yousboot/memory.git 46 | cd memory 47 | ``` 48 | 49 | 2. Install dependencies: 50 | 51 | ```sh 52 | pip install -r requirements.txt 53 | ``` 54 | 55 | 3. Run the application: 56 | ```sh 57 | python app.py 58 | ``` 59 | 60 | The app will start running locally, allowing you to access it via your browser. 61 | 62 | ## Running with Docker 63 | 64 | Alternatively, you can run Memory using Docker. 65 | 66 | 1. Build the Docker image: 67 | 68 | ```sh 69 | docker build -t memory . 70 | ``` 71 | 72 | 2. Run the Docker container: 73 | 74 | ```sh 75 | docker run -p 5000:5000 memory 76 | ``` 77 | 78 | Now, the app is running inside Docker, and you can access it at: 79 | 80 | ```sh 81 | http://localhost:5000 82 | ``` 83 | 84 | or 85 | 86 | ```sh 87 | http://127.0.0.1:5000 88 | 89 | ``` 90 | 91 | ## Contributing 92 | 93 | Memory is open-source, and contributions are welcome. If you have ideas or improvements, feel free to submit a pull request. 94 | 95 | ## Connect 96 | 97 | For feedback or contributions, reach out to me: 98 | 99 | - [LinkedIn](https://www.linkedin.com/in/yousbot/) 100 | - [GitHub](https://github.com/yousboot) 101 | 102 | Made with ❤️ by Youssef. 103 | 104 | ``` 105 | 106 | ``` 107 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import ( 2 | Flask, 3 | render_template, 4 | request, 5 | redirect, 6 | url_for, 7 | send_from_directory, 8 | jsonify, 9 | ) 10 | import sqlite3 11 | import os 12 | from flask_cors import CORS 13 | 14 | app = Flask(__name__) 15 | DB_FILE = "notes.db" 16 | NOTES_DIR = "notes" 17 | UPLOAD_IMAGE_FOLDER = os.path.join(NOTES_DIR, "images") 18 | UPLOAD_VIDEO_FOLDER = os.path.join(NOTES_DIR, "videos") 19 | 20 | app.config["UPLOAD_IMAGE_FOLDER"] = UPLOAD_IMAGE_FOLDER 21 | app.config["UPLOAD_VIDEO_FOLDER"] = UPLOAD_VIDEO_FOLDER 22 | # app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100MB limit for videos 23 | 24 | CORS( 25 | app, 26 | resources={r"/upload_image": {"origins": "*"}, r"/upload_video": {"origins": "*"}}, 27 | ) 28 | 29 | if not os.path.exists(UPLOAD_IMAGE_FOLDER): 30 | os.makedirs(UPLOAD_IMAGE_FOLDER) 31 | 32 | if not os.path.exists(UPLOAD_VIDEO_FOLDER): 33 | os.makedirs(UPLOAD_VIDEO_FOLDER) 34 | 35 | 36 | def init_db(): 37 | with sqlite3.connect(DB_FILE) as conn: 38 | c = conn.cursor() 39 | c.execute( 40 | """ 41 | CREATE TABLE IF NOT EXISTS folders ( 42 | id INTEGER PRIMARY KEY AUTOINCREMENT, 43 | name TEXT UNIQUE 44 | )""" 45 | ) 46 | c.execute( 47 | """ 48 | CREATE TABLE IF NOT EXISTS notes ( 49 | id INTEGER PRIMARY KEY AUTOINCREMENT, 50 | title TEXT UNIQUE, 51 | filename TEXT, 52 | folder_id INTEGER, 53 | FOREIGN KEY (folder_id) REFERENCES folders(id) ON DELETE CASCADE 54 | )""" 55 | ) 56 | 57 | c.execute("INSERT OR IGNORE INTO folders (name) VALUES ('Principal')") 58 | c.execute("INSERT OR IGNORE INTO folders (name) VALUES ('Templates')") 59 | conn.commit() 60 | 61 | 62 | init_db() 63 | 64 | 65 | @app.route("/") 66 | def index(): 67 | with sqlite3.connect(DB_FILE) as conn: 68 | c = conn.cursor() 69 | c.execute("SELECT id, name FROM folders") 70 | folders = c.fetchall() 71 | c.execute("SELECT id, title FROM notes") 72 | notes = c.fetchall() 73 | return render_template("index.html", notes=notes, folders=folders) 74 | 75 | 76 | @app.route("/add_folder", methods=["POST"]) 77 | def add_folder(): 78 | name = request.form["name"] 79 | with sqlite3.connect(DB_FILE) as conn: 80 | c = conn.cursor() 81 | c.execute("INSERT INTO folders (name) VALUES (?)", (name,)) 82 | conn.commit() 83 | return redirect(url_for("index")) 84 | 85 | 86 | @app.route("/edit_folder/", methods=["GET", "POST"]) 87 | def edit_folder(folder_id): 88 | with sqlite3.connect(DB_FILE) as conn: 89 | c = conn.cursor() 90 | if request.method == "POST": 91 | new_name = request.form["name"] 92 | c.execute("UPDATE folders SET name = ? WHERE id = ?", (new_name, folder_id)) 93 | conn.commit() 94 | return redirect(url_for("index")) 95 | else: 96 | c.execute("SELECT name FROM folders WHERE id = ?", (folder_id,)) 97 | folder = c.fetchone() 98 | return render_template("edit_folder.html", folder=folder, folder_id=folder_id) 99 | 100 | 101 | @app.route("/delete_folder/", methods=["POST"]) 102 | def delete_folder(folder_id): 103 | with sqlite3.connect(DB_FILE) as conn: 104 | c = conn.cursor() 105 | c.execute("DELETE FROM folders WHERE id = ?", (folder_id,)) 106 | conn.commit() 107 | return redirect(url_for("index")) 108 | 109 | 110 | @app.route("/add", methods=["POST"]) 111 | def add_note(): 112 | if request.is_json: # If JSON request 113 | data = request.get_json() 114 | else: # If form submission 115 | data = request.form 116 | 117 | title = data.get("title") 118 | folder_id = data.get("folder_id") 119 | subtitle = data.get("subtitle", "") 120 | 121 | if not title or not folder_id: 122 | return jsonify({"success": False, "error": "Missing title or folder ID"}), 400 123 | 124 | filename = f"{title}.md" 125 | filepath = os.path.join(NOTES_DIR, filename) 126 | 127 | with open(filepath, "w") as f: 128 | f.write(f"") 129 | 130 | with sqlite3.connect(DB_FILE) as conn: 131 | c = conn.cursor() 132 | c.execute( 133 | "INSERT INTO notes (title, filename, folder_id, subtitle) VALUES (?, ?, ?, ?)", 134 | (title, filename, folder_id, subtitle), 135 | ) 136 | conn.commit() 137 | 138 | return jsonify({"success": True}) 139 | 140 | 141 | @app.route("/edit/", methods=["GET", "POST"]) 142 | def edit_note(note_id): 143 | with sqlite3.connect(DB_FILE) as conn: 144 | c = conn.cursor() 145 | c.execute( 146 | "SELECT title, filename, subtitle FROM notes WHERE id = ?", (note_id,) 147 | ) 148 | note = c.fetchone() 149 | if request.method == "POST": 150 | content = request.form["content"] 151 | with open(os.path.join(NOTES_DIR, note[1]), "w") as f: 152 | f.write(content) 153 | return redirect(url_for("index")) 154 | with open(os.path.join(NOTES_DIR, note[1]), "r") as f: 155 | content = f.read() 156 | return render_template( 157 | "edit.html", title=note[0], subtitle=note[2], content=content, note_id=note_id 158 | ) 159 | 160 | 161 | @app.route("/delete/", methods=["POST"]) 162 | def delete_note(note_id): 163 | with sqlite3.connect(DB_FILE) as conn: 164 | c = conn.cursor() 165 | c.execute("SELECT filename FROM notes WHERE id=?", (note_id,)) 166 | row = c.fetchone() 167 | if row: 168 | path = os.path.join(NOTES_DIR, row[0]) 169 | if os.path.exists(path): 170 | os.remove(path) 171 | c.execute("DELETE FROM notes WHERE id=?", (note_id,)) 172 | conn.commit() 173 | return redirect(url_for("index")) 174 | 175 | 176 | @app.route("/upload_image", methods=["POST"]) 177 | def upload_image(): 178 | if "file" not in request.files: 179 | return jsonify({"error": "No file part"}), 400 180 | 181 | file = request.files["file"] 182 | if file.filename == "": 183 | return jsonify({"error": "No selected file"}), 400 184 | 185 | filepath = os.path.join(app.config["UPLOAD_IMAGE_FOLDER"], file.filename) 186 | file.save(filepath) 187 | return jsonify({"url": f"/notes/images/{file.filename}"}) 188 | 189 | 190 | @app.route("/upload_video", methods=["POST"]) 191 | def upload_video(): 192 | if "file" not in request.files: 193 | return jsonify({"error": "No file part"}), 400 194 | 195 | file = request.files["file"] 196 | if file.filename == "": 197 | return jsonify({"error": "No selected file"}), 400 198 | 199 | filepath = os.path.join(app.config["UPLOAD_VIDEO_FOLDER"], file.filename) 200 | file.save(filepath) 201 | return jsonify({"url": f"/notes/videos/{file.filename}"}) 202 | 203 | 204 | @app.route("/notes/images/") 205 | def get_uploaded_image(filename): 206 | return send_from_directory(app.config["UPLOAD_IMAGE_FOLDER"], filename) 207 | 208 | 209 | @app.route("/notes/videos/") 210 | def get_uploaded_video(filename): 211 | return send_from_directory(app.config["UPLOAD_VIDEO_FOLDER"], filename) 212 | 213 | 214 | @app.route("/save/", methods=["POST"]) 215 | def save_note(note_id): 216 | data = request.json 217 | new_content = data.get("content", "") 218 | 219 | with sqlite3.connect(DB_FILE) as conn: 220 | c = conn.cursor() 221 | c.execute("SELECT filename FROM notes WHERE id = ?", (note_id,)) 222 | note = c.fetchone() 223 | 224 | if note: 225 | filepath = os.path.join(NOTES_DIR, note[0]) 226 | with open(filepath, "w") as f: 227 | f.write(new_content) 228 | return jsonify({"success": True}) 229 | else: 230 | return jsonify({"success": False, "error": "Note not found"}), 404 231 | 232 | 233 | @app.route("/note/", methods=["GET"]) 234 | def view_note(note_id): 235 | with sqlite3.connect(DB_FILE) as conn: 236 | c = conn.cursor() 237 | c.execute( 238 | "SELECT title, filename, subtitle FROM notes WHERE id = ?", (note_id,) 239 | ) 240 | note = c.fetchone() 241 | 242 | if not note: 243 | return "Note not found", 404 244 | 245 | with open(os.path.join(NOTES_DIR, note[1]), "r") as f: 246 | content = f.read() 247 | 248 | return render_template( 249 | "note.html", title=note[0], subtitle=note[2], content=content, note_id=note_id 250 | ) 251 | 252 | 253 | @app.route("/get_notes", methods=["GET"]) 254 | def get_notes(): 255 | folder_id = request.args.get("folder_id") 256 | 257 | if not folder_id: 258 | return jsonify({"error": "Missing folder_id parameter"}), 400 259 | 260 | try: 261 | folder_id = int(folder_id) 262 | except ValueError: 263 | return jsonify({"error": "Invalid folder_id parameter"}), 400 264 | 265 | with sqlite3.connect(DB_FILE) as conn: 266 | c = conn.cursor() 267 | c.execute( 268 | "SELECT id, title, subtitle FROM notes WHERE folder_id = ?", (folder_id,) 269 | ) 270 | notes = [ 271 | {"id": row[0], "title": row[1], "subtitle": row[2]} for row in c.fetchall() 272 | ] 273 | return jsonify(notes) 274 | 275 | 276 | if __name__ == "__main__": 277 | app.run(debug=True) 278 | -------------------------------------------------------------------------------- /editor-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/editor-img.png -------------------------------------------------------------------------------- /home-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/home-img.png -------------------------------------------------------------------------------- /notes.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/notes.db -------------------------------------------------------------------------------- /notes/Getting Started.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
When you think about it, almost anything on the website is based on a simple idea; an empty page that holds content. The rest is up to the creator, how to structure it, how to display it, how to guide the user interface ... etc.

Yet, strangely enough i've struggled in finding a simple app to record my notes on while I work. I've used Evernote for years, then notion, then noto, and in the search for simplicty I started using vim text files; with every iteration I got confused and frustrated by the abundant functionalities in the note app. It's as if taking notes is not enough value; they feel the need to create other features, pack more and more into one small area, to the point where the simple act of taking a note or writing what's on your mind becomes a toil.

That's exactly what brought me to create this app, and I hope it relieves you from the pain of organizing your notes in simple folders.

The project is open source so anyone can tweak it according to his needs. It's coded in pure Javascript, html, css, python, and SQLite. The intended purpose is to be self-hosted locally, and easy to tweak and scale in other cases. Among the features projected for future updates:

  • Including IPFS integration to share notes securely online.
  • Import & Export notes.
  • Reordering folders and notes.
  • Code formatting & beautifier for better presentation.
  • Packaging it into a one click app for non-technical users.

Features:

As you might have noticed, the app UI is easy to use. There are no submit button, instead we use <enter>.

For example :

  • Create a new folder : Input your folder name, then press <enter>.

  • Create a new note : Input your title, folder and subtitle then press <enter>


This removes friction and makes the workflow faster. Shortcuts are also included, while editing a note :

  • <Ctrl-s> : Saves edits
  • <Ctrl-b> : Makes selection bold
  • <Ctrl-i>  : Makes selection italic
  • <Ctrl-u> : Underlines selection
  • <Ctrl-h> : Highlights selection


More shortcuts will be included in the future. We still have a bug of reversing the selection edits. Will fix it soon!
I don't want to bore you with more details, just play around and you will realize that your existing knowledge for other editors such as MS Word, Google Docs, Notion ... can be used here. Including creating, table, code snippets, quotes, paragraphs, numbered lists, bullet lists, images and videos.


Image or Youtube URLs are formatted instantly once pasted, and you can also upload your own media, which will then be stored in a the local folder of the app under : /notes.


Ideas are welcome and would like to add any other features that people might find it useful. The code might seem a little fuzzy, repetitive in times, quite a number of dead code and conflicting at times; and what would make some upset is the inexistant documentation which will come soon. 

I am well aware of these issues, and will refactor my code to be much readable and easier for adoption. The driving principle that I always adopt in my projects is :

Iteration creates perfection.

I hope this app can be of great use, and I am open to hear all your suggestions and remarks.

You can reach out to me in the following links :


Made with love

Youssef

 






10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | Flask-CORS 3 | sqlite3 -------------------------------------------------------------------------------- /static/Icon/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/Icon/favicon.png -------------------------------------------------------------------------------- /static/Icon/icon-nobg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/Icon/icon-nobg.png -------------------------------------------------------------------------------- /static/fonts/AGaramondPro-Italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/AGaramondPro-Italic.otf -------------------------------------------------------------------------------- /static/fonts/Amiri-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Amiri-Regular.ttf -------------------------------------------------------------------------------- /static/fonts/Biro Script Plus.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Biro Script Plus.ttf -------------------------------------------------------------------------------- /static/fonts/Didot.ttc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Didot.ttc -------------------------------------------------------------------------------- /static/fonts/Envy Code R.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Envy Code R.ttf -------------------------------------------------------------------------------- /static/fonts/Europa.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Europa.ttf -------------------------------------------------------------------------------- /static/fonts/FuturaBkBt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/FuturaBkBt.otf -------------------------------------------------------------------------------- /static/fonts/Garamond.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Garamond.otf -------------------------------------------------------------------------------- /static/fonts/Hack-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Hack-Bold.ttf -------------------------------------------------------------------------------- /static/fonts/Hack-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Hack-BoldItalic.ttf -------------------------------------------------------------------------------- /static/fonts/Hack-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Hack-Italic.ttf -------------------------------------------------------------------------------- /static/fonts/Hack-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Hack-Regular.ttf -------------------------------------------------------------------------------- /static/fonts/Handwrittening-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Handwrittening-Regular.ttf -------------------------------------------------------------------------------- /static/fonts/HankenGrotesk-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/HankenGrotesk-Bold.otf -------------------------------------------------------------------------------- /static/fonts/HankenGrotesk-SemiBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/HankenGrotesk-SemiBold.otf -------------------------------------------------------------------------------- /static/fonts/HelveticaNeueBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/HelveticaNeueBold.otf -------------------------------------------------------------------------------- /static/fonts/HomemadeApple.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/HomemadeApple.ttf -------------------------------------------------------------------------------- /static/fonts/Janna.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Janna.ttf -------------------------------------------------------------------------------- /static/fonts/Lovtony Script.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Lovtony Script.ttf -------------------------------------------------------------------------------- /static/fonts/Merriweather-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Merriweather-Regular.ttf -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-cheltenham-200-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-cheltenham-200-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-cheltenham-300-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-cheltenham-300-italic.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-cheltenham-300-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-cheltenham-300-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-cheltenham-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-cheltenham-400-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-cheltenham-500-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-cheltenham-500-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-cheltenham-700-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-cheltenham-700-italic.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-cheltenham-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-cheltenham-700-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-cheltenham-extra-condensed-bold-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-cheltenham-extra-condensed-bold-700-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-cheltenham-sh-400-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-cheltenham-sh-400-italic.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-cheltenham-sh-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-cheltenham-sh-400-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-cheltenham-sh-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-cheltenham-sh-700-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-franklin-300-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-franklin-300-italic.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-franklin-300-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-franklin-300-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-franklin-500-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-franklin-500-italic.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-franklin-500-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-franklin-500-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-franklin-700-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-franklin-700-italic.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-franklin-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-franklin-700-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-karnak-display-130124-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-karnak-display-130124-400-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-mag-sans-500-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-mag-sans-500-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-mag-sans-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-mag-sans-700-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-mag-serif-headline-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-mag-serif-headline-700-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-mag-slab-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-mag-slab-700-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-stymie-500-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-stymie-500-normal.woff -------------------------------------------------------------------------------- /static/fonts/NYT/nyt-stymie-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/NYT/nyt-stymie-700-normal.woff -------------------------------------------------------------------------------- /static/fonts/Nexa Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/Nexa Light.otf -------------------------------------------------------------------------------- /static/fonts/RodenBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/RodenBold.otf -------------------------------------------------------------------------------- /static/fonts/RodenRegular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/RodenRegular.otf -------------------------------------------------------------------------------- /static/fonts/ThipanydemoRegular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/ThipanydemoRegular.ttf -------------------------------------------------------------------------------- /static/fonts/calibri-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/calibri-regular.ttf -------------------------------------------------------------------------------- /static/fonts/couture-bld.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/couture-bld.otf -------------------------------------------------------------------------------- /static/fonts/edit/Gilroy-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/edit/Gilroy-Bold.ttf -------------------------------------------------------------------------------- /static/fonts/edit/Gilroy-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/edit/Gilroy-Medium.ttf -------------------------------------------------------------------------------- /static/fonts/edit/JosefinSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/edit/JosefinSans-Regular.ttf -------------------------------------------------------------------------------- /static/fonts/leelawad.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/leelawad.ttf -------------------------------------------------------------------------------- /static/fonts/madani.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/madani.ttf -------------------------------------------------------------------------------- /static/fonts/mathilde.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/fonts/mathilde.otf -------------------------------------------------------------------------------- /static/github-dark.min.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*! 2 | Theme: GitHub Dark 3 | Description: Dark theme as seen on github.com 4 | Author: github.com 5 | Maintainer: @Hirse 6 | Updated: 2021-05-15 7 | 8 | Outdated base version: https://github.com/primer/github-syntax-dark 9 | Current colors taken from GitHub's CSS 10 | */.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c} -------------------------------------------------------------------------------- /static/img/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/img/cover.png -------------------------------------------------------------------------------- /static/img/folder copy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/img/folder copy.jpg -------------------------------------------------------------------------------- /static/img/folder-old.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/img/folder-old.jpg -------------------------------------------------------------------------------- /static/img/gradient-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/img/gradient-bg.jpg -------------------------------------------------------------------------------- /static/img/grid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/img/grid.jpg -------------------------------------------------------------------------------- /static/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/img/icon.png -------------------------------------------------------------------------------- /static/img/note-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/img/note-bg.jpg -------------------------------------------------------------------------------- /static/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | console.log("DOM fully loaded and parsed"); 3 | }); 4 | 5 | function closeModal() { 6 | let modal = document.getElementById("modal"); 7 | if (modal) { 8 | modal.classList.add("hidden"); 9 | modal.style.display = "none"; 10 | } 11 | } 12 | 13 | function showNote(noteId) { 14 | let modal = document.getElementById("modal"); 15 | let modalContent = document.getElementById("modal-content"); 16 | 17 | if (!modal || !modalContent) { 18 | console.error("Error: Modal elements not found"); 19 | return; 20 | } 21 | 22 | fetch(`/show/${noteId}`) 23 | .then((response) => { 24 | if (!response.ok) { 25 | throw new Error( 26 | `Failed to fetch note content (HTTP ${response.status})` 27 | ); 28 | } 29 | return response.text(); 30 | }) 31 | .then((html) => { 32 | modalContent.innerHTML = html; 33 | modal.classList.remove("hidden"); 34 | modal.style.display = "flex"; 35 | }) 36 | .catch((error) => { 37 | console.error("Error loading note:", error); 38 | alert("Failed to load the note. Check console for details."); 39 | }); 40 | } 41 | 42 | function insertElement(type) { 43 | let newElement; 44 | let editableArea = document.getElementById("editor"); 45 | if (!editableArea) return; 46 | const emptyParagraph = document.createElement("p"); 47 | 48 | switch (type) { 49 | case "h1": 50 | newElement = document.createElement("p"); 51 | newElement.className = "text-2xl font-leelawad py-1 text-gray-900"; 52 | newElement.textContent = "Title"; 53 | break; 54 | case "h2": 55 | newElement = document.createElement("p"); 56 | newElement.className = "text-xl font-leelawad py-1 pt-2 text-gray-900"; 57 | newElement.textContent = "Subtitle"; 58 | break; 59 | case "img": 60 | newElement = 61 | "Image


"; 62 | break; 63 | 64 | case "video": 65 | newElement = document.createElement("video"); 66 | newElement.controls = true; 67 | newElement.className = "max-w-full mx-auto"; 68 | let source = document.createElement("source"); 69 | source.src = ""; 70 | source.type = "video/mp4"; 71 | newElement.appendChild(source); 72 | break; 73 | case "h3": 74 | newElement = document.createElement("h3"); 75 | newElement.className = "font-didot text-gray-800 text-xl tracking-wide"; 76 | newElement.textContent = "Paragraph Title"; 77 | break; 78 | case "p": 79 | newElement = document.createElement("p"); 80 | newElement.className = 81 | "font-didot text-gray-800 text-[0.9rem] leading-[1.75]"; 82 | newElement.textContent = "Paragraph text..."; 83 | break; 84 | case "s": 85 | newElement = document.createElement("p"); 86 | newElement.className = "font-biroscript text-[2rem]"; 87 | newElement.textContent = "Paragraph text..."; 88 | break; 89 | case "a": 90 | newElement = document.createElement("a"); 91 | newElement.href = "#"; 92 | newElement.className = "text-blue-500 underline"; 93 | newElement.textContent = "Link"; 94 | break; 95 | case "blockquote": 96 | newElement = document.createElement("blockquote"); 97 | newElement.className = 98 | "border-l-4 border-gray-500 pl-4 text-[1.3rem] text-agaramondpro"; 99 | newElement.textContent = "Quote text..."; 100 | break; 101 | case "code": 102 | newElement = document.createElement("pre"); 103 | const codeBlock = document.createElement("code"); 104 | codeBlock.className = 105 | "block bg-black text-white p-4 pl-6 tracking-wide rounded"; 106 | codeBlock.textContent = ` `; 107 | newElement.appendChild(codeBlock); 108 | break; 109 | 110 | case "ol": 111 | newElement = document.createElement("ol"); 112 | newElement.className = 113 | "list-decimal ml-6 font-didot text-gray-800 text-[0.9rem]"; 114 | newElement.innerHTML = "
  • Item 1
  • Item 2
  • "; 115 | break; 116 | case "ul": 117 | newElement = document.createElement("ul"); 118 | newElement.className = 119 | "list-disc ml-6 font-didot text-gray-800 text-[0.9rem]"; 120 | newElement.innerHTML = "
  • Item 1
  • Item 2
  • "; 121 | break; 122 | case "table": 123 | newElement = document.createElement("table"); 124 | newElement.className = 125 | "w-full border-collapse border font-janna border-gray-400"; 126 | newElement.innerHTML = ` 127 | 128 | Header 1 129 | Header 2 130 | 131 | 132 | Data 1 133 | Data 2 134 | `; 135 | break; 136 | case "checklist": 137 | const checklistItem = document.createElement("li"); 138 | checklistItem.className = "checklist-item flex items-center gap-2"; 139 | checklistItem.innerHTML = ` 140 | 141 | Checklist item 142 | `; 143 | 144 | const checkbox = checklistItem.querySelector(".checklist-checkbox"); 145 | checkbox.addEventListener("change", function () { 146 | if (checkbox.checked) { 147 | checklistItem.innerHTML = ` 148 | 149 | Checklist item 150 | `; 151 | } else { 152 | checklistItem.innerHTML = ` 153 | 154 | Checklist item 155 | `; 156 | } 157 | 158 | const newCheckbox = checklistItem.querySelector(".checklist-checkbox"); 159 | newCheckbox.addEventListener("change", function () { 160 | if (newCheckbox.checked) { 161 | checklistItem.innerHTML = ` 162 | 163 | Checklist item 164 | `; 165 | } else { 166 | checklistItem.innerHTML = ` 167 | 168 | Checklist item 169 | `; 170 | } 171 | }); 172 | }); 173 | 174 | checklistItem.addEventListener("keydown", function (e) { 175 | if (e.key === "Enter") { 176 | e.preventDefault(); 177 | const newItem = document.createElement("li"); 178 | newItem.className = "checklist-item flex items-center gap-2"; 179 | newItem.innerHTML = ` 180 | 181 | New checklist item 182 | `; 183 | checklistItem.insertAdjacentElement("afterend", newItem); 184 | 185 | const newCheckbox = newItem.querySelector(".checklist-checkbox"); 186 | newCheckbox.focus(); 187 | } 188 | }); 189 | 190 | newElement = checklistItem; 191 | break; 192 | 193 | case "delimiter": 194 | newElement = document.createElement("div"); 195 | newElement.className = "flex justify-center p-3"; 196 | 197 | newElement.innerHTML = ` 198 | Edit 203 | Edit 208 | Edit 213 | `; 214 | 215 | emptyParagraph.textContent = ""; 216 | 217 | const editor = document.getElementById("editor"); 218 | editor.appendChild(newElement); 219 | editor.appendChild(emptyParagraph); 220 | break; 221 | } 222 | 223 | if (newElement) { 224 | const selection = window.getSelection(); 225 | if (selection.rangeCount > 0) { 226 | const range = selection.getRangeAt(0); 227 | const editor = document.getElementById("editor"); 228 | 229 | if (editor.contains(range.commonAncestorContainer)) { 230 | range.deleteContents(); 231 | range.insertNode(newElement); 232 | newElement.focus(); 233 | } 234 | } 235 | } 236 | } 237 | 238 | function showPopup(id) { 239 | document.getElementById(id).classList.remove("hidden"); 240 | } 241 | 242 | function insertImage() { 243 | let url = document.getElementById("imageUrl").value; 244 | let editor = document.getElementById("editor"); 245 | if (url) { 246 | editor.innerHTML += ``; 247 | } 248 | document.getElementById("imagePopup").classList.add("hidden"); 249 | } 250 | 251 | function insertVideo() { 252 | let url = document.getElementById("videoUrl").value; 253 | let editor = document.getElementById("editor"); 254 | if (url.includes("youtube.com")) { 255 | let embedUrl = url.replace("watch?v=", "embed/"); 256 | editor.innerHTML += ``; 257 | } 258 | document.getElementById("videoPopup").classList.add("hidden"); 259 | } 260 | 261 | function insertTable() { 262 | let rows = document.getElementById("tableRows").value; 263 | let cols = document.getElementById("tableCols").value; 264 | let table = ``; 265 | for (let r = 0; r < rows; r++) { 266 | table += ""; 267 | for (let c = 0; c < cols; c++) { 268 | table += ""; 269 | } 270 | table += ""; 271 | } 272 | table += "
    Cell
    "; 273 | document.getElementById("editor").innerHTML += table; 274 | document.getElementById("tablePopup").classList.add("hidden"); 275 | } 276 | 277 | function uploadImage() { 278 | let fileInput = document.getElementById("imageUpload"); 279 | let file = fileInput.files[0]; 280 | 281 | if (!file) { 282 | alert("Please select a file to upload."); 283 | return; 284 | } 285 | 286 | let formData = new FormData(); 287 | formData.append("file", file); 288 | 289 | fetch("/upload_image", { 290 | method: "POST", 291 | body: formData, 292 | }) 293 | .then((response) => response.json()) 294 | .then((data) => { 295 | if (data.url) { 296 | insertImage(data.url); 297 | } else { 298 | alert("Upload failed: " + data.error); 299 | } 300 | }) 301 | .catch((error) => console.error("Error:", error)); 302 | } 303 | 304 | function insertImage(url) { 305 | let editor = document.getElementById("editor"); 306 | editor.innerHTML += ``; 307 | document.getElementById("imagePopup").classList.add("hidden"); 308 | } 309 | 310 | function uploadVideo() { 311 | let fileInput = document.getElementById("videoUpload"); 312 | let file = fileInput.files[0]; 313 | 314 | if (!file) { 315 | alert("Please select a file to upload."); 316 | return; 317 | } 318 | 319 | let formData = new FormData(); 320 | formData.append("file", file); 321 | 322 | fetch("/upload_video", { 323 | method: "POST", 324 | body: formData, 325 | }) 326 | .then((response) => response.json()) 327 | .then((data) => { 328 | if (data.url) { 329 | insertVideo(data.url); 330 | } else { 331 | alert("Upload failed: " + data.error); 332 | } 333 | }) 334 | .catch((error) => console.error("Error:", error)); 335 | } 336 | 337 | function insertVideo(url) { 338 | let editor = document.getElementById("editor"); 339 | editor.innerHTML += ``; 340 | document.getElementById("videoPopup").classList.add("hidden"); 341 | } 342 | 343 | document.addEventListener("DOMContentLoaded", () => { 344 | console.log("DOM fully loaded and parsed"); 345 | 346 | let saveButton = document.getElementById("saveButton"); 347 | let editor = document.getElementById("editor"); 348 | let originalContent = editor.innerHTML; 349 | let isChanged = false; 350 | 351 | if (saveButton) { 352 | saveButton.addEventListener("click", () => { 353 | if (!isChanged) return; 354 | 355 | saveButton.style.pointerEvents = "none"; 356 | 357 | let content = editor.innerHTML; 358 | let noteId = saveButton.getAttribute("data-note-id"); 359 | 360 | fetch(`/save/${noteId}`, { 361 | method: "POST", 362 | headers: { "Content-Type": "application/json" }, 363 | body: JSON.stringify({ content }), 364 | }) 365 | .then((response) => response.json()) 366 | .then((data) => { 367 | if (data.success) { 368 | originalContent = content; 369 | isChanged = false; 370 | updateSaveButtonIcon(); 371 | } else { 372 | alert("Error saving note."); 373 | } 374 | }) 375 | .catch((error) => console.error("Error:", error)) 376 | .finally(() => (saveButton.style.pointerEvents = "auto")); 377 | }); 378 | } 379 | 380 | editor.addEventListener("input", () => { 381 | isChanged = editor.innerHTML !== originalContent; 382 | updateSaveButtonIcon(); 383 | }); 384 | 385 | document.addEventListener("keydown", (e) => { 386 | if (e.ctrlKey && e.key === "s") { 387 | e.preventDefault(); 388 | if (saveButton) saveButton.click(); 389 | } 390 | }); 391 | 392 | function updateSaveButtonIcon() { 393 | let saveIcon = saveButton.querySelector("img"); 394 | saveIcon.src = isChanged ? "/static/svg/save.svg" : "/static/svg/check.svg"; 395 | } 396 | }); 397 | 398 | function shareNote(noteId) { 399 | let recipientUrl = prompt("Enter recipient's app URL:"); 400 | if (!recipientUrl) return; 401 | 402 | fetch(`/share/${noteId}`, { 403 | method: "POST", 404 | headers: { "Content-Type": "application/json" }, 405 | body: JSON.stringify({ recipient_url: recipientUrl }), 406 | }) 407 | .then((response) => response.json()) 408 | .then((data) => alert(data.message || "Note shared successfully")) 409 | .catch((error) => alert("Failed to share note")); 410 | } 411 | 412 | document.addEventListener("DOMContentLoaded", () => { 413 | console.log("DOM fully loaded and parsed"); 414 | 415 | let folderInput = document.querySelector('input[name="name"]'); 416 | 417 | if (folderInput) { 418 | folderInput.addEventListener("keypress", function (event) { 419 | if (event.key === "Enter") { 420 | event.preventDefault(); 421 | this.form.submit(); 422 | } 423 | }); 424 | } 425 | }); 426 | 427 | function openNotePopup() { 428 | let activeFolderId = localStorage.getItem("activeFolder"); 429 | let folderSelect = document.getElementById("noteFolder"); 430 | 431 | if (folderSelect && activeFolderId) { 432 | folderSelect.value = activeFolderId; 433 | } 434 | 435 | document.getElementById("notePopup").classList.remove("hidden"); 436 | } 437 | 438 | function closeNotePopup() { 439 | document.getElementById("notePopup").classList.add("hidden"); 440 | } 441 | 442 | function submitNote(event) { 443 | event.preventDefault(); 444 | 445 | let title = document.getElementById("noteTitle").value; 446 | let folderId = document.getElementById("noteFolder").value; 447 | let subtitle = document.getElementById("noteSubtitle").value; 448 | 449 | if (!title || !folderId) { 450 | alert("Title and folder are required."); 451 | return; 452 | } 453 | 454 | fetch("/add", { 455 | method: "POST", 456 | headers: { "Content-Type": "application/x-www-form-urlencoded" }, 457 | body: new URLSearchParams({ title, folder_id: folderId, subtitle }), 458 | }) 459 | .then((response) => response.json()) 460 | .then((data) => { 461 | if (data.success) { 462 | closeNotePopup(); 463 | window.location.href = "/"; 464 | } else { 465 | alert("Error creating note: " + data.error); 466 | } 467 | }) 468 | .catch((error) => console.error("Error:", error)); 469 | } 470 | 471 | document.addEventListener("DOMContentLoaded", () => { 472 | let noteForm = document.getElementById("noteForm"); 473 | if (noteForm) { 474 | noteForm.addEventListener("submit", submitNote); 475 | } 476 | }); 477 | 478 | document.addEventListener("DOMContentLoaded", function () { 479 | let activeFolderId = localStorage.getItem("activeFolder"); 480 | 481 | if (activeFolderId) { 482 | let activeElement = document.querySelector( 483 | `[data-folder-id='${activeFolderId}']` 484 | ); 485 | if (activeElement) { 486 | setActiveFolder(activeElement, activeFolderId); 487 | } 488 | } 489 | }); 490 | 491 | function setActiveFolder(selectedElement, folderId) { 492 | document.querySelectorAll("#folderList li").forEach((folder) => { 493 | folder.classList.remove("bg-custom-folder", "text-gray-700"); 494 | folder.classList.add("bg-custom-slidebar"); 495 | }); 496 | selectedElement.classList.remove("bg-custom-slidebar"); 497 | selectedElement.classList.add("bg-custom-folder", "text-gray-700"); 498 | localStorage.setItem("activeFolder", folderId); 499 | fetch(`/get_notes?folder_id=${folderId}`) 500 | .then((response) => response.json()) 501 | .then((data) => { 502 | let notesList = document.getElementById("notesList"); 503 | notesList.className = "grid grid-cols-2 gap-4"; 504 | notesList.innerHTML = ""; 505 | data.forEach((note) => { 506 | let li = document.createElement("li"); 507 | li.className = 508 | "group p-6 border-b bg-custom-folder rounded-2xl transition duration-200 hover:rounded-md"; 509 | li.innerHTML = ` 510 |
    511 | 512 |
    513 | ${note.title} 514 |

    ${note.subtitle}

    515 |
    516 |
    517 |
    518 | 521 | 524 |
    525 |
    526 | `; 527 | notesList.appendChild(li); 528 | }); 529 | }) 530 | .catch((error) => console.error("Error fetching notes:", error)); 531 | } 532 | 533 | document.addEventListener("DOMContentLoaded", () => { 534 | const notePopup = document.getElementById("notePopup"); 535 | notePopup.addEventListener("click", (e) => { 536 | if (e.target === notePopup) closeNotePopup(); 537 | }); 538 | }); 539 | 540 | document.addEventListener("DOMContentLoaded", () => { 541 | const noteForm = document.getElementById("noteForm"); 542 | noteForm.addEventListener("keydown", (e) => { 543 | if (e.key === "Enter") { 544 | e.preventDefault(); 545 | noteForm.dispatchEvent(new Event("submit", { cancelable: true })); 546 | } 547 | }); 548 | }); 549 | 550 | function editNote(noteId) { 551 | window.location.href = `/edit/${noteId}`; 552 | } 553 | 554 | let noteToDelete = null; 555 | function deleteNote(noteId) { 556 | noteToDelete = noteId; 557 | document.getElementById("deletePopup").classList.remove("hidden"); 558 | } 559 | function closeDeletePopup() { 560 | document.getElementById("deletePopup").classList.add("hidden"); 561 | noteToDelete = null; 562 | } 563 | document.addEventListener("DOMContentLoaded", () => { 564 | document 565 | .getElementById("confirmDeleteButton") 566 | .addEventListener("click", () => { 567 | if (noteToDelete) { 568 | fetch(`/delete/${noteToDelete}`, { method: "POST" }) 569 | .then(() => { 570 | closeDeletePopup(); 571 | window.location.reload(); 572 | }) 573 | .catch((err) => { 574 | console.error("Error deleting note:", err); 575 | closeDeletePopup(); 576 | }); 577 | } 578 | }); 579 | }); 580 | 581 | function editFolder(folderId) { 582 | const folderLi = document.querySelector(`li[data-folder-id='${folderId}']`); 583 | const span = folderLi.querySelector(".folder-name"); 584 | const currentName = span.textContent.trim(); 585 | const input = document.createElement("input"); 586 | input.type = "text"; 587 | input.value = currentName; 588 | input.className = 589 | "folder-name text-xl font-garamond font-bold bg-custom-folder mt-1 ml-4 border-none focus:border-none focus:outline-none"; 590 | folderLi.replaceChild(input, span); 591 | input.focus(); 592 | input.addEventListener("keydown", function (e) { 593 | if (e.key === "Enter") { 594 | e.preventDefault(); 595 | fetch(`/edit_folder/${folderId}`, { 596 | method: "POST", 597 | headers: { "Content-Type": "application/x-www-form-urlencoded" }, 598 | body: new URLSearchParams({ name: input.value }), 599 | }) 600 | .then((res) => { 601 | if (res.ok) { 602 | const newSpan = document.createElement("span"); 603 | newSpan.textContent = input.value; 604 | newSpan.className = 605 | "folder-name font-garamond text-xl font-bold mt-1 ml-4"; 606 | folderLi.replaceChild(newSpan, input); 607 | } else { 608 | alert("Error updating folder."); 609 | } 610 | }) 611 | .catch((err) => { 612 | console.error("Error updating folder:", err); 613 | alert("Error updating folder."); 614 | }); 615 | } 616 | }); 617 | } 618 | 619 | let folderToDelete = null; 620 | function deleteFolder(folderId) { 621 | folderToDelete = folderId; 622 | document.getElementById("deleteFolderPopup").classList.remove("hidden"); 623 | } 624 | function closeDeleteFolderPopup() { 625 | document.getElementById("deleteFolderPopup").classList.add("hidden"); 626 | folderToDelete = null; 627 | } 628 | document.addEventListener("DOMContentLoaded", () => { 629 | document 630 | .getElementById("confirmDeleteFolderButton") 631 | .addEventListener("click", () => { 632 | if (folderToDelete) { 633 | fetch(`/delete_folder/${folderToDelete}`, { method: "POST" }) 634 | .then(() => { 635 | closeDeleteFolderPopup(); 636 | window.location.reload(); 637 | }) 638 | .catch((err) => { 639 | console.error("Error deleting folder:", err); 640 | closeDeleteFolderPopup(); 641 | }); 642 | } 643 | }); 644 | }); 645 | 646 | document.addEventListener("DOMContentLoaded", () => { 647 | const titleInput = document.getElementById("noteTitle"); 648 | const subtitleInput = document.getElementById("noteSubtitle"); 649 | 650 | if (titleInput) { 651 | titleInput.addEventListener("input", function () { 652 | if (this.value.length > 67) this.value = this.value.slice(0, 70); 653 | }); 654 | } 655 | 656 | if (subtitleInput) { 657 | subtitleInput.addEventListener("input", function () { 658 | if (this.value.length > 250) this.value = this.value.slice(0, 200); 659 | }); 660 | } 661 | }); 662 | 663 | document.addEventListener("DOMContentLoaded", () => { 664 | const editor = document.getElementById("editor"); 665 | 666 | editor.addEventListener("paste", (event) => { 667 | let paste = (event.clipboardData || window.clipboardData).getData("text"); 668 | 669 | if (isImageUrl(paste)) { 670 | event.preventDefault(); 671 | insertImageDirectly(paste); 672 | } else if (isYouTubeUrl(paste)) { 673 | event.preventDefault(); 674 | insertYouTubeVideo(paste); 675 | } 676 | }); 677 | 678 | function isImageUrl(url) { 679 | return /\.(jpeg|jpg|gif|png|webp|svg)$/i.test(url); 680 | } 681 | 682 | function isYouTubeUrl(url) { 683 | return /^(https?:\/\/)?(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)/.test( 684 | url 685 | ); 686 | } 687 | 688 | function insertImageDirectly(url) { 689 | editor.innerHTML += ``; 690 | } 691 | 692 | function insertYouTubeVideo(url) { 693 | let embedUrl = url.replace("watch?v=", "embed/"); 694 | const div = document.createElement("div"); 695 | div.className = "w-full flex justify-center my-4"; 696 | div.innerHTML = ``; 697 | const selection = window.getSelection(); 698 | if (selection.rangeCount > 0) { 699 | const range = selection.getRangeAt(0); 700 | range.insertNode(div); 701 | range.collapse(false); 702 | } else { 703 | editor.appendChild(div); 704 | } 705 | } 706 | }); 707 | 708 | function handleImageUpload(event) { 709 | let file = event.target.files[0]; 710 | if (!file) return; 711 | 712 | let formData = new FormData(); 713 | formData.append("file", file); 714 | 715 | fetch("/upload_image", { 716 | method: "POST", 717 | body: formData, 718 | }) 719 | .then((response) => response.json()) 720 | .then((data) => { 721 | if (data.url) { 722 | insertImageDirectly(data.url); 723 | } else { 724 | alert("Upload failed: " + data.error); 725 | } 726 | }) 727 | .catch((error) => console.error("Error:", error)); 728 | } 729 | 730 | function insertImageDirectly(url) { 731 | let editor = document.getElementById("editor"); 732 | editor.innerHTML += ``; 733 | } 734 | 735 | function handleVideoUpload(event) { 736 | let file = event.target.files[0]; 737 | if (!file) return; 738 | 739 | let formData = new FormData(); 740 | formData.append("file", file); 741 | 742 | fetch("/upload_video", { 743 | method: "POST", 744 | body: formData, 745 | }) 746 | .then((response) => response.json()) 747 | .then((data) => { 748 | if (data.url) { 749 | insertVideoDirectly(data.url); 750 | } else { 751 | alert("Upload failed: " + data.error); 752 | } 753 | }) 754 | .catch((error) => console.error("Error:", error)); 755 | } 756 | 757 | function insertVideoDirectly(url) { 758 | let editor = document.getElementById("editor"); 759 | 760 | let videoContainer = document.createElement("div"); 761 | videoContainer.className = "w-full flex justify-center my-4"; 762 | 763 | let videoElement = document.createElement("video"); 764 | videoElement.className = "rounded-lg shadow-lg"; 765 | videoElement.width = 784; 766 | videoElement.height = 441; 767 | videoElement.controls = true; 768 | 769 | let source = document.createElement("source"); 770 | source.src = url; 771 | source.type = "video/mp4"; 772 | 773 | videoElement.appendChild(source); 774 | videoContainer.appendChild(videoElement); 775 | 776 | let emptyParagraph = document.createElement("p"); 777 | emptyParagraph.innerHTML = "
    "; 778 | 779 | const selection = window.getSelection(); 780 | if (selection.rangeCount > 0) { 781 | const range = selection.getRangeAt(0); 782 | range.insertNode(videoContainer); 783 | range.insertNode(emptyParagraph); 784 | range.setStartAfter(emptyParagraph); 785 | range.collapse(true); 786 | selection.removeAllRanges(); 787 | selection.addRange(range); 788 | } else { 789 | editor.appendChild(videoContainer); 790 | editor.appendChild(emptyParagraph); 791 | } 792 | } 793 | 794 | document.addEventListener("DOMContentLoaded", () => { 795 | const tableButton = document.getElementById("tableButton"); 796 | const tableGrid = document.getElementById("tableGrid"); 797 | const editor = document.getElementById("editor"); 798 | 799 | let lastRange = null; 800 | 801 | editor.addEventListener("mouseup", saveSelection); 802 | editor.addEventListener("keyup", saveSelection); 803 | 804 | function saveSelection() { 805 | const selection = window.getSelection(); 806 | if (selection.rangeCount > 0) { 807 | lastRange = selection.getRangeAt(0); 808 | } 809 | } 810 | 811 | for (let row = 1; row <= 10; row++) { 812 | for (let col = 1; col <= 10; col++) { 813 | let cell = document.createElement("div"); 814 | cell.className = 815 | "w-5 h-5 border rounded-sm border-gray-300 bg-custom-slidebar cursor-pointer transition-colors duration-200"; 816 | cell.dataset.row = row; 817 | cell.dataset.col = col; 818 | 819 | cell.addEventListener("mouseover", () => highlightGrid(row, col)); 820 | cell.addEventListener("click", () => insertTable(row, col)); 821 | 822 | tableGrid.appendChild(cell); 823 | } 824 | } 825 | 826 | tableButton.addEventListener("mouseenter", () => { 827 | tableGrid.classList.remove("hidden"); 828 | }); 829 | 830 | tableGrid.addEventListener("mouseenter", () => { 831 | tableGrid.classList.remove("hidden"); 832 | }); 833 | 834 | tableGrid.addEventListener("mouseleave", () => { 835 | tableGrid.classList.add("hidden"); 836 | }); 837 | 838 | tableButton.addEventListener("mouseleave", () => { 839 | setTimeout(() => { 840 | if (!tableGrid.matches(":hover")) tableGrid.classList.add("hidden"); 841 | }, 200); 842 | }); 843 | 844 | function highlightGrid(rows, cols) { 845 | document.querySelectorAll("#tableGrid div").forEach((cell) => { 846 | let r = parseInt(cell.dataset.row); 847 | let c = parseInt(cell.dataset.col); 848 | cell.classList.toggle("bg-custom-color2", r <= rows && c <= cols); 849 | }); 850 | } 851 | 852 | function insertTable(rows, cols) { 853 | let tableWrapper = document.createElement("div"); 854 | tableWrapper.className = "relative w-full my-4 group"; 855 | 856 | let table = document.createElement("table"); 857 | table.className = 858 | "border-collapse border border-gray-400 w-full rounded-md text-lg font-janna"; 859 | table.dataset.columns = cols; 860 | table.dataset.rows = rows; 861 | 862 | for (let r = 0; r < rows; r++) { 863 | let row = document.createElement("tr"); 864 | for (let c = 0; c < cols; c++) { 865 | let cell = document.createElement("td"); 866 | cell.className = "border border-gray-400 px-4 py-3"; 867 | cell.textContent = "Cell"; 868 | row.appendChild(cell); 869 | } 870 | table.appendChild(row); 871 | } 872 | 873 | let addColButton = document.createElement("button"); 874 | addColButton.innerHTML = 875 | '+'; 876 | addColButton.className = 877 | "absolute -right-12 top-0 h-full bg-custom-folder text-gray-600 px-3 py-2 rounded-md text-opacity-80 opacity-0 group-hover:opacity-100 transition flex items-center justify-center"; 878 | addColButton.onclick = () => addColumn(table); 879 | 880 | let addRowButton = document.createElement("button"); 881 | addRowButton.innerHTML = 882 | '+'; 883 | addRowButton.className = 884 | "absolute left-0 -bottom-12 w-full h-10 bg-custom-folder text-gray-600 text-2xl text-opacity-80 rounded-md opacity-0 group-hover:opacity-100 transition flex items-center justify-center mx-auto"; 885 | addRowButton.onclick = () => addRow(table); 886 | 887 | tableWrapper.appendChild(table); 888 | tableWrapper.appendChild(addColButton); 889 | tableWrapper.appendChild(addRowButton); 890 | 891 | if (lastRange) { 892 | let selection = window.getSelection(); 893 | selection.removeAllRanges(); 894 | selection.addRange(lastRange); 895 | 896 | lastRange.deleteContents(); 897 | lastRange.insertNode(tableWrapper); 898 | 899 | let newRange = document.createRange(); 900 | newRange.setStartAfter(tableWrapper); 901 | newRange.collapse(true); 902 | selection.removeAllRanges(); 903 | selection.addRange(newRange); 904 | } else { 905 | editor.appendChild(tableWrapper); 906 | } 907 | 908 | tableGrid.classList.add("hidden"); 909 | } 910 | 911 | function addRow(table) { 912 | let cols = table.dataset.columns; 913 | let newRow = document.createElement("tr"); 914 | 915 | for (let i = 0; i < cols; i++) { 916 | let cell = document.createElement("td"); 917 | cell.className = "border border-gray-400 px-4 py-3"; 918 | cell.textContent = "Cell"; 919 | newRow.appendChild(cell); 920 | } 921 | 922 | table.appendChild(newRow); 923 | table.dataset.rows = parseInt(table.dataset.rows) + 1; 924 | } 925 | 926 | function addColumn(table) { 927 | let currentColumns = parseInt(table.dataset.columns) || 0; 928 | if (currentColumns >= 12) return; 929 | let rows = table.querySelectorAll("tr"); 930 | rows.forEach((row) => { 931 | let cell = document.createElement("td"); 932 | cell.className = "border border-gray-400 px-4 py-3"; 933 | cell.textContent = "Cell"; 934 | row.appendChild(cell); 935 | }); 936 | table.dataset.columns = currentColumns + 1; 937 | } 938 | }); 939 | 940 | let lastRange = null; 941 | 942 | function saveSelection() { 943 | const selection = window.getSelection(); 944 | if (selection.rangeCount > 0) { 945 | lastRange = selection.getRangeAt(0); 946 | } 947 | } 948 | 949 | function insertAtCursor(node) { 950 | const editor = document.getElementById("editor"); 951 | if (lastRange) { 952 | const sel = window.getSelection(); 953 | sel.removeAllRanges(); 954 | sel.addRange(lastRange); 955 | lastRange.deleteContents(); 956 | lastRange.insertNode(node); 957 | } else { 958 | editor.appendChild(node); 959 | } 960 | } 961 | 962 | document.addEventListener("DOMContentLoaded", () => { 963 | const btn = document.getElementById("formatButton"); 964 | if (!btn) return console.error("Format button missing"); 965 | btn.addEventListener("click", async (e) => { 966 | e.preventDefault(); 967 | const codeElem = document.querySelector("#editor code"); 968 | if (!codeElem) return; 969 | const formatted = await prettier.format(codeElem.textContent, { 970 | parser: "babel", 971 | plugins: prettierPlugins, 972 | }); 973 | codeElem.innerHTML = formatted.replace(/\n/g, "
    "); 974 | hljs.highlightElement(codeElem); 975 | }); 976 | }); 977 | 978 | document.addEventListener("DOMContentLoaded", () => { 979 | const toolbar = document.getElementById("textToolbar"); 980 | const editor = document.getElementById("editor"); 981 | 982 | editor.addEventListener("mouseup", () => { 983 | const selection = window.getSelection(); 984 | if (selection.rangeCount > 0 && !selection.isCollapsed) { 985 | const range = selection.getRangeAt(0); 986 | const rect = range.getBoundingClientRect(); 987 | toolbar.style.left = `${rect.left + window.scrollX}px`; 988 | toolbar.style.top = `${rect.bottom + window.scrollY + 5}px`; 989 | toolbar.classList.remove("hidden"); 990 | } else { 991 | toolbar.classList.add("hidden"); 992 | } 993 | }); 994 | 995 | document.addEventListener("click", (e) => { 996 | if (!editor.contains(e.target) && !toolbar.contains(e.target)) { 997 | toolbar.classList.add("hidden"); 998 | } 999 | }); 1000 | }); 1001 | 1002 | let isChanged = false; 1003 | function markEditorAsChanged() { 1004 | isChanged = true; 1005 | updateSaveButtonIcon(); 1006 | } 1007 | document.addEventListener("DOMContentLoaded", () => { 1008 | let saveButton = document.getElementById("saveButton"); 1009 | let editor = document.getElementById("editor"); 1010 | let originalContent = editor.innerHTML; 1011 | 1012 | editor.addEventListener("input", () => { 1013 | isChanged = true; 1014 | updateSaveButtonIcon(); 1015 | }); 1016 | }); 1017 | 1018 | function toggleFormat(cmd) { 1019 | const selection = window.getSelection(); 1020 | if (!selection.rangeCount) return; 1021 | const range = selection.getRangeAt(0); 1022 | let commonAncestor = range.commonAncestorContainer; 1023 | 1024 | if (commonAncestor.nodeType === 3) { 1025 | commonAncestor = commonAncestor.parentNode; 1026 | } 1027 | 1028 | if (cmd === "highlight") { 1029 | const existingHighlight = commonAncestor.closest("span.yellow-highlight"); 1030 | if (existingHighlight) { 1031 | const fragment = document.createDocumentFragment(); 1032 | while (existingHighlight.firstChild) { 1033 | fragment.appendChild(existingHighlight.firstChild); 1034 | } 1035 | existingHighlight.parentNode.replaceChild(fragment, existingHighlight); 1036 | } else { 1037 | const highlightSpan = document.createElement("span"); 1038 | highlightSpan.classList.add("yellow-highlight"); 1039 | highlightSpan.appendChild(range.extractContents()); 1040 | range.insertNode(highlightSpan); 1041 | } 1042 | } else if (cmd === "link") { 1043 | const existingLink = commonAncestor.closest("a"); 1044 | if (existingLink) { 1045 | // Remove link by replacing it with its inner content 1046 | const fragment = document.createDocumentFragment(); 1047 | while (existingLink.firstChild) { 1048 | fragment.appendChild(existingLink.firstChild); 1049 | } 1050 | existingLink.parentNode.replaceChild(fragment, existingLink); 1051 | } else { 1052 | const url = prompt("Enter URL:", "https://"); 1053 | if (!url || url.trim() === "https://") return; 1054 | 1055 | const link = document.createElement("a"); 1056 | link.href = url; 1057 | link.target = "_blank"; 1058 | link.classList.add("link-color", "text-[1.1rem]", "font-link"); 1059 | link.appendChild(range.extractContents()); 1060 | 1061 | range.insertNode(link); 1062 | } 1063 | } else { 1064 | const styles = { 1065 | bold: { property: "fontWeight", value: "bold" }, 1066 | italic: { property: "fontStyle", value: "italic" }, 1067 | underline: { property: "textDecoration", value: "underline" }, 1068 | }; 1069 | 1070 | const style = styles[cmd]; 1071 | if (!style) return; 1072 | 1073 | const existingSpan = commonAncestor.closest("span"); 1074 | if (existingSpan && existingSpan.style[style.property] === style.value) { 1075 | const fragment = document.createDocumentFragment(); 1076 | while (existingSpan.firstChild) { 1077 | fragment.appendChild(existingSpan.firstChild); 1078 | } 1079 | existingSpan.parentNode.replaceChild(fragment, existingSpan); 1080 | } else { 1081 | const span = document.createElement("span"); 1082 | span.style[style.property] = style.value; 1083 | span.appendChild(range.extractContents()); 1084 | range.insertNode(span); 1085 | } 1086 | } 1087 | 1088 | selection.removeAllRanges(); 1089 | } 1090 | 1091 | document.addEventListener("keydown", (e) => { 1092 | if (!e.ctrlKey) return; 1093 | let k = e.key.toLowerCase(); 1094 | 1095 | if ("biuhk".includes(k)) { 1096 | e.preventDefault(); 1097 | toggleFormat( 1098 | k === "b" 1099 | ? "bold" 1100 | : k === "i" 1101 | ? "italic" 1102 | : k === "u" 1103 | ? "underline" 1104 | : k === "h" 1105 | ? "highlight" 1106 | : "link" 1107 | ); 1108 | } 1109 | }); 1110 | 1111 | document.addEventListener("DOMContentLoaded", () => { 1112 | const noteItems = document.querySelectorAll("#notesList li[data-url]"); 1113 | noteItems.forEach((item) => { 1114 | item.addEventListener("click", (e) => { 1115 | if (!e.target.closest("button")) { 1116 | window.location.href = item.dataset.url; 1117 | } 1118 | }); 1119 | }); 1120 | }); 1121 | 1122 | document.addEventListener("DOMContentLoaded", () => { 1123 | const editor = document.getElementById("editor"); 1124 | if (editor) { 1125 | editor.focus(); 1126 | placeCursorAtStart(editor); 1127 | } 1128 | }); 1129 | 1130 | function placeCursorAtStart(element) { 1131 | const range = document.createRange(); 1132 | const selection = window.getSelection(); 1133 | range.selectNodeContents(element); 1134 | range.collapse(true); 1135 | selection.removeAllRanges(); 1136 | selection.addRange(range); 1137 | } 1138 | -------------------------------------------------------------------------------- /static/styles.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Janna"; 3 | src: url("/static/fonts/Janna.ttf") format("truetype"); 4 | font-weight: normal; 5 | font-style: normal; 6 | } 7 | 8 | @font-face { 9 | font-family: "Garamond"; 10 | src: url("/static/fonts/Garamond.otf") format("truetype"); 11 | font-weight: normal; 12 | font-style: normal; 13 | } 14 | 15 | @font-face { 16 | font-family: "FuturaBkBt"; 17 | src: url("/static/fonts/FuturaBkBt.otf") format("truetype"); 18 | font-weight: normal; 19 | font-style: normal; 20 | } 21 | 22 | @font-face { 23 | font-family: "Madani"; 24 | src: url("/static/fonts/madani.ttf") format("truetype"); 25 | font-weight: normal; 26 | font-style: normal; 27 | } 28 | 29 | @font-face { 30 | font-family: "Lovtony"; 31 | src: url("/static/fonts/Lovtony\ Script.ttf") format("truetype"); 32 | font-weight: normal; 33 | font-style: normal; 34 | } 35 | 36 | @font-face { 37 | font-family: "Nexa"; 38 | src: url("/static/fonts/NYT/nyt-franklin-300-normal.woff") format("truetype"); 39 | font-weight: normal; 40 | font-style: normal; 41 | } 42 | 43 | @font-face { 44 | font-family: "EuropaBold"; 45 | src: url("/static/fonts/Europa.ttf") format("truetype"); 46 | font-weight: normal; 47 | font-style: normal; 48 | } 49 | 50 | @font-face { 51 | font-family: "RodenBold"; 52 | src: url("/static/fonts/RodenBold.otf") format("truetype"); 53 | font-weight: normal; 54 | font-style: normal; 55 | } 56 | 57 | @font-face { 58 | font-family: "RodenRegular"; 59 | src: url("/static/fonts/RodenRegular.otf") format("truetype"); 60 | font-weight: normal; 61 | font-style: normal; 62 | } 63 | 64 | @font-face { 65 | font-family: "Didot"; 66 | src: url("/static/fonts/Merriweather-Regular.ttf") format("truetype"); 67 | font-weight: normal; 68 | font-style: normal; 69 | } 70 | 71 | @font-face { 72 | font-family: "Leelawad"; 73 | src: url("/static/fonts/HankenGrotesk-Bold.otf") format("truetype"); 74 | font-weight: normal; 75 | font-style: normal; 76 | } 77 | 78 | @font-face { 79 | font-family: "HackRegular"; 80 | src: url("/static/fonts/Hack-Regular.ttf") format("truetype"); 81 | font-weight: normal; 82 | font-style: normal; 83 | } 84 | 85 | @font-face { 86 | font-family: "HackItalic"; 87 | src: url("/static/fonts/Hack-Italic.ttf") format("truetype"); 88 | font-weight: normal; 89 | font-style: normal; 90 | } 91 | 92 | @font-face { 93 | font-family: "BiroScriptPlus"; 94 | src: url("/static/fonts/Handwrittening-Regular.ttf") format("truetype"); 95 | font-weight: normal; 96 | font-style: normal; 97 | } 98 | 99 | @font-face { 100 | font-family: "AmiriRegular"; 101 | src: url("/static/fonts/Amiri-Regular.ttf") format("truetype"); 102 | font-weight: normal; 103 | font-style: normal; 104 | } 105 | 106 | @font-face { 107 | font-family: "AGaramondPro-Italic"; 108 | src: url("/static/fonts/AGaramondPro-Italic.otf") format("truetype"); 109 | font-weight: normal; 110 | font-style: normal; 111 | } 112 | 113 | @font-face { 114 | font-family: "code"; 115 | src: url("/static/fonts/Envy\ Code\ R.ttf") format("truetype"); 116 | font-weight: normal; 117 | font-style: normal; 118 | } 119 | @font-face { 120 | font-family: "link"; 121 | src: url("/static/fonts/calibri-regular.ttf") format("truetype"); 122 | font-weight: normal; 123 | font-style: normal; 124 | } 125 | 126 | .font-link { 127 | font-family: "link", sans-serif; 128 | } 129 | 130 | .font-code { 131 | font-family: "code", serif; 132 | } 133 | 134 | .font-agaramondpro { 135 | font-family: "AGaramondPro-Italic", serif; 136 | } 137 | 138 | .font-amiriregular { 139 | font-family: "AmiriRegular", serif; 140 | } 141 | 142 | .font-biroscript { 143 | font-family: "BiroScriptPlus", serif; 144 | } 145 | 146 | .font-hackregular { 147 | font-family: "HackRegular", serif; 148 | } 149 | .font-hackitalic { 150 | font-family: "HackItalic", serif; 151 | } 152 | 153 | .font-leelawad { 154 | font-family: "Leelawad", serif; 155 | } 156 | 157 | .font-didot { 158 | font-family: "Didot", serif; 159 | } 160 | 161 | .font-rodenregular { 162 | font-family: "RodenRegular", sans-serif; 163 | } 164 | 165 | .font-rodenbold { 166 | font-family: "RodenBold", sans-serif; 167 | } 168 | 169 | .font-janna { 170 | font-family: "Janna", sans-serif; 171 | } 172 | 173 | .font-garamond { 174 | font-family: "Garamond", serif; 175 | } 176 | 177 | .font-futurabkbt { 178 | font-family: "FuturaBkBt", sans-serif; 179 | } 180 | 181 | .font-madani { 182 | font-family: "Madani", sans-serif; 183 | } 184 | 185 | .font-lovtony { 186 | font-family: "Lovtony", sans-serif; 187 | } 188 | 189 | .font-nexa { 190 | font-family: "Nexa", sans-serif; 191 | } 192 | 193 | .font-europaBold { 194 | font-family: "EuropaBold", sans-serif; 195 | } 196 | 197 | .logo { 198 | font-family: "Lovtony", sans-serif; 199 | font-size: 50px; 200 | font-weight: bold; 201 | } 202 | 203 | .modal { 204 | display: none; 205 | position: fixed; 206 | top: 50%; 207 | left: 50%; 208 | transform: translate(-50%, -50%); 209 | background: white; 210 | padding: 20px; 211 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 212 | } 213 | 214 | .modal-content { 215 | background: white; 216 | padding: 20px; 217 | border-radius: 10px; 218 | max-width: 600px; 219 | margin: auto; 220 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 221 | } 222 | 223 | .backdrop-blur-custom { 224 | backdrop-filter: blur(10px); 225 | -webkit-backdrop-filter: blur(10px); /* Safari */ 226 | } 227 | 228 | .bg-custom-sidebar { 229 | background-color: #af937e; 230 | } 231 | 232 | .bg-custom-slidebar { 233 | background-color: #f5f3f0; 234 | transition: background-color 0.3s ease-in-out; 235 | } 236 | 237 | .bg-custom-folder { 238 | background-color: #eae5e3; 239 | } 240 | 241 | .bg-custom-content { 242 | background-color: #f1f1ef; 243 | } 244 | 245 | .bg-custom-color { 246 | background-color: #f5f3f0; 247 | } 248 | 249 | .bg-custom-color1 { 250 | background-color: #f6f3f0; 251 | } 252 | 253 | .bg-custom-color2 { 254 | background-color: #b7b4b1; 255 | } 256 | 257 | .bg-custom-color2:hover { 258 | background-color: #7b7a77; 259 | } 260 | 261 | .bg-custom-color3 { 262 | background-color: #7b7a77; 263 | } 264 | 265 | .bg-custom-color4 { 266 | background-color: #2b2b2b; 267 | } 268 | 269 | .text-color2 { 270 | color: #7b7a77; 271 | } 272 | 273 | .subtitle-color { 274 | color: #2d1535; 275 | } 276 | 277 | .link-color { 278 | color: #59186e; 279 | } 280 | 281 | .border-color { 282 | color: #2d1535; 283 | } 284 | 285 | .bg-folder { 286 | background-size: cover; 287 | background-position: center; 288 | background-repeat: no-repeat; 289 | } 290 | 291 | .bg-grid { 292 | background: url("/static/img/grid.jpg") no-repeat center center; 293 | background-size: cover; 294 | } 295 | 296 | .tool-btn { 297 | border-radius: 5px; 298 | cursor: pointer; 299 | transition: background 0.2s; 300 | } 301 | 302 | .tool-btn:hover { 303 | background: #cbd5e1; 304 | } 305 | 306 | .popup { 307 | background: white; 308 | padding: 10px; 309 | border: 1px solid gray; 310 | position: absolute; 311 | top: 50px; 312 | left: 50%; 313 | transform: translateX(-50%); 314 | } 315 | .hidden { 316 | display: none; 317 | } 318 | .input-field { 319 | margin: 5px; 320 | padding: 5px; 321 | border: 1px solid #ccc; 322 | } 323 | .popup-btn { 324 | background: #007bff; 325 | color: white; 326 | padding: 5px 10px; 327 | border: none; 328 | cursor: pointer; 329 | } 330 | 331 | .prose { 332 | font-family: "Garamond", serif; 333 | } 334 | .prose h1, 335 | .prose h2 { 336 | font-family: "Madani", sans-serif; 337 | } 338 | .prose pre code, 339 | .prose table { 340 | font-family: initial; 341 | } 342 | 343 | .cover-image { 344 | width: 100%; 345 | height: 150px; /* Adjust to desired height */ 346 | background-image: url("/static/img/cover.png"); 347 | background-size: cover; 348 | background-position: center; 349 | border-radius: 10px; /* Optional */ 350 | } 351 | 352 | .note-title, 353 | .note-subtitle { 354 | max-width: 450px; 355 | word-wrap: break-word; 356 | overflow: hidden; 357 | text-overflow: ellipsis; 358 | white-space: nowrap; /* Prevent text from wrapping to the next line */ 359 | } 360 | 361 | .note-subtitle { 362 | white-space: normal; /* Allow subtitle to wrap to the next line */ 363 | } 364 | 365 | .text-gradient { 366 | background-image: linear-gradient( 367 | to right, 368 | #02487a, 369 | /* green-200 */ #0f766e, 370 | /* teal-700 */ #581c87 /* purple-900 */ 371 | ); 372 | } 373 | 374 | .hljs { 375 | background-color: transparent !important; 376 | } 377 | 378 | #goBack { 379 | display: none; 380 | } 381 | #btnContainer:hover #goBack { 382 | display: flex; 383 | } 384 | 385 | .checklist-checkbox { 386 | border-radius: 7px; 387 | width: 1.3rem; 388 | height: 1.3rem; 389 | appearance: none; 390 | border: 1.5px solid #ccc; 391 | background-color: white; 392 | display: inline-block; 393 | vertical-align: middle; 394 | cursor: pointer; 395 | transition: all 0.3s ease; 396 | } 397 | 398 | .checklist-checkbox:checked { 399 | background-color: #ccbfb3; 400 | border-color: #ccbfb3; 401 | } 402 | 403 | .checklist-checkbox:focus { 404 | outline: none; 405 | box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.5); 406 | } 407 | 408 | .checklist-item span { 409 | font-size: 1.125rem; 410 | font-weight: 500; 411 | } 412 | 413 | #editor img { 414 | max-width: 50%; 415 | max-height: 50%; 416 | display: block; 417 | margin: 0; 418 | } 419 | 420 | .clickable-note { 421 | display: block; 422 | width: 200px; 423 | padding: 1.25rem 1.5rem; /* py-5 => 1.25rem, px-6 => 1.5rem */ 424 | cursor: pointer; 425 | } 426 | 427 | .header { 428 | position: sticky; 429 | top: 0; 430 | padding: 0; 431 | background: #f5f3f0; 432 | z-index: 20; 433 | overflow: visible; 434 | } 435 | .header::after { 436 | content: ""; 437 | position: absolute; 438 | left: 0; 439 | right: 0; 440 | bottom: -10px; 441 | height: 20px; 442 | background: inherit; 443 | filter: blur(3px); 444 | z-index: -1; 445 | } 446 | 447 | .text-code { 448 | white-space: pre-wrap; 449 | } 450 | 451 | .yellow-highlight { 452 | background-color: #ffe25b; 453 | } 454 | -------------------------------------------------------------------------------- /static/svg/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blankresearch/Memory/916d095dd4ed08666bce653223f06c8611fa9983/static/svg/.DS_Store -------------------------------------------------------------------------------- /static/svg/back.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/svg/bold.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | B 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 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /static/svg/bullet-list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bullet-List 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 | -------------------------------------------------------------------------------- /static/svg/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /static/svg/checklist.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /static/svg/code.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /static/svg/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /static/svg/dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/svg/dots.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /static/svg/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /static/svg/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/svg/gear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/svg/header-2-svgrepo-com.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /static/svg/header.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /static/svg/highlight.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /static/svg/image.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image 5 | 6 | -------------------------------------------------------------------------------- /static/svg/italic.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /static/svg/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /static/svg/num-list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/svg/paragraph.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /static/svg/quote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /static/svg/save-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/svg/save.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /static/svg/signature.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/svg/table.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /static/svg/title.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/svg/underline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/svg/video.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /templates/edit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | Edit Note 10 | 11 | 15 | 19 | 23 | 34 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
    48 |
    49 |
    50 |
    51 | 52 | 53 | 54 |
    55 |
    56 |
    57 |
    58 |
    59 |
    60 |
    63 | {{ title }} 64 |
    65 |
    66 |
    67 |
    70 | 80 | 90 | 100 | 110 | 111 | 121 | 128 | 129 | 139 | 146 | 147 |
    148 | 158 | 159 | 160 | 165 |
    166 | 167 | 177 | 178 | 188 | 198 | 199 | 209 | 219 | 226 |
    227 |
    231 | 235 | Go Back 240 | 241 | 252 |
    253 |
    254 |
    255 |
    256 |
    257 |
    258 |
    259 | 260 |
    261 |
    262 |
    263 |
    264 |
    265 |
    266 |
    267 | 268 |
    269 |
    270 |
    271 |
    272 |
    277 | {{ content | safe }} 278 |
    279 |
    280 | 281 | 282 | 337 |
    338 |
    339 |
    340 | 341 | 342 | 343 |
    344 |
    345 |
    346 | 347 | 348 | -------------------------------------------------------------------------------- /templates/edit_folder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Edit Folder 5 | 9 | 10 | 11 |
    12 |

    Edit Folder

    13 |
    18 |
    19 | 25 | 33 |
    34 |
    35 | 41 | 45 | Cancel 46 | 47 |
    48 |
    49 |
    50 | 51 | 52 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Memory 5 | 9 | 13 | 18 | 19 | 23 | 24 | 25 | 26 |
    27 | 28 | 29 |
    30 |
    31 | 32 |
    37 |
    38 | 45 |
    46 |
    47 |
    48 | 49 |
    50 |
      54 | {% for folder in folders %} 55 |
    • 60 | 63 | {{ folder[1][:25] ~ '...' if folder[1]|length > 28 else 64 | folder[1] }} 65 | 66 |
      69 | 76 | 83 |
      84 |
    • 85 | {% endfor %} 86 |
    87 |
    88 |
    89 | 90 | 91 |
    92 |
    93 | 94 |
    95 |
    96 | 102 |
    103 | 104 |
      108 | {% for note in notes %} 109 |
    • 113 |
      114 |
    • 115 | {% endfor %} 116 |
    117 |
    118 |
    119 |
    120 | 121 | 122 | 136 | 137 | 138 | 180 | 181 | 182 | 207 | 208 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /templates/note.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | {{ title }} 10 | 14 | 18 | 19 | 20 | 23 | 24 | 28 | 39 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
    53 |
    54 |
    55 | 56 | 57 | 58 |
    59 |
    60 |
    61 |
    62 |
    63 |
    66 | {{ title }} 67 |
    68 |
    69 | 70 |
    71 |
    72 | {{ subtitle }} 73 |
    74 | 78 | Edit 83 | 84 |
    85 | 86 |
    87 | 88 |
    93 | {{ content | safe }} 94 |
    95 |
    96 |
    97 |
    98 |
    99 | 100 | 101 | 102 |
    103 |
    104 |
    105 | 106 | 107 | --------------------------------------------------------------------------------