From df06a4129f680135bb4fd903cab0a3d6d6bbab4b Mon Sep 17 00:00:00 2001 From: Mateusz Date: Sat, 7 Nov 2026 16:54:40 +0100 Subject: [PATCH] auto save --- server/app.py | 39 ++++++++- server/static/retro.css | 58 +++++++++++++- server/templates/notes.html | 155 +++++++++++++++++++++++++++++++++--- 3 files changed, 233 insertions(+), 19 deletions(-) diff --git a/server/app.py b/server/app.py index 42b02fb..0cbdb93 100644 --- a/server/app.py +++ b/server/app.py @@ -577,7 +577,7 @@ def create_app(): def notes_create(): title = request.form.get("title", "").strip() content = request.form.get("content", "").strip() - if not title or not content: + if not title: return redirect(url_for("notes_page")) note = Note( user_id=current_user.id, @@ -588,13 +588,40 @@ def create_app(): user_db.session.commit() return _note_redirect(note.id) + @app.route("/notes/autosave", methods=["POST"]) + @login_required + def notes_autosave(): + data = request.get_json(silent=True) + if not data: + return jsonify({"error": "json required"}), 400 + note_id = data.get("id") + title = (data.get("title") or "").strip()[:200] + content = (data.get("content") or "").strip()[:2000] + if not title: + return jsonify({"error": "title required"}), 400 + if note_id: + note = _note_or_404(int(note_id)) + note.title = title + note.content = content + note.touch() + user_db.session.commit() + return jsonify({"id": note.id, "status": "saved"}) + note = Note( + user_id=current_user.id, + title=title, + content=content, + ) + user_db.session.add(note) + user_db.session.commit() + return jsonify({"id": note.id, "status": "created"}) + @app.route("/notes/", methods=["POST"]) @login_required def notes_update(note_id): note = _note_or_404(note_id) title = request.form.get("title", "").strip() content = request.form.get("content", "").strip() - if title and content: + if title: note.title = title[:200] note.content = content[:2000] note.touch() @@ -628,11 +655,17 @@ def create_app(): def notes_update_item(note_id, item_id): note = _note_or_404(note_id) item = NoteItem.query.filter_by(id=item_id, note_id=note.id).first_or_404() - text = request.form.get("text", "").strip() + if request.is_json: + data = request.get_json(silent=True) or {} + text = (data.get("text") or "").strip() + else: + text = request.form.get("text", "").strip() if text: item.text = text[:255] note.touch() user_db.session.commit() + if request.is_json: + return jsonify({"id": item.id, "status": "saved"}) return _note_redirect(note.id) @app.route( diff --git a/server/static/retro.css b/server/static/retro.css index d95b684..b1ae043 100644 --- a/server/static/retro.css +++ b/server/static/retro.css @@ -400,9 +400,61 @@ textarea { gap: 0.5em; } .add-item-form input { flex: 1; } -.note-delete-form { margin-top: 0.75rem; } -.note-delete-form button { background: var(--mc-red); color: var(--mc-bright); } -.note-delete-form button:hover { background: var(--mc-red-bright); } +.note-menu-btn { + background: transparent; + color: var(--mc-dim); + border: 2px solid var(--mc-dim); + box-shadow: none; + padding: 0.1em 0.4em; + font-size: 1em; + line-height: 1; + letter-spacing: 0; +} +.note-menu-btn:hover { color: var(--mc-bright); border-color: var(--mc-bright); } + +.note-title-row { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + margin-bottom: 0.25rem; +} + +.note-save-status { + font-size: 0.8em; + color: var(--mc-dim); + white-space: nowrap; +} +.note-save-status.saved { color: var(--mc-yellow); } +.note-save-status.unsaved { color: var(--mc-red); } + +.note-menu-dropdown { + position: absolute; + right: 0; + top: 100%; + background: var(--mc-deep); + border: 3px double var(--mc-bright); + box-shadow: 6px 6px 0 rgba(0, 0, 0, 0.45); + min-width: 140px; + z-index: 10; + margin-top: 0.2rem; +} +.note-menu-dropdown button { + display: block; + width: 100%; + text-align: left; + background: none; + border: none; + box-shadow: none; + padding: 0.4em 0.8em; + color: var(--mc-bright); + font: inherit; + text-transform: none; + letter-spacing: 0; + cursor: pointer; +} +.note-menu-dropdown button:hover { background: var(--mc-cyan); color: var(--mc-black); } /* --- Notifications --- */ diff --git a/server/templates/notes.html b/server/templates/notes.html index d5e7db1..29c242d 100644 --- a/server/templates/notes.html +++ b/server/templates/notes.html @@ -24,13 +24,13 @@

New note

-
+ + placeholder="Write a short note...">
@@ -41,15 +41,21 @@ {% for note in notes %}
-
- + +
+ + + + +
+ maxlength="2000">{{ note.content }}
- {{ note.updated_at.strftime("%Y-%m-%d %H:%M") }}
@@ -64,14 +70,13 @@ {% if item.is_done %}checked{% endif %} onchange="this.form.submit()" aria-label="Mark '{{ item.text }}' as done"> -
- + maxlength="255" required aria-label="Checklist item text" + data-autosave-url="{{ url_for('notes_update_item', note_id=note.id, item_id=item.id) }}">
[ Add item ]
-
- -
+
{% endfor %}
{{ user.username }}@yggdrasil:~/notes$
+