From 627f8053770a8500f0e78e5248385a1de020130a Mon Sep 17 00:00:00 2001 From: pika Date: Mon, 14 Apr 2025 22:37:25 +0200 Subject: [PATCH] removed persistant data --- .gitignore | 33 ++++ app/__pycache__/__init__.cpython-313.pyc | Bin 2496 -> 0 bytes app/__pycache__/routes.cpython-313.pyc | Bin 15001 -> 0 bytes app/auth/__pycache__/routes.cpython-313.pyc | Bin 4571 -> 4564 bytes app/auth/routes.py | 6 +- app/routes.py | 72 ++++++-- app/templates/base.html | 74 +++++++- app/templates/category_edit.html | 193 ++++++++++++++++++++ app/templates/document_edit.html | 63 +++++-- instance/docs.db | Bin 36864 -> 0 bytes 10 files changed, 412 insertions(+), 29 deletions(-) create mode 100644 .gitignore delete mode 100644 app/__pycache__/__init__.cpython-313.pyc delete mode 100644 app/__pycache__/routes.cpython-313.pyc create mode 100644 app/templates/category_edit.html delete mode 100644 instance/docs.db diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9fc790f --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# tests and coverage +*.pytest_cache +.coverage + +# database & logs +*.db +*.sqlite3 +*.log + +# venv +env +venv + +# other +.DS_Store + +# sphinx docs +_build +_static +_templates + +# javascript +package-lock.json +.vscode/symbols.json + +apps/static/assets/node_modules +apps/static/assets/yarn.lock +apps/static/assets/.temp + diff --git a/app/__pycache__/__init__.cpython-313.pyc b/app/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 039e4643322488475da630537134f1a9310593e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2496 zcmb7G&2Jk;6rZ)%Uu!##lQd~f>u!>Mxou)0TKbg&sq2W?aT>f1QdPsSvDeN9dpDh3 zwnr3k8038@^*1yyi>zkqY1MoKay2nqENPOVbZ6B2LMUdKp4#YlehzGmLr z_hx?M?O;$qFn)jU&$8l1=r<Tm)@?CA--4M~A?n&(;yHdTRH`Pb_tZV}5-KjlfkBG|d zSa6GN6XU3BtRfp90wYN8OpI0vvr2UVq`-t;R<(?*$z_Fr?8&HQBAW`Z{)Chrnc~_wn;D%{181CuWDv|Ru3(W zt5%0VZ^Y&K`8dX^rkWTJ&o2;mhAhHLZ%hIC6s?83??pa~+>3q|{W|bCJhU2q^HJ9i zk?$fuUidy*Ppw782Xpu59%%Qqhwnd*j;uzdrMILd{*|?ZN0<14_5P!ex|aJ-Eb-B` z?)^6>h-il>G>;+32)W?rgx~g0puLN(v}{+JmlGlTIBDE8m$k0~TiYC2kIn7O)2&q^ zy22`LjjZ41cIIi$-XAo9C5xdBE4si5W%5%}%1Bkau6EwF%W+>$pPN_A=g*8k$|lzftK#@g)w ziuorO1-WL<4p$iPEe=do6xmQjkVI7zOL2D4*ke$f?YyMrLqNCnRmrKElo3lnyXNH-AbE?781{UxGTu|6{g@Jb!U z8S1r8JRE)+v~t7M1}am&V2~2^R2#@lg@*N-s$6e*LdK9P?QJe>h`0biUe?J16=2t4 zqO)sB4zeV=h*DModKT zVG0d4G|i3@_;Z42M?-hrEDf`M69yclk6F9`nF@P+Em$T;nc+AiLyQbFVnHM;IC&ii z?PjfnM?|T>+tY+va+aCTF*3q@LTx@Z_!e@WnHXd@(9r6qcck$Yy+Ovn!0^xb0LW7( z$8k?k-zGY|i4JU{{!Mh~XXM*N(PwVt?!1|=JJvh*e%?_Ju6w#}eq86*h4AfI-Sfij n;>2eN$kPtAZ}2(V!-bxmMxOA^O5ORwjkwdx=+rMrv@-Y$Ikq{} diff --git a/app/__pycache__/routes.cpython-313.pyc b/app/__pycache__/routes.cpython-313.pyc deleted file mode 100644 index e35c0ac182e4ca38d936d09d8c25d9c82a3077dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15001 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 THo5sJr8%i~MWK_q1y2G1MXoi| diff --git a/app/auth/routes.py b/app/auth/routes.py index d4693da..8d833bf 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -45,9 +45,9 @@ def signup(): # Create root category for the user root_category = Category( - name='My Documents', - icon='mdi-folder', - description='Default document category', + name='root', + icon='mdi-folder-root', + description='System root directory', user_id=user.id, is_root=True ) diff --git a/app/routes.py b/app/routes.py index bbf63b6..dc61b53 100644 --- a/app/routes.py +++ b/app/routes.py @@ -57,18 +57,34 @@ def new_document(): category_id = request.args.get('category') document = None + preselected_category_id = None + + # Find the root category + root_category = Category.query.filter_by(user_id=current_user.id, is_root=True).first() if category_id: + # If a specific category was requested, use that category = Category.query.filter_by(id=category_id, user_id=current_user.id).first() if category: - document = Document( - title="Untitled Document", - content="", - category_id=category_id, - user_id=current_user.id - ) + preselected_category_id = int(category_id) + elif root_category: + # Otherwise default to the root category + preselected_category_id = root_category.id - return render_template('document_edit.html', document=document, categories=categories, tags=tags, preselected_category_id=category_id) + # Create a blank document + document = Document( + title="Untitled Document", + content="", + category_id=preselected_category_id, + user_id=current_user.id + ) + + # Make sure all categories include their children for the hierarchical dropdown + for category in categories: + if hasattr(category, 'to_dict'): + setattr(category, '_serialized', category.to_dict()) + + return render_template('document_edit.html', document=document, categories=categories, tags=tags, preselected_category_id=preselected_category_id) @main.route('/api/document', methods=['POST']) @login_required @@ -76,22 +92,35 @@ def save_document(): """Save a document (new or existing)""" data = request.json - # Get root category as default + # Find the root category to use as default root_category = Category.query.filter_by(user_id=current_user.id, is_root=True).first() - default_category_id = root_category.id if root_category else None + # Ensure we have a root category + if not root_category: + # Create a root category if it doesn't exist + root_category = Category( + name='root', + icon='mdi-folder-root', + description='System root directory', + user_id=current_user.id, + is_root=True + ) + db.session.add(root_category) + db.session.flush() # Get the ID without committing yet + + # All documents must have a category - defaults to root if 'id' in data and data['id']: # Update existing document - verify ownership document = Document.query.filter_by(id=data['id'], user_id=current_user.id).first_or_404() document.title = data['title'] document.content = data['content'] - document.category_id = data['category_id'] if data['category_id'] else default_category_id + document.category_id = data['category_id'] if data['category_id'] else root_category.id else: # Create new document document = Document( title=data['title'], content=data['content'], - category_id=data['category_id'] if data['category_id'] else default_category_id, + category_id=data['category_id'] if data['category_id'] else root_category.id, user_id=current_user.id ) db.session.add(document) @@ -265,4 +294,23 @@ def get_categories(): user_id=current_user.id, parent_id=None ).all() - return jsonify([category.to_dict() for category in root_categories]) \ No newline at end of file + return jsonify([category.to_dict() for category in root_categories]) + +@main.route('/category/new', methods=['GET']) +@login_required +def new_category(): + """Create a new category or subcategory""" + parent_id = request.args.get('parent_id') + parent = None + + if parent_id: + parent = Category.query.filter_by(id=parent_id, user_id=current_user.id).first_or_404() + + return render_template('category_edit.html', category=None, parent=parent) + +@main.route('/category//edit', methods=['GET']) +@login_required +def edit_category(category_id): + """Edit an existing category""" + category = Category.query.filter_by(id=category_id, user_id=current_user.id).first_or_404() + return render_template('category_edit.html', category=category, parent=category.parent) \ No newline at end of file diff --git a/app/templates/base.html b/app/templates/base.html index 4ffd79f..f773f9e 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -215,12 +215,80 @@ function createCategoryItem(category) { const li = document.createElement('li'); + // Create the category item container with flexbox to place the + icon + const categoryContainer = document.createElement('div'); + categoryContainer.className = 'flex items-center justify-between group'; + li.appendChild(categoryContainer); + + // Create the link to view the category const a = document.createElement('a'); a.href = `/category/${category.id}`; - a.className = 'flex items-center py-1 px-2 text-gray-400 hover:text-primary rounded transition-colors'; - a.innerHTML = ` ${category.name}`; - li.appendChild(a); + let categoryClass = 'flex-grow flex items-center py-1 px-2 text-gray-400 hover:text-primary rounded transition-colors'; + // Special styling for root + if (category.is_root) { + categoryClass += ' font-semibold text-primary'; + } + + a.className = categoryClass; + + // Special icon for root + const iconClass = category.is_root ? 'mdi-folder-root' : category.icon; + a.innerHTML = ` ${category.name}`; + categoryContainer.appendChild(a); + + // Create the dropdown menu container + const dropdownContainer = document.createElement('div'); + dropdownContainer.className = 'relative'; + categoryContainer.appendChild(dropdownContainer); + + // Create the plus button + const plusButton = document.createElement('button'); + plusButton.className = 'ml-1 p-1 text-gray-500 hover:text-primary rounded-full opacity-0 group-hover:opacity-100 transition-opacity'; + plusButton.innerHTML = ''; + dropdownContainer.appendChild(plusButton); + + // Create dropdown menu + const dropdown = document.createElement('div'); + dropdown.className = 'absolute right-0 top-full mt-1 py-1 bg-gray-800 border border-gray-700 rounded-md shadow-lg z-20 hidden w-48'; + dropdownContainer.appendChild(dropdown); + + // Add menu items + const newSubcategory = document.createElement('a'); + newSubcategory.href = `/category/new?parent_id=${category.id}`; + newSubcategory.className = 'block px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 hover:text-white w-full text-left'; + newSubcategory.innerHTML = ' New Subcategory'; + dropdown.appendChild(newSubcategory); + + const newDocument = document.createElement('a'); + newDocument.href = `/document/new?category=${category.id}`; + newDocument.className = 'block px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 hover:text-white w-full text-left'; + newDocument.innerHTML = ' New Document'; + dropdown.appendChild(newDocument); + + // Toggle dropdown + plusButton.addEventListener('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + dropdown.classList.toggle('hidden'); + + // Close other open dropdowns + document.querySelectorAll('.category-dropdown:not(.hidden)').forEach(el => { + if (el !== dropdown) el.classList.add('hidden'); + }); + }); + + // Add class for easy reference + dropdown.classList.add('category-dropdown'); + + // Add click handler to close dropdown when clicking outside + document.addEventListener('click', function(e) { + if (!plusButton.contains(e.target) && !dropdown.contains(e.target)) { + dropdown.classList.add('hidden'); + } + }); + + // Add children if (category.children && category.children.length > 0) { const childrenUl = document.createElement('ul'); childrenUl.className = 'ml-2 pl-2 border-l border-gray-700 my-1'; diff --git a/app/templates/category_edit.html b/app/templates/category_edit.html new file mode 100644 index 0000000..fb93c97 --- /dev/null +++ b/app/templates/category_edit.html @@ -0,0 +1,193 @@ +{% extends "base.html" %} + +{% block title %}{% if category %}Edit Category{% else %}New Category{% endif %} - Vim Docs{% endblock %} + +{% block header_title %}{% if category %}Edit Category{% else %}New Category{% endif %}{% endblock %} + +{% block header_actions %} + +{% endblock %} + +{% block content %} +
+
+
+
+ + +
+ +
+ + +

Example: mdi-folder-outline, mdi-code-tags, mdi-book-open-page-variant

+
+ +
+ + +
+ +
+ + +
+
+
+
+ + +
+ + Category saved successfully! +
+{% endblock %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file diff --git a/app/templates/document_edit.html b/app/templates/document_edit.html index dac0651..777ddf6 100644 --- a/app/templates/document_edit.html +++ b/app/templates/document_edit.html @@ -178,17 +178,7 @@
@@ -315,9 +305,60 @@ // Initialize document.addEventListener('DOMContentLoaded', function() { + // Populate categories dropdown with hierarchical structure + populateCategoriesDropdown(); + // Initial preview updatePreview(); + // Function to create hierarchical category dropdown + function populateCategoriesDropdown() { + const categorySelect = document.getElementById('doc-category'); + const categories = {{ categories|tojson|safe }}; + const preselectedId = {% if document and document.category_id %}{{ document.category_id }}{% elif preselected_category_id %}{{ preselected_category_id }}{% else %}null{% endif %}; + + // Find the root category + const rootCategory = categories.find(c => c.is_root); + let rootCategoryId = null; + if (rootCategory) { + rootCategoryId = rootCategory.id; + } + + function addCategoryOptions(categoryList, depth = 0) { + categoryList.forEach(category => { + const option = document.createElement('option'); + option.value = category.id; + + // Create indentation for hierarchy + const indent = '\u00A0\u00A0\u00A0\u00A0'.repeat(depth); + let prefix = ''; + if (depth > 0) { + prefix = '└─ '; + } + + option.textContent = indent + prefix + category.name; + + // Select this option if it matches the preselected ID + // Or if this is the root category and no preselection was made + option.selected = (category.id == preselectedId) || + (category.is_root && !preselectedId); + + categorySelect.appendChild(option); + + // Add children recursively if any + if (category.children && category.children.length > 0) { + addCategoryOptions(category.children, depth + 1); + } + }); + } + + // Get root categories and their children + const rootCategories = categories.filter(c => c.parent_id === null); + addCategoryOptions(rootCategories); + + console.log("Preselected category ID:", preselectedId || (rootCategoryId + " (root by default)")); + } + // Add debounce function for efficiency function debounce(func, wait) { let timeout; diff --git a/instance/docs.db b/instance/docs.db deleted file mode 100644 index a77b8dfefb976ccbf30b8d0f1c28386b5a45f0ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36864 zcmeI(&2HjW6bEoSWCBJ_sT4&lMv-pRi2~A)Vq^1>P0J*n8Cn8`kTlap(ZwH$Tfh`d zCs8$0leB%3?q}6!=o9oky6m#|Iza3cX1eGK{Vl-e+`{;8 zgpUMK6h0$F5Cmy*XET3VV~?woWj+%F%ZnBzVei-U`ue31limu6y$}Cf|LKFqdL#aq z^fvzcqCtp500Izz00bZa0SL?lZX(fWa(7p})jitjTBd7^dsg3Td-Qx1dJ;Qm>gASB zTIFX|orIc5DlW|ej!7!Dmi|I-l3Kk*YA4m|Qxcch`q~SczHC;G%FS2g3;h*I1=jPF z+zm?e@z3i`z4D^QI|cisNmGBWH}%?qemr|oB;}a@8Z=o~us;j=UE}FeY%O`TD+*5E zv@S>AciAV{HXVDe|GYhWO>DrZEr*^;H{Yy9lUrNjFNqM~v{Q608y8!k;W=K{BB$l% z!OL)U(XC$sWle%T%j!q;t9PfQRNZ7%Y754zB`v z@%xXgVIUORNGMfvkOQj>Wo8=U)#)f!HePEUYc6$_J^%J>J zv`>5YpxOh^$;Ri_tl@UuVIUQLP0b@XsMn5L%`&Td!rwS;f2CuCoYX3f6P={^D|kw# z`}we(r}1EE^4MhmcOI=JOB;)7lW$ID5Byvz#^2R9&4^!*Kfm_5rt;JIY6xx`eMdcp z^yG)-=&*X63Gr6GbEY*ihR(oq0&TpIdE0;v`Qhch_}^D@&Zs?fU5`Ag*QenuX4B=wqK z1SG|W@S*AJx)@DvZi=^}pDiBe_mxnVc;p=!nyze?hrAj-W(??P^x7So?F%}(nDvsC zVz}GFBZzll^{n=PdN;(=>#PRF#3ur~VS)ezAOHafKmY;|fB*y_009U<;7;JWCPszL zn@0KZ!1<1{?@g`--hNIgTC_?fgEeTGteTNkqGb1#+* zrxbFUtbVLzZCdEW*?x(MzXf)~1OW&@00Izz00bZa0SG_<0uX?}k_kj3n-c#Dga7^? zNxTsfejmyJCT@>%xp