This commit is contained in:
41
20260111204228-rss_articles.sync-conflict-20260530-154400-VT6366A.org
Executable file
41
20260111204228-rss_articles.sync-conflict-20260530-154400-VT6366A.org
Executable file
@@ -0,0 +1,41 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: 3a8274ef-f2fe-486d-9caf-0c56982b9917
|
||||||
|
:END:
|
||||||
|
#+title: RSS Articles
|
||||||
|
#+filetags: :research:articles:
|
||||||
|
|
||||||
|
* [[https://blog.pragmaticengineer.com/downdetector-and-the-real-cost-of-no-upstream-dependencies/][Downdetector and the real cost of no upstream dependencies]]
|
||||||
|
:PROPERTIES:
|
||||||
|
:FEED: The Pragmatic Engineer
|
||||||
|
:MINIFLUX_ID: 5250
|
||||||
|
:READ_AT: 2026-01-11 21:29
|
||||||
|
:END:
|
||||||
|
|
||||||
|
* [[https://coffeeandjunk.substack.com/p/loneliness][Loneliness Isn’t Just Something We Endure]]
|
||||||
|
:PROPERTIES:
|
||||||
|
:FEED: The Sunday Wisdom
|
||||||
|
:MINIFLUX_ID: 5363
|
||||||
|
:READ_AT: 2026-01-12 09:26
|
||||||
|
:END:
|
||||||
|
|
||||||
|
* [[https://bigthink.com/technology-innovation/dormant-volcanoes-copper-mining][What are dormant volcanoes good for? Copper mining]]
|
||||||
|
:PROPERTIES:
|
||||||
|
:FEED: Big Think
|
||||||
|
:MINIFLUX_ID: 5389
|
||||||
|
:READ_AT: 2026-01-12 11:14
|
||||||
|
:END:
|
||||||
|
|
||||||
|
* [[https://coffeeandjunk.substack.com/p/genius][Where Have All the Geniuses Gone?]]
|
||||||
|
:PROPERTIES:
|
||||||
|
:FEED: The Sunday Wisdom
|
||||||
|
:MINIFLUX_ID: 5358
|
||||||
|
:READ_AT: 2026-01-25 22:52
|
||||||
|
:END:
|
||||||
|
|
||||||
|
* [[https://coffeeandjunk.substack.com/p/forever][Not Everything Has to Be Timeless]]
|
||||||
|
:PROPERTIES:
|
||||||
|
:FEED: The Sunday Wisdom
|
||||||
|
:MINIFLUX_ID: 5361
|
||||||
|
:READ_AT: 2026-02-10 17:48
|
||||||
|
:END:
|
||||||
|
|
||||||
@@ -23,3 +23,5 @@ All daily journal entries, linked automatically.
|
|||||||
** [[file:daily/2026-05-22.org][2026-05-22]]
|
** [[file:daily/2026-05-22.org][2026-05-22]]
|
||||||
** [[file:daily/2026-05-28.org][2026-05-28]]
|
** [[file:daily/2026-05-28.org][2026-05-28]]
|
||||||
** [[file:daily/2026-05-29.org][2026-05-29]]
|
** [[file:daily/2026-05-29.org][2026-05-29]]
|
||||||
|
** [[file:daily/2026-06-01.org][2026-06-01]]
|
||||||
|
** [[file:daily/2026-06-02.org][2026-06-02]]
|
||||||
|
|||||||
@@ -6,5 +6,7 @@
|
|||||||
|
|
||||||
[[id:b4da3cfd-f7d8-46aa-b3d7-a231178c06b6][Dawrah]]
|
[[id:b4da3cfd-f7d8-46aa-b3d7-a231178c06b6][Dawrah]]
|
||||||
|
|
||||||
|
[[id:a68223f1-5d80-4ee1-8676-1be9dc742d0e][Naats]]
|
||||||
|
|
||||||
* Articles:
|
* Articles:
|
||||||
https://siblingsofilm.com/directions-for-dawrah/
|
https://siblingsofilm.com/directions-for-dawrah/
|
||||||
|
|||||||
36
20260601093154-cross_site_scripting_xss.org
Executable file
36
20260601093154-cross_site_scripting_xss.org
Executable file
@@ -0,0 +1,36 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: 01c89142-7e14-42b2-bd01-743656908fd2
|
||||||
|
:END:
|
||||||
|
#+title: Cross Site Scripting (XSS)
|
||||||
|
#+filetags: :career:security:notes:
|
||||||
|
|
||||||
|
* Definition:
|
||||||
|
|
||||||
|
- Cross Site Scripting (XSS) is a type of security vulnerability typically found in web applications. It allows attackers to inject malicious scripts into content from otherwise trusted websites.
|
||||||
|
|
||||||
|
* Types of XSS:
|
||||||
|
|
||||||
|
** 1. Stored XSS / Persistent XSS / Type 2 XSS:
|
||||||
|
The malicious script is permanently stored on the target server, such as in a database, message forum, visitor log, comment field, etc. When a user requests the stored information, the malicious script is served as part of the web page.
|
||||||
|
|
||||||
|
Example: An attacker posts a comment containing a malicious script on a blog. When other users view the comment, the script executes in their browsers, potentially stealing their cookies or performing other malicious actions.
|
||||||
|
|
||||||
|
** 2. Reflected XSS / Non-Persistent XSS / Type 1 XSS:
|
||||||
|
The malicious script is reflected off a web server, such as in an error message, search result, or any other response that includes some or all of the input sent to the server as part of the request. Reflected XSS is delivered to victims via another route, such as in an email or a third-party web site.
|
||||||
|
|
||||||
|
Example: An attacker crafts a URL that includes a malicious script. When a user clicks on the link, the script is reflected off the server and executed in the user's browser.
|
||||||
|
|
||||||
|
** 3. DOM-based XSS:
|
||||||
|
The vulnerability exists in the client-side code rather than the server-side code. The malicious script is executed as a result of modifying the DOM (Document Object Model) environment in the victim's browser, which causes the client-side code to execute in an unintended manner.
|
||||||
|
|
||||||
|
Example: An attacker manipulates the URL of a web page to include a malicious script. When the page processes the URL, it executes the script in the user's browser.
|
||||||
|
|
||||||
|
* Prevention:
|
||||||
|
|
||||||
|
- Input Validation: Ensure that all user input is properly validated and sanitized before being processed or stored. This includes using whitelisting techniques to allow only expected input.
|
||||||
|
|
||||||
|
- Output Encoding: Encode output to ensure that any potentially malicious scripts are rendered harmless when displayed in the browser. This can be done using functions that convert special characters to their corresponding HTML entities.
|
||||||
|
|
||||||
|
- Content Security Policy (CSP): Implement a Content Security Policy to restrict the sources from which scripts can be loaded and executed. This can help mitigate the impact of XSS attacks.
|
||||||
|
|
||||||
|
- Use Security Libraries: Utilize security libraries and frameworks that provide built-in protection against XSS vulnerabilities.
|
||||||
98
20260601134732-naats.org
Executable file
98
20260601134732-naats.org
Executable file
@@ -0,0 +1,98 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: a68223f1-5d80-4ee1-8676-1be9dc742d0e
|
||||||
|
:END:
|
||||||
|
#+title: Naats
|
||||||
|
|
||||||
|
* Tu Hai Banda Khuda Ka Khuda Ki Qasam ┇ Jameel Hansrot
|
||||||
|
https://www.youtube.com/watch?v=U5ZT8Kc0W3w&list=RDU5ZT8Kc0W3w&start_radio=1
|
||||||
|
|
||||||
|
** Urdu lyrics:
|
||||||
|
Tu Hai Banda Khuda Ka Khuda Ki Qasam
|
||||||
|
Tere Dil Mein Khuda Ki Mohabbat Nahi
|
||||||
|
Tu Musalman Hai Lekin Sirf Naam Ka
|
||||||
|
Ghar Mein Quran Hai Lekin Tilawat Nahi
|
||||||
|
|
||||||
|
Kaam Bhoole Se Neyki Ka Karta Nahi
|
||||||
|
Jiska Banda Hai Tu Uss Se Darta Nahi
|
||||||
|
Aanay Wali Qiyaamat Se Dar Be Khabar
|
||||||
|
Tu Samajhta Hai Shayad Qiyaamat Nahi
|
||||||
|
|
||||||
|
Dono Alam Ke Maalik Wohi Hai Wohi
|
||||||
|
Jiske Qabze Mein Hai Mawt Aur Zindagi
|
||||||
|
Hai Rustam Na Daare Sikandar Kaha
|
||||||
|
Uske Aagay Kisi Ki Hukoomat Nahi
|
||||||
|
|
||||||
|
Tujhko Maalik Ne Dunya Mein Peyda Kiya
|
||||||
|
Kaha Farishto Pe Teri Hukoomat Nahi
|
||||||
|
Tujh Ko Maalik Ne Dunya Mein Sab Kuch Diya
|
||||||
|
Dil Diya Aql Di Phir Na Sajdah Kiya
|
||||||
|
|
||||||
|
** English
|
||||||
|
You are a servant of God, by God’s oath,
|
||||||
|
But in your heart there is no love for God.
|
||||||
|
You are a Muslim, but only in name,
|
||||||
|
There is a Qur’an in your house, but no recitation.
|
||||||
|
|
||||||
|
You never do a good deed, even by mistake,
|
||||||
|
You do not fear the One whose servant you are.
|
||||||
|
O unaware one, fear the coming Day of Judgment,
|
||||||
|
You perhaps think that Judgment Day will never come.
|
||||||
|
|
||||||
|
He alone is the Master of both worlds,
|
||||||
|
In His control are death and life.
|
||||||
|
Where are Rustam, Darius, and Alexander now?
|
||||||
|
Before Him, no one has any rule or power.
|
||||||
|
|
||||||
|
Your Master created you in this world,
|
||||||
|
Did He not give you honour above the angels?
|
||||||
|
Your Master gave you everything in this world,
|
||||||
|
He gave you a heart and intellect, yet you did not prostrate.
|
||||||
|
|
||||||
|
* Ek Baar Madinay Mein Ho Jaye Mera Jana | Jameel Hansrot
|
||||||
|
https://www.youtube.com/watch?v=H_7MWxvw0h4&list=RDH_7MWxvw0h4&start_radio=1
|
||||||
|
** Urdu lyrics:
|
||||||
|
Ek Baar Madine Me Ho Jaye Mere Jaana
|
||||||
|
Phir Aur Na Kuch Maange Sarkaar Ka Deewana
|
||||||
|
|
||||||
|
Pal Pal Mera Dil Tadpe Din Raat Kare Zaari
|
||||||
|
Kab Aau Madine Me Kab Aye Meri Baari
|
||||||
|
Kab Jaake Mai Dekhuga Darbaar Woh Shahana
|
||||||
|
|
||||||
|
Is Aas Par Jeeta Hu Ek Roaz Bulaye Ge
|
||||||
|
Aur Gumbad E Khazra Ka Deedar Karayege
|
||||||
|
Phir Pesh Karuga Mai Ashko Bhara Nazrana
|
||||||
|
|
||||||
|
Bechain Nighahon Ko Deedaar Ataa Kar Do
|
||||||
|
Daman Mera Khushiyon Se Yaa Shah E Umam Bhar Do
|
||||||
|
Aabad Khuda Rakhe Aaqa Tera Maikhana
|
||||||
|
|
||||||
|
Itni Si Tamana Hai Ho Jaye Agar Poori
|
||||||
|
Jaha Dekhu Madina Mai Ho Jaye Jo Manzoori
|
||||||
|
Bin Deed Kiye Shaha Mar Jaye Na Deewana
|
||||||
|
|
||||||
|
Ek Baar Madine Me Ho Jaye Mere Jaana
|
||||||
|
Phir Aur Na Kuch Maange Sarkaar Ka Deewana
|
||||||
|
** English
|
||||||
|
|
||||||
|
**If only I could go to Madinah once, my beloved,**
|
||||||
|
**Then this devotee of the Master would ask for nothing more.**
|
||||||
|
|
||||||
|
Every moment my heart aches, day and night it keeps crying,
|
||||||
|
When will I come to Madinah? When will my turn arrive?
|
||||||
|
When will I finally go and see that majestic court?
|
||||||
|
|
||||||
|
I live with this hope that one day I will be called,
|
||||||
|
And I will be granted the sight of the Green Dome.
|
||||||
|
Then I will present my offering filled with tears.
|
||||||
|
|
||||||
|
Grant my restless eyes the blessing of seeing you,
|
||||||
|
O Leader of all nations, fill my empty hands with happiness.
|
||||||
|
May God keep your blessed sanctuary flourishing forever, O Master.
|
||||||
|
|
||||||
|
My only wish is this: if it could be fulfilled,
|
||||||
|
Wherever I look, may I see Madinah, if it is accepted.
|
||||||
|
O King, may this devotee not die before seeing you.
|
||||||
|
|
||||||
|
**If only I could go to Madinah once, my beloved,**
|
||||||
|
**Then this devotee of the Master would ask for nothing more.**
|
||||||
|
|
||||||
17
20260602101857-sql_joins.org
Executable file
17
20260602101857-sql_joins.org
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: de3bef27-81c9-49d1-85bb-edceb9a80e65
|
||||||
|
:END:
|
||||||
|
#+title: SQL Joins
|
||||||
|
#+filetags: :sql:joins:database:
|
||||||
|
|
||||||
|
|
||||||
|
* The SQL JOIN Clause
|
||||||
|
The JOIN clause is used to combine rows from two or more tables, based on a related column between them.
|
||||||
|
|
||||||
|
Here are the different types of JOINs in SQL:
|
||||||
|
|
||||||
|
**(INNER) JOIN**: Returns only rows that have matching values in both tables
|
||||||
|
**LEFT (OUTER) JOIN**: Returns all rows from the left table, and only the matched rows from the right table
|
||||||
|
**RIGHT (OUTER) JOIN**: Returns all rows from the right table, and only the matched rows from the left table
|
||||||
|
**FULL (OUTER) JOIN**: Returns all rows when there is a match in either the left or right table
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
#+filetags: :books:org:index:
|
#+filetags: :books:org:index:
|
||||||
#+DATE: 2025-05-19
|
#+DATE: 2025-05-19
|
||||||
#+STARTUP: content
|
#+STARTUP: content
|
||||||
#+PROPERTY: GENERATED_AT 2026-05-29T00:00:01.183286
|
#+PROPERTY: GENERATED_AT 2026-06-03T00:00:01.630629
|
||||||
|
|
||||||
* Career
|
* Career
|
||||||
** Clean Code_ A Handbook of Agile Software Craftsmanship - Robert C. Martin :Read:
|
** Clean Code_ A Handbook of Agile Software Craftsmanship - Robert C. Martin :Read:
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ Some core programming concepts that are essential for software development. The
|
|||||||
|
|
||||||
- [[id:b4858f6a-b05c-47f2-972e-905e0bb2c352][Database Permissions, Roles, and Accounts]]
|
- [[id:b4858f6a-b05c-47f2-972e-905e0bb2c352][Database Permissions, Roles, and Accounts]]
|
||||||
|
|
||||||
|
- [[id:de3bef27-81c9-49d1-85bb-edceb9a80e65][SQL Joins]]
|
||||||
|
|
||||||
* Misc
|
* Misc
|
||||||
|
|
||||||
- [[id:faa7f193-5af6-4a2f-a73e-540f833a7fd0][Windows Services]]
|
- [[id:faa7f193-5af6-4a2f-a73e-540f833a7fd0][Windows Services]]
|
||||||
@@ -63,6 +65,10 @@ Some core programming concepts that are essential for software development. The
|
|||||||
- [[id:e717c252-0e15-4403-898f-93163dd1b147][Dynamic Link Library (DLL)]]
|
- [[id:e717c252-0e15-4403-898f-93163dd1b147][Dynamic Link Library (DLL)]]
|
||||||
|
|
||||||
|
|
||||||
|
* Security:
|
||||||
|
|
||||||
|
- [[id:01c89142-7e14-42b2-bd01-743656908fd2][Cross Site Scripting (XSS)]]
|
||||||
|
|
||||||
* Concepts to learn and talk about (cs and maths)
|
* Concepts to learn and talk about (cs and maths)
|
||||||
- +xor+
|
- +xor+
|
||||||
- +big O notation+
|
- +big O notation+
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
#+title: XSS Pentesting Report Fix
|
#+title: XSS Pentesting Report Fix
|
||||||
#+filetags: :microlise:security:notes:
|
#+filetags: :microlise:security:notes:
|
||||||
|
|
||||||
|
* Links:
|
||||||
|
|
||||||
|
- [[id:01c89142-7e14-42b2-bd01-743656908fd2][Cross Site Scripting (XSS)]]
|
||||||
|
|
||||||
* Metadata
|
* Metadata
|
||||||
|
|
||||||
- Name :: Understanding Session XSS
|
- Name :: Understanding Session XSS
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#+AUTHOR: Auto-generated
|
#+AUTHOR: Auto-generated
|
||||||
#+filetags: :recipes:cooking:org:index:
|
#+filetags: :recipes:cooking:org:index:
|
||||||
#+STARTUP: content
|
#+STARTUP: content
|
||||||
#+PROPERTY: GENERATED_AT 2026-05-29T00:00:01.969551
|
#+PROPERTY: GENERATED_AT 2026-06-03T00:00:02.594221
|
||||||
|
|
||||||
# How to add notes: under any recipe heading, add :COOKED: <date> to record when you made it, and :TIPS: your tip text to add notes.
|
# How to add notes: under any recipe heading, add :COOKED: <date> to record when you made it, and :TIPS: your tip text to add notes.
|
||||||
# Multiple :COOKED: lines are supported. These are preserved across regenerations.
|
# Multiple :COOKED: lines are supported. These are preserved across regenerations.
|
||||||
|
|||||||
@@ -8,3 +8,24 @@
|
|||||||
- https://www.reddit.com/r/decaf/comments/1j2fpj9/lots_of_ppl_unmotivated_off_caffeine_we_need_to/
|
- https://www.reddit.com/r/decaf/comments/1j2fpj9/lots_of_ppl_unmotivated_off_caffeine_we_need_to/
|
||||||
- https://islamqa.org/hanafi/qibla-hanafi/35639/shared-ownership-schemes/
|
- https://islamqa.org/hanafi/qibla-hanafi/35639/shared-ownership-schemes/
|
||||||
- https://www.islamicfinanceguru.com/articles/shared-equity-schemes-alternative-islamic-mortgages
|
- https://www.islamicfinanceguru.com/articles/shared-equity-schemes-alternative-islamic-mortgages
|
||||||
|
|
||||||
|
|
||||||
|
* Interesting things to readup:
|
||||||
|
** https://en.wikipedia.org/wiki/2024_CrowdStrike-related_IT_outages
|
||||||
|
** https://fortune.com/2025/07/23/ai-coding-tool-replit-wiped-database-called-it-a-catastrophic-failure/
|
||||||
|
** https://www.noahzender.com/ideas#all-ideas
|
||||||
|
** https://shvetsm.github.io/posts/the-best-engineers-write-less-code/
|
||||||
|
** https://css-tricks.com/technical-writing-in-the-ai-age/
|
||||||
|
** https://www.elenaverna.com/p/youll-lose-your-job-in-202
|
||||||
|
** https://performance.dev/how-is-linear-so-fast-a-technical-breakdown
|
||||||
|
** https://www.sqlshack.com/debugging-stored-procedures-sql-server-management-studio-ssms/
|
||||||
|
** https://techcrunch.com/2026/05/27/tech-ceos-are-apparently-suffering-from-ai-psychosis/
|
||||||
|
** https://spin.atomicobject.com/using-agents-growth/
|
||||||
|
|
||||||
|
|
||||||
|
* Server resources:
|
||||||
|
** https://search.brave.com/search?q=docmost
|
||||||
|
|
||||||
|
|
||||||
|
* Repos
|
||||||
|
** https://github.com/0-AI-UG/cate
|
||||||
|
|||||||
@@ -92,6 +92,16 @@
|
|||||||
"id": "648c487d-d218-404a-b427-d186dc4c265d",
|
"id": "648c487d-d218-404a-b427-d186dc4c265d",
|
||||||
"title": "2026-05-29",
|
"title": "2026-05-29",
|
||||||
"url": "/daily/2026-05-29.html"
|
"url": "/daily/2026-05-29.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8a813115-3d7f-4f37-80fd-e9a4465e4347",
|
||||||
|
"title": "2026-06-01",
|
||||||
|
"url": "/daily/2026-06-01.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3f42a3fb-d64c-4d0c-95bc-5ac747b14f65",
|
||||||
|
"title": "2026-06-02",
|
||||||
|
"url": "/daily/2026-06-02.html"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"A": [
|
"A": [
|
||||||
@@ -273,6 +283,11 @@
|
|||||||
"id": "22737796-6D31-49D5-84F2-F7BC73E45144",
|
"id": "22737796-6D31-49D5-84F2-F7BC73E45144",
|
||||||
"title": "Concepts",
|
"title": "Concepts",
|
||||||
"url": "/Career Concepts/20260101010049-concepts.html"
|
"url": "/Career Concepts/20260101010049-concepts.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "01c89142-7e14-42b2-bd01-743656908fd2",
|
||||||
|
"title": "Cross Site Scripting (XSS)",
|
||||||
|
"url": "/20260601093154-cross_site_scripting_xss.html"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"D": [
|
"D": [
|
||||||
@@ -656,6 +671,11 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"N": [
|
"N": [
|
||||||
|
{
|
||||||
|
"id": "a68223f1-5d80-4ee1-8676-1be9dc742d0e",
|
||||||
|
"title": "Naats",
|
||||||
|
"url": "/20260601134732-naats.html"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "0880f089-a5ad-49cd-8ce3-f020a5941313",
|
"id": "0880f089-a5ad-49cd-8ce3-f020a5941313",
|
||||||
"title": "Networking MOC",
|
"title": "Networking MOC",
|
||||||
@@ -775,7 +795,7 @@
|
|||||||
{
|
{
|
||||||
"id": "3a8274ef-f2fe-486d-9caf-0c56982b9917",
|
"id": "3a8274ef-f2fe-486d-9caf-0c56982b9917",
|
||||||
"title": "RSS Articles",
|
"title": "RSS Articles",
|
||||||
"url": "/Misc/20260111204228-rss_articles.html"
|
"url": "/20260111204228-rss_articles.sync-conflict-20260530-154400-VT6366A.html"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"S": [
|
"S": [
|
||||||
@@ -829,6 +849,11 @@
|
|||||||
"title": "Solid Principles Examples",
|
"title": "Solid Principles Examples",
|
||||||
"url": "/Career Concepts/20260101012731-solid_principles_examples.html"
|
"url": "/Career Concepts/20260101012731-solid_principles_examples.html"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "de3bef27-81c9-49d1-85bb-edceb9a80e65",
|
||||||
|
"title": "SQL Joins",
|
||||||
|
"url": "/20260602101857-sql_joins.html"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "acba0d25-08db-4784-8cc2-fe5c437ba723",
|
"id": "acba0d25-08db-4784-8cc2-fe5c437ba723",
|
||||||
"title": "stanford_marshmallow_experiment",
|
"title": "stanford_marshmallow_experiment",
|
||||||
@@ -1002,7 +1027,7 @@
|
|||||||
{
|
{
|
||||||
"id": "b2ac09aa-e888-48d3-8357-2292f9b2526c",
|
"id": "b2ac09aa-e888-48d3-8357-2292f9b2526c",
|
||||||
"title": "XSS Pentesting Report Fix",
|
"title": "XSS Pentesting Report Fix",
|
||||||
"url": "/20260527152602-microlise_xss_rom_pentest.html"
|
"url": "/Career Concepts/20260527152602-microlise_xss_rom_pentest.html"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ This file is auto-generated. Do not edit manually.
|
|||||||
- [[id:5c89425f-2483-456f-8925-5a558d6a0868][2026-05-22]]
|
- [[id:5c89425f-2483-456f-8925-5a558d6a0868][2026-05-22]]
|
||||||
- [[id:99abd6e2-d9fc-423b-8930-c843f368557f][2026-05-28]]
|
- [[id:99abd6e2-d9fc-423b-8930-c843f368557f][2026-05-28]]
|
||||||
- [[id:648c487d-d218-404a-b427-d186dc4c265d][2026-05-29]]
|
- [[id:648c487d-d218-404a-b427-d186dc4c265d][2026-05-29]]
|
||||||
|
- [[id:8a813115-3d7f-4f37-80fd-e9a4465e4347][2026-06-01]]
|
||||||
|
- [[id:3f42a3fb-d64c-4d0c-95bc-5ac747b14f65][2026-06-02]]
|
||||||
|
|
||||||
* A
|
* A
|
||||||
- [[id:effa2bf5-8b04-42d3-b9c1-17160dc4ddc2][Actual]]
|
- [[id:effa2bf5-8b04-42d3-b9c1-17160dc4ddc2][Actual]]
|
||||||
@@ -65,6 +67,7 @@ This file is auto-generated. Do not edit manually.
|
|||||||
- [[id:dd55d635-59de-4ed9-8ff0-423782c2e0ae][clean-code]]
|
- [[id:dd55d635-59de-4ed9-8ff0-423782c2e0ae][clean-code]]
|
||||||
- [[id:43afdfdd-e1fe-4e3a-a6f6-0d99b253fc8a][Cloudflare]]
|
- [[id:43afdfdd-e1fe-4e3a-a6f6-0d99b253fc8a][Cloudflare]]
|
||||||
- [[id:22737796-6D31-49D5-84F2-F7BC73E45144][Concepts]]
|
- [[id:22737796-6D31-49D5-84F2-F7BC73E45144][Concepts]]
|
||||||
|
- [[id:01c89142-7e14-42b2-bd01-743656908fd2][Cross Site Scripting (XSS)]]
|
||||||
|
|
||||||
* D
|
* D
|
||||||
- [[id:7b8ba1b0-a00e-4fc1-b75e-55ba0e2c3bb7][Daily Notes MOC]]
|
- [[id:7b8ba1b0-a00e-4fc1-b75e-55ba0e2c3bb7][Daily Notes MOC]]
|
||||||
@@ -159,6 +162,7 @@ This file is auto-generated. Do not edit manually.
|
|||||||
- [[id:fdb8fa52-0c9d-4332-9f32-53bc6fee24b9][MVP and MVT]]
|
- [[id:fdb8fa52-0c9d-4332-9f32-53bc6fee24b9][MVP and MVT]]
|
||||||
|
|
||||||
* N
|
* N
|
||||||
|
- [[id:a68223f1-5d80-4ee1-8676-1be9dc742d0e][Naats]]
|
||||||
- [[id:0880f089-a5ad-49cd-8ce3-f020a5941313][Networking MOC]]
|
- [[id:0880f089-a5ad-49cd-8ce3-f020a5941313][Networking MOC]]
|
||||||
- [[id:a087da71-bcfb-4ddf-9565-b82113d5d27f][neuroplasticity]]
|
- [[id:a087da71-bcfb-4ddf-9565-b82113d5d27f][neuroplasticity]]
|
||||||
- [[id:56f39be4-1108-4020-be5a-1f3a0fbf96fa][Nextcloud]]
|
- [[id:56f39be4-1108-4020-be5a-1f3a0fbf96fa][Nextcloud]]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
initMediaFixes(document);
|
initMediaFixes(document);
|
||||||
initReadingProgress();
|
initReadingProgress();
|
||||||
hookSearchResults();
|
hookSearchResults();
|
||||||
|
initHomeDashboard();
|
||||||
});
|
});
|
||||||
|
|
||||||
/* =========================================================
|
/* =========================================================
|
||||||
@@ -93,14 +94,14 @@ function rearrangeDOM() {
|
|||||||
|
|
||||||
function buildNoteToolbar(toolbar) {
|
function buildNoteToolbar(toolbar) {
|
||||||
toolbar.innerHTML = `
|
toolbar.innerHTML = `
|
||||||
<button class="toolbar-btn" id="tb-back" title="Go back" aria-label="Go back">\u2190 back</button>
|
<button class="toolbar-btn toolbar-btn-icon" id="tb-back" title="Go back" aria-label="Go back">\u2190</button>
|
||||||
<button class="toolbar-btn" id="tb-forward" title="Go forward" aria-label="Go forward">forward \u2192</button>
|
<button class="toolbar-btn toolbar-btn-icon" id="tb-forward" title="Go forward" aria-label="Go forward">\u2192</button>
|
||||||
<div class="toolbar-sep"></div>
|
<div class="toolbar-sep"></div>
|
||||||
<button class="toolbar-btn" id="tb-toc" title="Toggle table of contents">contents</button>
|
<button class="toolbar-btn toolbar-btn-icon" id="tb-toc" title="Toggle table of contents" aria-label="Toggle table of contents">\u2261</button>
|
||||||
|
<button class="toolbar-btn toolbar-btn-icon" id="tb-backlinks" title="Toggle backlinks panel" aria-label="Toggle backlinks panel">\u29C9</button>
|
||||||
<div class="toolbar-spacer"></div>
|
<div class="toolbar-spacer"></div>
|
||||||
<span class="note-graph-pos" id="note-graph-pos"></span>
|
<span class="note-graph-pos" id="note-graph-pos"></span>
|
||||||
<div class="toolbar-sep"></div>
|
<button class="toolbar-btn toolbar-btn-icon" id="tb-close-all" title="Close all notes" aria-label="Close all notes">\u2715</button>
|
||||||
<button class="toolbar-btn" id="tb-backlinks" title="Toggle backlinks panel">backlinks</button>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,19 +109,49 @@ function buildNoteToolbar(toolbar) {
|
|||||||
SIDEBAR RAIL
|
SIDEBAR RAIL
|
||||||
========================================================= */
|
========================================================= */
|
||||||
|
|
||||||
|
const RECENT_KEY = 'zg-recent-notes';
|
||||||
|
const RECENT_MAX = 12;
|
||||||
|
const FOLDER_STATE_KEY = 'zg-folder-state';
|
||||||
|
|
||||||
|
let sidebarDataCache = null;
|
||||||
|
|
||||||
|
async function fetchSidebarData() {
|
||||||
|
if (sidebarDataCache) return sidebarDataCache;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/assets/sidebar-tree.json?v=${Date.now()}`, { cache: 'no-store' });
|
||||||
|
if (res.ok) sidebarDataCache = await res.json();
|
||||||
|
} catch (_) {}
|
||||||
|
return sidebarDataCache || { mocs: [], tree: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSidebar() {
|
||||||
|
const sidebar = document.getElementById('sidebar');
|
||||||
|
if (!sidebar) return;
|
||||||
|
sidebar.classList.remove('expanded', 'mobile-open');
|
||||||
|
sidebar.dataset.activePanel = '';
|
||||||
|
sidebar.querySelectorAll('.rail-btn').forEach(b => b.classList.remove('active'));
|
||||||
|
document.getElementById('sidebar-backdrop')?.classList.remove('visible');
|
||||||
|
}
|
||||||
|
|
||||||
function initSidebarRail() {
|
function initSidebarRail() {
|
||||||
const sidebar = document.getElementById('sidebar');
|
const sidebar = document.getElementById('sidebar');
|
||||||
if (!sidebar) return;
|
if (!sidebar) return;
|
||||||
|
|
||||||
|
const rail = el('div', { id: 'sidebar-rail', class: 'sidebar-rail' });
|
||||||
|
const body = el('div', { id: 'sidebar-body', class: 'sidebar-body' });
|
||||||
|
sidebar.append(rail, body);
|
||||||
|
|
||||||
const railBtns = [
|
const railBtns = [
|
||||||
{ label: '\u2630', title: 'All notes', id: 'rail-tree', panel: 'tree' },
|
{ label: '\u2B21', title: 'Atlas', id: 'rail-atlas', panel: 'atlas' },
|
||||||
{ label: '\u2315', title: 'Search', id: 'rail-search', action: 'search' },
|
{ label: '\u25F7', title: 'Recent', id: 'rail-recent', panel: 'recent' },
|
||||||
|
{ label: '\u2630', title: 'Browse', id: 'rail-browse', panel: 'browse' },
|
||||||
|
{ label: '\u2315', title: 'Search', id: 'rail-search', action: 'search' },
|
||||||
];
|
];
|
||||||
|
|
||||||
railBtns.forEach(({ label, title, id, panel, action }) => {
|
railBtns.forEach(({ label, title, id, panel, action }) => {
|
||||||
const btn = el('button', { class: 'rail-btn', id, title, 'aria-label': title });
|
const btn = el('button', { class: 'rail-btn', id, title, 'aria-label': title });
|
||||||
btn.textContent = label;
|
btn.textContent = label;
|
||||||
sidebar.appendChild(btn);
|
rail.appendChild(btn);
|
||||||
|
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
if (action === 'search') {
|
if (action === 'search') {
|
||||||
@@ -131,13 +162,12 @@ function initSidebarRail() {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isExpanded = sidebar.classList.contains('expanded');
|
const isExpanded = sidebar.classList.contains('expanded');
|
||||||
const samePanel = sidebar.dataset.activePanel === panel;
|
const samePanel = sidebar.dataset.activePanel === panel;
|
||||||
|
|
||||||
if (isExpanded && samePanel) {
|
if (isExpanded && samePanel) {
|
||||||
sidebar.classList.remove('expanded');
|
closeSidebar();
|
||||||
sidebar.dataset.activePanel = '';
|
|
||||||
btn.classList.remove('active');
|
|
||||||
} else {
|
} else {
|
||||||
sidebar.classList.add('expanded');
|
sidebar.classList.add('expanded');
|
||||||
sidebar.dataset.activePanel = panel;
|
sidebar.dataset.activePanel = panel;
|
||||||
@@ -148,57 +178,142 @@ function initSidebarRail() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mobile backdrop
|
|
||||||
let backdrop = document.getElementById('sidebar-backdrop');
|
let backdrop = document.getElementById('sidebar-backdrop');
|
||||||
if (!backdrop) {
|
if (!backdrop) {
|
||||||
backdrop = el('div', { id: 'sidebar-backdrop' });
|
backdrop = el('div', { id: 'sidebar-backdrop' });
|
||||||
document.body.appendChild(backdrop);
|
document.body.appendChild(backdrop);
|
||||||
}
|
}
|
||||||
backdrop.addEventListener('click', () => {
|
backdrop.addEventListener('click', closeSidebar);
|
||||||
sidebar.classList.remove('expanded', 'mobile-open');
|
|
||||||
backdrop.classList.remove('visible');
|
|
||||||
sidebar.querySelectorAll('.rail-btn').forEach(b => b.classList.remove('active'));
|
|
||||||
});
|
|
||||||
|
|
||||||
initSidebarToggle();
|
initSidebarToggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSidebarPanel(panel) {
|
function showSidebarPanel(panel) {
|
||||||
const sidebar = document.getElementById('sidebar');
|
const body = document.getElementById('sidebar-body');
|
||||||
sidebar.querySelectorAll('.sidebar-panel').forEach(p => p.remove());
|
if (!body) return;
|
||||||
|
body.innerHTML = '';
|
||||||
|
|
||||||
if (panel === 'tree') {
|
const panelEl = el('div', { class: 'sidebar-panel', 'data-panel': panel });
|
||||||
const panelEl = el('div', { class: 'sidebar-panel' });
|
panelEl.innerHTML = `
|
||||||
panelEl.innerHTML = `
|
<div class="sidebar-header">
|
||||||
<div class="sidebar-header">
|
<span class="sidebar-header-label">${panel}</span>
|
||||||
<span class="sidebar-header-label">all notes</span>
|
<button class="sidebar-close-btn" aria-label="Close sidebar">\u00D7</button>
|
||||||
<button class="sidebar-close-btn" aria-label="Close sidebar">\u00D7</button>
|
</div>
|
||||||
</div>
|
<div class="sidebar-panel-body" id="sidebar-panel-body"></div>
|
||||||
<div id="file-tree"></div>
|
`;
|
||||||
`;
|
body.appendChild(panelEl);
|
||||||
sidebar.appendChild(panelEl);
|
|
||||||
|
|
||||||
panelEl.querySelector('.sidebar-close-btn').addEventListener('click', () => {
|
panelEl.querySelector('.sidebar-close-btn').addEventListener('click', closeSidebar);
|
||||||
sidebar.classList.remove('expanded');
|
|
||||||
sidebar.dataset.activePanel = '';
|
|
||||||
sidebar.querySelectorAll('.rail-btn').forEach(b => b.classList.remove('active'));
|
|
||||||
});
|
|
||||||
|
|
||||||
buildFileTree(panelEl.querySelector('#file-tree'));
|
const container = panelEl.querySelector('#sidebar-panel-body');
|
||||||
|
if (panel === 'atlas') buildAtlasPanel(container);
|
||||||
|
else if (panel === 'recent') buildRecentPanel(container);
|
||||||
|
else if (panel === 'browse') buildBrowsePanel(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildAtlasPanel(container) {
|
||||||
|
const data = await fetchSidebarData();
|
||||||
|
const mocs = data.mocs || [];
|
||||||
|
|
||||||
|
if (!mocs.length) {
|
||||||
|
container.innerHTML = '<p class="sidebar-empty">No MOC hubs found.</p>';
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const grid = el('div', { class: 'moc-grid' });
|
||||||
|
mocs.forEach(moc => {
|
||||||
|
const card = el('button', {
|
||||||
|
class: 'moc-card' + (moc.pinned ? ' moc-card-pinned' : ''),
|
||||||
|
type: 'button',
|
||||||
|
});
|
||||||
|
card.innerHTML = `
|
||||||
|
<span class="moc-card-title">${escHtml(moc.title)}</span>
|
||||||
|
${moc.pinned ? '<span class="moc-card-badge">start</span>' : ''}
|
||||||
|
`;
|
||||||
|
card.addEventListener('click', () => {
|
||||||
|
closeSidebar();
|
||||||
|
openNote(normalisePathname(moc.url), moc.title);
|
||||||
|
});
|
||||||
|
grid.appendChild(card);
|
||||||
|
});
|
||||||
|
container.appendChild(grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRecentNotes() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem(RECENT_KEY) || '[]');
|
||||||
|
} catch (_) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function trackRecentNote(pathname, title) {
|
||||||
|
if (!pathname) return;
|
||||||
|
const entry = { pathname, title, visitedAt: Date.now() };
|
||||||
|
let recent = getRecentNotes().filter(r => r.pathname !== pathname);
|
||||||
|
recent.unshift(entry);
|
||||||
|
localStorage.setItem(RECENT_KEY, JSON.stringify(recent.slice(0, RECENT_MAX)));
|
||||||
|
const panel = document.querySelector('.sidebar-panel[data-panel="recent"] #sidebar-panel-body');
|
||||||
|
if (panel) buildRecentPanel(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatRelativeTime(ts) {
|
||||||
|
const diff = Date.now() - ts;
|
||||||
|
const mins = Math.floor(diff / 60000);
|
||||||
|
if (mins < 1) return 'just now';
|
||||||
|
if (mins < 60) return `${mins}m ago`;
|
||||||
|
const hrs = Math.floor(mins / 60);
|
||||||
|
if (hrs < 24) return `${hrs}h ago`;
|
||||||
|
const days = Math.floor(hrs / 24);
|
||||||
|
if (days < 7) return `${days}d ago`;
|
||||||
|
return new Date(ts).toLocaleDateString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRecentPanel(container) {
|
||||||
|
const recent = getRecentNotes();
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
if (!recent.length) {
|
||||||
|
container.innerHTML = '<p class="sidebar-empty">No notes visited yet.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = el('div', { class: 'recent-list' });
|
||||||
|
recent.forEach(item => {
|
||||||
|
const row = el('button', { class: 'recent-item', type: 'button' });
|
||||||
|
row.innerHTML = `
|
||||||
|
<span class="recent-item-title">${escHtml(item.title)}</span>
|
||||||
|
<span class="recent-item-time">${formatRelativeTime(item.visitedAt)}</span>
|
||||||
|
`;
|
||||||
|
row.addEventListener('click', () => {
|
||||||
|
closeSidebar();
|
||||||
|
openNote(item.pathname, item.title);
|
||||||
|
});
|
||||||
|
list.appendChild(row);
|
||||||
|
});
|
||||||
|
container.appendChild(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBrowsePanel(container) {
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="tree-filter-wrap">
|
||||||
|
<input type="search" class="tree-filter" placeholder="Filter notes\u2026" aria-label="Filter notes" />
|
||||||
|
</div>
|
||||||
|
<div id="file-tree"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const filterInput = container.querySelector('.tree-filter');
|
||||||
|
const treeRoot = container.querySelector('#file-tree');
|
||||||
|
|
||||||
|
filterInput.addEventListener('input', () => filterTree(treeRoot, filterInput.value.trim()));
|
||||||
|
|
||||||
|
buildFileTree(treeRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildFileTree(container) {
|
async function buildFileTree(container) {
|
||||||
const currentPath = location.pathname;
|
const currentPath = location.pathname;
|
||||||
let treeData = null;
|
const data = await fetchSidebarData();
|
||||||
|
let treeData = Array.isArray(data.tree) ? data.tree : null;
|
||||||
try {
|
|
||||||
const res = await fetch(`/assets/sidebar-tree.json?v=${Date.now()}`, { cache: 'no-store' });
|
|
||||||
if (res.ok) {
|
|
||||||
const json = await res.json();
|
|
||||||
if (Array.isArray(json.tree)) treeData = json.tree;
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
|
|
||||||
if (treeData) {
|
if (treeData) {
|
||||||
renderTree(treeData, container, currentPath);
|
renderTree(treeData, container, currentPath);
|
||||||
@@ -207,45 +322,120 @@ async function buildFileTree(container) {
|
|||||||
|
|
||||||
const groups = await buildGroupsFromSearchIndex();
|
const groups = await buildGroupsFromSearchIndex();
|
||||||
if (!groups?.length) {
|
if (!groups?.length) {
|
||||||
const empty = el('div', { class: 'tree-file' });
|
container.innerHTML = '<p class="sidebar-empty">No notes found.</p>';
|
||||||
empty.style.cssText = 'opacity:.4;font-size:.7rem;padding:8px 14px;';
|
|
||||||
empty.textContent = 'no notes found';
|
|
||||||
container.appendChild(empty);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const fallbackTree = groups.map(g => ({ label: g.label, children: [], files: g.files }));
|
const fallbackTree = groups.map(g => ({ label: g.label, children: [], files: g.files }));
|
||||||
renderTree(fallbackTree, container, currentPath);
|
renderTree(fallbackTree, container, currentPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTree(nodes, container, currentPath) {
|
function getFolderState() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem(FOLDER_STATE_KEY) || '{}');
|
||||||
|
} catch (_) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFolderState(key, isOpen) {
|
||||||
|
const state = getFolderState();
|
||||||
|
state[key] = isOpen;
|
||||||
|
localStorage.setItem(FOLDER_STATE_KEY, JSON.stringify(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
function countFilesInNode(node) {
|
||||||
|
let count = (node.files || []).length;
|
||||||
|
for (const child of node.children || []) {
|
||||||
|
count += countFilesInNode(child);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodeContainsPath(node, pathname, prefix = '') {
|
||||||
|
const folderKey = prefix ? `${prefix}/${node.label}` : node.label;
|
||||||
|
for (const f of node.files || []) {
|
||||||
|
if (normalisePathname(f.url) === pathname) return true;
|
||||||
|
}
|
||||||
|
for (const child of node.children || []) {
|
||||||
|
if (nodeContainsPath(child, pathname, folderKey)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTree(nodes, container, currentPath, prefix = '') {
|
||||||
nodes.forEach(node => {
|
nodes.forEach(node => {
|
||||||
const folderEl = el('div', { class: 'tree-folder', 'data-folder': node.label });
|
const folderKey = prefix ? `${prefix}/${node.label}` : node.label;
|
||||||
const icon = el('span', { class: 'tree-icon' });
|
const fileCount = countFilesInNode(node);
|
||||||
icon.textContent = '\u25BE ';
|
const containsActive = nodeContainsPath(node, currentPath, prefix);
|
||||||
|
const folderState = getFolderState();
|
||||||
|
const defaultOpen = containsActive || folderState[folderKey] === true;
|
||||||
|
const isOpen = defaultOpen;
|
||||||
|
|
||||||
|
const folderEl = el('div', {
|
||||||
|
class: 'tree-folder',
|
||||||
|
'data-folder': node.label,
|
||||||
|
'data-folder-key': folderKey,
|
||||||
|
});
|
||||||
|
const icon = el('span', { class: 'tree-icon' });
|
||||||
|
icon.textContent = isOpen ? '\u25BE ' : '\u25B8 ';
|
||||||
const labelSpan = el('span', { class: 'tree-folder-label' });
|
const labelSpan = el('span', { class: 'tree-folder-label' });
|
||||||
labelSpan.textContent = node.label;
|
labelSpan.textContent = node.label;
|
||||||
folderEl.append(icon, labelSpan);
|
const badge = el('span', { class: 'tree-folder-count' });
|
||||||
|
badge.textContent = String(fileCount);
|
||||||
|
folderEl.append(icon, labelSpan, badge);
|
||||||
|
|
||||||
const childrenWrap = el('div', { class: 'tree-children' });
|
const childrenWrap = el('div', { class: 'tree-children' });
|
||||||
|
if (!isOpen) childrenWrap.style.display = 'none';
|
||||||
|
|
||||||
|
const searchTerms = [node.label.toLowerCase()];
|
||||||
(node.files || []).forEach(f => {
|
(node.files || []).forEach(f => {
|
||||||
const pathname = normalisePathname(f.url);
|
const pathname = normalisePathname(f.url);
|
||||||
|
searchTerms.push(f.title.toLowerCase());
|
||||||
childrenWrap.appendChild(makeFileItem({ title: f.title, pathname }, currentPath));
|
childrenWrap.appendChild(makeFileItem({ title: f.title, pathname }, currentPath));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (node.children?.length) renderTree(node.children, childrenWrap, currentPath);
|
if (node.children?.length) {
|
||||||
|
renderTree(node.children, childrenWrap, currentPath, folderKey);
|
||||||
|
}
|
||||||
|
|
||||||
folderEl.addEventListener('click', () => {
|
folderEl.addEventListener('click', () => {
|
||||||
const isOpen = childrenWrap.style.display !== 'none';
|
const nowOpen = childrenWrap.style.display === 'none';
|
||||||
childrenWrap.style.display = isOpen ? 'none' : '';
|
childrenWrap.style.display = nowOpen ? '' : 'none';
|
||||||
icon.textContent = isOpen ? '\u25B8 ' : '\u25BE ';
|
icon.textContent = nowOpen ? '\u25BE ' : '\u25B8 ';
|
||||||
|
setFolderState(folderKey, nowOpen);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
folderEl.dataset.searchText = searchTerms.join(' ');
|
||||||
|
childrenWrap.dataset.searchText = searchTerms.join(' ');
|
||||||
|
|
||||||
container.appendChild(folderEl);
|
container.appendChild(folderEl);
|
||||||
container.appendChild(childrenWrap);
|
container.appendChild(childrenWrap);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterTree(container, query) {
|
||||||
|
const q = query.toLowerCase();
|
||||||
|
container.querySelectorAll('.tree-folder, .tree-children, .tree-file').forEach(el => {
|
||||||
|
if (!q) {
|
||||||
|
el.classList.remove('tree-filtered-out');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const text = (el.dataset.searchText || el.textContent || '').toLowerCase();
|
||||||
|
el.classList.toggle('tree-filtered-out', !text.includes(q));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (q) {
|
||||||
|
container.querySelectorAll('.tree-children').forEach(wrap => {
|
||||||
|
wrap.style.display = '';
|
||||||
|
const folder = wrap.previousElementSibling;
|
||||||
|
if (folder?.classList.contains('tree-folder')) {
|
||||||
|
const icon = folder.querySelector('.tree-icon');
|
||||||
|
if (icon) icon.textContent = '\u25BE ';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function buildGroupsFromSearchIndex() {
|
async function buildGroupsFromSearchIndex() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/search-index.json');
|
const res = await fetch('/search-index.json');
|
||||||
@@ -267,14 +457,17 @@ function makeFileItem({ title, pathname }, currentPath) {
|
|||||||
const fileEl = el('div', {
|
const fileEl = el('div', {
|
||||||
class: 'tree-file' + (pathname === currentPath ? ' active' : ''),
|
class: 'tree-file' + (pathname === currentPath ? ' active' : ''),
|
||||||
'data-pathname': pathname,
|
'data-pathname': pathname,
|
||||||
|
'data-search-text': title.toLowerCase(),
|
||||||
});
|
});
|
||||||
const iconSpan = el('span', { class: 'tree-icon' });
|
const iconSpan = el('span', { class: 'tree-icon' });
|
||||||
iconSpan.textContent = '\u00B7 ';
|
iconSpan.textContent = '\u00B7 ';
|
||||||
|
const titleSpan = el('span', { class: 'tree-file-title' });
|
||||||
|
titleSpan.textContent = title;
|
||||||
fileEl.appendChild(iconSpan);
|
fileEl.appendChild(iconSpan);
|
||||||
fileEl.appendChild(document.createTextNode(title));
|
fileEl.appendChild(titleSpan);
|
||||||
fileEl.title = title;
|
fileEl.title = title;
|
||||||
fileEl.addEventListener('click', () => {
|
fileEl.addEventListener('click', () => {
|
||||||
document.getElementById('sidebar')?.classList.remove('expanded');
|
closeSidebar();
|
||||||
openNote(pathname, title);
|
openNote(pathname, title);
|
||||||
});
|
});
|
||||||
return fileEl;
|
return fileEl;
|
||||||
@@ -378,6 +571,7 @@ history.replaceState(
|
|||||||
document.getElementById('tb-forward')?.addEventListener('click', goForward);
|
document.getElementById('tb-forward')?.addEventListener('click', goForward);
|
||||||
document.getElementById('tb-backlinks')?.addEventListener('click', toggleBacklinkPanel);
|
document.getElementById('tb-backlinks')?.addEventListener('click', toggleBacklinkPanel);
|
||||||
document.getElementById('tb-toc')?.addEventListener('click', toggleTOC);
|
document.getElementById('tb-toc')?.addEventListener('click', toggleTOC);
|
||||||
|
document.getElementById('tb-close-all')?.addEventListener('click', closeAllNotes);
|
||||||
|
|
||||||
// Backlink panel close
|
// Backlink panel close
|
||||||
document.getElementById('bp-close')?.addEventListener('click', () => {
|
document.getElementById('bp-close')?.addEventListener('click', () => {
|
||||||
@@ -548,6 +742,11 @@ function activateNote(id, skipBrowserWrite = true) {
|
|||||||
const area = document.getElementById('content-area');
|
const area = document.getElementById('content-area');
|
||||||
if (area) area.scrollTop = 0;
|
if (area) area.scrollTop = 0;
|
||||||
|
|
||||||
|
const current = noteHistory[historyIndex];
|
||||||
|
if (current) {
|
||||||
|
trackRecentNote(current.pathname, current.label);
|
||||||
|
}
|
||||||
|
|
||||||
updateBreadcrumb();
|
updateBreadcrumb();
|
||||||
updateToolbar();
|
updateToolbar();
|
||||||
|
|
||||||
@@ -567,6 +766,18 @@ function goForward() {
|
|||||||
window.history.forward();
|
window.history.forward();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeAllNotes() {
|
||||||
|
if (noteHistory.length <= 1) return;
|
||||||
|
const root = noteHistory[0];
|
||||||
|
noteHistory.slice(1).forEach(n => {
|
||||||
|
if (!n.isRoot) n.paneEl.remove();
|
||||||
|
});
|
||||||
|
noteHistory.splice(1);
|
||||||
|
historyIndex = 0;
|
||||||
|
activateNote(root.id, true);
|
||||||
|
history.replaceState(makeHistoryState(root), '', historyUrlFor(root));
|
||||||
|
}
|
||||||
|
|
||||||
function updateBreadcrumb() {
|
function updateBreadcrumb() {
|
||||||
const trail = document.getElementById('breadcrumb-trail');
|
const trail = document.getElementById('breadcrumb-trail');
|
||||||
if (!trail) return;
|
if (!trail) return;
|
||||||
@@ -934,9 +1145,9 @@ function initSidebarToggle() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const nowExpanded = sidebar.classList.toggle('expanded');
|
const nowExpanded = sidebar.classList.toggle('expanded');
|
||||||
if (nowExpanded && !sidebar.dataset.activePanel) {
|
if (nowExpanded && !sidebar.dataset.activePanel) {
|
||||||
sidebar.dataset.activePanel = 'tree';
|
sidebar.dataset.activePanel = 'atlas';
|
||||||
showSidebarPanel('tree');
|
showSidebarPanel('atlas');
|
||||||
sidebar.querySelector('#rail-tree')?.classList.add('active');
|
sidebar.querySelector('#rail-atlas')?.classList.add('active');
|
||||||
}
|
}
|
||||||
if (!nowExpanded) {
|
if (!nowExpanded) {
|
||||||
sidebar.dataset.activePanel = '';
|
sidebar.dataset.activePanel = '';
|
||||||
@@ -1285,6 +1496,51 @@ function normalisePathname(p) {
|
|||||||
// Lazy sidebar builder stub (built on demand)
|
// Lazy sidebar builder stub (built on demand)
|
||||||
async function buildSidebar() {}
|
async function buildSidebar() {}
|
||||||
|
|
||||||
|
async function initHomeDashboard() {
|
||||||
|
const pathname = normalisePathname(location.pathname);
|
||||||
|
if (pathname !== '/' && !pathname.endsWith('/index.html')) return;
|
||||||
|
|
||||||
|
const content = document.querySelector('#content');
|
||||||
|
if (!content || content.querySelector('.home-dashboard')) return;
|
||||||
|
|
||||||
|
const data = await fetchSidebarData();
|
||||||
|
const mocs = data.mocs || [];
|
||||||
|
if (!mocs.length) return;
|
||||||
|
|
||||||
|
const dash = el('div', { class: 'home-dashboard' });
|
||||||
|
dash.innerHTML = `
|
||||||
|
<header class="home-hero">
|
||||||
|
<h2 class="home-hero-title">Zettelgarten</h2>
|
||||||
|
<p class="home-hero-sub">A private library of notes, maps, and wandering thoughts.</p>
|
||||||
|
</header>
|
||||||
|
<div class="home-quick-links">
|
||||||
|
<button type="button" class="home-quick-link" data-action="search">Search <kbd>/</kbd></button>
|
||||||
|
<a class="home-quick-link" href="/all-files.html">All files</a>
|
||||||
|
</div>
|
||||||
|
<h3 class="home-section-label">Start here</h3>
|
||||||
|
<div class="home-moc-grid"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const grid = dash.querySelector('.home-moc-grid');
|
||||||
|
mocs.forEach(moc => {
|
||||||
|
const card = el('a', {
|
||||||
|
class: 'moc-card home-moc-card' + (moc.pinned ? ' moc-card-pinned' : ''),
|
||||||
|
href: moc.url,
|
||||||
|
});
|
||||||
|
card.innerHTML = `
|
||||||
|
<span class="moc-card-title">${escHtml(moc.title)}</span>
|
||||||
|
${moc.pinned ? '<span class="moc-card-badge">start</span>' : ''}
|
||||||
|
`;
|
||||||
|
grid.appendChild(card);
|
||||||
|
});
|
||||||
|
|
||||||
|
dash.querySelector('[data-action="search"]')?.addEventListener('click', () => {
|
||||||
|
window.openSearchModal?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
content.insertBefore(dash, content.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
// Pane-out animation keyframe
|
// Pane-out animation keyframe
|
||||||
const _s = document.createElement('style');
|
const _s = document.createElement('style');
|
||||||
_s.textContent = `
|
_s.textContent = `
|
||||||
|
|||||||
@@ -88,14 +88,12 @@
|
|||||||
========================================================= */
|
========================================================= */
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
#sidebar {
|
#sidebar:not(.expanded):not(.mobile-open) {
|
||||||
width: 0;
|
width: var(--rail-w);
|
||||||
border-right: none;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullwidth {
|
.fullwidth {
|
||||||
max-width: calc(100vw - 2rem);
|
max-width: calc(100vw - var(--rail-w) - 2rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,22 +170,22 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 80vw;
|
width: min(92vw, var(--sidebar-w));
|
||||||
max-width: 300px;
|
|
||||||
height: 100dvh;
|
height: 100dvh;
|
||||||
z-index: 400;
|
z-index: 400;
|
||||||
border-right: 1px solid var(--border-mid);
|
border-right: 1px solid var(--border-mid);
|
||||||
overflow-y: auto;
|
overflow: hidden;
|
||||||
overflow-x: hidden;
|
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
transition: transform var(--dur-mid) var(--ease-out),
|
transition: transform var(--dur-mid) var(--ease-out),
|
||||||
box-shadow var(--dur-mid) var(--ease-out);
|
box-shadow var(--dur-mid) var(--ease-out);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar.mobile-open {
|
#sidebar.mobile-open,
|
||||||
|
#sidebar.expanded {
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
box-shadow: 4px 0 32px rgba(0, 0, 0, 0.5);
|
box-shadow: 4px 0 32px rgba(0, 0, 0, 0.5);
|
||||||
|
width: min(92vw, var(--sidebar-w)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar-backdrop {
|
#sidebar-backdrop {
|
||||||
@@ -218,12 +216,7 @@
|
|||||||
#content-area {
|
#content-area {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
height: auto;
|
height: auto;
|
||||||
min-height: calc(100dvh - var(--header-h) - var(--tab-h));
|
min-height: calc(100dvh - var(--header-h) - var(--toolbar-h));
|
||||||
background-attachment: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content-area::before {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Note content ---- */
|
/* ---- Note content ---- */
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
ZETTELKASTEN GARDEN style.css
|
ZETTELKASTEN GARDEN style.css
|
||||||
========================================================= */
|
========================================================= */
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,500;0,600;1,500&family=DM+Mono:wght@400;500&family=Inter:wght@400;500;600&family=Noto+Sans+Arabic:wght@400;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,500;0,600;1,500&family=DM+Mono:wght@400;500&family=Spectral:ital,wght@0,300;0,400;0,500;1,300;1,400&family=Noto+Sans+Arabic:wght@400;700&display=swap');
|
||||||
|
|
||||||
/* =========================================================
|
/* =========================================================
|
||||||
TOKENS
|
TOKENS
|
||||||
@@ -10,25 +10,28 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
--header-h: 48px;
|
--header-h: 48px;
|
||||||
--toolbar-h: 36px;
|
--toolbar-h: 32px;
|
||||||
--rail-w: 52px;
|
--rail-w: 48px;
|
||||||
--panel-w: 260px;
|
--panel-w: 280px;
|
||||||
--content-max: none;
|
--sidebar-w: calc(var(--rail-w) + var(--panel-w));
|
||||||
--content-pad: clamp(1.5rem, 4vw, 4.5rem);
|
--content-max: min(72rem, calc(100vw - var(--rail-w) - 2.5rem));
|
||||||
|
--content-pad: clamp(1.25rem, 2.5vw, 2.25rem);
|
||||||
|
|
||||||
/* Dark academia: ink, vellum, forest green, oxblood, and aged brass */
|
/* Scriptorium: ink, vellum, oxblood, antique gold */
|
||||||
--bg: #11100c;
|
--bg: #0f0e0b;
|
||||||
--page-bg: #090907;
|
--page-bg: #070605;
|
||||||
--pane-bg: #15130f;
|
--pane-bg: #0a0908;
|
||||||
--surface: #1e1a14;
|
--reading-surface: #12100d;
|
||||||
--surface-2: #29231a;
|
--surface: #1a1712;
|
||||||
|
--surface-2: #242019;
|
||||||
|
|
||||||
--fg: #e8dfcd;
|
--fg: #e8dfcd;
|
||||||
--fg-dim: #b5a68f;
|
--fg-dim: #b5a68f;
|
||||||
--fg-faint: #766a58;
|
--fg-faint: #766a58;
|
||||||
--fg-ghost: #43382b;
|
--fg-ghost: #43382b;
|
||||||
|
|
||||||
--accent: #b68b45;
|
--accent: #c4a35a;
|
||||||
|
--accent-2: #6b2d3c;
|
||||||
--accent-dim: color-mix(in oklab, var(--accent) 35%, transparent);
|
--accent-dim: color-mix(in oklab, var(--accent) 35%, transparent);
|
||||||
--accent-sub: color-mix(in oklab, var(--accent) 10%, transparent);
|
--accent-sub: color-mix(in oklab, var(--accent) 10%, transparent);
|
||||||
--accent-glow:color-mix(in oklab, var(--accent) 6%, transparent);
|
--accent-glow:color-mix(in oklab, var(--accent) 6%, transparent);
|
||||||
@@ -38,13 +41,16 @@
|
|||||||
--heading-3: #a8604f;
|
--heading-3: #a8604f;
|
||||||
--link: #c7a763;
|
--link: #c7a763;
|
||||||
--link-hover:#f1e2c2;
|
--link-hover:#f1e2c2;
|
||||||
--tag-bg: #251f16;
|
--tag-bg: #1f1a14;
|
||||||
--tag-fg: var(--accent);
|
--tag-fg: var(--accent);
|
||||||
--border: #332c20;
|
--border: #2e2820;
|
||||||
--border-2: #4a3d29;
|
--border-2: #4a3d29;
|
||||||
--code-bg: #0c0c09;
|
--border-mid: #3a3228;
|
||||||
|
--chip-bg: var(--surface);
|
||||||
|
--code-bg: #080807;
|
||||||
|
|
||||||
--font-body: 'Inter', 'Noto Sans Arabic', system-ui, sans-serif;
|
--font-body: 'Spectral', 'Noto Sans Arabic', Georgia, serif;
|
||||||
|
--font-ui: system-ui, -apple-system, sans-serif;
|
||||||
--font-mono: 'DM Mono', 'Fira Mono', monospace;
|
--font-mono: 'DM Mono', 'Fira Mono', monospace;
|
||||||
--font-serif: 'Cormorant Garamond', Georgia, serif;
|
--font-serif: 'Cormorant Garamond', Georgia, serif;
|
||||||
|
|
||||||
@@ -58,24 +64,28 @@
|
|||||||
--r4:1rem; --r5:1.5rem; --r6:2rem; --r8:3rem;
|
--r4:1rem; --r5:1.5rem; --r6:2rem; --r8:3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Light theme */
|
/* Light theme — daylight reading room */
|
||||||
[data-theme="light"] {
|
[data-theme="light"] {
|
||||||
--bg: #f3eee3;
|
--bg: #f5f0e6;
|
||||||
--page-bg: #e9e0cf;
|
--page-bg: #ebe4d6;
|
||||||
--pane-bg: #f7f1e6;
|
--pane-bg: #f0ebe1;
|
||||||
|
--reading-surface: #f7f1e6;
|
||||||
--surface: #efe5d4;
|
--surface: #efe5d4;
|
||||||
--surface-2: #e3d5be;
|
--surface-2: #e3d5be;
|
||||||
--fg: #211c15;
|
--fg: #1c1812;
|
||||||
--fg-dim: #625744;
|
--fg-dim: #5c5040;
|
||||||
--fg-faint: #998b72;
|
--fg-faint: #8a7d68;
|
||||||
--fg-ghost: #d2c3a8;
|
--fg-ghost: #c8baa3;
|
||||||
--accent: #80602f;
|
--accent: #8a6b32;
|
||||||
|
--accent-2: #7e3a45;
|
||||||
--border: #d4c4a7;
|
--border: #d4c4a7;
|
||||||
--border-2: #b9a57f;
|
--border-2: #b9a57f;
|
||||||
|
--border-mid:#c4b08e;
|
||||||
|
--chip-bg: #e8dcc8;
|
||||||
--code-bg: #e8dcc8;
|
--code-bg: #e8dcc8;
|
||||||
--tag-bg: #dfd0b5;
|
--tag-bg: #dfd0b5;
|
||||||
--link: #684f28;
|
--link: #684f28;
|
||||||
--heading: #211c15;
|
--heading: #1c1812;
|
||||||
--heading-2: #5b4929;
|
--heading-2: #5b4929;
|
||||||
--heading-3: #7e4438;
|
--heading-3: #7e4438;
|
||||||
}
|
}
|
||||||
@@ -130,11 +140,32 @@ body, p, li, td, th, span, div {
|
|||||||
.banner-logo {
|
.banner-logo {
|
||||||
height: 26px; width: 26px;
|
height: 26px; width: 26px;
|
||||||
border-radius: 50%; object-fit: cover;
|
border-radius: 50%; object-fit: cover;
|
||||||
opacity: 0.85; flex-shrink: 0;
|
opacity: 0.9; flex-shrink: 0;
|
||||||
transition: opacity var(--dur-fast);
|
transition: opacity var(--dur-fast);
|
||||||
}
|
}
|
||||||
.banner-logo:hover { opacity: 1; }
|
.banner-logo:hover { opacity: 1; }
|
||||||
|
|
||||||
|
.banner-brand {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.55rem;
|
||||||
|
color: var(--heading);
|
||||||
|
text-decoration: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.banner-brand:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.banner-title {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: 1.15rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
/* Breadcrumb trail */
|
/* Breadcrumb trail */
|
||||||
#breadcrumb-trail {
|
#breadcrumb-trail {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -191,7 +222,7 @@ body, p, li, td, th, span, div {
|
|||||||
height: 30px;
|
height: 30px;
|
||||||
padding: 0 0.45rem 0 0.7rem;
|
padding: 0 0.45rem 0 0.7rem;
|
||||||
font-size: 0.78rem;
|
font-size: 0.78rem;
|
||||||
font-family: var(--font-body);
|
font-family: var(--font-ui);
|
||||||
color: var(--fg-dim);
|
color: var(--fg-dim);
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
@@ -390,14 +421,13 @@ body, p, li, td, th, span, div {
|
|||||||
width: var(--rail-w);
|
width: var(--rail-w);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background:
|
background:
|
||||||
linear-gradient(90deg, color-mix(in oklab, #233327 18%, transparent), transparent 78%),
|
linear-gradient(180deg, color-mix(in oklab, var(--accent-2) 8%, transparent), transparent 40%),
|
||||||
|
linear-gradient(90deg, color-mix(in oklab, #1a2e1f 22%, transparent), transparent 90%),
|
||||||
var(--bg);
|
var(--bg);
|
||||||
border-right: 1px solid var(--border);
|
border-right: 1px solid var(--border);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: stretch;
|
||||||
padding: var(--r3) 0;
|
|
||||||
gap: var(--r2);
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: width var(--dur-mid) var(--ease-out);
|
transition: width var(--dur-mid) var(--ease-out);
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
@@ -405,29 +435,60 @@ body, p, li, td, th, span, div {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#sidebar.expanded {
|
#sidebar.expanded {
|
||||||
width: var(--panel-w);
|
width: var(--sidebar-w);
|
||||||
align-items: flex-start;
|
}
|
||||||
overflow-y: auto;
|
|
||||||
|
.sidebar-rail {
|
||||||
|
width: var(--rail-w);
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: var(--r3) 0;
|
||||||
|
gap: var(--r2);
|
||||||
|
border-right: 1px solid color-mix(in oklab, var(--accent) 12%, var(--border));
|
||||||
|
background: color-mix(in oklab, var(--surface) 40%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-body {
|
||||||
|
display: none;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar.expanded .sidebar-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Rail icon buttons */
|
/* Rail icon buttons */
|
||||||
.rail-btn {
|
.rail-btn {
|
||||||
width: 36px; height: 36px;
|
width: 34px; height: 34px;
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: 1px solid transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: var(--fg-faint);
|
color: var(--fg-faint);
|
||||||
font-size: 1.05rem;
|
font-size: 1rem;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
transition: color var(--dur-fast), background var(--dur-fast);
|
transition: color var(--dur-fast), background var(--dur-fast), border-color var(--dur-fast), box-shadow var(--dur-fast);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.rail-btn:hover { color: var(--fg); background: var(--accent-sub); }
|
.rail-btn:hover {
|
||||||
.rail-btn.active { color: var(--accent); background: var(--accent-glow); }
|
color: var(--fg);
|
||||||
|
background: var(--accent-sub);
|
||||||
|
border-color: color-mix(in oklab, var(--accent) 20%, transparent);
|
||||||
|
}
|
||||||
|
.rail-btn.active {
|
||||||
|
color: var(--accent);
|
||||||
|
background: var(--accent-glow);
|
||||||
|
border-color: var(--accent-dim);
|
||||||
|
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--accent) 15%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
.rail-btn[title]:not(:disabled):hover::after {
|
.rail-btn[title]:not(:disabled):hover::after {
|
||||||
content: attr(title);
|
content: attr(title);
|
||||||
@@ -447,8 +508,31 @@ body, p, li, td, th, span, div {
|
|||||||
z-index: 500;
|
z-index: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-panel { display: none; width: 100%; flex-direction: column; }
|
.sidebar-panel {
|
||||||
#sidebar.expanded .sidebar-panel { display: flex; }
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-panel-body {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--border-2) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-empty {
|
||||||
|
padding: 1.25rem 14px;
|
||||||
|
color: var(--fg-faint);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-style: italic;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -481,7 +565,133 @@ body, p, li, td, th, span, div {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-filter-wrap {
|
||||||
|
padding: 6px 10px 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-filter {
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 8px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-family: var(--font-ui);
|
||||||
|
color: var(--fg);
|
||||||
|
background: var(--surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
|
||||||
|
}
|
||||||
|
.tree-filter:focus {
|
||||||
|
border-color: var(--accent-dim);
|
||||||
|
box-shadow: 0 0 0 2px var(--accent-sub);
|
||||||
|
}
|
||||||
|
.tree-filter::placeholder { color: var(--fg-faint); }
|
||||||
|
|
||||||
|
.tree-filtered-out { display: none !important; }
|
||||||
|
|
||||||
|
/* MOC cards (Atlas + home) */
|
||||||
|
.moc-grid,
|
||||||
|
.home-moc-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moc-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 10px;
|
||||||
|
text-align: left;
|
||||||
|
background: var(--surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-left: 2px solid color-mix(in oklab, var(--accent) 50%, transparent);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--fg);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: transform var(--dur-fast), border-color var(--dur-fast), background var(--dur-fast), box-shadow var(--dur-fast);
|
||||||
|
}
|
||||||
|
.moc-card:hover {
|
||||||
|
background: var(--surface-2);
|
||||||
|
border-color: var(--accent-dim);
|
||||||
|
border-left-color: var(--accent);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 16px color-mix(in oklab, var(--accent) 8%, transparent);
|
||||||
|
}
|
||||||
|
.moc-card-pinned {
|
||||||
|
border-left-color: var(--accent);
|
||||||
|
background: color-mix(in oklab, var(--accent) 5%, var(--surface));
|
||||||
|
}
|
||||||
|
.moc-card-title {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.3;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.moc-card-badge {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.55rem;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--accent);
|
||||||
|
padding: 2px 5px;
|
||||||
|
border: 1px solid var(--accent-dim);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recent list */
|
||||||
|
.recent-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 2px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 7px 10px;
|
||||||
|
text-align: left;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--fg-dim);
|
||||||
|
transition: background var(--dur-fast), color var(--dur-fast), border-color var(--dur-fast);
|
||||||
|
}
|
||||||
|
.recent-item:hover {
|
||||||
|
background: var(--accent-sub);
|
||||||
|
color: var(--fg);
|
||||||
|
border-color: color-mix(in oklab, var(--accent) 15%, transparent);
|
||||||
|
}
|
||||||
|
.recent-item-title {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.recent-item-time {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.62rem;
|
||||||
|
color: var(--fg-faint);
|
||||||
|
letter-spacing: 0.02em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Firefox */
|
/* Firefox */
|
||||||
@@ -512,7 +722,7 @@ body, p, li, td, th, span, div {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
padding: 4px 10px 4px 12px;
|
padding: 4px 8px 4px 10px;
|
||||||
font-size: 0.68rem;
|
font-size: 0.68rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--fg-faint);
|
color: var(--fg-faint);
|
||||||
@@ -520,32 +730,43 @@ body, p, li, td, th, span, div {
|
|||||||
margin: 2px 4px;
|
margin: 2px 4px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
letter-spacing: 0.06em;
|
letter-spacing: 0.04em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
transition: background var(--dur-fast), color var(--dur-fast);
|
transition: background var(--dur-fast), color var(--dur-fast);
|
||||||
}
|
}
|
||||||
.tree-folder:hover { background: var(--accent-sub); color: var(--fg); }
|
.tree-folder:hover { background: var(--accent-sub); color: var(--fg); }
|
||||||
|
|
||||||
|
.tree-folder-count {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 0.58rem;
|
||||||
|
color: var(--fg-ghost);
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.tree-file {
|
.tree-file {
|
||||||
/* display: flex; */
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
padding: 3px 10px 3px 12px;
|
padding: 3px 8px 3px 10px;
|
||||||
font-size: 0.76rem;
|
font-size: 0.76rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--fg-dim);
|
color: var(--fg-dim);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
margin: 1px 4px;
|
margin: 1px 4px;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
transition: background var(--dur-fast), color var(--dur-fast);
|
transition: background var(--dur-fast), color var(--dur-fast);
|
||||||
}
|
}
|
||||||
|
.tree-file-title {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
.tree-file:hover { background: var(--accent-sub); color: var(--fg); }
|
.tree-file:hover { background: var(--accent-sub); color: var(--fg); }
|
||||||
.tree-file.active {
|
.tree-file.active {
|
||||||
background: color-mix(in oklab, var(--accent) 14%, transparent);
|
background: color-mix(in oklab, var(--accent) 14%, transparent);
|
||||||
@@ -578,10 +799,10 @@ body, p, li, td, th, span, div {
|
|||||||
#note-toolbar {
|
#note-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--r2);
|
gap: var(--r1);
|
||||||
padding: 4px var(--r4);
|
padding: 2px var(--r3);
|
||||||
height: var(--toolbar-h);
|
height: var(--toolbar-h);
|
||||||
background: color-mix(in oklab, var(--bg) 92%, #233327 8%);
|
background: transparent;
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
@@ -591,6 +812,7 @@ body, p, li, td, th, span, div {
|
|||||||
.toolbar-btn {
|
.toolbar-btn {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
padding: 3px 8px;
|
padding: 3px 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@@ -603,6 +825,12 @@ body, p, li, td, th, span, div {
|
|||||||
transition: all var(--dur-fast);
|
transition: all var(--dur-fast);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.toolbar-btn-icon {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
.toolbar-btn:hover:not(:disabled) {
|
.toolbar-btn:hover:not(:disabled) {
|
||||||
border-color: var(--border-2);
|
border-color: var(--border-2);
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
@@ -623,10 +851,11 @@ body, p, li, td, th, span, div {
|
|||||||
.toolbar-spacer { flex: 1; }
|
.toolbar-spacer { flex: 1; }
|
||||||
|
|
||||||
.note-graph-pos {
|
.note-graph-pos {
|
||||||
color: var(--fg-faint);
|
color: var(--fg-ghost);
|
||||||
font-size: 0.68rem;
|
font-size: 0.62rem;
|
||||||
letter-spacing: 0.03em;
|
letter-spacing: 0.04em;
|
||||||
max-width: 200px;
|
font-style: italic;
|
||||||
|
max-width: 180px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -647,36 +876,45 @@ body, p, li, td, th, span, div {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
background-color: var(--pane-bg);
|
background:
|
||||||
background-image:
|
radial-gradient(ellipse 80% 60% at 50% 0%, color-mix(in oklab, var(--accent) 4%, transparent), transparent 70%),
|
||||||
linear-gradient(180deg, color-mix(in oklab, #233327 12%, transparent), transparent 22rem),
|
var(--pane-bg);
|
||||||
linear-gradient(90deg, color-mix(in oklab, var(--accent) 7%, transparent) 0 1px, transparent 1px),
|
|
||||||
repeating-linear-gradient(
|
|
||||||
180deg,
|
|
||||||
transparent, transparent 27px,
|
|
||||||
color-mix(in oklab, var(--fg) 2%, transparent) 27px,
|
|
||||||
color-mix(in oklab, var(--fg) 2%, transparent) 28px
|
|
||||||
);
|
|
||||||
position: relative;
|
position: relative;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: var(--border) transparent;
|
scrollbar-color: var(--border) transparent;
|
||||||
background-attachment: local;
|
|
||||||
}
|
}
|
||||||
#content-area::-webkit-scrollbar { width: 5px; }
|
#content-area::-webkit-scrollbar { width: 5px; }
|
||||||
#content-area::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
#content-area::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
||||||
|
|
||||||
#content-area::before {
|
.tab-content {
|
||||||
content: '';
|
display: none;
|
||||||
position: absolute;
|
min-height: 100%;
|
||||||
top: 0; left: 52px; width: 1px; height: 100%;
|
position: relative;
|
||||||
background: color-mix(in oklab, var(--accent) 8%, transparent);
|
z-index: 1;
|
||||||
pointer-events: none;
|
padding: var(--r4) var(--r2) var(--r6);
|
||||||
z-index: 0;
|
}
|
||||||
|
#sidebar.expanded ~ #main .tab-content,
|
||||||
|
body:has(#sidebar.expanded) .tab-content {
|
||||||
|
--content-max: min(68rem, calc(100vw - var(--sidebar-w) - 2rem));
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-content { display: none; min-height: 100%; position: relative; z-index: 1; }
|
|
||||||
.tab-content.active { display: block; }
|
.tab-content.active { display: block; }
|
||||||
|
|
||||||
|
.tab-content > #content,
|
||||||
|
.tab-content > .tab-content-inner {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--content-max);
|
||||||
|
padding: var(--r6) var(--content-pad) calc(var(--r8) * 1.5);
|
||||||
|
background: var(--reading-surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-top: 1px solid color-mix(in oklab, var(--accent) 25%, var(--border));
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow:
|
||||||
|
0 1px 0 color-mix(in oklab, var(--accent) 8%, transparent),
|
||||||
|
0 24px 64px color-mix(in oklab, #000 35%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
/* =========================================================
|
/* =========================================================
|
||||||
NOTE CONTENT
|
NOTE CONTENT
|
||||||
========================================================= */
|
========================================================= */
|
||||||
@@ -685,7 +923,89 @@ body, p, li, td, th, span, div {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: var(--content-max);
|
max-width: var(--content-max);
|
||||||
padding: var(--r6) var(--content-pad) calc(var(--r8) * 2.5);
|
}
|
||||||
|
|
||||||
|
/* =========================================================
|
||||||
|
HOME DASHBOARD
|
||||||
|
========================================================= */
|
||||||
|
|
||||||
|
.home-dashboard {
|
||||||
|
margin-bottom: var(--r8);
|
||||||
|
padding-bottom: var(--r6);
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero-title {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--heading);
|
||||||
|
margin: 0 0 var(--r2);
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero-sub {
|
||||||
|
margin: 0 0 var(--r5);
|
||||||
|
color: var(--fg-dim);
|
||||||
|
font-size: 1rem;
|
||||||
|
font-style: italic;
|
||||||
|
max-width: 36ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-quick-links {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--r2);
|
||||||
|
margin-bottom: var(--r6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-quick-link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
padding: 5px 12px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
color: var(--fg-dim);
|
||||||
|
background: var(--surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color var(--dur-fast), color var(--dur-fast), background var(--dur-fast);
|
||||||
|
}
|
||||||
|
.home-quick-link:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
border-color: var(--accent-dim);
|
||||||
|
background: var(--accent-glow);
|
||||||
|
}
|
||||||
|
.home-quick-link kbd {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
padding: 1px 4px;
|
||||||
|
border: 1px solid var(--border-2);
|
||||||
|
border-radius: 3px;
|
||||||
|
color: var(--fg-faint);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-section-label {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.62rem;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--fg-faint);
|
||||||
|
margin: 0 0 var(--r3);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-moc-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-moc-card {
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =========================================================
|
/* =========================================================
|
||||||
@@ -1076,8 +1396,8 @@ li ul li::before { content: "\00B7"; font-weight: normal; color: var(--fg-faint)
|
|||||||
blockquote {
|
blockquote {
|
||||||
margin: var(--r5) 0;
|
margin: var(--r5) 0;
|
||||||
padding: var(--r4) var(--r5);
|
padding: var(--r4) var(--r5);
|
||||||
border-left: 2px solid var(--accent);
|
border-left: 3px solid var(--accent-2);
|
||||||
background: var(--accent-glow);
|
background: color-mix(in oklab, var(--accent-2) 6%, transparent);
|
||||||
border-radius: 0 6px 6px 0;
|
border-radius: 0 6px 6px 0;
|
||||||
color: var(--fg-dim);
|
color: var(--fg-dim);
|
||||||
font-family: var(--font-serif);
|
font-family: var(--font-serif);
|
||||||
@@ -1383,15 +1703,16 @@ pre.src {
|
|||||||
========================================================= */
|
========================================================= */
|
||||||
|
|
||||||
#postamble, footer {
|
#postamble, footer {
|
||||||
color: var(--fg-faint);
|
color: var(--fg-ghost);
|
||||||
padding: var(--r2) var(--r5);
|
padding: var(--r2) var(--r5);
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid color-mix(in oklab, var(--border) 60%, transparent);
|
||||||
background: var(--bg);
|
background: var(--page-bg);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 0.68rem;
|
font-size: 0.62rem;
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
letter-spacing: 0.03em;
|
letter-spacing: 0.03em;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
#postamble a, footer a { color: var(--fg-faint); }
|
#postamble a, footer a { color: var(--fg-faint); }
|
||||||
#postamble a:hover, footer a:hover { color: var(--fg); }
|
#postamble a:hover, footer a:hover { color: var(--fg); }
|
||||||
@@ -1433,7 +1754,7 @@ pre.src {
|
|||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
:root {
|
:root {
|
||||||
--rail-w: 0px;
|
--rail-w: 48px;
|
||||||
--content-pad: 1rem;
|
--content-pad: 1rem;
|
||||||
}
|
}
|
||||||
#sidebar {
|
#sidebar {
|
||||||
@@ -1445,14 +1766,26 @@ pre.src {
|
|||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
#sidebar.mobile-open,
|
#sidebar.mobile-open,
|
||||||
#sidebar.expanded { width: var(--panel-w) !important; }
|
#sidebar.expanded { width: min(92vw, var(--sidebar-w)) !important; }
|
||||||
|
|
||||||
#backlink-panel.open { width: 200px; }
|
#backlink-panel.open { width: 200px; }
|
||||||
#breadcrumb-trail { display: none; }
|
#breadcrumb-trail { display: none; }
|
||||||
|
|
||||||
#note-toolbar { flex-wrap: wrap; height: auto; }
|
#note-toolbar { flex-wrap: wrap; height: auto; min-height: var(--toolbar-h); }
|
||||||
|
|
||||||
/* Code blocks on mobile */
|
.tab-content {
|
||||||
|
padding: var(--r3) var(--r2) var(--r6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content > #content,
|
||||||
|
.tab-content > .tab-content-inner {
|
||||||
|
padding: var(--r4) var(--content-pad) var(--r6);
|
||||||
|
box-shadow: 0 8px 32px color-mix(in oklab, #000 25%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-moc-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
.org-src-container > pre,
|
.org-src-container > pre,
|
||||||
pre.src {
|
pre.src {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
|||||||
@@ -3,9 +3,16 @@
|
|||||||
:END:
|
:END:
|
||||||
#+title: 2026-05-29
|
#+title: 2026-05-29
|
||||||
* <2026-05-29 Fri> TODO
|
* <2026-05-29 Fri> TODO
|
||||||
** TODO [#A] Replicate the PDEF and document it
|
** DONE [#A] Replicate the PDEF and document it
|
||||||
** TODO [#B] Investigate the fix for the PDEF
|
** DONE [#B] Investigate the fix for the PDEF
|
||||||
** TODO [#C] A few more test cases for the XSS report
|
** [#C] A few more test cases for the XSS report
|
||||||
** TODO [#A] Training
|
** DONE [#A] Training
|
||||||
** TODO Read 20 pages
|
** DONE [#B] Record the demo showcasing the triage results
|
||||||
** TODO Class
|
** DONE [#B] Complete follows data... competency
|
||||||
|
** DONE Read 20 pages
|
||||||
|
** DONE Class
|
||||||
|
* https://github.com/0-AI-UG/cate
|
||||||
|
* https://performance.dev/how-is-linear-so-fast-a-technical-breakdown
|
||||||
|
* https://www.sqlshack.com/debugging-stored-procedures-sql-server-management-studio-ssms/
|
||||||
|
* https://techcrunch.com/2026/05/27/tech-ceos-are-apparently-suffering-from-ai-psychosis/
|
||||||
|
* https://spin.atomicobject.com/using-agents-growth/
|
||||||
|
|||||||
11
daily/2026-06-01.org
Executable file
11
daily/2026-06-01.org
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: 8a813115-3d7f-4f37-80fd-e9a4465e4347
|
||||||
|
:END:
|
||||||
|
#+title: 2026-06-01
|
||||||
|
* <2026-06-01 Mon> TODO:
|
||||||
|
** DONE Finalise and finish the PDEF
|
||||||
|
** DONE Continue working on the XSS Rom issue (try documenting a fix)
|
||||||
|
** DONE Hifz class
|
||||||
|
** Read 5 pages
|
||||||
|
** DONE Class
|
||||||
|
|
||||||
10
daily/2026-06-02.org
Executable file
10
daily/2026-06-02.org
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: 3f42a3fb-d64c-4d0c-95bc-5ac747b14f65
|
||||||
|
:END:
|
||||||
|
#+title: 2026-06-02
|
||||||
|
* <2026-06-02 Tue> TODO:
|
||||||
|
** TODO Work on fix for the xss issue
|
||||||
|
** TODO Try finding the tests associated to the 2 files needing to be changed for XSS
|
||||||
|
** TODO Hifz class
|
||||||
|
** TODO Read 5 pages
|
||||||
|
** TODO Class
|
||||||
@@ -1,131 +1,112 @@
|
|||||||
"""
|
"""
|
||||||
generate_sidebar_tree.py
|
generate_sidebar_tree.py
|
||||||
|
|
||||||
Generates sidebar-tree.json from your org-roam directory.
|
Generates sidebar-tree.json from the org-roam directory.
|
||||||
Groups notes by #+category: property.
|
|
||||||
Notes without a category go into "uncategorised".
|
|
||||||
|
|
||||||
Output shape:
|
Output shape:
|
||||||
{
|
{
|
||||||
"groups": [
|
"mocs": [
|
||||||
|
{ "title": "Brain MOC", "url": "/20241210233721-brain_moc.html", "pinned": true },
|
||||||
|
...
|
||||||
|
],
|
||||||
|
"tree": [
|
||||||
{
|
{
|
||||||
"label": "java",
|
"label": "Articles",
|
||||||
"files": [
|
"children": [],
|
||||||
{ "title": "Java MOC", "url": "/20250402185735-java_moc.html" },
|
"files": [{ "title": "...", "url": "/Articles/....html" }]
|
||||||
...
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
{
|
],
|
||||||
"label": "uncategorised",
|
"exclude": [".packages"]
|
||||||
"files": [ ... ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from collections import defaultdict
|
|
||||||
|
EXCLUDE_DIRS = {".packages", ".git", ".cursor"}
|
||||||
|
EXCLUDE_FILES = {"index.org", "all-files.org"}
|
||||||
|
MOC_PINNED_TITLES = {"brain moc"}
|
||||||
|
PINNED_URL_SUFFIX = "brain_moc.html"
|
||||||
|
|
||||||
|
|
||||||
|
def should_skip(file_path: Path, org_dir: Path) -> bool:
|
||||||
|
"""Return True if this org file should be excluded from the sidebar."""
|
||||||
|
try:
|
||||||
|
relative = file_path.relative_to(org_dir)
|
||||||
|
except ValueError:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if file_path.name in EXCLUDE_FILES:
|
||||||
|
return True
|
||||||
|
|
||||||
|
for part in relative.parts[:-1]:
|
||||||
|
if part in EXCLUDE_DIRS or part.startswith("."):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_org_metadata(file_path: Path) -> dict:
|
def get_org_metadata(file_path: Path) -> dict:
|
||||||
"""Extract title, category (and optionally id) from an Org file."""
|
"""Extract title, filetags, and id from an Org file."""
|
||||||
title_pattern = re.compile(r"^#\+title:\s*(.+)", re.IGNORECASE | re.MULTILINE)
|
title_pattern = re.compile(r"^#\+title:\s*(.+)", re.IGNORECASE | re.MULTILINE)
|
||||||
category_pattern = re.compile(r"^#\+category:\s*(.+)", re.IGNORECASE | re.MULTILINE)
|
tags_pattern = re.compile(r"^#\+filetags:\s*(.+)", re.IGNORECASE | re.MULTILINE)
|
||||||
id_pattern = re.compile(r"^:ID:\s*(.+)", re.MULTILINE)
|
id_pattern = re.compile(r"^:ID:\s*(.+)", re.MULTILINE)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
content = file_path.read_text(encoding="utf-8", errors="ignore")
|
content = file_path.read_text(encoding="utf-8", errors="ignore")
|
||||||
except OSError:
|
except OSError:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
title_match = title_pattern.search(content)
|
title_match = title_pattern.search(content)
|
||||||
category_match = category_pattern.search(content)
|
tags_match = tags_pattern.search(content)
|
||||||
id_match = id_pattern.search(content)
|
id_match = id_pattern.search(content)
|
||||||
|
|
||||||
|
tags_raw = tags_match.group(1).strip() if tags_match else ""
|
||||||
|
tags = {t.strip(":").lower() for t in re.findall(r":[^:\s]+:", tags_raw)}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"title": title_match.group(1).strip() if title_match else "Untitled",
|
"title": title_match.group(1).strip() if title_match else "Untitled",
|
||||||
"category": category_match.group(1).strip() if category_match else None,
|
"tags": tags,
|
||||||
"id": id_match.group(1).strip() if id_match else None,
|
"id": id_match.group(1).strip() if id_match else None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def generate_sidebar_tree(org_dir: Path, out_file: Path) -> None:
|
def is_moc(meta: dict) -> bool:
|
||||||
"""Generate sidebar-tree.json grouped by #+category:."""
|
return "moc" in meta.get("tags", set())
|
||||||
print(" Generating sidebar-tree.json...")
|
|
||||||
|
|
||||||
org_dir = org_dir.expanduser().resolve()
|
|
||||||
out_file = out_file.expanduser()
|
|
||||||
|
|
||||||
# Collect all .org files, skip lock/temp files
|
def build_mocs(org_dir: Path) -> list[dict]:
|
||||||
org_files = [
|
"""Collect MOC notes tagged with :moc:."""
|
||||||
f for f in org_dir.rglob("*.org")
|
mocs = []
|
||||||
if not re.search(r"/\.#[^/]+\.org$", str(f))
|
|
||||||
]
|
|
||||||
|
|
||||||
groups: dict[str, list[dict]] = defaultdict(list)
|
for file_path in org_dir.rglob("*.org"):
|
||||||
|
if re.search(r"/\.#[^/]+\.org$", str(file_path)):
|
||||||
for file_path in org_files:
|
continue
|
||||||
meta = get_org_metadata(file_path)
|
if should_skip(file_path, org_dir):
|
||||||
if not meta:
|
continue
|
||||||
|
|
||||||
|
meta = get_org_metadata(file_path)
|
||||||
|
if not meta or not is_moc(meta):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Build the URL the same way as search-index.json
|
|
||||||
try:
|
try:
|
||||||
relative = file_path.relative_to(org_dir).with_suffix("")
|
relative = file_path.relative_to(org_dir).with_suffix("")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
entry = {
|
url = f"/{relative}.html"
|
||||||
"title": meta["title"],
|
title = meta["title"]
|
||||||
"url": f"/{relative}.html",
|
pinned = (
|
||||||
}
|
title.strip().lower() in MOC_PINNED_TITLES
|
||||||
|
or url.endswith(PINNED_URL_SUFFIX)
|
||||||
|
)
|
||||||
|
mocs.append({"title": title, "url": url, "pinned": pinned})
|
||||||
|
|
||||||
category = (meta["category"] or "").strip().lower() or "uncategorised"
|
mocs.sort(key=lambda m: (not m["pinned"], m["title"].lower()))
|
||||||
groups[category].append(entry)
|
return mocs
|
||||||
|
|
||||||
# Sort files within each group alphabetically by title
|
|
||||||
for cat in groups:
|
|
||||||
groups[cat].sort(key=lambda e: e["title"].lower())
|
|
||||||
|
|
||||||
# Sort groups alphabetically, but always put "uncategorised" last
|
|
||||||
sorted_labels = sorted(
|
|
||||||
groups.keys(),
|
|
||||||
key=lambda k: (k == "uncategorised", k.lower())
|
|
||||||
)
|
|
||||||
output = {
|
|
||||||
"tree": build_tree(org_dir)
|
|
||||||
}
|
|
||||||
out_file.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
with open(out_file, "w", encoding="utf-8") as f:
|
|
||||||
json.dump(output, f, ensure_ascii=False, indent=2)
|
|
||||||
|
|
||||||
|
|
||||||
files_count, folder_count = count_nodes(output["tree"])
|
|
||||||
|
|
||||||
print(f" sidebar-tree.json generated ({folder_count} folders, {files_count} files)")
|
|
||||||
print(f" → {out_file}")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def count_nodes(nodes):
|
|
||||||
total_files = 0
|
|
||||||
total_folders = 0
|
|
||||||
|
|
||||||
if not nodes:
|
|
||||||
return 0, 0 # ✅ IMPORTANT BASE CASE
|
|
||||||
|
|
||||||
for node in nodes:
|
|
||||||
total_folders += 1
|
|
||||||
total_files += len(node.get("files", []))
|
|
||||||
|
|
||||||
child_files, child_folders = count_nodes(node.get("children", []))
|
|
||||||
total_files += child_files
|
|
||||||
total_folders += child_folders
|
|
||||||
|
|
||||||
return total_files, total_folders
|
|
||||||
|
|
||||||
def build_tree(org_dir: Path) -> list:
|
def build_tree(org_dir: Path) -> list:
|
||||||
"""Build nested folder tree based on filesystem structure."""
|
"""Build nested folder tree based on filesystem structure."""
|
||||||
@@ -135,8 +116,6 @@ def build_tree(org_dir: Path) -> list:
|
|||||||
return
|
return
|
||||||
|
|
||||||
label = parts[0]
|
label = parts[0]
|
||||||
|
|
||||||
# Find or create folder
|
|
||||||
node = next((n for n in nodes if n["label"] == label), None)
|
node = next((n for n in nodes if n["label"] == label), None)
|
||||||
if not node:
|
if not node:
|
||||||
node = {"label": label, "children": [], "files": []}
|
node = {"label": label, "children": [], "files": []}
|
||||||
@@ -148,13 +127,14 @@ def build_tree(org_dir: Path) -> list:
|
|||||||
insert_node(node["children"], parts[1:], entry)
|
insert_node(node["children"], parts[1:], entry)
|
||||||
|
|
||||||
tree = []
|
tree = []
|
||||||
|
root_files = []
|
||||||
|
|
||||||
org_files = [
|
for file_path in org_dir.rglob("*.org"):
|
||||||
f for f in org_dir.rglob("*.org")
|
if re.search(r"/\.#[^/]+\.org$", str(file_path)):
|
||||||
if not re.search(r"/\.#[^/]+\.org$", str(f))
|
continue
|
||||||
]
|
if should_skip(file_path, org_dir):
|
||||||
|
continue
|
||||||
|
|
||||||
for file_path in org_files:
|
|
||||||
meta = get_org_metadata(file_path)
|
meta = get_org_metadata(file_path)
|
||||||
if not meta:
|
if not meta:
|
||||||
continue
|
continue
|
||||||
@@ -169,10 +149,16 @@ def build_tree(org_dir: Path) -> list:
|
|||||||
"url": f"/{relative.with_suffix('')}.html",
|
"url": f"/{relative.with_suffix('')}.html",
|
||||||
}
|
}
|
||||||
|
|
||||||
folder_parts = relative.parts[:-1] # ignore filename
|
folder_parts = relative.parts[:-1]
|
||||||
insert_node(tree, list(folder_parts), entry)
|
if not folder_parts:
|
||||||
|
root_files.append(entry)
|
||||||
|
else:
|
||||||
|
insert_node(tree, list(folder_parts), entry)
|
||||||
|
|
||||||
|
if root_files:
|
||||||
|
root_files.sort(key=lambda f: f["title"].lower())
|
||||||
|
tree.insert(0, {"label": "Garden", "children": [], "files": root_files})
|
||||||
|
|
||||||
# Sort everything
|
|
||||||
def sort_tree(nodes):
|
def sort_tree(nodes):
|
||||||
nodes.sort(key=lambda n: n["label"].lower())
|
nodes.sort(key=lambda n: n["label"].lower())
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
@@ -180,14 +166,54 @@ def build_tree(org_dir: Path) -> list:
|
|||||||
sort_tree(node["children"])
|
sort_tree(node["children"])
|
||||||
|
|
||||||
sort_tree(tree)
|
sort_tree(tree)
|
||||||
|
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def count_nodes(nodes):
|
||||||
base_dir = Path.home() / "master-folder" / "org_files" / "org_roam"
|
total_files = 0
|
||||||
sidebar_tree = base_dir / "output" / "assets" / "sidebar-tree.json"
|
total_folders = 0
|
||||||
|
|
||||||
|
if not nodes:
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
for node in nodes:
|
||||||
|
total_folders += 1
|
||||||
|
total_files += len(node.get("files", []))
|
||||||
|
child_files, child_folders = count_nodes(node.get("children", []))
|
||||||
|
total_files += child_files
|
||||||
|
total_folders += child_folders
|
||||||
|
|
||||||
|
return total_files, total_folders
|
||||||
|
|
||||||
|
|
||||||
|
def generate_sidebar_tree(org_dir: Path, out_file: Path) -> None:
|
||||||
|
"""Generate sidebar-tree.json with MOC hubs and folder tree."""
|
||||||
|
print(" Generating sidebar-tree.json...")
|
||||||
|
|
||||||
|
org_dir = org_dir.expanduser().resolve()
|
||||||
|
out_file = out_file.expanduser()
|
||||||
|
|
||||||
|
output = {
|
||||||
|
"mocs": build_mocs(org_dir),
|
||||||
|
"tree": build_tree(org_dir),
|
||||||
|
"exclude": sorted(EXCLUDE_DIRS),
|
||||||
|
}
|
||||||
|
|
||||||
|
out_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with open(out_file, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(output, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
files_count, folder_count = count_nodes(output["tree"])
|
||||||
|
print(
|
||||||
|
f" sidebar-tree.json generated "
|
||||||
|
f"({len(output['mocs'])} mocs, {folder_count} folders, {files_count} files)"
|
||||||
|
)
|
||||||
|
print(f" → {out_file}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
base_dir = Path.home() / "master-folder" / "org_files" / "org_roam"
|
||||||
|
sidebar_tree = base_dir / "output" / "assets" / "sidebar-tree.json"
|
||||||
generate_sidebar_tree(base_dir, sidebar_tree)
|
generate_sidebar_tree(base_dir, sidebar_tree)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -310,6 +310,10 @@ window.mvOpen=function(el){
|
|||||||
"
|
"
|
||||||
<div class=\"banner-header\">
|
<div class=\"banner-header\">
|
||||||
<div class=\"banner-left\">
|
<div class=\"banner-left\">
|
||||||
|
<a href=\"/index.html\" class=\"banner-brand\">
|
||||||
|
<img src=\"/assets/icons/icons8-z-key-64.png\" alt=\"\" class=\"banner-logo\" width=\"26\" height=\"26\" />
|
||||||
|
<span class=\"banner-title\">Zettelgarten</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=\"banner-search\">
|
<div class=\"banner-search\">
|
||||||
@@ -353,7 +357,6 @@ window.mvOpen=function(el){
|
|||||||
|
|
||||||
<div class=\"banner-actions\">
|
<div class=\"banner-actions\">
|
||||||
<button id=\"theme-toggle\" aria-label=\"Toggle theme\" title=\"Toggle light/dark\"></button>
|
<button id=\"theme-toggle\" aria-label=\"Toggle theme\" title=\"Toggle light/dark\"></button>
|
||||||
<button id=\"close-all\">close all</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
"
|
"
|
||||||
|
|||||||
6
orgroam-build.log
Normal file → Executable file
6
orgroam-build.log
Normal file → Executable file
@@ -37,14 +37,14 @@ If you don’t use the ‘package’ package manager but still get
|
|||||||
this warning, then your chosen package manager likely has a
|
this warning, then your chosen package manager likely has a
|
||||||
similar defect.
|
similar defect.
|
||||||
Loading /home/zaine/master-folder/org_files/org_roam/lisp/macros.el (source)...
|
Loading /home/zaine/master-folder/org_files/org_roam/lisp/macros.el (source)...
|
||||||
Starting full rebuild at 2026-05-29 08:34:51
|
Starting full rebuild at 2026-05-29 17:08:40
|
||||||
🎬 Scanning /home/zaine/master-folder/org_files/org_roam/assets/ for .mov files…
|
🎬 Scanning /home/zaine/master-folder/org_files/org_roam/assets/ for .mov files…
|
||||||
(no .mov files found)
|
(no .mov files found)
|
||||||
🧩 Generating all-files.org from JSON…
|
🧩 Generating all-files.org from JSON…
|
||||||
✅ all-files.org generated
|
✅ all-files.org generated
|
||||||
✅ Full rebuild complete in 16.29 seconds
|
✅ Full rebuild complete in 15.65 seconds
|
||||||
Generating the JSON output 2...
|
Generating the JSON output 2...
|
||||||
/usr/bin/python3 generate_sidebar_tree.py
|
/usr/bin/python3 generate_sidebar_tree.py
|
||||||
Generating sidebar-tree.json...
|
Generating sidebar-tree.json...
|
||||||
sidebar-tree.json generated (11 folders, 190 files)
|
sidebar-tree.json generated (11 mocs, 10 folders, 195 files)
|
||||||
→ /home/zaine/master-folder/org_files/org_roam/output/assets/sidebar-tree.json
|
→ /home/zaine/master-folder/org_files/org_roam/output/assets/sidebar-tree.json
|
||||||
|
|||||||
Reference in New Issue
Block a user