new-doxa-monorepo
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
.vibe/
|
||||||
@@ -1,3 +1,259 @@
|
|||||||
# doxa
|
# DOXA
|
||||||
|
|
||||||
Sada nástrojů pracující s textem z rodiny Doxa
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT license">
|
||||||
|
<img src="https://img.shields.io/badge/language-Go-00ADD8?style=flat-square&logo=go&logoColor=white" alt="Go">
|
||||||
|
<img src="https://img.shields.io/badge/language-JavaScript-F7DF1E?style=flat-square&logo=javascript&logoColor=black" alt="JavaScript">
|
||||||
|
<img src="https://img.shields.io/badge/platform-Linux%20%7C%20Windows%20%7C%20FreeBSD-6DA55F?style=flat-square" alt="Platforms">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="#-doxa">English</a> |
|
||||||
|
<a href="#-doxa-cz">Čeština</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌍 DOXA
|
||||||
|
|
||||||
|
**DOXA** is a collection of three minimalist, locally-focused applications for note management, message encryption, and command-line text processing. All projects share a common philosophy: **complete independence, privacy, and control over your data**. No cloud services, no tracking, no external dependencies. Your data belongs only to you.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 Projects in the DOXA Collection
|
||||||
|
|
||||||
|
### 1. 📝 [DOXA Notes](doxa-notes/)
|
||||||
|
|
||||||
|
**Web and desktop application for note management**
|
||||||
|
|
||||||
|
- **Description**: A minimalist note-taking application built on pure web standards (HTML5, CSS3, Vanilla JavaScript). Allows creating, editing, organizing, and exporting notes in an open JSON format.
|
||||||
|
- **Philosophy**: Local-first, no cloud databases, no tracking. Data is stored in the browser (LocalStorage/IndexedDB) or in local files.
|
||||||
|
- **Technologies**: Vanilla JavaScript (ES6+), HTML5, CSS3, JSON.
|
||||||
|
- **Platforms**: Web (browser), Linux (AppImage).
|
||||||
|
- **Features**:
|
||||||
|
- Tag support for note organization.
|
||||||
|
- Auto-save and manual JSON export/import.
|
||||||
|
- Dark/light mode and system settings.
|
||||||
|
- Drag & drop for reordering notes.
|
||||||
|
|
||||||
|
📖 [Read detailed documentation](doxa-notes/README.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 💻 [DOXA CLI](doxa-cli/)
|
||||||
|
|
||||||
|
**Command-line application for note management**
|
||||||
|
|
||||||
|
- **Description**: A command-line interface (CLI) version of DOXA Notes. Written in Go, it offers a modern Terminal User Interface (TUI) for managing notes directly from the terminal.
|
||||||
|
- **Philosophy**: Same philosophy as DOXA Notes – fully local, no external dependencies, data in JSON format.
|
||||||
|
- **Technologies**: Go (Golang), Bubble Tea (TUI framework).
|
||||||
|
- **Platforms**: Linux, FreeBSD.
|
||||||
|
- **Features**:
|
||||||
|
- Add, view, edit, delete, and search notes.
|
||||||
|
- Export and import notes in JSON format.
|
||||||
|
- Full compatibility with DOXA Notes (shared JSON format).
|
||||||
|
- Tag and filter support.
|
||||||
|
|
||||||
|
📖 [Read detailed documentation](doxa-cli/README.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. 🔐 [DOXA Secret](doxa-secret/)
|
||||||
|
|
||||||
|
**Tool for encrypting and protecting secret messages**
|
||||||
|
|
||||||
|
- **Description**: A minimalist tool for encrypting text messages using custom alphabets defined in JSON. Each character is converted into a unique 5-character code.
|
||||||
|
- **Philosophy**: Local encryption, no data sent to servers. Your secrets remain only on your device.
|
||||||
|
- **Technologies**: Go (Golang), Bubble Tea (TUI framework).
|
||||||
|
- **Platforms**: Linux, FreeBSD.
|
||||||
|
- **Features**:
|
||||||
|
- Custom encryption alphabets (JSON format).
|
||||||
|
- Support for multiple ciphers and switching between them.
|
||||||
|
- Automatic diacritic normalization (basic Latin only).
|
||||||
|
- Import and export of encrypted messages (TXT format).
|
||||||
|
- Bilingual interface (Czech/English).
|
||||||
|
|
||||||
|
⚠️ **Important**: The application works only with basic Latin characters (no diacritics). Czech diacritics are automatically normalized (e.g., "ž" → "z", "á" → "a").
|
||||||
|
|
||||||
|
📖 [Read detailed documentation](doxa-secret/README.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔗 Common Features of All DOXA Projects
|
||||||
|
|
||||||
|
| Feature | DOXA Notes | DOXA CLI | DOXA Secret |
|
||||||
|
|---------|------------|----------|--------------|
|
||||||
|
| **Local Data Storage** | ✅ | ✅ | ✅ |
|
||||||
|
| **Open Format (JSON)** | ✅ | ✅ | ✅ |
|
||||||
|
| **No Cloud Dependencies** | ✅ | ✅ | ✅ |
|
||||||
|
| **Open Source (MIT)** | ✅ | ✅ | ✅ |
|
||||||
|
| **Minimalist Design** | ✅ | ✅ | ✅ |
|
||||||
|
| **Tag Support** | ✅ | ✅ | ❌ |
|
||||||
|
| **Search and Filtering** | ✅ | ✅ | ❌ |
|
||||||
|
| **Encryption** | ❌ | ❌ | ✅ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠 Installation
|
||||||
|
|
||||||
|
Each project has its own installation process. Detailed guides can be found in the respective README files:
|
||||||
|
|
||||||
|
- [DOXA Notes – Installation](doxa-notes/03_install.md)
|
||||||
|
- [DOXA CLI – Installation](doxa-cli/README.md#installation)
|
||||||
|
- [DOXA Secret – Installation](doxa-secret/README_EN.md#installation)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📜 License
|
||||||
|
|
||||||
|
All projects in the DOXA collection are **open-source** and licensed under the **MIT License**. See the `LICENSE` files in each project for details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 DOXA Philosophy
|
||||||
|
|
||||||
|
> "No matter how complex the circumstances around you are, when you build your own temple of ideas, you start from the first stone. This code is your first stone."
|
||||||
|
> — Stoic reference from Kankys
|
||||||
|
|
||||||
|
DOXA believes in:
|
||||||
|
- **Digital Sovereignty**: Your data belongs to you.
|
||||||
|
- **Minimalism**: Less is more. Clean code, clear mind.
|
||||||
|
- **Local-First**: No cloud dependencies, no tracking.
|
||||||
|
- **Openness**: Open code, open data, open mind.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 Contact and Support
|
||||||
|
|
||||||
|
- **Author**: Kankys
|
||||||
|
- **Repositories**:
|
||||||
|
- [DOXA on Codeberg](https://codeberg.org/Kankys/doxa)
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🇨🇿 DOXA (CZ)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="#-doxa">English</a> |
|
||||||
|
<a href="#-doxa-cz">Čeština</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌍 DOXA
|
||||||
|
|
||||||
|
**DOXA** je sbírka tří minimalistických, lokálně orientovaných aplikací pro správu poznámek, šifrování zpráv a práci s textem z příkazové řádky. Všechny projekty sdílejí společnou filozofii: **úplná nezávislost, soukromí a kontrola nad vašimi daty**. Žádné cloudové služby, žádné sledování, žádné externí závislosti. Vaše data patří pouze vám.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 Projekty v kolekci DOXA
|
||||||
|
|
||||||
|
### 1. 📝 [DOXA Notes](doxa-notes/)
|
||||||
|
|
||||||
|
**Webová a desktopová aplikace pro správu poznámek**
|
||||||
|
|
||||||
|
- **Popis**: Minimalistická poznámková aplikace postavená na čistých webových standardech (HTML5, CSS3, Vanilla JavaScript). Umožňuje vytvářet, upravovat, organizovat a exportovat poznámky v otevřeném JSON formátu.
|
||||||
|
- **Filozofie**: Local-first, žádné cloudové databáze, žádné sledování. Data se ukládají v prohlížeči (LocalStorage/IndexedDB) nebo v lokálních souborech.
|
||||||
|
- **Technologie**: Vanilla JavaScript (ES6+), HTML5, CSS3, JSON.
|
||||||
|
- **Platformy**: Web (prohlížeč), Linux (AppImage).
|
||||||
|
- **Vlastnosti**:
|
||||||
|
- Podpora tagů pro organizaci poznámek.
|
||||||
|
- Automatické ukládání (autosave) a manuální export/import JSON.
|
||||||
|
- Tmavý/světlý režim a systémové nastavení.
|
||||||
|
- Drag & drop pro přeskupování poznámek.
|
||||||
|
|
||||||
|
📖 [Přečtěte si podrobnější dokumentaci](doxa-notes/README.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 💻 [DOXA CLI](doxa-cli/)
|
||||||
|
|
||||||
|
**Terminálová aplikace pro správu poznámek**
|
||||||
|
|
||||||
|
- **Popis**: Příkazová řádka (CLI) verze DOXA Notes. Napsána v jazyce Go, nabízí moderní terminálové uživatelské rozhraní (TUI) pro správu poznámek přímo z terminálu.
|
||||||
|
- **Filozofie**: Stejná filozofie jako DOXA Notes – plně lokální, žádné externí závislosti, data v JSON formátu.
|
||||||
|
- **Technologie**: Go (Golang), Bubble Tea (TUI framework).
|
||||||
|
- **Platformy**: Linux, FreeBSD.
|
||||||
|
- **Vlastnosti**:
|
||||||
|
- Přidávání, prohlížení, úprava, mazání a vyhledávání poznámek.
|
||||||
|
- Export a import poznámek v JSON formátu.
|
||||||
|
- Plná kompatibilita s DOXA Notes (sdílený JSON formát).
|
||||||
|
- Podpora tagů a filtrování.
|
||||||
|
|
||||||
|
📖 [Přečtěte si podrobnější dokumentaci](doxa-cli/README_CZ.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. 🔐 [DOXA Secret](doxa-secret/)
|
||||||
|
|
||||||
|
**Nástroj pro šifrování a ochranu tajných zpráv**
|
||||||
|
|
||||||
|
- **Popis**: Minimalistický nástroj pro šifrování textových zpráv pomocí vlastních abeced definovaných v JSON. Každý znak je převeden na unikátní 5znakový kód.
|
||||||
|
- **Filozofie**: Lokální šifrování, žádné odesílání dat na servery. Vaše tajemství zůstávají pouze na vašem zařízení.
|
||||||
|
- **Technologie**: Go (Golang), Bubble Tea (TUI framework).
|
||||||
|
- **Platformy**: Linux, FreeBSD.
|
||||||
|
- **Vlastnosti**:
|
||||||
|
- Vlastní šifrovací abecedy (JSON formát).
|
||||||
|
- Podpora více šifer a přepínání mezi nimi.
|
||||||
|
- Automatická normalizace diakritiky (pouze základní latinka).
|
||||||
|
- Import a export šifrovaných zpráv (TXT formát).
|
||||||
|
- Dvoujazyčné rozhraní (česky/anglicky).
|
||||||
|
|
||||||
|
⚠️ **Důležité**: Aplikace pracuje pouze se znaky základní latinky (bez diakritiky). Česká diakritika je automaticky normalizována (např. "ž" → "z", "á" → "a").
|
||||||
|
|
||||||
|
📖 [Přečtěte si podrobnější dokumentaci](doxa-secret/README.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔗 Společné vlastnosti všech projektů DOXA
|
||||||
|
|
||||||
|
| Vlastnost | DOXA Notes | DOXA CLI | DOXA Secret |
|
||||||
|
|-----------|------------|----------|--------------|
|
||||||
|
| **Lokální uložení dat** | ✅ | ✅ | ✅ |
|
||||||
|
| **Otevřený formát (JSON)** | ✅ | ✅ | ✅ |
|
||||||
|
| **Žádné cloudové závislosti** | ✅ | ✅ | ✅ |
|
||||||
|
| **Open Source (MIT)** | ✅ | ✅ | ✅ |
|
||||||
|
| **Minimalistický design** | ✅ | ✅ | ✅ |
|
||||||
|
| **Podpora tagů** | ✅ | ✅ | ❌ |
|
||||||
|
| **Vyhledávání a filtrování** | ✅ | ✅ | ❌ |
|
||||||
|
| **Šifrování** | ❌ | ❌ | ✅ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠 Instalace
|
||||||
|
|
||||||
|
Každý projekt má vlastní instalační postup. Podrobné návody naleznete v příslušných README souborech:
|
||||||
|
|
||||||
|
- [DOXA Notes – Instalace](doxa-notes/03_install.md)
|
||||||
|
- [DOXA CLI – Instalace](doxa-cli/README_CZ.md#instalace)
|
||||||
|
- [DOXA Secret – Instalace](doxa-secret/README.md#instalace)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📜 Licence
|
||||||
|
|
||||||
|
Všechny projekty v kolekci DOXA jsou **open-source** a licencovány pod **MIT licencí**. Podrobnosti naleznete v souborech `LICENSE` v jednotlivých projektech.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Filozofie DOXA
|
||||||
|
|
||||||
|
> "Nezáleží na tom, jak složité jsou okolnosti kolem tebe. Když stavíš vlastní chrám myšlenek, začínáš od prvního kamene. Tento kód je tvým prvním kamenem."
|
||||||
|
> — Stoický odkaz od Kankyse
|
||||||
|
|
||||||
|
DOXA věří v:
|
||||||
|
- **Digitální suverenitu**: Vaše data patří vám.
|
||||||
|
- **Minimalismus**: Méně je více. Čistý kód, čistá mysl.
|
||||||
|
- **Lokální první**: Žádné cloudové závislosti, žádné sledování.
|
||||||
|
- **Otevřenost**: Otevřený kód, otevřená data, otevřená mysl.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 Kontakt a podpora
|
||||||
|
|
||||||
|
- **Autor**: Kankys
|
||||||
|
- **Repozitáře**:
|
||||||
|
- [DOXA na Codeberg](https://codeberg.org/Kankys/doxa)
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# **🇬🇧 DOXA-CLI PROJECT**
|
||||||
|
|
||||||
|
##### **🇨🇿 [Czech Version](About_CZ.md)**
|
||||||
|
|
||||||
|
#### **Technical and architectural manifesto of a minimalist terminal-based note-taking application**
|
||||||
|
|
||||||
|
**Project goal:** To create a companion to the web-based [DOXA Notes](https://doxa-notes.org/), bringing the exact same philosophy into the focused environment of the command line. Built with the Go language, DOXA-CLI operates entirely locally, utilizing the identical open JSON data standard without relying on external clouds or complex frameworks.
|
||||||
|
|
||||||
|
## **1\. TECHNICAL ARCHITECTURE**
|
||||||
|
|
||||||
|
To maintain maximum speed, raw performance, and absolute minimalism, DOXA-CLI is built on solid foundations:
|
||||||
|
Language: Pure Go (Golang) ensuring native compilation, high execution speed, and zero heavy runtime dependencies.
|
||||||
|
Interface: Terminal User Interface (CLI) designed to keep the mind free from graphical clutter and focused purely on text.
|
||||||
|
Data layer: Fully compatible JSON format shared with the web version. Notes contain unique timestamps, identifiers, content, tags.
|
||||||
|
|
||||||
|
## **2\. DATA FORMAT STRUCTURE (JSON)**
|
||||||
|
|
||||||
|
Ensuring seamless interoperability with the web version, DOXA-CLI reads and writes data using the standard shared schema:
|
||||||
|
|
||||||
|
JSON
|
||||||
|
\[
|
||||||
|
{
|
||||||
|
"id":
|
||||||
|
"name": " ",
|
||||||
|
"content": " ",
|
||||||
|
"tags": \[
|
||||||
|
" "
|
||||||
|
\],
|
||||||
|
"folder": " ", // unused/empty
|
||||||
|
"created\_at": " ",
|
||||||
|
"modified\_at": " "
|
||||||
|
}
|
||||||
|
\]
|
||||||
|
|
||||||
|
## **3\. LOCAL EXECUTION & PHILOSOPHY**
|
||||||
|
|
||||||
|
**Architecture (Purely local execution)**
|
||||||
|
**Technical storage solution** \- Data resides directly on your local disk in a standardized JSON file (doxa\_notes.json), giving you absolute control with zero network latency.
|
||||||
|
**Security aspect** \- Total self-reliance. Your thoughts remain entirely on your machine.
|
||||||
|
|
||||||
|
## **License**
|
||||||
|
|
||||||
|
The application is open-source and licensed under the MIT license.
|
||||||
|
\--
|
||||||
|
"The terminal asks for nothing, distracts by nothing, and offers total control. Bring order to your thoughts through pure code." — Stoic reference from Kankys
|
||||||
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# **🇨🇿 PROJEKT DOXA-CLI**
|
||||||
|
|
||||||
|
#### **Technický a architektonický manifest minimalistické terminálové poznámkové aplikace**
|
||||||
|
|
||||||
|
**Záměr projektu:** Vytvořit věrnou sestru webové verze [DOXA Notes](https://doxa-notes.org/), která přenáší stejnou myšlenku do soustředěného prostředí příkazové řádky. Aplikace je napsaná v jazyce Go, funguje zcela lokálně a sdílí identický otevřený formát JSON bez závislosti na cloudu či složitých knihovnách.
|
||||||
|
|
||||||
|
## **1\. TECHNICKÁ ARCHITEKTURA**
|
||||||
|
|
||||||
|
Pro zachování maximální rychlosti, surového výkonu a absolutního minimalismu je DOXA-CLI postavena na pevných základech:
|
||||||
|
Jazyk: Čisté Go (Golang), které zaručuje nativní kompilaci, vysokou rychlost provedení a žádné zbytečné externí závislosti.
|
||||||
|
Rozhraní: Terminálové rozhraní (CLI), které zbavuje mysl grafického chaosu a nechává vyniknout čistému textu.
|
||||||
|
Datová vrstva: Plně kompatibilní formát JSON sdílený s webovou verzí. Poznámky obsahují přesná časová razítka, identifikátory, obsah, štítky.
|
||||||
|
|
||||||
|
## **2\. STRUKTURA DATOVÉHO FORMÁTU (JSON)**
|
||||||
|
|
||||||
|
Pro zajištění dokonalé propojenosti s webovou verzí čte a zapisuje DOXA-CLI data ve stejném sdíleném schématu:
|
||||||
|
|
||||||
|
JSON
|
||||||
|
\[
|
||||||
|
{
|
||||||
|
"id":
|
||||||
|
"name": " ",
|
||||||
|
"content": " ",
|
||||||
|
"tags": \[
|
||||||
|
" "
|
||||||
|
\],
|
||||||
|
"folder": " ", // nepoužito/prázdné
|
||||||
|
"created\_at": " ",
|
||||||
|
"modified\_at": " "
|
||||||
|
}
|
||||||
|
\]
|
||||||
|
|
||||||
|
## **3\. LOKÁLNÍ SPUŠTĚNÍ A FILOZOFIE**
|
||||||
|
|
||||||
|
**Architektura (Čistě lokální běh)**
|
||||||
|
**Technické řešení úložiště** \- Data sídlí přímo na tvém lokálním disku ve standardizovaném souboru JSON (doxa\_notes.json), což ti dává absolutní kontrolu bez jakékoliv závislosti na síti.
|
||||||
|
**Bezpečnostní aspekt** \- Úplná soběstačnost. Tvoje myšlenky zůstávají pevně na tvém stroji.
|
||||||
|
|
||||||
|
## **Licence**
|
||||||
|
|
||||||
|
Aplikace je open-source a licencována pod MIT licencí.
|
||||||
|
\--
|
||||||
|
"Terminál nežádá nic, neodvádí pozornost ničím a nabízí úplnou kontrolu. Vnes řád do svých myšlenek skrze čistý kód."
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Kankys
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||||
|
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
||||||
|
following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||||
|
portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||||
|
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||||
|
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
# 🦅 USER MANUAL (DOXA-CLI)
|
||||||
|
|
||||||
|
**DOXA-CLI** is a minimalist, local-first command-line notes management tool. It is designed to bring the same philosophy of focus and utility from the web-based [DOXA Notes](https://doxa-notes.org/) into your terminal, utilizing the exact same open JSON data standard without relying on external clouds. Written in **Go**, it offers a modern Terminal User Interface (**TUI**) built with Charm's Bubble Tea and Lip Gloss.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/go-1.22+-00ADD8?style=flat-square&logo=go&logoColor=white" alt="Go 1.22+">
|
||||||
|
<img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT license">
|
||||||
|
<img src="https://img.shields.io/badge/tested-Linux-brightgreen?style=flat-square" alt="Tested on Linux">
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/freebsd-AB2B28?style=for-the-badge&logo=freebsd&logoColor=white" alt="freeBSD">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TABLE OF CONTENTS
|
||||||
|
1. [Installation & Execution](#1-installation--execution)
|
||||||
|
2. [Storage Architecture & Schema](#2-storage-architecture--schema)
|
||||||
|
3. [Interactive Interface & States](#3-interactive-interface--states)
|
||||||
|
4. [Keyboard Shortcuts Reference](#4-keyboard-shortcuts-reference)
|
||||||
|
5. [Export System Details](#5-export-system-details)
|
||||||
|
6. [Uninstallation](#6-uninstallation)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. INSTALLATION & EXECUTION
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
Before installing, make sure you have **Go** installed on your system (version **1.20** or higher is recommended).
|
||||||
|
|
||||||
|
### Installation Steps (Linux / macOS)
|
||||||
|
1. **Clone the repository:**
|
||||||
|
```bash
|
||||||
|
git clone https://codeberg.org/Kankys/doxa-cli.git
|
||||||
|
cd doxa-cli
|
||||||
|
```
|
||||||
|
2. **Run the installation script:**
|
||||||
|
```bash
|
||||||
|
chmod +x install.sh
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
*The script checks your Go installation, downloads all Go modules, compiles the binary, installs the executable `doxa-cli` to `~/.local/bin/`, and prepares an empty JSON database file at `~/.doxa-cli/doxa_notes.json`.*
|
||||||
|
|
||||||
|
3. **Add to PATH (if necessary):**
|
||||||
|
If the `doxa-cli` command is not recognized, add the following line to your shell profile (e.g., `~/.bashrc` or `~/.zshrc`):
|
||||||
|
```bash
|
||||||
|
export PATH=$PATH:$HOME/.local/bin
|
||||||
|
```
|
||||||
|
Then restart your terminal or reload your profile using `source ~/.bashrc`.
|
||||||
|
|
||||||
|
### Post-Installation Cleanup
|
||||||
|
Once completed, you can safely remove the cloned repository directory:
|
||||||
|
```bash
|
||||||
|
rm -rf doxa-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
# **Operation and installation of doxa-cli on FreeBSD**
|
||||||
|
|
||||||
|
Building the application for FreeBSD does not always work correctly, if the instructions below do not help you, you can run it directly from the repository. And in .bashrc, make an alias for `doxa-cli`. You can run the application from the repository directly from the directory where it is cloned with the command `go run .`.
|
||||||
|
|
||||||
|
The FreeBSD system has its own specifics - it uses different default shells, kernel module management and interpreter paths than Linux. For a problem-free operation of the application, proceed as follows:
|
||||||
|
|
||||||
|
## **1\. Editing the installation script (install.sh)**
|
||||||
|
|
||||||
|
Before running the installation, it is necessary to check the header (shebang) of the install.sh script, because FreeBSD strictly requires the correct path to the command interpreter:
|
||||||
|
|
||||||
|
> * Open install.sh and make sure that the first line refers to the universal path, not hard-coded to the Linux bash:
|
||||||
|
```bash
|
||||||
|
\#\!/usr/bin/env bash
|
||||||
|
```
|
||||||
|
|
||||||
|
## **2\. Running the installation**
|
||||||
|
|
||||||
|
Standard cloning and running procedure:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://codeberg.org/Kankys/doxa-cli.git
|
||||||
|
cd doxa-cli
|
||||||
|
chmod \+x install.sh
|
||||||
|
./install.sh
|
||||||
|
rm \-rf doxa-cli
|
||||||
|
```
|
||||||
|
*The script will automatically compile the application and place the binary file in the \~/.local/bin/ directory.*
|
||||||
|
|
||||||
|
## **3\. Setting the PATH environment variable for FreeBSD (sh shell)**
|
||||||
|
|
||||||
|
FreeBSD uses /bin/sh as the default shell, which does not support the source command. To permanently and immediately enable the doxa-cli command, follow these steps:
|
||||||
|
|
||||||
|
> 1. **Add the path to the profile:**
|
||||||
|
```bash
|
||||||
|
echo 'export PATH="$PATH:$HOME/.local/bin"' \>\> \~/.profile
|
||||||
|
```
|
||||||
|
> 2. **Load the changes into the current terminal (using a dot, not the source command):**
|
||||||
|
```bash
|
||||||
|
. ~/.profile
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. STORAGE ARCHITECTURE & SCHEMA
|
||||||
|
|
||||||
|
The application runs **strictly locally**, keeping all your ideas entirely on your machine.
|
||||||
|
|
||||||
|
- **Storage Location:** `~/.doxa-cli/doxa_notes.json` (falls back to a local `doxa_notes.json` in the current working directory if the user home directory cannot be resolved).
|
||||||
|
- **Backups:** To backup your notes, simply copy `doxa_notes.json` to a backup location regularly.
|
||||||
|
|
||||||
|
### JSON Data Structure
|
||||||
|
Notes are written using a standardized JSON schema, ensuring perfect interoperability with the DOXA web client:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1722240000000,
|
||||||
|
"name": "My Note",
|
||||||
|
"content": "Note content with support for multi-line strings.",
|
||||||
|
"tags": ["personal", "work"],
|
||||||
|
"folder": "", // unused/empty
|
||||||
|
"created_at": "2026-07-29T14:30:00Z",
|
||||||
|
"modified_at": "2026-07-29T14:35:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. INTERACTIVE INTERFACE & STATES
|
||||||
|
|
||||||
|
The application automatically scales and adjusts to your active terminal window size:
|
||||||
|
|
||||||
|
### A. Notes List (Main View) (`StateViewNotes`)
|
||||||
|
The dashboard displays your list of notes.
|
||||||
|
- Scroll through notes using the arrow keys or `j`/`k`.
|
||||||
|
- The selected note expands to display its full content, ID, and tags.
|
||||||
|
- The bottom bar contains available key shortcuts and a colored status bar showing operation confirmations.
|
||||||
|
|
||||||
|
### B. Add / Edit Note Forms (`StateAddNote` / `StateEditNote`)
|
||||||
|
An interactive form with three inputs:
|
||||||
|
1. **Name:** (Required) Name of the note.
|
||||||
|
2. **Content:** (Required) Large multi-line text input.
|
||||||
|
3. **Tags:** (Optional) Comma-separated list of tags.
|
||||||
|
|
||||||
|
*Use the `Tab` or `Shift+Tab` keys to traverse between form inputs.*
|
||||||
|
|
||||||
|
### C. Search Field (`StateSearch`)
|
||||||
|
Allows you to perform a case-insensitive full-text filter on the notes database. Matches are evaluated across names, contents, and tags.
|
||||||
|
|
||||||
|
### D. Browse Tags Screen (`StateBrowse`)
|
||||||
|
A specialized menu that displays a unique collection of all tags currently in use.
|
||||||
|
- Press `t` to list all tags.
|
||||||
|
- Select an item and press `Enter` to filter the main note list by your choice.
|
||||||
|
|
||||||
|
### E. Delete Confirmation Dialogue (`StateConfirmDelete`)
|
||||||
|
A safety box showcasing the name and a snippet of the note selected for deletion, requiring a strict `y` (Yes) or `n` (No) keypress to prevent accidental loss of data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. KEYBOARD SHORTCUTS REFERENCE
|
||||||
|
|
||||||
|
### Global Commands
|
||||||
|
| Key | Action |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `ctrl + c` | Instantly exit the application from any screen |
|
||||||
|
| `q` / `esc` | Go back to the previous screen / Cancel current action / Quit application (from the main view) |
|
||||||
|
|
||||||
|
### Notes List (Main View) Commands (`StateViewNotes`)
|
||||||
|
| Key | Action |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `↑` / `k` | Move selection up |
|
||||||
|
| `↓` / `j` | Move selection down |
|
||||||
|
| `1` | Create a new note |
|
||||||
|
| `2` / `Enter` | Edit the selected note |
|
||||||
|
| `3` | Export the selected note to a raw text file |
|
||||||
|
| `4` | Delete the selected note (requires confirmation) |
|
||||||
|
| `/` | Start searching |
|
||||||
|
| `b` | Browse by Tag |
|
||||||
|
| `l` | Toggle user interface language (CS <=> EN) |
|
||||||
|
|
||||||
|
### Form Inputs (Add / Edit Note)
|
||||||
|
| Key | Action |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `Tab` | Move cursor focus to the next field (Name -> Content -> Tags -> Name...) |
|
||||||
|
| `Shift + Tab` | Move cursor focus to the previous field |
|
||||||
|
| `Enter` | **Inside Content field:** Inserts a new line (carriage return).<br>**Inside any other field:** Saves/submits the form. |
|
||||||
|
|
||||||
|
### Browse Mode (`StateBrowse`)
|
||||||
|
| Key | Action |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `t` | Display **Tags** list |
|
||||||
|
| `↑` / `k` / `↓` / `j` | Scroll through the items |
|
||||||
|
| `Enter` | Filter main note list by the selected tag |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. EXPORT SYSTEM DETAILS
|
||||||
|
|
||||||
|
Pressing key `3` on a note in the main list outputs its contents to a plain `.txt` file:
|
||||||
|
|
||||||
|
- **Export Directory:** `~/doxa-cli-txt/` (automatically created).
|
||||||
|
- **Output Filename:** `<Note_Name>_<ID>.txt` (spaces are automatically escaped into underscores).
|
||||||
|
- **Exported File Schema:**
|
||||||
|
*(Note: The exported header fields are written using Czech labels as hardcoded in the Go export engine).*
|
||||||
|
```text
|
||||||
|
Název: <Note Name>
|
||||||
|
Tagy: <tag1, tag2, ...>
|
||||||
|
Vytvořeno: YYYY-MM-DD HH:MM
|
||||||
|
|
||||||
|
<Full Text Note Content>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. UNINSTALLATION
|
||||||
|
|
||||||
|
To fully uninstall DOXA-CLI from your machine:
|
||||||
|
|
||||||
|
1. **Remove the executable binary:**
|
||||||
|
```bash
|
||||||
|
rm ~/.local/bin/doxa-cli
|
||||||
|
```
|
||||||
|
2. **Remove the configuration and local database (Warning: This will delete all of your notes!):**
|
||||||
|
```bash
|
||||||
|
rm -rf ~/.doxa-cli
|
||||||
|
```
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
# 🦅 NÁVOD K POUŽITÍ (DOXA-CLI)
|
||||||
|
|
||||||
|
**DOXA-CLI** je minimalistický, lokálně orientovaný nástroj pro správu poznámek z příkazové řádky. Slouží jako přímý ekvivalent k webové verzi [DOXA Notes](https://doxa-notes.org/), se kterou sdílí filozofii soustředění a identický datový formát JSON. Aplikace je napsaná v jazyce **Go** a využívá moderní terminálové rozhraní (**TUI**) postavené na knihovnách Bubble Tea a Lip Gloss od Charm.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/go-1.22+-00ADD8?style=flat-square&logo=go&logoColor=white" alt="Go 1.22+">
|
||||||
|
<img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT license">
|
||||||
|
<img src="https://img.shields.io/badge/tested-Linux-brightgreen?style=flat-square" alt="Tested on Linux">
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/freebsd-AB2B28?style=for-the-badge&logo=freebsd&logoColor=white" alt="freeBSD">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OBSAH
|
||||||
|
1. [Instalace a spuštění](#1-instalace-a-spuštění)
|
||||||
|
2. [Chování a struktura úložiště](#2-chování-a-struktura-úložiště)
|
||||||
|
3. [Uživatelské rozhraní a stavy](#3-uživatelské-rozhraní-a-stavy)
|
||||||
|
4. [Klávesové zkratky a ovládání](#4-klávesové-zkratky-a-ovládání)
|
||||||
|
5. [Detaily o exportu](#5-detaily-o-exportu)
|
||||||
|
6. [Odinstalace](#6-odinstalace)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. INSTALACE A SPUŠTĚNÍ
|
||||||
|
|
||||||
|
### Systémové předpoklady
|
||||||
|
Před instalací je nutné mít na systému nainstalované vývojové prostředí **Go** (verze **1.20** nebo vyšší).
|
||||||
|
|
||||||
|
### Postup instalace (Linux / macOS)
|
||||||
|
1. **Stažení repozitáře:**
|
||||||
|
```bash
|
||||||
|
git clone https://codeberg.org/Kankys/doxa-cli.git
|
||||||
|
cd doxa-cli
|
||||||
|
```
|
||||||
|
2. **Spuštění instalačního skriptu:**
|
||||||
|
```bash
|
||||||
|
chmod +x install.sh
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
*Instalační skript automaticky stáhne závislosti Go, zkompiluje aplikaci, přesune binární soubor `doxa-cli` do adresáře `~/.local/bin/` a vytvoří výchozí prázdnou databázi v `~/.doxa-cli/doxa_notes.json`.*
|
||||||
|
|
||||||
|
3. **Přidání do PATH (pokud je potřeba):**
|
||||||
|
Pokud po instalaci nelze příkaz `doxa-cli` spustit, přidejte do svého profilu (např. `~/.bashrc` nebo `~/.zshrc`) následující řádek:
|
||||||
|
```bash
|
||||||
|
export PATH=$PATH:$HOME/.local/bin
|
||||||
|
```
|
||||||
|
Následně restartujte terminál nebo načtěte změny pomocí `source ~/.bashrc`.
|
||||||
|
|
||||||
|
### Úklid po instalaci
|
||||||
|
Jakmile skript doběhne, složku s klonovaným repozitářem můžete bezpečně odstranit:
|
||||||
|
```bash
|
||||||
|
rm -rf doxa-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# **Provoz a instalace doxa-cli na FreeBSD**
|
||||||
|
|
||||||
|
Build aplikace pro FreeBSD nefunguje vždy korektně, pokud vám nepomůže návod níže je možné ji spouštět přímo z repozitáře. A v .bashrc si udělat alias pro `doxa-cli`. Alikaci z repozitáře spustíte přímo z adresáře, kde je klonován příkazem `go run .`.
|
||||||
|
|
||||||
|
Systém FreeBSD má svá specifika – používá odlišné výchozí shelly, správu modulů jádra a cesty k interpretům než Linux. Pro bezproblémový chod aplikace postupuj následovně:
|
||||||
|
|
||||||
|
## **1\. Úprava instalačního skriptu (install.sh)**
|
||||||
|
|
||||||
|
Před spuštěním instalace je nutné zkontrolovat hlavičku (shebang) skriptu install.sh, protože FreeBSD striktně vyžaduje správnou cestu k interpretu příkazů:
|
||||||
|
|
||||||
|
> * Otevři install.sh a ujisti se, že první řádek odkazuje na univerzální cestu, nikoli natvrdo na linuxový bash:
|
||||||
|
```bash
|
||||||
|
\#\!/usr/bin/env bash
|
||||||
|
```
|
||||||
|
|
||||||
|
## **2\. Spuštění instalace**
|
||||||
|
|
||||||
|
Standardní postup klonování a spuštění:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://codeberg.org/Kankys/doxa-cli.git
|
||||||
|
cd doxa-cli
|
||||||
|
chmod \+x install.sh
|
||||||
|
./install.sh
|
||||||
|
rm \-rf doxa-cli
|
||||||
|
```
|
||||||
|
*Skript automaticky zkompiluje aplikaci a umístí binární soubor do adresáře \~/.local/bin/.*
|
||||||
|
|
||||||
|
## **3\. Nastavení proměnné prostředí PATH pro FreeBSD (shell sh)**
|
||||||
|
|
||||||
|
FreeBSD používá ve výchozím stavu jako standardní shell /bin/sh, který nepodporuje příkaz source. Pro trvalé i okamžité zprístupnění příkazu doxa-cli proveď tyto kroky:
|
||||||
|
|
||||||
|
> 1. **Přidej cestu do profilu:**
|
||||||
|
```bash
|
||||||
|
echo 'export PATH="$PATH:$HOME/.local/bin"' \>\> \~/.profile
|
||||||
|
```
|
||||||
|
> 2. **Načti změny v aktuálním terminálu (pomocí tečky, nikoliv příkazu source):**
|
||||||
|
```bash
|
||||||
|
. ~/.profile
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. CHOVÁNÍ A STRUKTURA ÚLOŽIŠTĚ
|
||||||
|
|
||||||
|
Aplikace funguje **zcela offline** a data ukládá na váš lokální disk.
|
||||||
|
|
||||||
|
- **Umístění databáze:** `~/.doxa-cli/doxa_notes.json` (na systémech, kde nelze detekovat domovský adresář, se vytvoří `doxa_notes.json` v aktuálním pracovním adresáři).
|
||||||
|
- **Záloha:** Pro zálohu stačí zkopírovat soubor `doxa_notes.json` na bezpečné místo.
|
||||||
|
|
||||||
|
### Formát dat (JSON)
|
||||||
|
Každá poznámka v JSON poli obsahuje následující strukturu kompatibilní s webovou verzí DOXA Notes:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1722240000000,
|
||||||
|
"name": "Moje Poznámka",
|
||||||
|
"content": "Obsah poznámky s podporou více řádků.",
|
||||||
|
"tags": ["osobní", "práce"],
|
||||||
|
"folder": "", // nepoužito/prázdné
|
||||||
|
"created_at": "2026-07-29T14:30:00Z",
|
||||||
|
"modified_at": "2026-07-29T14:35:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. UŽIVATELSKÉ ROZHRANÍ A STAVY
|
||||||
|
|
||||||
|
Aplikace se dynamicky přizpůsobuje velikosti terminálu a nabízí několik stavů:
|
||||||
|
|
||||||
|
### A. Hlavní seznam poznámek (`StateViewNotes`)
|
||||||
|
Zde vidíte přehled všech uložených poznámek.
|
||||||
|
- Pomocí šipek vybíráte aktivní poznámku.
|
||||||
|
- U vybrané poznámky se zobrazí její kompletní obsah, ID a přiřazené tagy.
|
||||||
|
- Ve spodní části obrazovky se nachází menu rychlých akcí a stavový řádek (ukazuje potvrzení o uložení, úpravě či exportu).
|
||||||
|
|
||||||
|
### B. Formulář pro Přidání / Úpravu poznámky (`StateAddNote` / `StateEditNote`)
|
||||||
|
Interaktivní rozhraní se 3 vstupními poli:
|
||||||
|
1. **Název:** (Povinné) Název vaší poznámky.
|
||||||
|
2. **Obsah:** (Povinné) Víceřádkové pole pro text poznámky.
|
||||||
|
3. **Tagy:** (Nepovinné) Štítky oddělené čárkou.
|
||||||
|
|
||||||
|
*Pohyb mezi poli se provádí klávesou `Tab` (dopředu) nebo `Shift+Tab` (dozadu).*
|
||||||
|
|
||||||
|
### C. Vyhledávání (`StateSearch`)
|
||||||
|
Umožňuje fulltextově filtrovat seznam poznámek. Vyhledává se shoda v názvu, obsahu i v jednotlivých tazích (velikost písmen se ignoruje).
|
||||||
|
|
||||||
|
### D. Procházení Tagů (`StateBrowse`)
|
||||||
|
Umožňuje rychle vyfiltrovat poznámky na základě existujících tagů.
|
||||||
|
- Klávesou `t` zobrazíte seznam všech existujících tagů.
|
||||||
|
- Výběrem položky a stisknutím `Enter` aplikujete filtr na hlavní seznam poznámek.
|
||||||
|
|
||||||
|
### E. Potvrzení smazání (`StateConfirmDelete`)
|
||||||
|
Bezpečnostní mezikrok, který zobrazí detail odstraňované poznámky a vyžaduje explicitní potvrzení stisknutím klávesy `y` (Ano) nebo `n` (Ne).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. KLÁVESOVÉ ZKRATKY A OVLÁDÁNÍ
|
||||||
|
|
||||||
|
### Globální ovládání
|
||||||
|
| Klávesa | Akce |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `ctrl + c` | Okamžité ukončení aplikace z jakéhokoliv stavu |
|
||||||
|
| `q` / `esc` | Návrat do předchozího stavu / Zrušení akce / Ukončení aplikace (z hlavního seznamu) |
|
||||||
|
|
||||||
|
### Ovládání v Hlavním seznamu poznámek (`StateViewNotes`)
|
||||||
|
| Klávesa | Akce |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `↑` / `k` | Posunout výběr nahoru |
|
||||||
|
| `↓` / `j` | Posunout výběr dolů |
|
||||||
|
| `1` | Přidat novou poznámku (otevře formulář) |
|
||||||
|
| `2` / `Enter` | Upravit vybranou poznámku (otevře formulář) |
|
||||||
|
| `3` | Exportovat vybranou poznámku |
|
||||||
|
| `4` | Smazat vybranou poznámku (otevře potvrzovací dialog) |
|
||||||
|
| `/` | Otevřít vyhledávání |
|
||||||
|
| `l` | Přepnout jazyk rozhraní (CS <=> EN) |
|
||||||
|
|
||||||
|
### Ovládání ve formulářích (Přidání / Úprava)
|
||||||
|
| Klávesa | Akce |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `Tab` | Přechod na další pole (Název -> Obsah -> Tagy -> Název...) |
|
||||||
|
| `Shift + Tab` | Přechod na předchozí pole |
|
||||||
|
| `Enter` | **Pokud jste v poli Obsah:** Vloží nový řádek.<br>**Pokud jste v jakémkoliv jiném poli:** Uloží poznámku. |
|
||||||
|
|
||||||
|
### Ovládání v režimu Procházení (`StateBrowse`)
|
||||||
|
| Klávesa | Akce |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `t` | Přepnout na seznam **Tagů** |
|
||||||
|
| `↑` / `k` / `↓` / `j` | Pohyb v seznamu |
|
||||||
|
| `Enter` | Filtrovat hlavní seznam podle vybrané tagu |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. DETAILY O EXPORTU
|
||||||
|
|
||||||
|
Při stisknutí klávesy `3` na vybrané poznámce v hlavním seznamu dojde k exportu poznámky do čistého textového souboru.
|
||||||
|
|
||||||
|
- **Adresář exportu:** `~/doxa-cli-txt/` (bude automaticky vytvořen v domovském adresáři).
|
||||||
|
- **Název exportovaného souboru:** `<Nazev_Poznamky>_<ID>.txt` (mezery v názvu jsou nahrazeny podtržítky).
|
||||||
|
- **Vnitřní struktura exportovaného souboru:**
|
||||||
|
*(Poznámka: Hlavička exportu je generována s českými popisky bez ohledu na zvolený jazyk rozhraní).*
|
||||||
|
```text
|
||||||
|
Název: <Název poznámky>
|
||||||
|
Tagy: <tag1, tag2, ...>
|
||||||
|
Vytvořeno: RRRR-MM-DD HH:MM
|
||||||
|
|
||||||
|
<Kompletní textový obsah poznámky>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. ODINSTALACE
|
||||||
|
|
||||||
|
Pokud se rozhodnete aplikaci ze systému odstranit, postupujte takto:
|
||||||
|
|
||||||
|
1. **Smazání spustitelného souboru:**
|
||||||
|
```bash
|
||||||
|
rm ~/.local/bin/doxa-cli
|
||||||
|
```
|
||||||
|
2. **Smazání databáze a konfigurace (Upozornění: Smaže všechny vaše poznámky!):**
|
||||||
|
```bash
|
||||||
|
rm -rf ~/.doxa-cli
|
||||||
|
```
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
DOXA-CLI
|
||||||
|
|
||||||
|
DOXA-CLI is a command-line tool for managing your notes. It allows you to add, view, edit, delete, export notes, as well as filter and search them. The application is written in Go and offers a modern Terminal User Interface (TUI).
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/go-1.22+-00ADD8?style=flat-square&logo=go&logoColor=white" alt="Go 1.22+">
|
||||||
|
<img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT license">
|
||||||
|
<img src="https://img.shields.io/badge/tested-Linux-brightgreen?style=flat-square" alt="Tested on Linux">
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/freebsd-AB2B28?style=for-the-badge&logo=freebsd&logoColor=white" alt="freeBSD">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before installing DOXA-CLI, it is assumed that you have **Go installed on your system. The recommended version is 1.20 or higher.
|
||||||
|
You can download Go from the official website: [https://go.dev/doc/install](https://go.dev/doc/install)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install DOXA-CLI on your system, follow these steps:
|
||||||
|
|
||||||
|
1. **Clone the repository**:
|
||||||
|
Get the latest version of the code from Git:
|
||||||
|
``` bash
|
||||||
|
git clone https://codeberg.org/Kankys/doxa-cli.git
|
||||||
|
cd doxa-cli # Go to the project directory
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Run the installation script**:
|
||||||
|
``` bash
|
||||||
|
chmod +x install.sh
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
The installation script will do the following:
|
||||||
|
* Check if you have Go installed.
|
||||||
|
* Download all necessary Go modules and project dependencies.
|
||||||
|
* Compile the application for your operating system.
|
||||||
|
* Install the `doxa-cli` executable file in the `$HOME/.local/bin/` directory.
|
||||||
|
* Creates the `~/doxa-cli` configuration directory and the `doxa_notes.json` file if they do not exist.
|
||||||
|
|
||||||
|
3. **Add to PATH (if necessary)**:
|
||||||
|
After successful installation, you should be able to run `doxa-cli` from another location in the terminal. If the `doxa-cli` command is not recognized, it is assumed that the `$HOME/.local/bin` directory is not in your `PATH` system variable.
|
||||||
|
|
||||||
|
You can fix this by adding the following line to your shell profile (e.g. `~/.bashrc` for Bash, `~/.zshrc` for Zsh, or `~/.profile` for other systems):
|
||||||
|
``` bash
|
||||||
|
export PATH=$PATH:$HOME/.local/bin
|
||||||
|
```
|
||||||
|
After editing the profile file, **restart the terminal** or load the profile (e.g. `source ~/.bashrc`).
|
||||||
|
|
||||||
|
## Post-installation cleanup
|
||||||
|
|
||||||
|
Once the installation is successfully completed, the source files in the project directory are no longer needed. You can delete the entire folder with the cloned repository:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
rm -rf doxa-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
After successful installation, you can run the application from another directory by typing:
|
||||||
|
``` bash
|
||||||
|
doxa-cli
|
||||||
|
```
|
||||||
|
For detailed control in the TUI interface, follow the help displayed directly in the application.
|
||||||
|
|
||||||
|
## Backup
|
||||||
|
|
||||||
|
To backup your notes, you must save the `doxa_notes.json` file from the `~/.doxa-cli` configuration directory anywhere in your backup directory.
|
||||||
|
|
||||||
|
**Note**: The `doxa_notes.json` file contains all your notes and it is important to save it regularly.
|
||||||
|
|
||||||
|
## Uninstall
|
||||||
|
|
||||||
|
To uninstall DOXA-CLI, follow these steps:
|
||||||
|
|
||||||
|
1. **Remove the executable**:
|
||||||
|
Remove the binary from your PATH:
|
||||||
|
``` bash
|
||||||
|
rm ~/.local/bin/doxa-cli
|
||||||
|
```
|
||||||
|
*(If you installed the binary in a different directory, use its path.)*
|
||||||
|
|
||||||
|
2. **Remove the configuration directory (optional)**:
|
||||||
|
If you want to remove all application data and configuration, the configuration directory:
|
||||||
|
``` bash
|
||||||
|
rm -rf ~/.doxa-cli
|
||||||
|
```
|
||||||
|
**WARNING**: This step will delete ALL your notes. If you want to keep your data, skip this step.
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
DOXA-CLI
|
||||||
|
|
||||||
|
DOXA-CLI je nástroj pro správu vašich poznámek z příkazové řádky. Umožňuje přidávat, zobrazovat, upravovat, mazat, exportovat poznámky a také je filtrovat a vyhledávat. Aplikace je napsána v Go a nabízí moderní terminálové uživatelské rozhraní (TUI).
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/go-1.22+-00ADD8?style=flat-square&logo=go&logoColor=white" alt="Go 1.22+">
|
||||||
|
<img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT license">
|
||||||
|
<img src="https://img.shields.io/badge/tested-Linux-brightgreen?style=flat-square" alt="Tested on Linux">
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/freebsd-AB2B28?style=for-the-badge&logo=freebsd&logoColor=white" alt="freeBSD">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Předpoklady
|
||||||
|
|
||||||
|
Před instalací DOXA-CLI se ujistěte, že máte na svém systému nainstalovaný **Go**. Doporučená verze je 1.20 nebo vyšší.
|
||||||
|
Stáhnout Go můžete z oficiálního webu: [https://go.dev/doc/install](https://go.dev/doc/install)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Instalace
|
||||||
|
|
||||||
|
Pro instalaci DOXA-CLI na váš systém postupujte podle následujících kroků:
|
||||||
|
|
||||||
|
1. **Naklonujte repozitář**:
|
||||||
|
Získejte nejnovější verzi kódu z Gitea:
|
||||||
|
```bash
|
||||||
|
git clone https://codeberg.org/Kankys/doxa-cli.git
|
||||||
|
cd doxa-cli # Přejděte do adresáře projektu
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Spusťte instalační skript**:
|
||||||
|
```bash
|
||||||
|
chmod +x install.sh
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
Instalační skript provede následující:
|
||||||
|
* Zkontroluje, zda máte nainstalované Go.
|
||||||
|
* Stáhne všechny potřebné Go moduly a závislosti projektu.
|
||||||
|
* Zkompiluje aplikaci pro váš operační systém.
|
||||||
|
* Nainstaluje spustitelný soubor `doxa-cli` do adresáře `$HOME/.local/bin/`.
|
||||||
|
* Vytvoří konfigurační adresář `~/.doxa-cli` a soubor `doxa_notes.json`, pokud neexistují.
|
||||||
|
|
||||||
|
3. **Přidání do PATH (pokud je nutné)**:
|
||||||
|
Po úspěšné instalaci byste měli být schopni spustit `doxa-cli` z jakéhokoli místa v terminálu. Pokud příkaz `doxa-cli` není rozpoznán, je pravděpodobné, že adresář `$HOME/.local/bin` není ve vaší systémové proměnné `PATH`.
|
||||||
|
|
||||||
|
Můžete to opravit přidáním následujícího řádku do vašeho shell profilu (např. `~/.bashrc` pro Bash, `~/.zshrc` pro Zsh, nebo `~/.profile` pro jiné systémy):
|
||||||
|
```bash
|
||||||
|
export PATH=$PATH:$HOME/.local/bin
|
||||||
|
```
|
||||||
|
Po úpravě souboru profilu **restartujte terminál** nebo načtěte profil (např. `source ~/.bashrc`).
|
||||||
|
|
||||||
|
## Úklid po instalaci
|
||||||
|
|
||||||
|
Jakmile je instalace úspěšně dokončena, zdrojové soubory v adresáři projektu již nejsou potřeba. Celou složku s klonovaným repozitářem můžete smazat:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -rf doxa-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
## Použití
|
||||||
|
|
||||||
|
Po úspěšné instalaci můžete aplikaci spustit z jakéhokoliv adresáře zadáním:
|
||||||
|
```bash
|
||||||
|
doxa-cli
|
||||||
|
```
|
||||||
|
Pro detailní ovládání v TUI rozhraní se řiďte nápovědou zobrazovanou přímo v aplikaci.
|
||||||
|
|
||||||
|
## Záloha
|
||||||
|
|
||||||
|
Pro zálohu vašich poznámek je nutné si uložit soubor `doxa_notes.json` z konfiguračního adresáře `~/.doxa-cli` kamkoliv do vašeho záložního adresáře.
|
||||||
|
|
||||||
|
**Poznámka**: Soubor `doxa_notes.json` obsahuje všechny vaše poznámky a je důležité si jej pravidelně uložit.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Odinstalace
|
||||||
|
|
||||||
|
Pro odinstalaci DOXA-CLI postupujte takto:
|
||||||
|
|
||||||
|
1. **Odstraňte spustitelný soubor**:
|
||||||
|
Odstraňte binárku aplikace z vašeho PATH:
|
||||||
|
```bash
|
||||||
|
rm ~/.local/bin/doxa-cli
|
||||||
|
```
|
||||||
|
*(Pokud jste binárku nainstalovali do jiného adresáře, použijte jeho cestu.)*
|
||||||
|
|
||||||
|
2. **Odstraňte konfigurační adresář (volitelné)**:
|
||||||
|
Pokud chcete odstranit i veškerá data a konfiguraci aplikace, odstraňte konfigurační adresář:
|
||||||
|
```bash
|
||||||
|
rm -rf ~/.doxa-cli
|
||||||
|
```
|
||||||
|
**UPOZORNĚNÍ**: Tento krok smaže VŠECHNY vaše poznámky. Pokud si chcete data ponechat, tento krok vynechejte.
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
### 🇬🇧 If you want to support this project or my future ones, I would be very grateful. All support goes towards my education or directly to the infrastructure as payments for VPS or payments for development-related services.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 🇨🇿 Pokud chcete podpořit tento projekt, nebo mé dálší budu vám velmi vděčný. Veškerá podpora jde na má vzdělání nebo přímo do ifrastruktury jako platby za VPS či platby za služby spojené s vývojem.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
module doxa-cli
|
||||||
|
|
||||||
|
go 1.26.5
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
|
github.com/charmbracelet/bubbles v1.0.0 // indirect
|
||||||
|
github.com/charmbracelet/bubbletea v1.3.10 // indirect
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
||||||
|
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
||||||
|
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
|
||||||
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||||
|
github.com/clipperhouse/displaywidth v0.9.0 // indirect
|
||||||
|
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
|
||||||
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||||
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||||
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
|
github.com/muesli/termenv v0.16.0 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
|
golang.org/x/text v0.3.8 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
|
github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
|
||||||
|
github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
|
||||||
|
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||||
|
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||||
|
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||||
|
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
|
||||||
|
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||||
|
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||||
|
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
|
||||||
|
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
||||||
|
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
||||||
|
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||||
|
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
|
||||||
|
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
|
||||||
|
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||||
|
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||||
|
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||||
|
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||||
|
github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA=
|
||||||
|
github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA=
|
||||||
|
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||||
|
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||||
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||||
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||||
|
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||||
|
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||||
|
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||||
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||||
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
|
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||||
|
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||||
|
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# DOXA-CLI Installation Script
|
||||||
|
|
||||||
|
# --- Nastavení ---
|
||||||
|
|
||||||
|
APP_NAME="doxa-cli"
|
||||||
|
INSTALL_DIR="$HOME/.local/bin"
|
||||||
|
CONFIG_DIR="$HOME/.doxa-cli"
|
||||||
|
CONFIG_FILE="doxa_notes.json"
|
||||||
|
|
||||||
|
# --- Funkce ---
|
||||||
|
|
||||||
|
error_exit() {
|
||||||
|
echo -e "\033[1;31mERROR: $1\033[0m" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
success_message() {
|
||||||
|
echo -e "\033[1;32mSUCCESS: $1\033[0m"
|
||||||
|
}
|
||||||
|
|
||||||
|
info_message() {
|
||||||
|
echo -e "\033[1;34mINFO: $1\033[0m"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Hlavní část skriptu ---
|
||||||
|
|
||||||
|
info_message "Kontrola Go instalace..."
|
||||||
|
if ! command -v go &> /dev/null
|
||||||
|
then
|
||||||
|
error_exit "Go není nainstalováno. Prosím, nainstalujte Go před spuštěním tohoto skriptu."
|
||||||
|
fi
|
||||||
|
success_message "Go je nainstalováno."
|
||||||
|
|
||||||
|
info_message "Stažení závislostí projektu..."
|
||||||
|
if ! go mod download
|
||||||
|
then
|
||||||
|
error_exit "Nepodařilo se stáhnout závislosti Go. Zkontrolujte GO moduly a připojení k internetu."
|
||||||
|
fi
|
||||||
|
success_message "Závislosti projektu úspěšně staženy."
|
||||||
|
|
||||||
|
info_message "Kompilace aplikace..."
|
||||||
|
APP_BINARY="$APP_NAME"
|
||||||
|
if ! go build -o "$APP_BINARY" .
|
||||||
|
then
|
||||||
|
error_exit "Nepodařilo se zkompilovat aplikaci. Zkontrolujte chyby v kódu."
|
||||||
|
fi
|
||||||
|
success_message "Aplikace úspěšně zkompilována do './$APP_BINARY'."
|
||||||
|
|
||||||
|
info_message "Instalace aplikace..."
|
||||||
|
|
||||||
|
if ! mkdir -p "$INSTALL_DIR"
|
||||||
|
then
|
||||||
|
error_exit "Nepodařilo se vytvořit cílový adresář '$INSTALL_DIR'. Zkontrolujte oprávnění."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! cp "$APP_BINARY" "$INSTALL_DIR/"
|
||||||
|
then
|
||||||
|
error_exit "Nepodařilo se zkopírovat binárku do '$INSTALL_DIR'. Zkontrolujte oprávnění."
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x "$INSTALL_DIR/$APP_NAME"
|
||||||
|
success_message "Aplikace '$APP_NAME' úspěšně nainstalována do '$INSTALL_DIR'."
|
||||||
|
|
||||||
|
info_message "Nastavení konfiguračního adresáře..."
|
||||||
|
|
||||||
|
if ! mkdir -p "$CONFIG_DIR"
|
||||||
|
then
|
||||||
|
error_exit "Nepodařilo se vytvořit konfigurační adresář '$CONFIG_DIR'. Zkontrolujte oprávnění."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$CONFIG_DIR/$CONFIG_FILE" ]
|
||||||
|
then
|
||||||
|
echo "[]" > "$CONFIG_DIR/$CONFIG_FILE"
|
||||||
|
if [ $? -ne 0 ]
|
||||||
|
then
|
||||||
|
error_exit "Nepodařilo se vytvořit konfigurační soubor '$CONFIG_DIR/$CONFIG_FILE'."
|
||||||
|
fi
|
||||||
|
success_message "Konfigurační soubor '$CONFIG_DIR/$CONFIG_FILE' vytvořen."
|
||||||
|
else
|
||||||
|
info_message "Konfigurační soubor '$CONFIG_DIR/$CONFIG_FILE' již existuje."
|
||||||
|
fi
|
||||||
|
|
||||||
|
info_message "Instalace dokončena."
|
||||||
|
success_message "Nyní můžete spustit aplikaci '$APP_NAME' z jakéhokoli místa v terminálu."
|
||||||
|
echo ""
|
||||||
|
echo -e "\033[1;33mPRO TIP:\nPokud \`$APP_NAME\` nefunguje z jakéhokoli místa, možná budete muset přidat '$INSTALL_DIR' do vašeho PATH.\nČasto se to dělá přidáním následujícího řádku do vašich shell profilů (~/.bashrc, ~/.zshrc, ~/.profile):\n\nexport PATH=\$PATH:$INSTALL_DIR\n\nPoté restartujte terminál nebo načtěte profil (např. \`source ~/.bashrc\`).\033[0m"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Vyčištění
|
||||||
|
rm "$APP_BINARY"
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Inicializace Bubble Tea modelu pomocí NewModel() z tui.go
|
||||||
|
m := NewModel()
|
||||||
|
|
||||||
|
// Spuštění Bubble Tea programu
|
||||||
|
p := tea.NewProgram(m)
|
||||||
|
if _, err := p.Run(); err != nil {
|
||||||
|
fmt.Printf("Chyba při spuštění programu: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestTruncate(t *testing.T) {
|
||||||
|
result := truncate("Ahoj světe", 5)
|
||||||
|
expected := "Ahoj..."
|
||||||
|
|
||||||
|
if result != expected {
|
||||||
|
t.Errorf("Očekáváno %q, ale dostali jsme %q", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Note struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Folder string `json:"folder"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
ModifiedAt time.Time `json:"modified_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStoragePath() string {
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return "doxa_notes.json" // fallback do aktuálního adresáře
|
||||||
|
}
|
||||||
|
configDir := filepath.Join(home, ".doxa-cli")
|
||||||
|
|
||||||
|
// Vytvoření složky, pokud neexistuje
|
||||||
|
if _, err := os.Stat(configDir); os.IsNotExist(err) {
|
||||||
|
os.MkdirAll(configDir, 0755)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(configDir, "doxa_notes.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadNotes() []Note {
|
||||||
|
path := getStoragePath()
|
||||||
|
file, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return []Note{}
|
||||||
|
}
|
||||||
|
var notes []Note
|
||||||
|
json.Unmarshal(file, ¬es)
|
||||||
|
return notes
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveNotes(notes []Note) {
|
||||||
|
path := getStoragePath()
|
||||||
|
data, err := json.MarshalIndent(notes, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Chyba při ukládání dat:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
os.WriteFile(path, data, 0644)
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 314 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,110 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type Translation struct {
|
||||||
|
Title string
|
||||||
|
MenuAdd string
|
||||||
|
MenuEdit string
|
||||||
|
MenuExport string
|
||||||
|
MenuDelete string
|
||||||
|
MenuQuit string
|
||||||
|
MenuLang string
|
||||||
|
MenuSearch string
|
||||||
|
MenuBrowse string
|
||||||
|
ListTitle string
|
||||||
|
NoNotes string
|
||||||
|
AddTitle string
|
||||||
|
EditTitle string
|
||||||
|
LabelName string
|
||||||
|
LabelContent string
|
||||||
|
LabelTags string
|
||||||
|
PlaceholderName string
|
||||||
|
PlaceholderContent string
|
||||||
|
PlaceholderTags string
|
||||||
|
HelpForm string
|
||||||
|
HelpList string
|
||||||
|
StatusSaved string
|
||||||
|
StatusEdited string
|
||||||
|
StatusDeleted string
|
||||||
|
StatusExported string
|
||||||
|
ErrRequired string
|
||||||
|
SearchTitle string
|
||||||
|
BrowseTags string
|
||||||
|
AllNotes string
|
||||||
|
ConfirmDelete string
|
||||||
|
ConfirmYes string
|
||||||
|
ConfirmNo string
|
||||||
|
HelpModal string
|
||||||
|
}
|
||||||
|
|
||||||
|
var Translations = map[string]Translation{
|
||||||
|
"cs": {
|
||||||
|
Title: "DOXA-CLI 🦅",
|
||||||
|
MenuAdd: "1: Přidat",
|
||||||
|
MenuEdit: "2: Upravit",
|
||||||
|
MenuExport: "3: Export",
|
||||||
|
MenuDelete: "4: Smazat",
|
||||||
|
MenuQuit: "q: Konec",
|
||||||
|
MenuLang: "l: EN",
|
||||||
|
MenuSearch: "/: Hledat",
|
||||||
|
MenuBrowse: "b: Tagy",
|
||||||
|
ListTitle: "--- SEZNAM POZNÁMEK ---",
|
||||||
|
NoNotes: " (Prázdno...)",
|
||||||
|
AddTitle: "PŘIDAT POZNÁMKU",
|
||||||
|
EditTitle: "UPRAVIT POZNÁMKU",
|
||||||
|
LabelName: "Název:",
|
||||||
|
LabelContent: "Obsah:",
|
||||||
|
LabelTags: "Tagy:",
|
||||||
|
PlaceholderName: "Název (povinné)",
|
||||||
|
PlaceholderContent: "Enter: Nový řádek | Tab: Další pole",
|
||||||
|
PlaceholderTags: "Tagy (nepovinné, oddělené čárkou)",
|
||||||
|
HelpForm: "[Tab] Další pole | [Enter] Uložit (mimo obsah) | [q] Zrušit",
|
||||||
|
HelpList: "↑/↓: Vybrat | Enter: Upravit | /: Hledat | b: Procházet",
|
||||||
|
StatusSaved: "Poznámka uložena.",
|
||||||
|
StatusEdited: "Změny uloženy.",
|
||||||
|
StatusDeleted: "Poznámka smazána.",
|
||||||
|
StatusExported: "Poznámka vyexportována.",
|
||||||
|
ErrRequired: "Chyba: Název a obsah jsou povinné!",
|
||||||
|
SearchTitle: "HLEDAT: ",
|
||||||
|
BrowseTags: "TAGY",
|
||||||
|
AllNotes: "Všechny poznámky",
|
||||||
|
ConfirmDelete: "Opravdu chcete smazat tuto poznámku?",
|
||||||
|
ConfirmYes: "y: Ano",
|
||||||
|
ConfirmNo: "n: Ne / Esc",
|
||||||
|
HelpModal: "q: Zavřít | 2: Editovat | ↑/↓: Posun",
|
||||||
|
},
|
||||||
|
"en": {
|
||||||
|
Title: "DOXA-CLI 🦅",
|
||||||
|
MenuAdd: "1: Add",
|
||||||
|
MenuEdit: "2: Edit",
|
||||||
|
MenuExport: "3: Export",
|
||||||
|
MenuDelete: "4: Delete",
|
||||||
|
MenuQuit: "q: Quit",
|
||||||
|
MenuLang: "l: CS",
|
||||||
|
MenuSearch: "/: Search",
|
||||||
|
MenuBrowse: "b: Tags",
|
||||||
|
ListTitle: "--- NOTES LIST ---",
|
||||||
|
NoNotes: " (Empty...)",
|
||||||
|
AddTitle: "ADD NOTE",
|
||||||
|
EditTitle: "EDIT NOTE",
|
||||||
|
LabelName: "Name:",
|
||||||
|
LabelContent: "Content:",
|
||||||
|
LabelTags: "Tags:",
|
||||||
|
PlaceholderName: "Name (required)",
|
||||||
|
PlaceholderContent: "Enter: New line | Tab: Next field",
|
||||||
|
PlaceholderTags: "Tags (optional, comma separated)",
|
||||||
|
HelpForm: "[Tab] Next field | [Enter] Save (outside content) | [q] Cancel",
|
||||||
|
HelpList: "↑/↓: Select | Enter: Edit | /: Search | b: Browse",
|
||||||
|
StatusSaved: "Note saved.",
|
||||||
|
StatusEdited: "Changes saved.",
|
||||||
|
StatusDeleted: "Note deleted.",
|
||||||
|
StatusExported: "Note exported.",
|
||||||
|
ErrRequired: "Error: Name and content are required!",
|
||||||
|
SearchTitle: "SEARCH: ",
|
||||||
|
BrowseTags: "TAGS",
|
||||||
|
AllNotes: "All notes",
|
||||||
|
ConfirmDelete: "Are you sure you want to delete this note?",
|
||||||
|
ConfirmYes: "y: Yes",
|
||||||
|
ConfirmNo: "n: No / Esc",
|
||||||
|
HelpModal: "q: Close | 2: Edit | ↑/↓: Scroll",
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,749 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/bubbles/textarea"
|
||||||
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
|
"github.com/charmbracelet/bubbles/viewport"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
appStyle = lipgloss.NewStyle().
|
||||||
|
Padding(1, 2).
|
||||||
|
Border(lipgloss.RoundedBorder()).
|
||||||
|
BorderForeground(lipgloss.Color("62"))
|
||||||
|
|
||||||
|
titleStyle = lipgloss.NewStyle().
|
||||||
|
Bold(true).
|
||||||
|
Foreground(lipgloss.Color("#FAFAFA")).
|
||||||
|
Background(lipgloss.Color("#7D56F4")).
|
||||||
|
Padding(0, 1).
|
||||||
|
MarginBottom(1)
|
||||||
|
|
||||||
|
selectedStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#7D56F4")).
|
||||||
|
Bold(true)
|
||||||
|
|
||||||
|
statusStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("205"))
|
||||||
|
|
||||||
|
focusedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
|
||||||
|
cursorStyle = focusedStyle.Copy()
|
||||||
|
|
||||||
|
confirmBoxStyle = lipgloss.NewStyle().
|
||||||
|
Border(lipgloss.RoundedBorder()).
|
||||||
|
BorderForeground(lipgloss.Color("205")).
|
||||||
|
Padding(1, 2).Align(lipgloss.Center)
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
StateStartup AppState = iota
|
||||||
|
StateViewNotes
|
||||||
|
StateViewNoteModal
|
||||||
|
StateAddNote
|
||||||
|
StateEditNote
|
||||||
|
StateSearch
|
||||||
|
StateBrowseTags
|
||||||
|
StateConfirmDelete
|
||||||
|
)
|
||||||
|
type model struct {
|
||||||
|
notes []Note
|
||||||
|
filteredNotes []Note
|
||||||
|
cursor int
|
||||||
|
state AppState
|
||||||
|
browseTags []string
|
||||||
|
filterText string
|
||||||
|
textInputs []textinput.Model
|
||||||
|
contentInput textarea.Model
|
||||||
|
searchInput textinput.Model
|
||||||
|
viewport viewport.Model
|
||||||
|
ready bool
|
||||||
|
focusIndex int
|
||||||
|
selectedNoteID int64
|
||||||
|
statusMsg string
|
||||||
|
lang string
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewModel() model {
|
||||||
|
inputs := make([]textinput.Model, 2)
|
||||||
|
for i := range inputs {
|
||||||
|
t := textinput.New()
|
||||||
|
t.Cursor.Style = cursorStyle
|
||||||
|
inputs[i] = t
|
||||||
|
}
|
||||||
|
inputs[0].Focus()
|
||||||
|
inputs[0].PromptStyle = focusedStyle
|
||||||
|
inputs[0].TextStyle = focusedStyle
|
||||||
|
|
||||||
|
ta := textarea.New()
|
||||||
|
ta.SetWidth(60)
|
||||||
|
ta.SetHeight(8)
|
||||||
|
ta.FocusedStyle.CursorLine = lipgloss.NewStyle().Background(lipgloss.Color("62"))
|
||||||
|
|
||||||
|
si := textinput.New()
|
||||||
|
si.Prompt = "🔍 "
|
||||||
|
si.Placeholder = "Hledat..."
|
||||||
|
|
||||||
|
m := model{
|
||||||
|
notes: loadNotes(),
|
||||||
|
cursor: 0,
|
||||||
|
state: StateStartup,
|
||||||
|
textInputs: inputs,
|
||||||
|
contentInput: ta,
|
||||||
|
searchInput: si,
|
||||||
|
focusIndex: 0,
|
||||||
|
lang: "cs",
|
||||||
|
}
|
||||||
|
m.applyFilter()
|
||||||
|
m.updatePlaceholders()
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) Init() tea.Cmd {
|
||||||
|
return tea.Batch(textinput.Blink, textarea.Blink, tea.EnterAltScreen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *model) applyFilter() {
|
||||||
|
if m.filterText == "" {
|
||||||
|
m.filteredNotes = m.notes
|
||||||
|
} else {
|
||||||
|
m.filteredNotes = []Note{}
|
||||||
|
query := strings.ToLower(m.filterText)
|
||||||
|
for _, n := range m.notes {
|
||||||
|
if strings.Contains(strings.ToLower(n.Name), query) ||
|
||||||
|
strings.Contains(strings.ToLower(n.Content), query) ||
|
||||||
|
strings.Contains(strings.ToLower(strings.Join(n.Tags, " ")), query) {
|
||||||
|
m.filteredNotes = append(m.filteredNotes, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m.cursor >= len(m.filteredNotes) {
|
||||||
|
m.cursor = 0
|
||||||
|
if len(m.filteredNotes) > 0 {
|
||||||
|
m.cursor = len(m.filteredNotes) - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *model) getBrowseTags() {
|
||||||
|
set := make(map[string]bool)
|
||||||
|
for _, n := range m.notes {
|
||||||
|
for _, t := range n.Tags {
|
||||||
|
if t != "" {
|
||||||
|
set[t] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.browseTags = []string{}
|
||||||
|
for k := range set {
|
||||||
|
m.browseTags = append(m.browseTags, k)
|
||||||
|
}
|
||||||
|
// Manual sort
|
||||||
|
for i := 0; i < len(m.browseTags); i++ {
|
||||||
|
for j := i + 1; j < len(m.browseTags); j++ {
|
||||||
|
if m.browseTags[i] > m.browseTags[j] {
|
||||||
|
m.browseTags[i], m.browseTags[j] = m.browseTags[j], m.browseTags[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.cursor = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
var cmd tea.Cmd
|
||||||
|
tr := Translations[m.lang]
|
||||||
|
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case tea.WindowSizeMsg:
|
||||||
|
m.width = msg.Width
|
||||||
|
m.height = msg.Height
|
||||||
|
m.contentInput.SetWidth(msg.Width - 25)
|
||||||
|
m.contentInput.SetHeight(m.height/2 - 5)
|
||||||
|
|
||||||
|
headerHeight := 6
|
||||||
|
footerHeight := 3
|
||||||
|
vpHeight := msg.Height - headerHeight - footerHeight
|
||||||
|
if vpHeight < 1 {
|
||||||
|
vpHeight = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if !m.ready {
|
||||||
|
m.viewport = viewport.New(msg.Width-6, vpHeight)
|
||||||
|
m.ready = true
|
||||||
|
} else {
|
||||||
|
m.viewport.Width = msg.Width - 6
|
||||||
|
m.viewport.Height = vpHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
case tea.KeyMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
case "ctrl+c":
|
||||||
|
return m, tea.Quit
|
||||||
|
case "esc":
|
||||||
|
if m.state == StateConfirmDelete {
|
||||||
|
m.state = StateViewNotes
|
||||||
|
m.statusMsg = ""
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
if m.state == StateViewNotes {
|
||||||
|
if m.filterText != "" {
|
||||||
|
m.filterText = ""
|
||||||
|
m.applyFilter()
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
m.resetToView()
|
||||||
|
return m, nil
|
||||||
|
case "q":
|
||||||
|
if m.state == StateStartup {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
if m.state == StateConfirmDelete {
|
||||||
|
m.state = StateViewNotes
|
||||||
|
m.statusMsg = ""
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
if m.state == StateViewNoteModal {
|
||||||
|
m.state = StateViewNotes
|
||||||
|
// Reset viewport to full list size
|
||||||
|
headerHeight := 6
|
||||||
|
footerHeight := 3
|
||||||
|
vpHeight := m.height - headerHeight - footerHeight
|
||||||
|
if vpHeight < 1 { vpHeight = 1 }
|
||||||
|
m.viewport.Width = m.width - 6
|
||||||
|
m.viewport.Height = vpHeight
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
if m.state == StateViewNotes {
|
||||||
|
if m.filterText != "" {
|
||||||
|
m.filterText = ""
|
||||||
|
m.applyFilter()
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
m.resetToView()
|
||||||
|
return m, nil
|
||||||
|
|
||||||
|
case "l":
|
||||||
|
if m.state == StateViewNotes {
|
||||||
|
if m.lang == "cs" {
|
||||||
|
m.lang = "en"
|
||||||
|
} else {
|
||||||
|
m.lang = "cs"
|
||||||
|
}
|
||||||
|
m.updatePlaceholders()
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case "/":
|
||||||
|
if m.state == StateViewNotes {
|
||||||
|
m.state = StateSearch
|
||||||
|
m.searchInput.Focus()
|
||||||
|
m.searchInput.SetValue("")
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case "tab", "shift+tab":
|
||||||
|
if m.state == StateAddNote || m.state == StateEditNote {
|
||||||
|
if msg.String() == "shift+tab" {
|
||||||
|
m.focusIndex--
|
||||||
|
} else {
|
||||||
|
m.focusIndex++
|
||||||
|
}
|
||||||
|
if m.focusIndex > 2 {
|
||||||
|
m.focusIndex = 0
|
||||||
|
}
|
||||||
|
if m.focusIndex < 0 {
|
||||||
|
m.focusIndex = 2
|
||||||
|
}
|
||||||
|
return m.updateFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
case "up", "k":
|
||||||
|
if m.state == StateViewNotes && m.cursor > 0 {
|
||||||
|
m.cursor--
|
||||||
|
m.viewport.LineUp(1)
|
||||||
|
}
|
||||||
|
if m.state == StateViewNoteModal {
|
||||||
|
m.viewport.LineUp(1)
|
||||||
|
}
|
||||||
|
if m.state == StateBrowseTags && m.cursor > 0 {
|
||||||
|
m.cursor--
|
||||||
|
}
|
||||||
|
case "down", "j":
|
||||||
|
if m.state == StateViewNotes {
|
||||||
|
max := len(m.filteredNotes)
|
||||||
|
if m.cursor < max-1 {
|
||||||
|
m.cursor++
|
||||||
|
m.viewport.LineDown(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m.state == StateViewNoteModal {
|
||||||
|
m.viewport.LineDown(1)
|
||||||
|
}
|
||||||
|
if m.state == StateBrowseTags {
|
||||||
|
max := len(m.browseTags)
|
||||||
|
if m.cursor < max-1 {
|
||||||
|
m.cursor++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "enter":
|
||||||
|
switch m.state {
|
||||||
|
case StateStartup:
|
||||||
|
m.state = StateViewNotes
|
||||||
|
case StateViewNotes:
|
||||||
|
if len(m.filteredNotes) > 0 {
|
||||||
|
m.state = StateViewNoteModal
|
||||||
|
m.selectedNoteID = m.filteredNotes[m.cursor].ID
|
||||||
|
|
||||||
|
var n Note
|
||||||
|
for _, note := range m.notes {
|
||||||
|
if note.ID == m.selectedNoteID {
|
||||||
|
n = note
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modalContent := fmt.Sprintf("%s\n---\n[%d] %s\n\n%s\n\nTagy: %s",
|
||||||
|
tr.HelpModal,
|
||||||
|
n.ID, n.Name, n.Content, strings.Join(n.Tags, ", "),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set viewport size for modal (smaller than full screen)
|
||||||
|
modalWidth := m.width - 20
|
||||||
|
if modalWidth < 40 { modalWidth = 40 }
|
||||||
|
modalHeight := m.height - 10
|
||||||
|
if modalHeight < 10 { modalHeight = 10 }
|
||||||
|
|
||||||
|
m.viewport.Width = modalWidth
|
||||||
|
m.viewport.Height = modalHeight
|
||||||
|
m.viewport.SetContent(modalContent)
|
||||||
|
m.viewport.YOffset = 0
|
||||||
|
}
|
||||||
|
case StateSearch:
|
||||||
|
m.filterText = m.searchInput.Value()
|
||||||
|
m.applyFilter()
|
||||||
|
m.state = StateViewNotes
|
||||||
|
case StateBrowseTags:
|
||||||
|
if len(m.browseTags) > 0 {
|
||||||
|
m.filterText = m.browseTags[m.cursor]
|
||||||
|
m.applyFilter()
|
||||||
|
}
|
||||||
|
m.state = StateViewNotes
|
||||||
|
case StateAddNote:
|
||||||
|
if m.focusIndex != 1 {
|
||||||
|
m.handleAddNote()
|
||||||
|
}
|
||||||
|
case StateEditNote:
|
||||||
|
if m.focusIndex != 1 {
|
||||||
|
m.handleEditNote()
|
||||||
|
}
|
||||||
|
case StateConfirmDelete:
|
||||||
|
if m.focusIndex == 0 {
|
||||||
|
m.handleDeleteNote()
|
||||||
|
m.state = StateViewNotes
|
||||||
|
m.statusMsg = tr.StatusDeleted
|
||||||
|
return m, nil
|
||||||
|
} else {
|
||||||
|
m.state = StateViewNotes
|
||||||
|
m.statusMsg = ""
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "b":
|
||||||
|
if m.state == StateViewNotes {
|
||||||
|
m.state = StateBrowseTags
|
||||||
|
m.getBrowseTags()
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
case "1":
|
||||||
|
if m.state == StateViewNotes {
|
||||||
|
m.state = StateAddNote
|
||||||
|
m.resetInputs()
|
||||||
|
m.textInputs[0].SetValue("")
|
||||||
|
m.updatePlaceholders()
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
case "2":
|
||||||
|
if m.state == StateViewNotes && len(m.filteredNotes) > 0 {
|
||||||
|
m.state = StateEditNote
|
||||||
|
m.selectedNoteID = m.filteredNotes[m.cursor].ID
|
||||||
|
m.populateInputs(m.filteredNotes[m.cursor])
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
if m.state == StateViewNoteModal {
|
||||||
|
for _, n := range m.notes {
|
||||||
|
if n.ID == m.selectedNoteID {
|
||||||
|
m.state = StateEditNote
|
||||||
|
m.populateInputs(n)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
case "3":
|
||||||
|
if m.state == StateViewNotes && len(m.filteredNotes) > 0 {
|
||||||
|
exportNote(m.notes, m.filteredNotes[m.cursor].ID)
|
||||||
|
m.statusMsg = tr.StatusExported
|
||||||
|
}
|
||||||
|
case "4":
|
||||||
|
if m.state == StateViewNotes && len(m.filteredNotes) > 0 {
|
||||||
|
m.state = StateConfirmDelete
|
||||||
|
m.selectedNoteID = m.filteredNotes[m.cursor].ID
|
||||||
|
m.focusIndex = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.state == StateSearch {
|
||||||
|
m.searchInput, cmd = m.searchInput.Update(msg)
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.state == StateAddNote || m.state == StateEditNote {
|
||||||
|
if m.focusIndex == 1 {
|
||||||
|
m.contentInput, cmd = m.contentInput.Update(msg)
|
||||||
|
} else {
|
||||||
|
idx := m.focusIndex
|
||||||
|
if idx > 1 {
|
||||||
|
idx--
|
||||||
|
}
|
||||||
|
m.textInputs[idx], cmd = m.textInputs[idx].Update(msg)
|
||||||
|
}
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.state == StateConfirmDelete {
|
||||||
|
if msg, ok := msg.(tea.KeyMsg); ok {
|
||||||
|
switch msg.String() {
|
||||||
|
case "y":
|
||||||
|
m.handleDeleteNote()
|
||||||
|
m.state = StateViewNotes
|
||||||
|
m.statusMsg = tr.StatusDeleted
|
||||||
|
return m, nil
|
||||||
|
case "n", "esc":
|
||||||
|
m.state = StateViewNotes
|
||||||
|
m.statusMsg = ""
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *model) updateFocus() (tea.Model, tea.Cmd) {
|
||||||
|
var cmds []tea.Cmd
|
||||||
|
for i := range m.textInputs {
|
||||||
|
m.textInputs[i].Blur()
|
||||||
|
m.textInputs[i].PromptStyle = lipgloss.NewStyle()
|
||||||
|
}
|
||||||
|
m.contentInput.Blur()
|
||||||
|
m.searchInput.Blur()
|
||||||
|
|
||||||
|
switch m.focusIndex {
|
||||||
|
case 0:
|
||||||
|
cmds = append(cmds, m.textInputs[0].Focus())
|
||||||
|
m.textInputs[0].PromptStyle = focusedStyle
|
||||||
|
case 1:
|
||||||
|
cmds = append(cmds, m.contentInput.Focus())
|
||||||
|
case 2:
|
||||||
|
cmds = append(cmds, m.textInputs[1].Focus())
|
||||||
|
m.textInputs[1].PromptStyle = focusedStyle
|
||||||
|
}
|
||||||
|
return m, tea.Batch(cmds...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) View() string {
|
||||||
|
tr := Translations[m.lang]
|
||||||
|
var b strings.Builder
|
||||||
|
|
||||||
|
b.WriteString(titleStyle.Render(tr.Title))
|
||||||
|
b.WriteString("\n")
|
||||||
|
|
||||||
|
if m.statusMsg != "" {
|
||||||
|
b.WriteString(statusStyle.Render(m.statusMsg) + "\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch m.state {
|
||||||
|
case StateStartup:
|
||||||
|
eagle := `
|
||||||
|
_
|
||||||
|
( ` + lipgloss.NewStyle().Foreground(lipgloss.Color("205")).Render("`") + `
|
||||||
|
\ ` + lipgloss.NewStyle().Foreground(lipgloss.Color("205")).Render("\\") + `
|
||||||
|
/ / _
|
||||||
|
__ / / ( \
|
||||||
|
( "---" / / /
|
||||||
|
\ / / /
|
||||||
|
\ /-----' /
|
||||||
|
\ / /
|
||||||
|
\ / /
|
||||||
|
'-------'
|
||||||
|
`
|
||||||
|
b.WriteString(lipgloss.Place(m.width-12, 14, lipgloss.Center, lipgloss.Center,
|
||||||
|
eagle + "\n" +
|
||||||
|
lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#7D56F4")).Render("DOXA-CLI 🦅") + "\n\n" +
|
||||||
|
"Local-First Notes & Sovereignty\n\n" +
|
||||||
|
"Enter: Start | q: Quit\n\n" +
|
||||||
|
"License: MIT | © 2026 Kankys"))
|
||||||
|
|
||||||
|
|
||||||
|
case StateSearch:
|
||||||
|
b.WriteString(tr.SearchTitle + "\n" + m.searchInput.View() + "\n\n" + tr.HelpForm)
|
||||||
|
|
||||||
|
case StateBrowseTags:
|
||||||
|
b.WriteString(fmt.Sprintf("--- %s ---\n", tr.BrowseTags))
|
||||||
|
b.WriteString("Enter: Filtrovat | q: Zpět\n\n")
|
||||||
|
for i, item := range m.browseTags {
|
||||||
|
cursor := " "
|
||||||
|
if m.cursor == i {
|
||||||
|
cursor = "> "
|
||||||
|
}
|
||||||
|
b.WriteString(fmt.Sprintf("%s%s\n", cursor, item))
|
||||||
|
}
|
||||||
|
|
||||||
|
case StateViewNotes:
|
||||||
|
filterInfo := ""
|
||||||
|
if m.filterText != "" {
|
||||||
|
filterInfo = fmt.Sprintf(" [Filtr: %s]", m.filterText)
|
||||||
|
}
|
||||||
|
|
||||||
|
var listBuilder strings.Builder
|
||||||
|
if len(m.filteredNotes) == 0 {
|
||||||
|
listBuilder.WriteString(tr.NoNotes + "\n")
|
||||||
|
} else {
|
||||||
|
for i, n := range m.filteredNotes {
|
||||||
|
if m.cursor == i {
|
||||||
|
listBuilder.WriteString(selectedStyle.Render(fmt.Sprintf("> [%d] %s", n.ID, n.Name)) + "\n")
|
||||||
|
} else {
|
||||||
|
listBuilder.WriteString(fmt.Sprintf(" [%d] %s\n", n.ID, n.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.ready {
|
||||||
|
m.viewport.SetContent(listBuilder.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString(fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s\n\n",
|
||||||
|
tr.MenuAdd, tr.MenuEdit, tr.MenuExport, tr.MenuDelete, tr.MenuSearch, tr.MenuBrowse, tr.MenuLang))
|
||||||
|
b.WriteString(tr.ListTitle + filterInfo + "\n")
|
||||||
|
|
||||||
|
if m.ready {
|
||||||
|
b.WriteString(m.viewport.View() + "\n")
|
||||||
|
} else {
|
||||||
|
b.WriteString(listBuilder.String())
|
||||||
|
}
|
||||||
|
b.WriteString("\n" + tr.HelpList)
|
||||||
|
|
||||||
|
case StateAddNote, StateEditNote:
|
||||||
|
title := tr.AddTitle
|
||||||
|
if m.state == StateEditNote {
|
||||||
|
title = fmt.Sprintf("%s (%d)", tr.EditTitle, m.selectedNoteID)
|
||||||
|
}
|
||||||
|
b.WriteString(title + "\n\n")
|
||||||
|
|
||||||
|
b.WriteString(fmt.Sprintf("%-8s %s\n", tr.LabelName, m.textInputs[0].View()))
|
||||||
|
b.WriteString(fmt.Sprintf("%-8s\n%s\n\n", tr.LabelContent, m.contentInput.View()))
|
||||||
|
b.WriteString(fmt.Sprintf("%-8s %s\n", tr.LabelTags, m.textInputs[1].View()))
|
||||||
|
|
||||||
|
b.WriteString("\n" + tr.HelpForm)
|
||||||
|
|
||||||
|
case StateViewNoteModal:
|
||||||
|
if m.ready {
|
||||||
|
b.WriteString(confirmBoxStyle.Render(m.viewport.View()))
|
||||||
|
} else {
|
||||||
|
b.WriteString(confirmBoxStyle.Render("Načítání..."))
|
||||||
|
}
|
||||||
|
|
||||||
|
case StateConfirmDelete:
|
||||||
|
var noteToDelete Note
|
||||||
|
found := false
|
||||||
|
for _, n := range m.notes {
|
||||||
|
if n.ID == m.selectedNoteID {
|
||||||
|
noteToDelete = n
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
m.state = StateViewNotes
|
||||||
|
m.statusMsg = "Chyba: Poznámka k smazání nebyla nalezena!"
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmText := fmt.Sprintf("%s\n\n[%d] %s\nObsah: %s\n\n%s | %s",
|
||||||
|
tr.ConfirmDelete,
|
||||||
|
noteToDelete.ID,
|
||||||
|
noteToDelete.Name,
|
||||||
|
truncate(noteToDelete.Content, 40),
|
||||||
|
tr.ConfirmYes, tr.ConfirmNo,
|
||||||
|
)
|
||||||
|
b.WriteString(confirmBoxStyle.Render(confirmText))
|
||||||
|
}
|
||||||
|
|
||||||
|
content := b.String()
|
||||||
|
if m.width > 0 && m.height > 0 {
|
||||||
|
return appStyle.Width(m.width - 6).Height(m.height - 4).Render(content)
|
||||||
|
}
|
||||||
|
return appStyle.Render(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *model) updatePlaceholders() {
|
||||||
|
tr := Translations[m.lang]
|
||||||
|
m.textInputs[0].Placeholder = tr.PlaceholderName
|
||||||
|
m.contentInput.Placeholder = tr.PlaceholderContent
|
||||||
|
m.textInputs[1].Placeholder = tr.PlaceholderTags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *model) resetToView() {
|
||||||
|
m.state = StateViewNotes
|
||||||
|
m.statusMsg = ""
|
||||||
|
m.filterText = ""
|
||||||
|
m.applyFilter()
|
||||||
|
m.resetInputs()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *model) resetInputs() {
|
||||||
|
for i := range m.textInputs {
|
||||||
|
m.textInputs[i].SetValue("")
|
||||||
|
m.textInputs[i].Blur()
|
||||||
|
}
|
||||||
|
m.contentInput.SetValue("")
|
||||||
|
m.searchInput.SetValue("")
|
||||||
|
m.focusIndex = 0
|
||||||
|
m.updateFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *model) populateInputs(n Note) {
|
||||||
|
m.textInputs[0].SetValue(n.Name)
|
||||||
|
m.contentInput.SetValue(n.Content)
|
||||||
|
m.textInputs[1].SetValue(strings.Join(n.Tags, ", "))
|
||||||
|
m.focusIndex = 0
|
||||||
|
m.updateFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *model) handleAddNote() {
|
||||||
|
tr := Translations[m.lang]
|
||||||
|
name := m.textInputs[0].Value()
|
||||||
|
content := m.contentInput.Value()
|
||||||
|
if name == "" || content == "" {
|
||||||
|
m.statusMsg = tr.ErrRequired
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tags := []string{}
|
||||||
|
if m.textInputs[1].Value() != "" {
|
||||||
|
tags = strings.Split(m.textInputs[1].Value(), ",")
|
||||||
|
for i := range tags {
|
||||||
|
tags[i] = strings.TrimSpace(tags[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
m.notes = append(m.notes, Note{
|
||||||
|
ID: now.UnixMilli(), Name: name, Content: content, Folder: "", Tags: tags, CreatedAt: now, ModifiedAt: now,
|
||||||
|
})
|
||||||
|
saveNotes(m.notes)
|
||||||
|
m.applyFilter()
|
||||||
|
m.statusMsg = tr.StatusSaved
|
||||||
|
m.state = StateViewNotes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *model) handleEditNote() {
|
||||||
|
tr := Translations[m.lang]
|
||||||
|
for i, n := range m.notes {
|
||||||
|
if n.ID == m.selectedNoteID {
|
||||||
|
m.notes[i].Name = m.textInputs[0].Value()
|
||||||
|
m.notes[i].Content = m.contentInput.Value()
|
||||||
|
m.notes[i].Folder = ""
|
||||||
|
tags := []string{}
|
||||||
|
if m.textInputs[1].Value() != "" {
|
||||||
|
tags = strings.Split(m.textInputs[1].Value(), ",")
|
||||||
|
for j := range tags {
|
||||||
|
tags[j] = strings.TrimSpace(tags[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.notes[i].Tags = tags
|
||||||
|
m.notes[i].ModifiedAt = time.Now()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
saveNotes(m.notes)
|
||||||
|
m.applyFilter()
|
||||||
|
m.statusMsg = tr.StatusEdited
|
||||||
|
m.state = StateViewNotes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *model) handleDeleteNote() {
|
||||||
|
noteIndex := -1
|
||||||
|
for i, n := range m.notes {
|
||||||
|
if n.ID == m.selectedNoteID {
|
||||||
|
noteIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if noteIndex != -1 {
|
||||||
|
m.notes = append(m.notes[:noteIndex], m.notes[noteIndex+1:]...)
|
||||||
|
saveNotes(m.notes)
|
||||||
|
}
|
||||||
|
m.applyFilter()
|
||||||
|
}
|
||||||
|
|
||||||
|
func exportNote(notes []Note, id int64) {
|
||||||
|
for _, note := range notes {
|
||||||
|
if note.ID == id {
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Chyba při zjišťování domovského adresáře: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
exportDir := fmt.Sprintf("%s/doxa-cli-txt", homeDir)
|
||||||
|
if err := os.MkdirAll(exportDir, 0755); err != nil {
|
||||||
|
fmt.Printf("Chyba při vytváření adresáře pro export: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName := fmt.Sprintf("%s_%d.txt", strings.ReplaceAll(note.Name, " ", "_"), note.ID)
|
||||||
|
fullPath := fmt.Sprintf("%s/%s", exportDir, fileName)
|
||||||
|
|
||||||
|
file, err := os.Create(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Chyba při vytváření souboru pro export: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
content := fmt.Sprintf("Název: %s\nTagy: %s\nVytvořeno: %s\n\n%s",
|
||||||
|
note.Name, strings.Join(note.Tags, ", "), note.CreatedAt.Format("2006-01-02 15:04"), note.Content)
|
||||||
|
|
||||||
|
_, _ = file.WriteString(content)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func truncate(s string, max int) string {
|
||||||
|
if len(s) > max {
|
||||||
|
return strings.TrimSpace(s[:max]) + "..."
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type errMsg error
|
||||||
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 127 KiB |
@@ -0,0 +1,335 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="cs">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>DOXA cli</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #0d1117;
|
||||||
|
color: #c9d1d9;
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: #161b22;
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-header {
|
||||||
|
background-color: #21262d;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-bottom: 1px solid #30363d;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-circle {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close { background-color: #ff5f56; }
|
||||||
|
.btn-minimize { background-color: #ffbd2e; }
|
||||||
|
.btn-maximize { background-color: #27c93f; }
|
||||||
|
|
||||||
|
.terminal-title {
|
||||||
|
margin-left: 1rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #8b949e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-switch {
|
||||||
|
background-color: #30363d;
|
||||||
|
border: 1px solid #8b949e;
|
||||||
|
color: #f0f6fc;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-switch:hover {
|
||||||
|
background-color: #8b949e;
|
||||||
|
color: #0d1117;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-body {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #58a6ff;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
color: #8b949e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt {
|
||||||
|
color: #7ee787;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command {
|
||||||
|
color: #f0f6fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: #0d1117;
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
color: #a5d6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
color: #ffa657;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li::before {
|
||||||
|
content: "> ";
|
||||||
|
color: #7ee787;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #58a6ff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual-highlight {
|
||||||
|
background-color: rgba(255, 166, 87, 0.1);
|
||||||
|
border-left: 4px solid #ffa657;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual-highlight a {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ffa657;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
margin-top: 2rem;
|
||||||
|
border-top: 1px solid #30363d;
|
||||||
|
padding-top: 1rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #8b949e;
|
||||||
|
text-align: center;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lang visibility styles */
|
||||||
|
[data-lang] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="terminal-header">
|
||||||
|
<div class="terminal-header-left">
|
||||||
|
<div class="terminal-buttons">
|
||||||
|
<div class="btn-circle btn-close"></div>
|
||||||
|
<div class="btn-circle btn-minimize"></div>
|
||||||
|
<div class="btn-circle btn-maximize"></div>
|
||||||
|
</div>
|
||||||
|
<div class="terminal-title">bash - doxa-cli</div>
|
||||||
|
</div>
|
||||||
|
<button class="lang-switch" onclick="toggleLang()" id="langBtn">EN</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="terminal-body">
|
||||||
|
<h1>DOXA cli</h1>
|
||||||
|
|
||||||
|
<!-- CZ Mutation -->
|
||||||
|
<div data-lang="cs">
|
||||||
|
<p>DOXA-CLI je nástroj pro správu vašich poznámek z příkazové řádky. Umožňuje přidávat, zobrazovat, upravovat, mazat, exportovat poznámky a také je filtrovat a vyhledávat. Aplikace je napsána v Go a nabízí moderní terminálové uživatelské rozhraní (TUI).</p>
|
||||||
|
</div>
|
||||||
|
<!-- EN Mutation -->
|
||||||
|
<div data-lang="en">
|
||||||
|
<p>DOXA-CLI is a command-line tool for managing your notes. It allows you to add, view, edit, delete, export notes, as well as filter and search them. The application is written in Go and offers a modern Terminal User Interface (TUI).</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/go-1.22+-00ADD8?style=flat-square&logo=go&logoColor=white" alt="Go 1.22+">
|
||||||
|
<img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT license">
|
||||||
|
<img src="https://img.shields.io/badge/tested-Linux-brightgreen?style=flat-square" alt="Tested on Linux">
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/freebsd-AB2B28?style=for-the-badge&logo=freebsd&logoColor=white" alt="freeBSD">
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<img src="./img/dashboard.png" width="100%" alt="Dashboard">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- CZ Mutation -->
|
||||||
|
<div data-lang="cs">
|
||||||
|
<div class="manual-highlight">
|
||||||
|
🔥 <strong>Důležité:</strong> Podrobný návod k použití, instalaci a přehled klávesových zkratek naleznete v oficiálním manuálu:
|
||||||
|
<a href="https://codeberg.org/Kankys/doxa-cli/src/branch/master/MANUAL_CZ.md" target="_blank">Český uživatelský manuál (MANUAL_CZ.md)</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-title">$ instalace</div>
|
||||||
|
<pre><code>git clone https://codeberg.org/Kankys/doxa-cli.git
|
||||||
|
cd doxa-cli
|
||||||
|
chmod +x install.sh
|
||||||
|
./install.sh</code></pre>
|
||||||
|
|
||||||
|
<div class="section-title">$ vlastnosti</div>
|
||||||
|
<ul>
|
||||||
|
<li>Jazyk: Čisté Go (Golang), které zaručuje nativní kompilaci, vysokou rychlost provedení a žádné zbytečné externí závislosti.</li>
|
||||||
|
<li>Rozhraní: Terminálové rozhraní (CLI), které zbavuje mysl grafického chaosu a nechává vyniknout čistému textu.</li>
|
||||||
|
<li>Datová vrstva: Plně kompatibilní formát JSON sdílený s webovou verzí. Poznámky obsahují přesná časová razítka, identifikátory, obsah a štítky.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="section-title">$ použití</div>
|
||||||
|
<pre><code># Spuštění aplikace
|
||||||
|
doxa-cli</code></pre>
|
||||||
|
|
||||||
|
<div class="section-title">$ odkazy</div>
|
||||||
|
<p>Zdrojový kód, dokumentaci, návody a další informace naleznete na <a href="https://codeberg.org/Kankys/doxa-cli" target="_blank">Codebergu</a>.</p>
|
||||||
|
<p>Web DOXA Notes naleznete na <a href="https://doxa-notes.org" target="_blank">oficálním webu</a>.</p>
|
||||||
|
<p>Web doxa-secret naleznete na <a href="https://secret.doxa-notes.org" target="_blank">oficálním webu</a>.</p>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
"Terminál nežádá nic, neodvádí pozornost ničím a nabízí úplnou kontrolu. Vnes řád do svých myšlenek skrze čistý kód."
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- EN Mutation -->
|
||||||
|
<div data-lang="en">
|
||||||
|
<div class="manual-highlight">
|
||||||
|
🔥 <strong>Important:</strong> A detailed guide for installation, and keyboard shortcuts can be found in the official manual:
|
||||||
|
<a href="https://codeberg.org/Kankys/doxa-cli/src/branch/master/MANUAL.md" target="_blank">English User Manual (MANUAL.md)</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-title">$ installation</div>
|
||||||
|
<pre><code>git clone https://codeberg.org/Kankys/doxa-cli.git
|
||||||
|
cd doxa-cli
|
||||||
|
chmod +x install.sh
|
||||||
|
./install.sh</code></pre>
|
||||||
|
|
||||||
|
<div class="section-title">$ features</div>
|
||||||
|
<ul>
|
||||||
|
<li>Language: Clean Go (Golang) ensuring native compilation, fast execution speeds, and no bloated external dependencies.</li>
|
||||||
|
<li>Interface: Terminal-based interface (CLI) to strip away graphical noise and let pure text stand out.</li>
|
||||||
|
<li>Data Layer: Fully compatible JSON format shared with the web version. Notes include accurate timestamps, IDs, content, and tags.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="section-title">$ usage</div>
|
||||||
|
<pre><code># Start the application
|
||||||
|
doxa-cli</code></pre>
|
||||||
|
|
||||||
|
<div class="section-title">$ links</div>
|
||||||
|
<p>Source code, documentation, guides, and more can be found on <a href="https://codeberg.org/Kankys/doxa-cli" target="_blank">Codeberg</a>.</p>
|
||||||
|
<p>DOXA Notes web can be found on the <a href="https://doxa-notes.org" target="_blank">official website</a>.</p>
|
||||||
|
<p>doxa-secret web can be found on the <a href="https://secret.doxa-notes.org" target="_blank">official website</a>.</p>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
"The terminal requests nothing, distracts with nothing, and offers absolute control. Bring order to your thoughts through clean code."
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<p class="copyright" style="text-align: center;">Doxa-cli © 2026 | Local-First & Freedom 🦅</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let currentLang = 'cs';
|
||||||
|
|
||||||
|
function applyLang() {
|
||||||
|
document.querySelectorAll('[data-lang]').forEach(el => {
|
||||||
|
if (el.getAttribute('data-lang') === currentLang) {
|
||||||
|
el.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
el.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.getElementById('langBtn').innerText = currentLang === 'cs' ? 'EN' : 'CS';
|
||||||
|
document.documentElement.lang = currentLang;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleLang() {
|
||||||
|
currentLang = currentLang === 'cs' ? 'en' : 'cs';
|
||||||
|
localStorage.setItem('doxa-cli-lang', currentLang);
|
||||||
|
applyLang();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize language
|
||||||
|
(function initLanguage() {
|
||||||
|
// 1. Check local storage
|
||||||
|
let savedLang = localStorage.getItem('doxa-cli-lang');
|
||||||
|
if (savedLang) {
|
||||||
|
currentLang = savedLang;
|
||||||
|
} else {
|
||||||
|
// 2. Check system language
|
||||||
|
let sysLang = navigator.language || navigator.userLanguage || 'en';
|
||||||
|
sysLang = sysLang.toLowerCase();
|
||||||
|
if (sysLang.startsWith('cs') || sysLang.startsWith('sk')) {
|
||||||
|
currentLang = 'cs';
|
||||||
|
} else {
|
||||||
|
currentLang = 'en';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applyLang();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
.vibe/
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Manifest digitální suverenity a minimalistického myšlení
|
||||||
|
### Záměr projektu:
|
||||||
|
Vytvořit absolutně nezávislé, bezpečné a minimalistické prostředí pro uchovávání lidských myšlenek, poznámek a životních reflexí. V souladu se základními principy osobní svobody a soběstačnosti aplikace vědomě odmítá cloudové databáze, servery třetích stran a centralizovaná API. DOXA věří, že lidská mysl nepotřebuje být sledována algoritmy, aby mohla růst.
|
||||||
|
|
||||||
|
### 1. Radikální Local-First Filozofie
|
||||||
|
|
||||||
|
Aplikace funguje na principu absolutní decentralizace. Běží na tvém zařízení a tam také umírá i žije.
|
||||||
|
|
||||||
|
* Žádný backend, žádné API: DOXA vědomě neobsahuje žádný kód pro odesílání dat na vzdálené servery. Tím je eliminováno riziko úniku dat, výpadku serveru nebo změny obchodních podmínek poskytovatele.
|
||||||
|
* Absolutní soukromí: Data jsou uložena výhradně v tvém prohlížeči (localStorage) nebo lokálním souboru. Co napíšeš, to zůstává tvým výhradním vlastnictvím.
|
||||||
|
## 2. Formát JSON: Trvanlivost napříč dekádami
|
||||||
|
|
||||||
|
Data jsou strukturována do otevřeného, čistého a lidsky čitelného formátu JSON. Žádné proprietární databáze.
|
||||||
|
|
||||||
|
* Tvá data ti patří. Díky možnosti manuálního exportu si můžeš svůj myšlenkový fond odnést kamkoli – uložit si ho na svůj bezpečný lokální disk, nahrát na vlastní Git (GitHub/Gitea), nebo jej synchronizovat skrze cloudovou službu, kterou ty sám uznáš za bezpečnou.
|
||||||
|
* Čistota formátu zaručuje, že tvé poznámky budou čitelné i za dvacet let na jakémkoli zařízení, bez ohledu na to, zda DOXA bude stále existovat.
|
||||||
|
## 3. Technická čistota a minimalismus (Vanilla Stack)
|
||||||
|
|
||||||
|
Pro zachování maximální rychlosti, nezávislosti a nulových externích závislostí je aplikace postavena výhradně na čistých webových standardech:
|
||||||
|
|
||||||
|
* Frontend (HTML5/CSS3): Minimalistické rozhraní, které nezatěžuje mysl barevným chaosem a vizuálním balastem moderního internetu.
|
||||||
|
*
|
||||||
|
* Logika (Vanilla JavaScript ES6+): Žádné těžkopádné frameworky (React/Vue), které podléhají módním trendům a neustálým změnám.
|
||||||
|
|
||||||
|
**Čistý kód je trvanlivý jako tesání do kamene. „Nezáleží na tom, jak složité jsou okolnosti kolem tebe. Když stavíš vlastní chrám myšlenek, začínáš od prvního kamene. Tento kód a tvá data jsou tvým vlastním, nedobytným hradem.“**— Stoický odkaz pro svobodné myslitele
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Návod k použití — DOXA (v26.07.01)
|
||||||
|
|
||||||
|
### Vytvoření nové poznámky
|
||||||
|
|
||||||
|
Klikni na tlačítko ➕ Nová poznámka a zadej název.
|
||||||
|
Položka se objeví v seznamu. Klikem načteš název a obsah do editoru nahoře.
|
||||||
|
Uprav obsah a klikni Uložit nyní.
|
||||||
|
|
||||||
|
## Tagy
|
||||||
|
|
||||||
|
Tagy jsou slova nebo fráze, která popisuje obsah poznámky. V aplikaci Doxa díky nim můžete filtrovat poznámky podle tagu.
|
||||||
|
|
||||||
|
* Klikni do pole tagy a zadej název tagu (uloží se stejně s poznámkou).
|
||||||
|
* Poznámku pomocí tagu pak vyhledáš v poli s lupou Filtrovat poznámky podle tagu....
|
||||||
|
|
||||||
|
### Editace a mazání
|
||||||
|
|
||||||
|
* Inline: klikni na poznámku, uprav obsah a stiskni Uložit nyní.
|
||||||
|
* Modal: klikni na ikonu ✎ vedle položky pro pokročilou editaci v modal okně.
|
||||||
|
* Mazání: ikona 🗑 u položky — po potvrzení se poznámka smaže.
|
||||||
|
|
||||||
|
### Přesouvání pořadí poznámek
|
||||||
|
|
||||||
|
Použij šipky ↑ / ↓ u položek.
|
||||||
|
Nebo přetahuj položky (drag & drop) na požadované místo — při přetažení se ukáže indikátor vložení.
|
||||||
|
|
||||||
|
### Přepínání vzhledu
|
||||||
|
|
||||||
|
V horním menu najdeš tlačítko pro přepínání vzhledu. Aplikace podporuje tři režimy:
|
||||||
|
|
||||||
|
* Světlý — ikona: ☀️
|
||||||
|
* Tmavý — ikona: 🌙
|
||||||
|
* Respektovat systém — ikona: 🌓 — aplikace bude sledovat systémovou volbu (prefers-color-scheme) a automaticky se přepne, pokud se změní systémový motiv.
|
||||||
|
|
||||||
|
Kliknutím na tlačítko v menu cyklíš mezi režimy (Light → Dark → System). Vybraná volba se uloží do localStorage pod klíčem doxa_theme a přetrvá mezi stránkami a obnovami. Pokud chceš volbu resetovat, otevři konzoli prohlížeče a spusť: localStorage.removeItem('doxa_theme') nebo vymaž storage v nástrojích pro vývojáře.
|
||||||
|
|
||||||
|
### Automatické ukládání
|
||||||
|
|
||||||
|
Aplikace nyní automaticky ukládá změny poznámek lokálně během psaní (autosave). V běžném prohlížeči se změny ukládají do localStorage (klíč doxa_notes_v1) bez nutnosti manuálního ukládání — data se uloží po krátké prodlevě. Pokud aplikaci spouštíš v Electron/Node režimu, změny se zapisují přímo do lokálního souboru.
|
||||||
|
|
||||||
|
**Průběžně si úkládej i JSON soubor manuálně!!**
|
||||||
|
|
||||||
|
### Manuální uložení
|
||||||
|
|
||||||
|
Vedle editoru najdeš tlačítko Uložit nyní. I když autosave ukládá průběžně, tlačítko je užitečné v těchto případech:
|
||||||
|
|
||||||
|
Okamžité uložení — provede persist bez čekání na autosave (užitečné před zavřením aplikace nebo pokud chcete rychlou jistotu, že jsou změny zapsány).
|
||||||
|
Vizuální potvrzení — po uložení se zobrazí stav „Uloženo“, což dává uživateli okamžitou zpětnou vazbu.
|
||||||
|
Bez stažení — tlačítko Uložit poznámku neprovádí stažení souboru; pokud chcete stáhnout JSON, použijte tlačítko Exportovat JSON.
|
||||||
|
|
||||||
|
Pro přenos dat mezi zařízeními použijte tlačítko Exportovat JSON (stáhne soubor doxa.json), nebo z konzole spusťte saveNotesToFile(true).
|
||||||
|
|
||||||
|
### Import / Export
|
||||||
|
|
||||||
|
Pro import klikni Importovat JSON a vyber soubor. Podporované formáty:
|
||||||
|
|
||||||
|
Objekt s polem notes: { "notes": [ ... ] }
|
||||||
|
Přímo pole: [ { ... }, { ... } ]
|
||||||
|
|
||||||
|
Export je nyní dostupný přes tlačítko Exportovat JSON (vedle tlačítka Uložit). Aplikace zároveň provádí autosave lokálně — nic neztratíte, pokud zapomenete manuálně uložit. Chcete-li stáhnout JSON přes skript, můžete také spustit v konzoli saveNotesToFile(true).
|
||||||
|
|
||||||
|
### Bezpečnost a zálohy
|
||||||
|
|
||||||
|
Výchozí režim je local‑first: data zůstávají ve tvém prohlížeči (localStorage) nebo lokálním souboru, aplikace je sama neodesílá.
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Instalace
|
||||||
|
### Rychlý start - pouze pro variantu spouštění aplikace u sebe lokalně
|
||||||
|
|
||||||
|
Pokud aplikaci spouštíš z webu https://doxa-notes.org/ tento návod není validní!
|
||||||
|
|
||||||
|
1.Stáhni/klonuj repozitář a přejdi do složky aplikace:
|
||||||
|
|
||||||
|
**cd doxa/wep_app**
|
||||||
|
|
||||||
|
Spusť jednoduchý lokální server (doporučeno):
|
||||||
|
|
||||||
|
**python3 -m http.server 8000**
|
||||||
|
|
||||||
|
a otevři v prohlížeči: **http://localhost:8000**
|
||||||
|
|
||||||
|
2. Alternativně můžeš použít Node.js a pomocí npm install nainstaluj live-server a následně spusť pomocí příkazu: **live-server** ve složce aplikace.
|
||||||
|
|
||||||
|
### Desktop App
|
||||||
|
|
||||||
|
1. Stáhni si desktop verzi aplikace z [Codeberg uložiště projektu](https://codeberg.org/Kankys/doxa/releases)
|
||||||
|
2. Staženou aplikaci spustíš spustíš otevřením souboru windows Dlinux - .AppImage
|
||||||
|
3. Soubory aplikací najdeš ve složce:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Na tvém Linuxu: ~/.config/doxa/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Nezapomeň si dělat pravidélné zálohy JSON (exportovat JSON)**
|
||||||
|
|
||||||
|
**Nezapomeň si pravidelně aktualizovat aplikaci, abys mohl mít nejnovější verzi.**
|
||||||
|
|
||||||
|
Desktop app: nevycházejí s pravidelností jako web app, z důvodu buildu a náročnosti na zdroje. Desktop app bude vydáván pouze po větších funkčních změnách.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Bugs
|
||||||
|
|
||||||
|
Nalezené chyby prosím hlašte na Gitu projektu (kde naleznete hlášené či nevyřešené bugy) [Link na hlášení chyb](https://codeberg.org/Kankys/doxa/issues)
|
||||||
|
|
||||||
|
# Repozitář
|
||||||
|
|
||||||
|
Pokud chcete přispět svým kódem budu velice rád, ale je tu několik pravidel:
|
||||||
|
|
||||||
|
* před větší feature, vždy zvažte jestli úprava je vhodná, případně ji nejdříve prodiskutujme. Škoda zahodit Pull Request z důvodu směřování projektu jiným směrem.
|
||||||
|
|
||||||
|
* Vždy si odpíchněte větev z master. Do master nikdy nic ne push(ovat)!! Vždy udělejte Pull Request do masteru.
|
||||||
|
|
||||||
|
* Do souboru PR.md vždy před Pull Request vložte do tabulky název větve, stručný popis změn a jméno.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Automatizované testy
|
||||||
|
|
||||||
|
* Testy jsou psané v Playwright a v TypeScript
|
||||||
|
* Využíváme POM
|
||||||
|
* Pokud je to možné s novou funkcionalitou úprávýme řádně testy
|
||||||
|
|
||||||
|
# Podporovatelé a přispěvovatelé
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
## Release notes:
|
||||||
|
| Branch | Change | Version|Name |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| dokumentace01 | Vytvoření struktury dokumentace a první kód aplikace | Kankys |
|
||||||
|
|update_app |dark/light mode, oprv textací, rozšíření návodu, přídaní markdown a tlačítek| 26.O6.01.beta|Kankys |
|
||||||
|
| dokumentace02 | oprava issue https://codeberg.org/Kankys/doxa/issues/3, textové editace, přidání baneru | 26.06.02.beta|Kankys |
|
||||||
|
| dokumentace03 | smazani server casti, oprava textaci, vytvoření manifest | 26.6.02.beta|Kankys |
|
||||||
|
| desktop_app_electron | vytvoření app pro **windows a linux**, upravené návody |26.06.03.beta |Kankys |
|
||||||
|
|new_project_doxa|přejmenování projektu na Doxa, nový repozitář, new css style návrh | 26.06.04.beta|Kankys |
|
||||||
|
| tags |přidání tagů do poznámek | 26.06.05.beta|Kankys |
|
||||||
|
| first_stable | nová doména doxa-notes.org, první stabilní vydání, rychlá volba /, funkce náhledu poznámek, rozdělení doxa-notes.org na web app a desktop app | 26.07.01|Kankys |
|
||||||
|
|bug-H1| oprava bugu v h1| 26.07.01.|Kankys |
|
||||||
|
|wiki| přidání wiki stránky (mkdocs)| 26.07.02.|Kankys |
|
||||||
|
| playwright| založení playwright testů, úprava SEO | 26.07.03.| Kankys |
|
||||||
|
|web_zalozky | přidání systému složek, rozšíření hledání|26.07.04| kankys|
|
||||||
|
|playwright-configuration| konfigurace playwright, aktualizace package.json, upravit AppPage.ts| 26.07.04| Kankys|
|
||||||
|
|en_wiki + roller| přidání wiki stránky (mkdocs) v EN + roller| 26.07.05.|Kankys |
|
||||||
|
|
||||||
|
Verze: se označují rok.měsíc.verze (26.06.03.beta)
|
||||||
|
|
||||||
|
První ostrá verze výjde jako (předpoklad): 26.07.01
|
||||||
|
|
||||||
|
Desktop app: nevycházejí s pravidelností jako web app, z důvodu buildu a náročnosti na zdroje. Desktop app bude vydáván pouze po větších funkčních změnách.
|
||||||
|
|
||||||
|
Desktop aplikace pro Linux a Windows jsou zabaleny do instalátorů AppImage - Linux a exe (Windows).
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# TODO
|
||||||
|
|
||||||
|
- [x] Desktop app linux
|
||||||
|
- [ ] Desktop app macOS
|
||||||
|
- [X] Export poznámky do Markdown
|
||||||
|
- [ ] Lokalizace do AJ
|
||||||
|
- [X] tagy
|
||||||
|
- [X] rychlé vyhledávaní
|
||||||
|
- [X] vlastní doména
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Kankys
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||||
|
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
||||||
|
following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||||
|
portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||||
|
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||||
|
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# 🇬🇧 DOXA Notes PROJECT
|
||||||
|
|
||||||
|
##### 🇨🇿 [Czech Version](#czech-version)
|
||||||
|
|
||||||
|
#### Technical and architectural manifesto of a minimalist note-taking application
|
||||||
|
|
||||||
|
**Project goal:** To create an absolutely independent, secure and minimalist environment for storing people's
|
||||||
|
thoughts, notes and life reflections. In accordance with the liberal principles of self-sufficiency, the application does not rely on
|
||||||
|
third-party cloud databases, but operates fully locally with the open JSON format on its own server
|
||||||
|
backend.
|
||||||
|
|
||||||
|
## 1. TECHNICAL ARCHITECTURE
|
||||||
|
To maintain maximum speed, code purity and zero external dependencies, the application will be built on a pure
|
||||||
|
web standard (so-called Vanilla stack):
|
||||||
|
|
||||||
|
Frontend: Semantic HTML5 for structure, CSS3 for a minimalist interface that does not burden the mind with colorful chaos.
|
||||||
|
|
||||||
|
Client logic: Pure JavaScript (ES6+) without frameworks (React/Vue). Everything runs natively in the browser.
|
||||||
|
|
||||||
|
Data layer: Standardized JSON format. Notes are structured as an array of objects, where each
|
||||||
|
item has a unique identifier, timestamp, and text content.
|
||||||
|
|
||||||
|
Desktop applications for Linux and Windows are packaged in AppImage installers - Linux and exe (Windows).
|
||||||
|
|
||||||
|
## 2. DATA FORMAT STRUCTURE (JSON)
|
||||||
|
Data purity ensures that your thoughts will be readable even in twenty years on any device. The basic schema of one note is as follows:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"id": "1719147600000",
|
||||||
|
"title": "Morning reflection",
|
||||||
|
"content": "Digital illusion denied today. Mind is calm and steady.",
|
||||||
|
"created_at": "2026-06-23T08:00:00Z",
|
||||||
|
"updated_at": "2026-06-23T08:00:00Z"}
|
||||||
|
|
||||||
|
]}
|
||||||
|
```
|
||||||
|
## 3. VPS DEPLOYMENT STRATEGY (DOXA.LUKAN.CZ)
|
||||||
|
Since the application (web version) will run on its own virtual private network (VPS) with the architecture:
|
||||||
|
|
||||||
|
### **Architecture (Purely client-side)**
|
||||||
|
|
||||||
|
**Technical data storage solution** - The application runs on a subdomain, but the data is stored in the browser (LocalStorage / IndexedDB) with option to manually export/import JSON file.
|
||||||
|
|
||||||
|
**Security aspect** - Absolute security. No data leaves your browser to the network.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The application is open-source and licensed under the MIT license.
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
"It doesn't matter how complex the circumstances around you are. When you build your own temple of ideas, you start from the first stone. This code is your first stone." — Stoic reference from Kankys
|
||||||
|
|
||||||
|
---
|
||||||
|
<div id="czech-version"></div>
|
||||||
|
|
||||||
|
# 🇨🇿 PROJEKT DOXA Notes
|
||||||
|
|
||||||
|
#### Technický a architektonický manifest minimalistické poznámkové aplikace
|
||||||
|
|
||||||
|
**Záměr projektu:** Vytvořit absolutně nezávislé, bezpečné a minimalistické prostředí pro uchovávání lidských
|
||||||
|
myšlenek, poznámek a životních reflexí. V souladu se liberálními principy soběstačnosti aplikace nespoléhá na
|
||||||
|
cloudové databáze třetích stran, nýbrž operuje plně lokálně s otevřeným formátem JSON na vlastním serverovém
|
||||||
|
zázemí.
|
||||||
|
|
||||||
|
## 1. TECHNICKÁ ARCHITEKTURA
|
||||||
|
Pro zachování maximální rychlosti, čistoty kódu a nulových externích závislostí bude aplikace postavena na čistém
|
||||||
|
webovém standardu (tzv. Vanilla stack):
|
||||||
|
|
||||||
|
Frontend: Sémantické HTML5 pro strukturu, CSS3 pro minimalistické rozhraní, které nezatěžuje mysl
|
||||||
|
barevným chaosem.
|
||||||
|
|
||||||
|
Klientská logika: Čistý JavaScript (ES6+) bez frameworků (React/Vue). Vše běží nativně v prohlížeči.
|
||||||
|
|
||||||
|
Datová vrstva: Standardizovaný formát JSON. Poznámky jsou strukturovány jako pole objektů, kde každá
|
||||||
|
položka má jednoznačný identifikátor, časové razítko a textový obsah.
|
||||||
|
|
||||||
|
Desktop aplikace pro Linux a Windows jsou zabaleny do instalátorů AppImage - Linux a exe (Windows).
|
||||||
|
|
||||||
|
## 2. STRUKTURA DATOVÉHO FORMÁTU (JSON)
|
||||||
|
Čistota dat zaručuje, že tvé myšlenky budou čitelné i za dvacet let na jakémkoli zařízení. Základní schéma jedné
|
||||||
|
poznámky vypadá následovně:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"id": "1719147600000",
|
||||||
|
"title": "Ranní reflexe",
|
||||||
|
"content": "Dnes odepřena digitální iluze. Mysl je klidná a pevná.",
|
||||||
|
"created_at": "2026-06-23T08:00:00Z",
|
||||||
|
"updated_at": "2026-06-23T08:00:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## 3. STRATEGIE NASAZENÍ NA VPS (DOXA.LUKAN.CZ)
|
||||||
|
Jelikož aplikace (webová verze) bude běží na vlastní virtuální privátní síti (VPS) s architekturou:
|
||||||
|
|
||||||
|
### **Architektura (Čistě klientská)**
|
||||||
|
|
||||||
|
**Technické řešení ukládání dat** - Aplikace běží na subdoméně, ale data se ukládají v prohlížeči (LocalStorage / IndexedDB) s možností manuálního exportu/importu JSON souboru.
|
||||||
|
|
||||||
|
**Bezpečnostní aspekt** - Absolutní bezpečnost. Žádná data neodcházejí z tvého prohlížeči na síť.
|
||||||
|
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
Aplikace je open-source a licencována pod MIT licencí.
|
||||||
|
|
||||||
|
--
|
||||||
|
|
||||||
|
"Nezáleží na tom, jak složité jsou okolnosti kolem tebe. Když stavíš vlastní chrám myšlenek, začínáš od prvního kamene. Tento kód je tvým prvním kamenem." — Stoický odkaz od Kankyse
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="cs">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>O projektu — DOXA</title>
|
||||||
|
<link rel="stylesheet" href="assets/style/style.css" />
|
||||||
|
<link rel="shortcut icon" href="assets/img/brain-solid-full.svg" />
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css"
|
||||||
|
/>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<nav class="top-nav">
|
||||||
|
<a href="index.html">Domů</a>
|
||||||
|
<a href="navod.html">Návod</a>
|
||||||
|
<a href="privacy.html">Ochrana soukromí</a>
|
||||||
|
<a href="about.html">O projektu</a>
|
||||||
|
<button
|
||||||
|
id="themeToggle"
|
||||||
|
class="theme-toggle"
|
||||||
|
aria-label="Přepnout motiv"
|
||||||
|
>
|
||||||
|
🌙
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<h1>O projektu — DOXA</h1>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p><img src="assets/img/doxa.png" alt="Doxa notes" width="50%" height="auto" style="display: block; margin: 0 auto;" /></p>
|
||||||
|
|
||||||
|
<h2>Manifest digitální suverenity a minimalistického myšlení</h2>
|
||||||
|
<p><strong>Záměr projektu:</strong></p>
|
||||||
|
<p>Vytvořit absolutně nezávislé, bezpečné a minimalistické prostředí pro uchovávání lidských myšlenek, poznámek a životních reflexí. V souladu se základními principy osobní svobody a soběstačnosti aplikace vědomě odmítá cloudové databáze, servery třetích stran a centralizovaná API. Doxa věří, že lidská mysl nepotřebuje být sledována algoritmy, aby mohla růst.</p>
|
||||||
|
<h3>1. Radikální Local-First Filozofie</h3>
|
||||||
|
<p>Aplikace funguje na principu absolutní decentralizace. Běží na tvém zařízení a tam také umírá i žije.</p>
|
||||||
|
<ul>
|
||||||
|
<li>Žádný backend, žádné API: Doxa vědomě neobsahuje žádný kód pro odesílání dat na vzdálené servery. Tím je eliminováno riziko úniku dat, výpadku serveru nebo změny obchodních podmínek poskytovatele.</li>
|
||||||
|
<li>Absolutní soukromí: Data jsou uložena výhradně v tvém prohlížeči (localStorage) nebo lokálním souboru. Co napíšeš, to zůstává tvým výhradním vlastnictvím.</li>
|
||||||
|
</ul>
|
||||||
|
<h3>2. Formát JSON: Trvanlivost napříč dekádami</h3>
|
||||||
|
<p>Data jsou strukturována do otevřeného, čistého a lidsky čitelného formátu JSON. Žádné proprietární databáze.</p>
|
||||||
|
<ul>
|
||||||
|
<li>Tvá data ti patří. Díky možnosti manuálního exportu si můžeš svůj myšlenkový fond odnést kamkoli – uložit si ho na svůj bezpečný lokální disk, nahrát na vlastní Git (GitHub/Gitea), nebo jej synchronizovat skrze cloudovou službu, kterou ty sám uznáš za bezpečnou.</li>
|
||||||
|
<li>Čistota formátu zaručuje, že tvé poznámky budou čitelné i za dvacet let na jakémkoli zařízení, bez ohledu na to, zda Doxa bude stále existovat.</li>
|
||||||
|
</ul>
|
||||||
|
<h3>3. Technická čistota a minimalismus (Vanilla Stack)</h3>
|
||||||
|
<p>Pro zachování maximální rychlosti, nezávislosti a nulových externích závislostí je aplikace postavena výhradně na čistých webových standardech: </p>
|
||||||
|
<ul>
|
||||||
|
<li>Frontend (HTML5/CSS3): Minimalistické rozhraní, které nezatěžuje mysl barevným chaosem a vizuálním balastem moderního internetu.</li>
|
||||||
|
<li>Logika (Vanilla JavaScript ES6+): Žádné těžkopádné frameworky (React/Vue), které podléhají módním trendům a neustálým změnám. Čistý kód je trvanlivý jako tesání do kamene.</li>
|
||||||
|
</ul>
|
||||||
|
<p>„Nezáleží na tom, jak složité jsou okolnosti kolem tebe. Když stavíš vlastní chrám myšlenek, začínáš od prvního kamene. Tento kód a tvá data jsou tvým vlastním, nedobytným hradem.“
|
||||||
|
|
||||||
|
— Stoický odkaz pro svobodné myslitele</p>
|
||||||
|
<hr>
|
||||||
|
<h2>Architektura</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Frontend: čisté HTML/CSS/JS (bez frameworků).</li>
|
||||||
|
<li>Datový formát: otevřený JSON — trvanlivý a přenosný.</li>
|
||||||
|
<li>
|
||||||
|
Local‑first: data jsou lokální (localStorage nebo soubor).
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Co bylo přidáno naposledy</h2>
|
||||||
|
<li>Podpora více poznámek v jednom JSON (název + obsah).</li>
|
||||||
|
<li>Přidání, editace (inline + modal), mazání poznámek.</li>
|
||||||
|
<li>Přesouvání pořadí (šípky + drag & drop).</li>
|
||||||
|
<li>
|
||||||
|
Import / Export JSON a fallback mezi localStorage a lokálním
|
||||||
|
souborem (Electron).
|
||||||
|
</li>
|
||||||
|
<li>Přidána Markdown podpora</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Další plány</h2>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li>Rozšíření UI (hledání, tagy, historie/undo).</li>
|
||||||
|
</ul>
|
||||||
|
<hr>
|
||||||
|
<p>
|
||||||
|
Projekt najdeš v repozitáři
|
||||||
|
<a href="https://codeberg.org/kankys/doxa" target="_blank"
|
||||||
|
>Codeberg</a
|
||||||
|
>. Pokud chceš přispět, otevři issue nebo PR.
|
||||||
|
</p>
|
||||||
|
<p>Web autora, další projekty a podpora: <a href="https://lukaskanka.cz/" target="_blank">Lukáš Kanka</a></p>
|
||||||
|
<p>Aplikace je open-source a licencována pod MIT licencí.</p>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p><a href="index.html">Zpět na hlavní stránku</a></p>
|
||||||
|
</div>
|
||||||
|
<footer><p class="copyright">©2026 Doxa Notes - <a href="https://lukaskanka.cz/" target="_blank">Kankys</a></p></a</footer>
|
||||||
|
<script src="assets/js/script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M184 120C184 89.1 209.1 64 240 64L264 64C281.7 64 296 78.3 296 96L296 544C296 561.7 281.7 576 264 576L232 576C202.2 576 177.1 555.6 170 528C169.3 528 168.7 528 168 528C123.8 528 88 492.2 88 448C88 430 94 413.4 104 400C84.6 385.4 72 362.2 72 336C72 305.1 89.6 278.2 115.2 264.9C108.1 252.9 104 238.9 104 224C104 179.8 139.8 144 184 144L184 120zM456 120L456 144C500.2 144 536 179.8 536 224C536 239 531.9 253 524.8 264.9C550.5 278.2 568 305 568 336C568 362.2 555.4 385.4 536 400C546 413.4 552 430 552 448C552 492.2 516.2 528 472 528C471.3 528 470.7 528 470 528C462.9 555.6 437.8 576 408 576L376 576C358.3 576 344 561.7 344 544L344 96C344 78.3 358.3 64 376 64L400 64C430.9 64 456 89.1 456 120z"/></svg>
|
||||||
|
After Width: | Height: | Size: 920 B |
|
After Width: | Height: | Size: 658 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,382 @@
|
|||||||
|
/* Theme variables */
|
||||||
|
:root {
|
||||||
|
--bg: #fafafa;
|
||||||
|
--text: #333333;
|
||||||
|
--muted: #666666;
|
||||||
|
|
||||||
|
--button-bg: #333333;
|
||||||
|
--button-text: #ffffff;
|
||||||
|
|
||||||
|
--note-bg: #ffffff;
|
||||||
|
--note-border: #dddddd;
|
||||||
|
--note-hover: #f5f5f5;
|
||||||
|
|
||||||
|
--accent: #0b63d6;
|
||||||
|
|
||||||
|
--modal-bg: #ffffff;
|
||||||
|
--modal-overlay: rgba(0, 0, 0, 0.45);
|
||||||
|
|
||||||
|
--active-bg: #e6f0ff;
|
||||||
|
--active-border: #a6c8ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme overrides */
|
||||||
|
body.dark {
|
||||||
|
--bg: #0b1220;
|
||||||
|
--text: #e6eef8;
|
||||||
|
--muted: #9aa6b2;
|
||||||
|
|
||||||
|
--button-bg: #1f2937;
|
||||||
|
--button-text: #ffffff;
|
||||||
|
|
||||||
|
--note-bg: #071022;
|
||||||
|
--note-border: #263046;
|
||||||
|
--note-hover: #092033;
|
||||||
|
|
||||||
|
--accent: #3b82f6;
|
||||||
|
|
||||||
|
--modal-bg: #0b1220;
|
||||||
|
--modal-overlay: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
|
--active-bg: rgba(59, 130, 246, 0.14);
|
||||||
|
--active-border: #60a5fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base */
|
||||||
|
body {
|
||||||
|
font-family: monospace;
|
||||||
|
background: var(--bg);
|
||||||
|
padding: 40px;
|
||||||
|
color: var(--text);
|
||||||
|
transition:
|
||||||
|
background 200ms ease,
|
||||||
|
color 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a,
|
||||||
|
.top-nav .theme-toggle {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a:hover,
|
||||||
|
.top-nav .theme-toggle:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a.active {
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteTitle {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 150px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notesContainer {
|
||||||
|
margin-top: 10px;
|
||||||
|
border-top: 1px solid var(--note-border);
|
||||||
|
padding-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item {
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--note-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.active {
|
||||||
|
background: var(--active-bg);
|
||||||
|
border-color: var(--active-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-notes {
|
||||||
|
color: var(--muted);
|
||||||
|
font-style: italic;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note list layout */
|
||||||
|
.note-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-left {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding-right: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-title {
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-preview {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: monospace;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.drag-over {
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.drag-after {
|
||||||
|
box-shadow: inset 0 -3px 0 0 rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.dragging {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--modal-overlay);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-overlay.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
background: var(--modal-bg);
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 92%;
|
||||||
|
max-width: 720px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 8px;
|
||||||
|
top: 8px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal input,
|
||||||
|
.modal textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
.container {
|
||||||
|
max-width: 900px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notes-hint {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Editor toolbar & preview */
|
||||||
|
.editor-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 6px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* active toolbar button */
|
||||||
|
.editor-toolbar button.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--button-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-wrap {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-wrap textarea {
|
||||||
|
flex: 1 1 60%;
|
||||||
|
min-height: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
flex: 1 1 40%;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
background: var(--note-bg);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-actions {
|
||||||
|
margin-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-status {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rendered markdown styles */
|
||||||
|
.preview h1,
|
||||||
|
.preview h2,
|
||||||
|
.preview h3 {
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
.preview pre {
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
padding: 8px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.preview code {
|
||||||
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.editor-wrap {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.preview {
|
||||||
|
max-height: 240px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 16px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M184 120C184 89.1 209.1 64 240 64L264 64C281.7 64 296 78.3 296 96L296 544C296 561.7 281.7 576 264 576L232 576C202.2 576 177.1 555.6 170 528C169.3 528 168.7 528 168 528C123.8 528 88 492.2 88 448C88 430 94 413.4 104 400C84.6 385.4 72 362.2 72 336C72 305.1 89.6 278.2 115.2 264.9C108.1 252.9 104 238.9 104 224C104 179.8 139.8 144 184 144L184 120zM456 120L456 144C500.2 144 536 179.8 536 224C536 239 531.9 253 524.8 264.9C550.5 278.2 568 305 568 336C568 362.2 555.4 385.4 536 400C546 413.4 552 430 552 448C552 492.2 516.2 528 472 528C471.3 528 470.7 528 470 528C462.9 555.6 437.8 576 408 576L376 576C358.3 576 344 561.7 344 544L344 96C344 78.3 358.3 64 376 64L400 64C430.9 64 456 89.1 456 120z"/></svg>
|
||||||
|
After Width: | Height: | Size: 920 B |
@@ -0,0 +1,180 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="cs">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Doxa</title>
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="https://doxa.lukan.cz/" />
|
||||||
|
<meta
|
||||||
|
property="og:title"
|
||||||
|
content="Doxa je navržena jako local‑first aplikace — soukromí je primární designový princip."
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="Krátké shrnutí: Doxa je minimalistická, local‑first poznámková aplikace založená na otevřeném JSON formátu. Data zůstávají lokálně (localStorage nebo soubor) — aplikace nic sama neodesílá."
|
||||||
|
/>
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta
|
||||||
|
name="twitter:title"
|
||||||
|
content="Doxa je navržena jako local‑first aplikace — soukromí je primární designový princip."
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:description"
|
||||||
|
content="Doxa je navržena jako local‑first aplikace — soukromí je primární designový princip."
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="Doxa"
|
||||||
|
content="Doxa je navržena jako local‑first aplikace — soukromí je primární designový princip."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="assets/style/style.css" />
|
||||||
|
<link rel="shortcut icon" href="assets/img/brain-solid-full.svg" />
|
||||||
|
<!-- Markdown and sanitization libraries (used for preview). CDN fallback; if offline, preview will be disabled. -->
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css"
|
||||||
|
/>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<nav class="top-nav">
|
||||||
|
<a href="index.html">Domů</a>
|
||||||
|
<a href="navod.html">Návod</a>
|
||||||
|
<a href="privacy.html">Ochrana soukromí</a>
|
||||||
|
<a href="about.html">O projektu</a>
|
||||||
|
<button
|
||||||
|
id="themeToggle"
|
||||||
|
class="theme-toggle"
|
||||||
|
aria-label="Přepnout motiv"
|
||||||
|
>
|
||||||
|
🌙
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
<h1>Doxa</h1>
|
||||||
|
<input id="noteTitle" placeholder="Název poznámky" />
|
||||||
|
|
||||||
|
<div class="editor-toolbar" id="editorToolbar">
|
||||||
|
<button type="button" data-action="h1" title="Nadpis H1">
|
||||||
|
H1
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="h2" title="Nadpis H2">
|
||||||
|
H2
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="h3" title="Nadpis H3">
|
||||||
|
H3
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" data-action="bold" title="Tučné (Ctrl+B)">
|
||||||
|
<strong>B</strong>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-action="italic"
|
||||||
|
title="Kurzíva (Ctrl+I)"
|
||||||
|
>
|
||||||
|
<em>I</em>
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="strike" title="Přeškrtnutí">
|
||||||
|
S
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" data-action="ul" title="Odrážkový seznam">
|
||||||
|
•
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="ol" title="Číslovaný seznam">
|
||||||
|
1.
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-action="task"
|
||||||
|
title="Úkol (checkbox)"
|
||||||
|
>
|
||||||
|
☐
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" data-action="quote" title="Citace">
|
||||||
|
❝
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="hr" title="Vodorovná čára">
|
||||||
|
—
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" data-action="code" title="Inline kód">
|
||||||
|
`kód`
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="codeblock" title="Blok kódu">
|
||||||
|
</>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" data-action="link" title="Vložit odkaz">
|
||||||
|
🔗
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-action="image"
|
||||||
|
title="Vložit obrázek"
|
||||||
|
>
|
||||||
|
🖼
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-action="table"
|
||||||
|
title="Vložit tabulku"
|
||||||
|
>
|
||||||
|
▦
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="livePreviewToggle"
|
||||||
|
title="Živý náhled"
|
||||||
|
>
|
||||||
|
Live
|
||||||
|
</button>
|
||||||
|
<button type="button" id="togglePreviewMain" title="Náhled">
|
||||||
|
Náhled
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-wrap">
|
||||||
|
<textarea
|
||||||
|
id="noteInput"
|
||||||
|
placeholder="Zapiš poznámku..."
|
||||||
|
></textarea>
|
||||||
|
<div
|
||||||
|
id="notePreview"
|
||||||
|
class="preview hidden"
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-actions">
|
||||||
|
<button
|
||||||
|
onclick="saveCurrentNote()"
|
||||||
|
title="Uložit nyní (manuální uložení)"
|
||||||
|
aria-label="Uložit nyní"
|
||||||
|
>
|
||||||
|
Uložit nyní
|
||||||
|
</button>
|
||||||
|
<button onclick="saveNotesToFile(true)">Exportovat JSON</button>
|
||||||
|
<button onclick="importujPoznamky()">Importovat JSON</button>
|
||||||
|
<button id="newNoteButton">➕ Nová poznámka</button>
|
||||||
|
<span
|
||||||
|
id="saveStatus"
|
||||||
|
class="save-status"
|
||||||
|
aria-live="polite"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="notesContainer"></div>
|
||||||
|
<div id="notesHint" class="notes-hint">
|
||||||
|
Přetáhněte poznámky pro změnu pořadí nebo použijte šipky vedle
|
||||||
|
položek.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="assets/js/script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
const { app, BrowserWindow } = require('electron');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
const win = new BrowserWindow({
|
||||||
|
width: 1200,
|
||||||
|
height: 800,
|
||||||
|
icon: path.join(__dirname, 'brain-solid-full.svg'), // Pokud máš ikonu, jinak můžeš řádek smazat
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Načte tvůj hlavní HTML soubor webu
|
||||||
|
win.loadFile('index.html');
|
||||||
|
|
||||||
|
// Skryje horní menu (File, Edit atd.) pro čistý, minimalistický vzhled
|
||||||
|
win.setMenu(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
createWindow();
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') app.quit();
|
||||||
|
});
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="cs">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Návod — Doxa</title>
|
||||||
|
<link rel="stylesheet" href="assets/style/style.css" />
|
||||||
|
<link rel="shortcut icon" href="assets/img/brain-solid-full.svg" />
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<nav class="top-nav">
|
||||||
|
<a href="index.html">Domů</a>
|
||||||
|
<a href="navod.html">Návod</a>
|
||||||
|
<a href="privacy.html">Ochrana soukromí</a>
|
||||||
|
<a href="about.html">O projektu</a>
|
||||||
|
<button id="themeToggle" class="theme-toggle" aria-label="Přepnout motiv">🌙</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<h1>Návod k použití — Doxa (v26.6.04.beta)</h1>
|
||||||
|
|
||||||
|
<p>Krátké shrnutí: Doxa je minimalistická, <strong>local‑first</strong> poznámková aplikace založená na otevřeném JSON formátu s podporou Markdown. Data zůstávají lokálně (localStorage nebo soubor) — aplikace nic sama neodesílá.</p>
|
||||||
|
|
||||||
|
<h2>Rychlý start - pouze pro variantu spouštění aplikace u sebe lokalně</h2>
|
||||||
|
<p>Pokud aplikaci spouštíš z webu <strong>https://doxa.lukan.cz/</strong> pokračuj dalším bodem!</p>
|
||||||
|
<ol>
|
||||||
|
<li>Stáhni/klonuj repozitář a přejdi do složky aplikace:
|
||||||
|
<pre><strong>cd doxa/web_app</strong></pre>
|
||||||
|
</li>
|
||||||
|
<li>Spusť jednoduchý lokální server (doporučeno):
|
||||||
|
<pre><strong>python3 -m http.server 8000</strong></pre>
|
||||||
|
a otevři v prohlížeči: <code><strong>http://localhost:8000</strong></code>
|
||||||
|
</li>
|
||||||
|
<li>alternativně můžeš použít Node.js a pomocí npm install nainstaluj live-server a následně spusť pomocí příkazu: <code><strong>live-server</strong></code> ve složce aplikace.
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2>Desktová aplikace</h2>
|
||||||
|
<p>1. Desktová aplikace je dostupná pro Windows a Linux. Stačí si ji stáhnout z <a href="https://codeberg.org/Kankys/doxa/releases" target="_blank">Codeberg</a> a spustit.</p>
|
||||||
|
<p>2. Staženou aplikaci spustíš spustíš otevřením souboru linux - .AppImage</p>
|
||||||
|
<p>3. Soubory aplikací najdeš ve složce:</p>
|
||||||
|
|
||||||
|
<p><strong>Linux:</strong> <code>~/.config/doxa/</code></p>
|
||||||
|
<p><strong>Nezapomeň si dělat pravidélné zálohy JSON (exportovat JSON)</strong></p>
|
||||||
|
<p><strong>Nezapomeň si pravidelně aktualizovat aplikaci, abys mohl mít nejnovější verzi.</strong></p>
|
||||||
|
<p><strong>POZOR: v desktop verzi se nachází chyba s přidáváním nových poznámek. Aby jste mohli přidat novou poznámku, musíte nejprve kliknout na jinou záložku v menu a poté zpět na záložku poznámek.</strong></p>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h2>Vytvoření nové poznámky</h2>
|
||||||
|
<ol>
|
||||||
|
<li>Klikni na tlačítko <strong>➕ Nová poznámka</strong> a zadej název.</li>
|
||||||
|
<li>Položka se objeví v seznamu. Klikem načteš název a obsah do editoru nahoře.</li>
|
||||||
|
<li>Uprav obsah a klikni <strong>Uložit nyní</strong>.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2>Editace a mazání</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Inline: klikni na poznámku, uprav obsah a stiskni <strong>Uložit nyní</strong>.</li>
|
||||||
|
<li>Modal: klikni na ikonu ✎ vedle položky pro pokročilou editaci v modal okně.</li>
|
||||||
|
<li>Mazání: ikona 🗑 u položky — po potvrzení se poznámka smaže.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Přesouvání pořadí poznámek</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Použij šipky ↑ / ↓ u položek.</li>
|
||||||
|
<li>Nebo přetahuj položky (drag & drop) na požadované místo — při přetažení se ukáže indikátor vložení.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Přepínání vzhledu</h2>
|
||||||
|
<p>V horním menu najdeš tlačítko pro přepínání vzhledu. Aplikace podporuje tři režimy:
|
||||||
|
<ul>
|
||||||
|
<li><strong>Světlý</strong> — ikona: ☀️</li>
|
||||||
|
<li><strong>Tmavý</strong> — ikona: 🌙</li>
|
||||||
|
<li><strong>Respektovat systém</strong> — ikona: 🌓 — aplikace bude sledovat systémovou volbu (prefers-color-scheme) a automaticky se přepne, pokud se změní systémový motiv.</li>
|
||||||
|
</ul>
|
||||||
|
Kliknutím na tlačítko v menu cyklíš mezi režimy (Light → Dark → System). Vybraná volba se uloží do <code>localStorage</code> pod klíčem <code>doxa_theme</code> a přetrvá mezi stránkami a obnovami. Pokud chceš volbu resetovat, otevři konzoli prohlížeče a spusť: <code>localStorage.removeItem('doxa_theme')</code> nebo vymaž storage v nástrojích pro vývojáře.</p>
|
||||||
|
|
||||||
|
<h2>Automatické ukládání</h2>
|
||||||
|
<p>Aplikace nyní automaticky ukládá změny poznámek lokálně během psaní (autosave). V běžném prohlížeči se změny ukládají do <code>localStorage</code> (klíč <code>doxa_notes_v1</code>) bez nutnosti manuálního ukládání — data se uloží po krátké prodlevě. Pokud aplikaci spouštíš v Electron/Node režimu, změny se zapisují přímo do lokálního souboru.</p>
|
||||||
|
<p style="color: red;"><strong>Průběžně si úkládej i JSON soubor manuálně!!</strong></p>
|
||||||
|
<h2>Manuální uložení</h2>
|
||||||
|
<p>Vedle editoru najdeš tlačítko <strong>Uložit nyní</strong>. I když autosave ukládá průběžně, tlačítko je užitečné v těchto případech:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Okamžité uložení</strong> — provede persist bez čekání na autosave (užitečné před zavřením aplikace nebo pokud chcete rychlou jistotu, že jsou změny zapsány).</li>
|
||||||
|
<li><strong>Vizuální potvrzení</strong> — po uložení se zobrazí stav „Uloženo“, což dává uživateli okamžitou zpětnou vazbu.</li>
|
||||||
|
<li><strong>Bez stažení</strong> — tlačítko <strong>Uložit poznámku</strong> neprovádí stažení souboru; pokud chcete stáhnout JSON, použijte tlačítko <strong>Exportovat JSON</strong>.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>Pro přenos dat mezi zařízeními použijte tlačítko <strong>Exportovat JSON</strong> (stáhne soubor <code>doxa.json</code>), nebo z konzole spusťte <code>saveNotesToFile(true)</code>.</p>
|
||||||
|
|
||||||
|
<h2>Import / Export</h2>
|
||||||
|
<p>Pro import klikni <strong>Importovat JSON</strong> a vyber soubor. Podporované formáty:
|
||||||
|
<ul>
|
||||||
|
<li>Objekt s polem <code>notes</code>: <code>{ "notes": [ ... ] }</code></li>
|
||||||
|
<li>Přímo pole: <code>[ { ... }, { ... } ]</code></li>
|
||||||
|
</ul>
|
||||||
|
Export je nyní dostupný přes tlačítko <strong>Exportovat JSON</strong> (vedle tlačítka Uložit). Aplikace zároveň provádí autosave lokálně — nic neztratíte, pokud zapomenete manuálně uložit. Chcete-li stáhnout JSON přes skript, můžete také spustit v konzoli <code>saveNotesToFile(true)</code>.</p>
|
||||||
|
|
||||||
|
<h2>Bezpečnost a zálohy</h2>
|
||||||
|
<p>Výchozí režim je <strong>local‑first</strong>: data zůstávají ve tvém prohlížeči (localStorage) nebo lokálním souboru, aplikace je sama neodesílá.</p>
|
||||||
|
<hr>
|
||||||
|
<h2>Editor používá Markdown</h2>
|
||||||
|
<p>Editor pracuje s <strong>Markdown</strong> syntaxí. Všechny úpravy se ukládají jako Markdown (čistý text) — náhled ukazuje vykreslené HTML. Níže základní příklady (vložte je přímo do editoru nebo použijte tlačítka v toolbaru):</p>
|
||||||
|
|
||||||
|
<pre><code>Tučné: **tučný text**
|
||||||
|
Kurzíva: *kurzíva*
|
||||||
|
Přeškrtnutí: ~~přeškrtnuté~~
|
||||||
|
Inline kód: `kód`
|
||||||
|
Kódový blok:
|
||||||
|
```
|
||||||
|
console.log('hello')
|
||||||
|
```
|
||||||
|
Nadpisy:
|
||||||
|
# Nadpis H1
|
||||||
|
## Nadpis H2
|
||||||
|
### Nadpis H3
|
||||||
|
Seznam (odrážky):
|
||||||
|
- položka 1
|
||||||
|
- položka 2
|
||||||
|
Číslovaný seznam:
|
||||||
|
1. první
|
||||||
|
2. druhý
|
||||||
|
Citace:
|
||||||
|
> Toto je citace
|
||||||
|
Tabulka:
|
||||||
|
| Sloupec 1 | Sloupec 2 |
|
||||||
|
| --- | --- |
|
||||||
|
| bunka 1 | bunka 2 |
|
||||||
|
| bunka 3 | bunka 4 |
|
||||||
|
Obrázek (referenční URL):
|
||||||
|

|
||||||
|
Odkaz:
|
||||||
|
[text](https://example.com)
|
||||||
|
Úkoly (task list):
|
||||||
|
- [ ] nedokončeno
|
||||||
|
- [x] hotovo
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>Důležité tipy:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Kliknutím na tlačítka v toolbaru vložíte potřebné Markdown tokeny automaticky.</li>
|
||||||
|
<li>Stiskněte <kbd>Ctrl/Cmd+B</kbd> pro tučné, <kbd>Ctrl/Cmd+I</kbd> pro kurzívu a <kbd>Ctrl/Cmd+`</kbd> pro inline kód.</li>
|
||||||
|
<li>Tlačítko <strong>Náhled</strong> přepne vykreslení Markdownu; používejte jej k ověření výsledku.</li>
|
||||||
|
<li>Tlačítko <strong>Live</strong> zapne průběžný živý náhled — preview se automaticky aktualizuje během psaní a zobrazí se, pokud nebyl ručně skrytý.</li>
|
||||||
|
<li>Tlačítko <strong>Tabulka</strong> (▦) vloží šablonu tabulky a během vložení vás vyzve na počet sloupců a řádků.</li>
|
||||||
|
<li>Obrázky vkládejte jako URL — aplikace obrázky nesynchronizuje ani nahrává.</li>
|
||||||
|
<li>Markdown je přenosný: poznámky zůstávají čitelné i bez aplikace (export/import).</li>
|
||||||
|
</ul>
|
||||||
|
<h2>Časté problémy</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Při otevírání přes <code>file://</code> může fetch statického JSONu selhávat — použij lokální server nebo Electron.</li>
|
||||||
|
<li>Přenos dat mezi zařízeními? Stáhni JSON a importuj ho na druhém zařízení.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p><a href="index.html">Zpět na hlavní stránku</a></p>
|
||||||
|
</div>
|
||||||
|
<footer><p class="copyright">©2026 Doxa Notes - <a href="https://lukaskanka.cz/" target="_blank">Kankys</a></p></a</footer>
|
||||||
|
<script src="assets/js/script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "doxa",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Ostrovy myšlenek v moři chaosu",
|
||||||
|
"author": "Kankys",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "npx electron .",
|
||||||
|
"dist": "electron-builder"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"electron": "30.5.1",
|
||||||
|
"electron-builder": "^24.13.0"
|
||||||
|
},
|
||||||
|
"allowScripts": {
|
||||||
|
"electron": true
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"appId": "cz.lukan.doxa",
|
||||||
|
"productName": "Doxa.26.06.03.beta",
|
||||||
|
"electronVersion": "30.5.1",
|
||||||
|
"win": {
|
||||||
|
"target": ["nsis"]
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"target": ["AppImage"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="cs">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Ochrana soukromí — Doxa</title>
|
||||||
|
<link rel="stylesheet" href="assets/style/style.css" />
|
||||||
|
<link rel="shortcut icon" href="assets/img/brain-solid-full.svg" />
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css"
|
||||||
|
/>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<nav class="top-nav">
|
||||||
|
<a href="index.html">Domů</a>
|
||||||
|
<a href="navod.html">Návod</a>
|
||||||
|
<a href="privacy.html">Ochrana soukromí</a>
|
||||||
|
<a href="about.html">O projektu</a>
|
||||||
|
<button
|
||||||
|
id="themeToggle"
|
||||||
|
class="theme-toggle"
|
||||||
|
aria-label="Přepnout motiv"
|
||||||
|
>
|
||||||
|
🌙
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<h1>Ochrana soukromí</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Doxa je navržena jako <strong>local‑first</strong> aplikace
|
||||||
|
— soukromí je primární designový princip. Níže shrnutí, jak
|
||||||
|
zacházím s daty v aktuálním stavu projektu.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Co aplikace ukládá</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Poznámky: <code>id</code>, <code>name</code>,
|
||||||
|
<code>content</code>, <code>created_at</code>,
|
||||||
|
<code>modified_at</code> (pokud existuje).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Žádné automatické telemetry ani sledování není
|
||||||
|
implementováno.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Kde jsou data uložena</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
V prohlížeči: <strong>localStorage</strong> (klíč
|
||||||
|
<code>Doxa_notes_v1</code>).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
V Electron/Node režimu: soubor na disku (např.
|
||||||
|
<code>assets/js/data/doxa.json</code>).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Export/import: uživatel může stáhnout JSON a nahrát ho na
|
||||||
|
jiném zařízení — je to manuální proces.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Co aplikace NEODESÍLÁ</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Výchozí instalace neposílá poznámky na žádný vzdálený
|
||||||
|
server.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Bez implementace serverové synchronizace nejsou data
|
||||||
|
odesílána ani zálohována vůči třetím stranám.
|
||||||
|
</li>
|
||||||
|
</ul
|
||||||
|
|
||||||
|
<h2>Odpovědnost uživatele</h2>
|
||||||
|
<p>
|
||||||
|
Protože data jsou lokální, odpovědnost za zálohy a bezpečnou
|
||||||
|
konfiguraci serveru (pokud použita) nese vlastník instalace.
|
||||||
|
Doporučuji pravidelné exporty a bezpečné úložiště záloh.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><a href="index.html">Zpět na hlavní stránku</a></p>
|
||||||
|
</div>
|
||||||
|
<footer><p class="copyright">©2026 Doxa Notes - <a href="https://lukaskanka.cz/" target="_blank">Kankys</a></p></a</footer>
|
||||||
|
<script src="assets/js/script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
# SEO a Playwright Testy - Audit a Plán Zlepšení
|
||||||
|
|
||||||
|
## Problémy Identifikované
|
||||||
|
|
||||||
|
### 🔴 SEO - Web (doxa-notes.org)
|
||||||
|
|
||||||
|
#### Kritické problémy:
|
||||||
|
1. **Chybějící Open Graph obrázek v index.html**
|
||||||
|
- `og:image` není specifikován, pouze Twitter image
|
||||||
|
- Bez OG image se socmédia nezobrazí korektně
|
||||||
|
|
||||||
|
2. **Chybějící meta tags pro mobily**
|
||||||
|
- Chybí `viewport` meta tag (nemá měřítko a zoomu)
|
||||||
|
- Dopad: špatné zobrazení na mobilech, horší SEO ranking
|
||||||
|
|
||||||
|
3. **Nejednotný formát title tagů**
|
||||||
|
- index.html: "DOXA Notes — Local-First poznámky..."
|
||||||
|
- about.html: "O projektu — Doxa" (bez "DOXA Notes")
|
||||||
|
- privacy.html: "Ochrana soukromí — DOXA Notes"
|
||||||
|
- Nejednotnost není ideální, měly by být konsistentní
|
||||||
|
|
||||||
|
4. **Chybí meta description na web_app/index.html**
|
||||||
|
- Web app má `robots: noindex, nofollow` (správně)
|
||||||
|
- Ale chybí base meta description, jen title
|
||||||
|
|
||||||
|
5. **Canonical URL - web_app/index.html**
|
||||||
|
- Chybí canonical tag na app stránce (jen na webu)
|
||||||
|
|
||||||
|
6. **Nejednotné sociální meta tags**
|
||||||
|
- O projektu a privacy nemají OG image, Twitter image
|
||||||
|
- Neúplné Twitter card metadata
|
||||||
|
|
||||||
|
7. **Missing lang attribute na web_app**
|
||||||
|
- web_app/index.html má `lang="cs"`, ale chybí OG locale
|
||||||
|
|
||||||
|
8. **robots.txt nedostatky**
|
||||||
|
- Sitemap odkazuje jen na wiki, ne na hlavní web
|
||||||
|
- Chybí explicit allow/disallow pro jednotlivé web stránky
|
||||||
|
|
||||||
|
### 🔴 SEO - Web App
|
||||||
|
|
||||||
|
1. **robots.txt správně blokuje web_app (noindex)**
|
||||||
|
- Správně: `Disallow: /web_app/`
|
||||||
|
|
||||||
|
2. **Chybějící Open Graph metadata**
|
||||||
|
- Není `og:*` tags vůbec
|
||||||
|
- Twitter card není definován
|
||||||
|
|
||||||
|
3. **Nedostatečný JSON-LD schema**
|
||||||
|
- Na web_app není vůbec strukturovaná data
|
||||||
|
- Jen na index.html SoftwareApplication schema
|
||||||
|
|
||||||
|
### 🔴 Playwright Testy - Problémy
|
||||||
|
|
||||||
|
1. **Chybí test pro Web App SEO**
|
||||||
|
- noteApp.test.ts nemá žádné SEO testy
|
||||||
|
- Měl by testovat noindex/nofollow, title, meta description
|
||||||
|
|
||||||
|
2. **Chybí testy pro OG tagy**
|
||||||
|
- HomePage.ts nemá metodu `getOGImage()`, `getOGTitle()`
|
||||||
|
- Chybí testy pro Twitter card
|
||||||
|
|
||||||
|
3. **Nevyhovující test abecedy menu**
|
||||||
|
- textHelpers nemá "Návod" v menu (ale existuje na webu)
|
||||||
|
- Neúplný seznam očekávaných textů
|
||||||
|
|
||||||
|
4. **Chybí accessibility testy (a11y)**
|
||||||
|
- Žádné testy pro ARIA labels, heading struktura
|
||||||
|
- Nejednotné heading struktura (h1, h2, h3)
|
||||||
|
|
||||||
|
5. **Chybí performance/lighthouse testy**
|
||||||
|
- Žádné testy pro load time, Core Web Vitals
|
||||||
|
- Žádné testy pro lazy loading obrázků
|
||||||
|
|
||||||
|
6. **Playwright config - omezený scope**
|
||||||
|
- Jen chromium, chybí firefox, webkit
|
||||||
|
- Reporter je jen HTML, žádné JSON/JUnit
|
||||||
|
|
||||||
|
7. **Chybí testy pro 404/error stránky**
|
||||||
|
- Žádné testy pro chybové strany
|
||||||
|
- Žádné testy pro chybné CSS/JS asety
|
||||||
|
|
||||||
|
8. **AppPage POM - nedostatečné selektory**
|
||||||
|
- Závisí na ID selektorech, měly by být odolnější
|
||||||
|
- Chybí error handling v navigate()
|
||||||
|
|
||||||
|
9. **Chybí data-testid atributy**
|
||||||
|
- HTML nemá `data-testid` pro stabilní test selektory
|
||||||
|
- Zranitelné na změny CSS/stylů
|
||||||
|
|
||||||
|
10. **Testy neuklízí state**
|
||||||
|
- Testy nevymazávají localStorage mezi spuštěním
|
||||||
|
- Chybí cleanup po testech
|
||||||
|
|
||||||
|
## Pracovní Plán
|
||||||
|
|
||||||
|
### Fáze 1: SEO Optimalizace (Primární)
|
||||||
|
- [ ] Přidat meta viewport tag do všech HTML
|
||||||
|
- [ ] Přidat Open Graph obrázek do všech stran
|
||||||
|
- [ ] Unifikovat title tagy podle formátu
|
||||||
|
- [ ] Doplnit meta descriptions na chybějící stránky
|
||||||
|
- [ ] Přidat OG locale pro Czech
|
||||||
|
- [ ] Doplnit Twitter Card tagy na všechny stránky
|
||||||
|
- [ ] Vytvořit sitemap.xml pro web (ne jen wiki)
|
||||||
|
- [ ] Zlepšit robots.txt
|
||||||
|
|
||||||
|
### Fáze 2: Web App Metadata
|
||||||
|
- [ ] Přidat OG tagy na web_app (s noindex varováním)
|
||||||
|
- [ ] Přidat JSON-LD schema pro WebApplication
|
||||||
|
- [ ] Ověřit noindex direktiva na web_app
|
||||||
|
|
||||||
|
### Fáze 3: Playwright Testy - SEO
|
||||||
|
- [ ] Přidat testy pro OG metadata
|
||||||
|
- [ ] Přidat testy pro Twitter Card
|
||||||
|
- [ ] Přidat testy pro meta descriptions
|
||||||
|
- [ ] Vytvořit SEO utility metody v HomePage.ts
|
||||||
|
- [ ] Vytvořit Web App SEO testy
|
||||||
|
|
||||||
|
### Fáze 4: Playwright Testy - Rozšíření
|
||||||
|
- [ ] Přidat a11y testy (ARIA, heading struktura)
|
||||||
|
- [ ] Přidat testy pro mobile viewport
|
||||||
|
- [ ] Přidat Firefox a WebKit do playwright.config
|
||||||
|
- [ ] Přidat data-testid atributy do HTML
|
||||||
|
- [ ] Přidat AppPage testy pro error states
|
||||||
|
- [ ] Přidat cleanup/setup hooks
|
||||||
|
|
||||||
|
### Fáze 5: Další Vylepšení
|
||||||
|
- [ ] Přidat lighthouse integration testy
|
||||||
|
- [ ] Přidat JSON reporter k playwright
|
||||||
|
- [ ] Přidat CI/CD integration
|
||||||
|
|
||||||
|
## Poznámky
|
||||||
|
- Zaměření primárně na SEO (dle požadavku)
|
||||||
|
- Web vs Web App: Web je SEO friendly, App je noindex (správně)
|
||||||
|
- Playwright testy potřebují kompletní refresh
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Testovací plán projektu DOXA
|
||||||
|
|
||||||
|
## 1. Web (Landing page)
|
||||||
|
- [x] Ověření viditelnosti všech položek menu.
|
||||||
|
- [x] Navigace na vnitřní stránky (O projektu, Ochrana soukromí).
|
||||||
|
- [x] Kontrola funkčnosti externího odkazu na aplikaci.
|
||||||
|
- [x] **SEO testy:** Validace `title`, `meta description` a `canonical link` na všech stránkách.
|
||||||
|
- [ ] *Doporučení:* Přidat test na responsivitu (zobrazení na mobilu/tabletu).
|
||||||
|
|
||||||
|
## 2. Aplikace (Editor poznámek)
|
||||||
|
- [x] Vytvoření, editace a uložení poznámky včetně tagů.
|
||||||
|
- [x] Ověření zobrazení `Slash Commands` menu po napsání "/".
|
||||||
|
- [ ] **Validace vstupů:** Test uložení prázdné poznámky.
|
||||||
|
- [ ] **Správa tagů:** Test smazání tagu z existující poznámky.
|
||||||
|
- [ ] **Persistence:** (Pokud lze) Ověřit, že po znovunačtení stránky poznámka zůstala.
|
||||||
|
|
||||||
|
## 3. Infrastruktura a kvalita
|
||||||
|
- [ ] Implementovat testy na `Accessibility` (přístupnost) pomocí `@axe-core/playwright`.
|
||||||
|
- [ ] Přidat vizuální regresní testy (snapshot testing) pro editor.
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
# 📋 Plán rozšíření automatizovaných testů pro DOXA Notes
|
||||||
|
|
||||||
|
## 1. Analýza současného stavu vs. Cíl
|
||||||
|
* **Současný stav:** Testuje se pouze viditelnost menu a navigace na webu `https://doxa-notes.org`. Core aplikace (`https://app.doxa-notes.org` / `web_app`) nemá žádné testy.
|
||||||
|
* **Cíl:** Vytvořit robustní e2e testy, které pokryjí kompletní životní cyklus poznámky (CRUD), tagy, drag-and-drop, exporty, motivy vzhledu a rychlé příkazy (Slash Commands) na lokálně běžícím serveru.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Navržené testovací scénáře (Test Suite)
|
||||||
|
|
||||||
|
### Oblast A: Jádro aplikace (složka `test/playwright/tests/app/`)
|
||||||
|
Tato část bude testovat funkčnost v `web_app/index.html`.
|
||||||
|
|
||||||
|
1. **Správa poznámek (CRUD)**
|
||||||
|
* **Vytvoření:** Kliknutí na `➕ Nová poznámka`, zadání názvu do promptu, ověření, že se přidala do seznamu a otevřela v editoru.
|
||||||
|
* **Editace:** Zápis textu do editoru, uložení pomocí tlačítka `Uložit nyní` a ověření stavu „Uloženo“.
|
||||||
|
* **Smazání:** Kliknutí na 🗑, potvrzení dialogu (`page.on('dialog')`) a ověření, že poznámka zmizela.
|
||||||
|
* **Pokročilá editace:** Otevření modálu přes ikonu ✎, změna textu, uložení a ověření.
|
||||||
|
|
||||||
|
2. **Tagování a vyhledávání**
|
||||||
|
* **Přidání tagů:** Vyplnění pole `#noteTags` a ověření, že se tagy uložily k poznámce v seznamu.
|
||||||
|
* **Filtrování:** Zadání tagu do vyhledávacího pole a ověření, že se zobrazí pouze poznámky s tímto tagem.
|
||||||
|
|
||||||
|
3. **Markdown a Náhled (Live Preview)**
|
||||||
|
* **Formátování:** Vložení formátování přes tlačítka v toolbaru (Bold, Italic, Nadpisy) a ověření, zda se v textu objevily správné Markdown znaky (`**`, `*`, `#`).
|
||||||
|
* **Náhled:** Přepnutí do režimu „Náhled“ a ověření, že se text správně vyrenderoval do HTML (např. `<strong>` pro tučný text).
|
||||||
|
|
||||||
|
4. **Přesouvání poznámek (Reordering)**
|
||||||
|
* **Šipky:** Kliknutí na šipku dolů/nahoru u poznámky a ověření, zda se změnilo její pořadí v seznamu.
|
||||||
|
* **Drag & Drop:** Simulace přetažení jedné poznámky na druhou a ověření nového pořadí.
|
||||||
|
|
||||||
|
5. **Import & Export**
|
||||||
|
* **Export do Markdownu:** Kliknutí na `Exportovat do Markdown`, zachycení staženého souboru (`page.waitForEvent('download')`) a ověření jeho obsahu.
|
||||||
|
* **Export JSON:** Ověření stažení souboru `doxa.json`.
|
||||||
|
|
||||||
|
6. **Slash Commands (Rychlá volba `/`)**
|
||||||
|
* **Zobrazení menu:** Napsání znaku `/` do editoru a ověření zobrazení slash menu.
|
||||||
|
* **Filtrování:** Napsání `/da` a ověření, že v menu zůstala pouze možnost `/date`.
|
||||||
|
* **Vložení:** Výběr příkazu (Enter/klik) a ověření, že se na pozici kurzoru vložilo např. dnešní datum.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Architektura testů (Page Object Model)
|
||||||
|
|
||||||
|
Pro udržení čistoty kódu vytvoříme nový Page Object `AppPage.ts` a přesuneme spouštění aplikací na lokální server v configu.
|
||||||
|
|
||||||
|
### Krok 1: Úprava `playwright.config.ts`
|
||||||
|
Chceme, aby testy běžely nezávisle na produkční síti. Nakonfigurujeme lokální webserver:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './tests',
|
||||||
|
fullyParallel: true,
|
||||||
|
reporter: 'html',
|
||||||
|
use: {
|
||||||
|
baseURL: 'http://localhost:8080', // Lokální adresa pro testy
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
},
|
||||||
|
webServer: {
|
||||||
|
command: 'npx http-server ../../web_app -p 8080', // Spustí web_app lokálně
|
||||||
|
url: 'http://localhost:8080',
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Krok 2: Vytvoření Page Object Modelu (`tests/app/pages/AppPage.ts`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Page, Locator, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
export class AppPage {
|
||||||
|
readonly page: Page;
|
||||||
|
readonly newNoteButton: Locator;
|
||||||
|
readonly noteTitleInput: Locator;
|
||||||
|
readonly noteTagsInput: Locator;
|
||||||
|
readonly noteEditor: Locator;
|
||||||
|
readonly saveButton: Locator;
|
||||||
|
readonly notesContainer: Locator;
|
||||||
|
readonly searchTagInput: Locator;
|
||||||
|
readonly slashMenu: Locator;
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
this.page = page;
|
||||||
|
this.newNoteButton = page.locator('#newNoteButton');
|
||||||
|
this.noteTitleInput = page.locator('#noteTitle');
|
||||||
|
this.noteTagsInput = page.locator('#noteTags');
|
||||||
|
this.noteEditor = page.locator('#noteInput');
|
||||||
|
this.saveButton = page.getByRole('button', { name: 'Uložit nyní' });
|
||||||
|
this.notesContainer = page.locator('#notesContainer');
|
||||||
|
this.searchTagInput = page.locator('#searchTagInput');
|
||||||
|
this.slashMenu = page.locator('#slashCommandMenu');
|
||||||
|
}
|
||||||
|
|
||||||
|
async navigate() {
|
||||||
|
await this.page.goto('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
async createNewNote(name: string) {
|
||||||
|
// Zachytíme nativní JS prompt
|
||||||
|
this.page.once('dialog', async dialog => {
|
||||||
|
await dialog.accept(name);
|
||||||
|
});
|
||||||
|
await this.newNoteButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fillNote(content: string, tags?: string) {
|
||||||
|
await this.noteEditor.fill(content);
|
||||||
|
if (tags) {
|
||||||
|
await this.noteTagsInput.fill(tags);
|
||||||
|
}
|
||||||
|
await this.saveButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getNoteList() {
|
||||||
|
return this.notesContainer.locator('.note-item');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Krok 3: Vzorový test (`tests/app/noteApp.test.ts`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { AppPage } from './pages/AppPage';
|
||||||
|
|
||||||
|
test.describe('DOXA Web App - Core Functionality', () => {
|
||||||
|
let appPage: AppPage;
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
appPage = new AppPage(page);
|
||||||
|
await appPage.navigate();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Vytvoření, editace a ověření poznámky s tagy', async () => {
|
||||||
|
// 1. Vytvoření nové poznámky
|
||||||
|
const noteName = 'Moje Stoická Poznámka';
|
||||||
|
await appPage.createNewNote(noteName);
|
||||||
|
|
||||||
|
// Ověříme, že se poznámka objevila v seznamu jako aktivní
|
||||||
|
const notes = await appPage.getNoteList();
|
||||||
|
await expect(notes.first()).toContainText(noteName);
|
||||||
|
await expect(notes.first()).toHaveClass(/active/);
|
||||||
|
|
||||||
|
// 2. Editace poznámky a tagů
|
||||||
|
await appPage.fillNote('Dnes odepřena digitální iluze.', 'filozofie, stoicismus');
|
||||||
|
|
||||||
|
// 3. Ověření uložení a zobrazení tagu
|
||||||
|
await expect(notes.first().locator('.note-preview-tags')).toContainText('🏷️ filozofie, stoicismus');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Slash Commands menu se zobrazí po napsání /', async () => {
|
||||||
|
const noteName = 'Test slash příkazů';
|
||||||
|
await appPage.createNewNote(noteName);
|
||||||
|
|
||||||
|
// Klikneme do editoru a napíšeme "/"
|
||||||
|
await appPage.noteEditor.focus();
|
||||||
|
await appPage.page.keyboard.type('/');
|
||||||
|
|
||||||
|
// Ověříme, že se menu zobrazilo
|
||||||
|
await expect(appPage.slashMenu).toBeVisible();
|
||||||
|
await expect(appPage.slashMenu).toContainText('Vložit dnešní datum');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Postup implementace (Roadmap)
|
||||||
|
|
||||||
|
1. **Fáze 1: Příprava prostředí**
|
||||||
|
* Nainstalovat `http-server` jako dev dependency do rootu nebo využít playwright webServer (`npm install --save-dev http-server`).
|
||||||
|
* Aktualizovat `playwright.config.ts`.
|
||||||
|
2. **Fáze 2: Základní POM a CRUD**
|
||||||
|
* Vytvořit soubor `AppPage.ts`.
|
||||||
|
* Pokrýt základní chování (vytvoření, uložení, smazání poznámky).
|
||||||
|
3. **Fáze 3: Pokročilé interakce**
|
||||||
|
* Přidat testy na Drag & Drop řazení poznámek.
|
||||||
|
* Přidat testy na klávesové zkratky a Slash Command menu.
|
||||||
|
4. **Fáze 4: Výstupy a Integrace (CI)**
|
||||||
|
* Pokrýt stahování a nahrávání JSON záloh.
|
||||||
|
* Zapojit testy do GitHub Actions/Codeberg CI tak, aby automaticky běžely při každém Pull Requestu.
|
||||||
|
|
||||||
|
Pokud se ti tento plán líbí, mohu rovnou začít s přípravou konfigurace a implementací prvního testovacího balíčku pro samotnou aplikaci! Dej mi vědět, jak chceš pokračovat.
|
||||||
|
|
||||||
|
plansCopilot
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
1. Upravit `plawright.config.ts` (doxa)
|
||||||
|
2. Do `package.json` přidat script BASE_URL (doxa)
|
||||||
|
3. Upravit HomePage.ts a další dle (doxa)
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
## Spuštění testů
|
||||||
|
|
||||||
|
- local `npx playwright test --ui` nebo `npx playwright test`
|
||||||
|
- prod `test:prod` nebo dle nastavení script v `package.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
script package.json:
|
||||||
|
|
||||||
|
```
|
||||||
|
"scripts": {
|
||||||
|
"test": "playwright test",
|
||||||
|
"test:prod": "BASE_URL=https://doxa-notes.org playwright test homePage.test.ts",
|
||||||
|
"test:prodapp": "BASE_URL=https://app.doxa-notes.org playwright test noteApp.test.ts "
|
||||||
|
}
|
||||||
|
```
|
||||||
|
playwright.config.ts:
|
||||||
|
|
||||||
|
```
|
||||||
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './tests',
|
||||||
|
fullyParallel: true,
|
||||||
|
reporter: 'html',
|
||||||
|
use: {
|
||||||
|
// Pokud v terminálu nastavíš BASE_URL, použije ho. Jinak použije localhost.
|
||||||
|
baseURL: process.env.BASE_URL || 'http://localhost:8080',
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
},
|
||||||
|
webServer: {
|
||||||
|
// WebServer se spustí pouze tehdy, pokud NENÍ nastavena jiná BASE_URL
|
||||||
|
// (nechceme spouštět lokální server, když testujeme produkci)
|
||||||
|
command: process.env.BASE_URL ? undefined : 'npx http-server ../../ -p 8080',
|
||||||
|
url: process.env.BASE_URL || 'http://localhost:8080',
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
HomePage.test.ts:
|
||||||
|
|
||||||
|
```
|
||||||
|
readonly page: Page;
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
this.page = page;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Přidej metodu pro navigaci na konkrétní soubor
|
||||||
|
async navigate(file: string = 'index.html') {
|
||||||
|
// Pokud jsme na lokálu, přidáme složku /web/
|
||||||
|
// Pokud jsme na produkci, předpokládáme root /
|
||||||
|
const prefix = process.env.BASE_URL?.includes('doxa-notes.org') ? '' : 'web/';
|
||||||
|
await this.page.goto(`${prefix}${file}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
# Playwright
|
||||||
|
node_modules/
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
/playwright/.auth/
|
||||||
@@ -0,0 +1,727 @@
|
|||||||
|
{
|
||||||
|
"name": "playwright",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "playwright",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.61.1",
|
||||||
|
"@types/node": "^26.1.1",
|
||||||
|
"http-server": "^14.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@playwright/test": {
|
||||||
|
"version": "1.61.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz",
|
||||||
|
"integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright": "1.61.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "26.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz",
|
||||||
|
"integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~8.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/async": {
|
||||||
|
"version": "3.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||||
|
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/basic-auth": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "5.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/call-bind-apply-helpers": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"function-bind": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/call-bound": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
|
"get-intrinsic": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/corser": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
|
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dunder-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.1",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"gopd": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-define-property": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-errors": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-object-atoms": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/eventemitter3": {
|
||||||
|
"version": "4.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
|
||||||
|
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/function-bind": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-intrinsic": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
|
"es-define-property": "^1.0.1",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"es-object-atoms": "^1.1.1",
|
||||||
|
"function-bind": "^1.1.2",
|
||||||
|
"get-proto": "^1.0.1",
|
||||||
|
"gopd": "^1.2.0",
|
||||||
|
"has-symbols": "^1.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
|
"math-intrinsics": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dunder-proto": "^1.0.1",
|
||||||
|
"es-object-atoms": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gopd": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-symbols": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hasown": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/he": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"he": "bin/he"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/html-encoding-sniffer": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-encoding": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/http-proxy": {
|
||||||
|
"version": "1.18.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
|
||||||
|
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"eventemitter3": "^4.0.0",
|
||||||
|
"follow-redirects": "^1.0.0",
|
||||||
|
"requires-port": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/http-server": {
|
||||||
|
"version": "14.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz",
|
||||||
|
"integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"basic-auth": "^2.0.1",
|
||||||
|
"chalk": "^4.1.2",
|
||||||
|
"corser": "^2.0.1",
|
||||||
|
"he": "^1.2.0",
|
||||||
|
"html-encoding-sniffer": "^3.0.0",
|
||||||
|
"http-proxy": "^1.18.1",
|
||||||
|
"mime": "^1.6.0",
|
||||||
|
"minimist": "^1.2.6",
|
||||||
|
"opener": "^1.5.1",
|
||||||
|
"portfinder": "^1.0.28",
|
||||||
|
"secure-compare": "3.0.1",
|
||||||
|
"union": "~0.5.0",
|
||||||
|
"url-join": "^4.0.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"http-server": "bin/http-server"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/iconv-lite": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/math-intrinsics": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"mime": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minimist": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/object-inspect": {
|
||||||
|
"version": "1.13.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
|
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/opener": {
|
||||||
|
"version": "1.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
||||||
|
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "(WTFPL OR MIT)",
|
||||||
|
"bin": {
|
||||||
|
"opener": "bin/opener-bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.61.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz",
|
||||||
|
"integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.61.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.61.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz",
|
||||||
|
"integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/portfinder": {
|
||||||
|
"version": "1.0.38",
|
||||||
|
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz",
|
||||||
|
"integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"async": "^3.2.6",
|
||||||
|
"debug": "^4.3.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qs": {
|
||||||
|
"version": "6.15.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
|
||||||
|
"integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"es-define-property": "^1.0.1",
|
||||||
|
"side-channel": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/requires-port": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/secure-compare": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/side-channel": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"object-inspect": "^1.13.4",
|
||||||
|
"side-channel-list": "^1.0.1",
|
||||||
|
"side-channel-map": "^1.0.1",
|
||||||
|
"side-channel-weakmap": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/side-channel-list": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"object-inspect": "^1.13.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/side-channel-map": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bound": "^1.0.2",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.5",
|
||||||
|
"object-inspect": "^1.13.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/side-channel-weakmap": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bound": "^1.0.2",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.5",
|
||||||
|
"object-inspect": "^1.13.3",
|
||||||
|
"side-channel-map": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/supports-color": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "8.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
|
||||||
|
"integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/union": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"qs": "^6.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/url-join": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-encoding": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
|
||||||
|
"deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": "0.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "playwright",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "commonjs",
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.61.1",
|
||||||
|
"@types/node": "^26.1.1",
|
||||||
|
"http-server": "^14.1.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "playwright test",
|
||||||
|
"test:prod": "BASE_URL=https://doxa-notes.org playwright test homePage.test.ts",
|
||||||
|
"test:prodapp": "BASE_URL=https://app.doxa-notes.org playwright test noteApp.test.ts "
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './tests',
|
||||||
|
fullyParallel: true,
|
||||||
|
reporter: 'html',
|
||||||
|
use: {
|
||||||
|
// Pokud v terminálu nastavíš BASE_URL, použije ho. Jinak použije localhost.
|
||||||
|
baseURL: process.env.BASE_URL || 'http://localhost:8080',
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
},
|
||||||
|
webServer: {
|
||||||
|
// WebServer se spustí pouze tehdy, pokud NENÍ nastavena jiná BASE_URL
|
||||||
|
// (nechceme spouštět lokální server, když testujeme produkci)
|
||||||
|
command: process.env.BASE_URL ? undefined : 'npx http-server ../../ -p 8080',
|
||||||
|
url: process.env.BASE_URL || 'http://localhost:8080',
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { AppPage } from '../pages/AppPage';
|
||||||
|
|
||||||
|
test.describe('DOXA Web App - Core Functionality', () => {
|
||||||
|
let appPage: AppPage;
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
appPage = new AppPage(page);
|
||||||
|
await appPage.navigate();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Vytvoření, editace a ověření poznámky s tagy', async () => {
|
||||||
|
// 1. Vytvoření nové poznámky
|
||||||
|
const noteName = 'Moje Stoická Poznámka';
|
||||||
|
await appPage.createNewNote(noteName);
|
||||||
|
|
||||||
|
// Ověříme, že se poznámka objevila v seznamu jako aktivní
|
||||||
|
const notes = await appPage.getNoteList();
|
||||||
|
await expect(notes.first()).toContainText(noteName);
|
||||||
|
await expect(notes.first()).toHaveClass(/active/);
|
||||||
|
|
||||||
|
// 2. Editace poznámky a tagů
|
||||||
|
await appPage.fillNote('Dnes odepřena digitální iluze.', 'filozofie, stoicismus');
|
||||||
|
|
||||||
|
// 3. Ověření uložení a zobrazení tagu
|
||||||
|
await expect(notes.first().locator('.note-preview-tags')).toContainText('🏷️ filozofie, stoicismus');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Slash Commands menu se zobrazí po napsání /', async () => {
|
||||||
|
const noteName = 'Test slash příkazů';
|
||||||
|
await appPage.createNewNote(noteName);
|
||||||
|
|
||||||
|
// Klikneme do editoru a napíšeme "/"
|
||||||
|
await appPage.noteEditor.focus();
|
||||||
|
await appPage.page.keyboard.type('/');
|
||||||
|
|
||||||
|
// Ověříme, že se menu zobrazilo
|
||||||
|
await expect(appPage.slashMenu).toBeVisible();
|
||||||
|
await expect(appPage.slashMenu).toContainText('Vložit dnešní datum');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export const expectedMenuTexts = [
|
||||||
|
"Domů",
|
||||||
|
"Wiki",
|
||||||
|
"Ochrana soukromí",
|
||||||
|
"O projektu",
|
||||||
|
];
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { Page, Locator, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
export class AppPage {
|
||||||
|
readonly page: Page;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
readonly newNoteButton: Locator;
|
||||||
|
readonly noteTitleInput: Locator;
|
||||||
|
readonly noteTagsInput: Locator;
|
||||||
|
readonly noteEditor: Locator;
|
||||||
|
readonly saveButton: Locator;
|
||||||
|
readonly notesContainer: Locator;
|
||||||
|
readonly searchTagInput: Locator;
|
||||||
|
readonly slashMenu: Locator;
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
this.page = page;
|
||||||
|
this.newNoteButton = page.locator('#newNoteButton');
|
||||||
|
this.noteTitleInput = page.locator('#noteTitle');
|
||||||
|
this.noteTagsInput = page.locator('#noteTags');
|
||||||
|
this.noteEditor = page.locator('#noteInput');
|
||||||
|
this.saveButton = page.getByRole('button', { name: 'Uložit nyní' });
|
||||||
|
this.notesContainer = page.locator('#notesContainer');
|
||||||
|
this.searchTagInput = page.locator('#searchTagInput');
|
||||||
|
this.slashMenu = page.locator('#slashCommandMenu');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Přidej metodu pro navigaci na konkrétní soubor
|
||||||
|
async navigate(file: string = 'index.html') {
|
||||||
|
// Pokud jsme na lokálu, přidáme složku /web/
|
||||||
|
// Pokud jsme na produkci, předpokládáme root /
|
||||||
|
const prefix = process.env.BASE_URL?.includes('app.doxa-notes.org') ? '' : 'web_app/';
|
||||||
|
await this.page.goto(`${prefix}${file}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// async navigate() {
|
||||||
|
// await this.page.goto(this.path);
|
||||||
|
// }
|
||||||
|
|
||||||
|
async createNewNote(name: string) {
|
||||||
|
// Zachytíme nativní JS prompt
|
||||||
|
this.page.once('dialog', async dialog => {
|
||||||
|
await dialog.accept(name);
|
||||||
|
});
|
||||||
|
await this.newNoteButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fillNote(content: string, tags?: string) {
|
||||||
|
await this.noteEditor.fill(content);
|
||||||
|
if (tags) {
|
||||||
|
await this.noteTagsInput.fill(tags);
|
||||||
|
}
|
||||||
|
await this.saveButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getNoteList() {
|
||||||
|
return this.notesContainer.locator('.note-item');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { expect, type Locator, type Page } from '@playwright/test';
|
||||||
|
|
||||||
|
export class HomePage {
|
||||||
|
readonly page: Page;
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
this.page = page;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Přidej metodu pro navigaci na konkrétní soubor
|
||||||
|
async navigate(file: string = 'index.html') {
|
||||||
|
// Pokud jsme na lokálu, přidáme složku /web/
|
||||||
|
// Pokud jsme na produkci, předpokládáme root /
|
||||||
|
const prefix = process.env.BASE_URL?.includes('doxa-notes.org') ? '' : 'web/';
|
||||||
|
await this.page.goto(`${prefix}${file}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Otestování, zda jsou všechny odkazy v menu viditelné
|
||||||
|
async checkMenuTexts(expectedTexts: string[]) {
|
||||||
|
for (const text of expectedTexts) {
|
||||||
|
const item = this.page.getByText(text, { exact: true });
|
||||||
|
await expect(item).toBeVisible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test externí odkazy
|
||||||
|
|
||||||
|
async getPageTitle(): Promise<string> {
|
||||||
|
return await this.page.title();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMetaDescription(): Promise<string | null> {
|
||||||
|
const meta = this.page.locator('meta[name="description"]');
|
||||||
|
return await meta.getAttribute('content');
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCanonical(): Promise<string | null> {
|
||||||
|
const link = this.page.locator('link[rel="canonical"]');
|
||||||
|
return await link.getAttribute('href');
|
||||||
|
}
|
||||||
|
|
||||||
|
// test kliku na externí odkaz
|
||||||
|
async clickExternalLink(text: string) {
|
||||||
|
await this.page.locator(text).click();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { HomePage } from '../pages/HomePage';
|
||||||
|
import { expectedMenuTexts } from '../assets/text/textHelpers';
|
||||||
|
|
||||||
|
|
||||||
|
test.describe('DOXA Notes HomePage - POM', () => {
|
||||||
|
let homePage: HomePage;
|
||||||
|
|
||||||
|
// Před každým testem vytvoříme novou instanci stránky
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
homePage = new HomePage(page);
|
||||||
|
await homePage.navigate();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('navigate', () => {
|
||||||
|
|
||||||
|
test('check all menu items visibility', async () => {
|
||||||
|
await homePage.checkMenuTexts(expectedMenuTexts);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('navigation to Ochrana soukromí', async () => {
|
||||||
|
await homePage.navigate('privacy.html'); // Naviguje správně podle prostředí
|
||||||
|
await expect(homePage.page).toHaveURL(/\/privacy\.html$/); // Odolné testování URL
|
||||||
|
});
|
||||||
|
|
||||||
|
test('navigation to O projektu', async () => {
|
||||||
|
await homePage.navigate('about.html');
|
||||||
|
await expect(homePage.page).toHaveURL(/\/about\.html$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('externí odkaz do DOXA app', async ({ page }) => {
|
||||||
|
await homePage.clickExternalLink('.btnHomePage');
|
||||||
|
const newPage = await page.waitForEvent('popup');
|
||||||
|
const newpage = await newPage.waitForLoadState();
|
||||||
|
expect(await newPage.url()).toBe('https://app.doxa-notes.org/');
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('SEO - Search Engine Optimization', () => {
|
||||||
|
test('check title, meta description and canonical link on home page', async () => {
|
||||||
|
const title = await homePage.getPageTitle();
|
||||||
|
expect(title).toBe('DOXA Notes — Local-First poznámky a minimalistický Markdown editor');
|
||||||
|
|
||||||
|
const desc = await homePage.getMetaDescription();
|
||||||
|
expect(desc).toBe('DOXA Notes je minimalistická, local‑first poznámková aplikace s podporou Markdownu a otevřeným formátem JSON. Absolutní soukromí pro vaše myšlenky.');
|
||||||
|
|
||||||
|
const canonical = await homePage.getCanonical();
|
||||||
|
expect(canonical).toBe('https://doxa-notes.org/index.html');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('check title and canonical link on O projektu page', async ({ page }) => {
|
||||||
|
await homePage.navigate('O projektu');
|
||||||
|
await expect(page).toHaveTitle('O projektu — DOXA Notes');
|
||||||
|
const canonical = await page.locator('link[rel="canonical"]').getAttribute('href');
|
||||||
|
expect(canonical).toBe('https://doxa-notes.org/about.html');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('check title and canonical link on Ochrana soukromí page', async ({ page }) => {
|
||||||
|
await homePage.navigate('privacy.html');
|
||||||
|
await expect(homePage.page).toHaveTitle('Ochrana soukromí — DOXA Notes');
|
||||||
|
const canonical = await page.locator('link[rel="canonical"]').getAttribute('href');
|
||||||
|
expect(canonical).toBe('https://doxa-notes.org/privacy.html');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="cs">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title data-i18n-key="about.pageTitle">O projektu — DOXA Notes</title>
|
||||||
|
<meta name="description" data-i18n-key="about.pageDescription" data-i18n-attr="content"
|
||||||
|
content="Přečtěte si manifest a filozofické i technické cíle projektu DOXA Notes. Technická suverenita, local-first přístup a bezpečné ukládání myšlenek." />
|
||||||
|
<link rel="canonical" href="https://doxa-notes.org/about.html" />
|
||||||
|
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="https://doxa-notes.org/about.html" />
|
||||||
|
<meta property="og:title" data-i18n-key="about.ogTitle" data-i18n-attr="content" content="O projektu — DOXA Notes" />
|
||||||
|
<meta property="og:description" data-i18n-key="about.ogDescription" data-i18n-attr="content" content="Manifest a filozofické cíle projektu DOXA Notes. Technická suverenita, local-first přístup a bezpečné ukládání myšlenek." />
|
||||||
|
<meta property="og:image" content="https://doxa-notes.org/assets/img/doxa.png" />
|
||||||
|
<meta property="og:image:alt" content="DOXA Notes - Local-First Notes & Sovereignty" />
|
||||||
|
<meta property="og:locale" data-i18n-key="ogLocale" data-i18n-attr="content" content="cs_CZ" />
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" data-i18n-key="about.twitterTitle" data-i18n-attr="content" content="O projektu — DOXA Notes" />
|
||||||
|
<meta name="twitter:description" data-i18n-key="about.twitterDescription" data-i18n-attr="content" content="Manifest a filozofické cíle projektu DOXA Notes." />
|
||||||
|
<meta name="twitter:image" content="https://doxa-notes.org/assets/img/doxa.png" />
|
||||||
|
<meta name="twitter:image:alt" content="DOXA Notes - Local-First Notes & Sovereignty" />
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="assets/style/style.css" />
|
||||||
|
<link rel="shortcut icon" href="assets/img/brain-solid-full.svg" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<nav class="top-nav">
|
||||||
|
<a href="index.html" data-i18n-key="nav.home">Domů</a>
|
||||||
|
<a href="https://wiki.doxa-notes.org/index.html" target="_blank" data-i18n-key="nav.wiki">Wiki</a>
|
||||||
|
<a href="privacy.html" data-i18n-key="nav.privacy">Ochrana soukromí</a>
|
||||||
|
<a href="about.html" data-i18n-key="nav.about">O projektu</a>
|
||||||
|
<button id="langToggle" class="theme-toggle" title="Přepnout jazyk / Switch language">
|
||||||
|
🇬🇧 / 🇨🇿
|
||||||
|
</button>
|
||||||
|
<button id="themeToggle" class="theme-toggle" data-i18n-key="nav.themeToggle" data-i18n-attr="aria-label" aria-label="Přepnout motiv">
|
||||||
|
🌙
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<h1 data-i18n-key="about.mainTitle">O projektu — Doxa</h1>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p><img src="assets/img/doxa.png" alt="Doxa" width="70%" height="auto"
|
||||||
|
style="display: block; margin: 0 auto;border-radius: 10px;" /></p>
|
||||||
|
<br>
|
||||||
|
<h2 style="text-align: center;" data-i18n-key="manifest.title">Manifest digitální suverenity a minimalistického myšlení</h2>
|
||||||
|
<br>
|
||||||
|
<p><strong data-i18n-key="manifest.projectIntent.title">Záměr projektu:</strong></p>
|
||||||
|
<p data-i18n-key="manifest.projectIntent.body">Vytvořit absolutně nezávislé, bezpečné a minimalistické prostředí pro uchovávání lidských myšlenek, poznámek
|
||||||
|
a životních reflexí. V souladu se základními principy osobní svobody a soběstačnosti aplikace vědomě odmítá
|
||||||
|
cloudové databáze, servery třetích stran a centralizovaná API. Doxa věří, že lidská mysl nepotřebuje být
|
||||||
|
sledována algoritmy, aby mohla růst.</p>
|
||||||
|
<br>
|
||||||
|
<h3 data-i18n-key="manifest.localFirst.title">1. Radikální Local-First Filozofie</h3>
|
||||||
|
<p data-i18n-key="manifest.localFirst.subtitle">Aplikace funguje na principu absolutní decentralizace. Běží na tvém zařízení a tam také umírá i žije.</p>
|
||||||
|
<ul>
|
||||||
|
<li data-i18n-key="manifest.localFirst.noBackend">Žádný backend, žádné API: Doxa vědomě neobsahuje žádný kód pro odesílání dat na vzdálené servery. Tím je
|
||||||
|
eliminováno riziko úniku dat, výpadku serveru nebo změny obchodních podmínek poskytovatele.</li>
|
||||||
|
<li data-i18n-key="manifest.localFirst.privacy">Absolutní soukromí: Data jsou uložena výhradně v tvém prohlížeči (localStorage) nebo lokálním souboru.
|
||||||
|
Co napíšeš, to zůstává tvým výhradním vlastnictvím.</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<h3 data-i18n-key="manifest.jsonFormat.title">2. Formát JSON: Trvanlivost napříč dekádami</h3>
|
||||||
|
<p data-i18n-key="manifest.jsonFormat.subtitle">Data jsou strukturována do otevřeného, čistého a lidsky čitelného formátu JSON. Žádné proprietární databáze.</p>
|
||||||
|
<ul>
|
||||||
|
<li data-i18n-key="manifest.jsonFormat.ownership">Tvá data ti patří. Díky možnosti manuálního exportu si můžeš svůj myšlenkový fond odnést kamkoli –
|
||||||
|
uložit si ho na svůj bezpečný lokální disk, nahrát na vlastní Git (GitHub/Gitea), nebo jej
|
||||||
|
synchronizovat skrze cloudovou službu, kterou ty sám uznáš za bezpečnou.</li>
|
||||||
|
<li data-i18n-key="manifest.jsonFormat.longevity">Čistota formátu zaručuje, že tvé poznámky budou čitelné i za dvacet let na jakémkoli zařízení, bez
|
||||||
|
ohledu na to, zda Doxa bude stále existovat.</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<h3 data-i18n-key="manifest.vanillaStack.title">3. Technická čistota a minimalismus (Vanilla Stack)</h3>
|
||||||
|
<p data-i18n-key="manifest.vanillaStack.subtitle">Pro zachování maximální rychlosti, nezávislosti a nulových externích závislostí je aplikace postavena
|
||||||
|
výhradně na čistých webových standardech: </p>
|
||||||
|
<ul>
|
||||||
|
<li data-i18n-key="manifest.vanillaStack.frontend">Frontend (HTML5/CSS3): Minimalistické rozhraní, které nezatěžuje mysl barevným chaosem a vizuálním
|
||||||
|
balastem moderního internetu.</li>
|
||||||
|
<li data-i18n-key="manifest.vanillaStack.logic">Logika (Vanilla JavaScript ES6+): Žádné těžkopádné frameworky (React/Vue), které podléhají módním
|
||||||
|
trendům a neustálým změnám. Čistý kód je trvanlivý jako tesání do kamene.</li>
|
||||||
|
</ul>
|
||||||
|
<blockquote style="white-space: pre-wrap;">
|
||||||
|
<p data-i18n-key="manifest.quote">„Nezáleží na tom, jak složité jsou okolnosti kolem tebe. Když stavíš vlastní chrám myšlenek, začínáš od prvního kamene. Tento kód a tvá data jsou tvým vlastním, nedobytným hradem.“</p>
|
||||||
|
<footer data-i18n-key="manifest.quote.author">— Stoický odkaz pro svobodné myslitele</footer>
|
||||||
|
</blockquote>
|
||||||
|
<br>
|
||||||
|
<hr id="lineHR">
|
||||||
|
<br>
|
||||||
|
<h2 data-i18n-key="about.architecture.title">Architektura</h2>
|
||||||
|
<ul>
|
||||||
|
<li data-i18n-key="about.architecture.item1">Frontend: čisté HTML/CSS/JS (bez frameworků).</li>
|
||||||
|
<li data-i18n-key="about.architecture.item2">Datový formát: otevřený JSON — trvanlivý a přenosný.</li>
|
||||||
|
<li data-i18n-key="about.architecture.item3">Local‑first: data jsou lokální (localStorage nebo soubor).</li>
|
||||||
|
<li data-i18n-key="about.architecture.item4">Desktop aplikace pro Linux je zabalena do instalátorů AppImage.</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<h2 data-i18n-key="about.recent.title">Co bylo přidáno naposledy</h2>
|
||||||
|
<ul>
|
||||||
|
<li data-i18n-key="about.recent.item1">Podpora více poznámek v jednom JSON (název + obsah).</li>
|
||||||
|
<li data-i18n-key="about.recent.item2">Přidání, editace (inline + modal), mazání poznámek.</li>
|
||||||
|
<li data-i18n-key="about.recent.item3">Přesouvání pořadí (šípky + drag & drop).</li>
|
||||||
|
<li data-i18n-key="about.recent.item4">Import / Export JSON a fallback mezi localStorage a lokálním souborem (Electron).</li>
|
||||||
|
<li data-i18n-key="about.recent.item5">Přidána Markdown podpora</li>
|
||||||
|
<li data-i18n-key="about.recent.item6">Desktop aplikace pro Linux</li>
|
||||||
|
<li data-i18n-key="about.recent.item7">Přidání tagů do poznámek</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<h2 data-i18n-key="about.plans.title">Další plány</h2>
|
||||||
|
<ul>
|
||||||
|
<li data-i18n-key="about.plans.item1">Rozšíření UI (hledání, historie/undo).</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<hr id="lineHR">
|
||||||
|
<br>
|
||||||
|
<p data-i18n-key="about.repo.text" style="white-space: pre-wrap;">Projekt najdeš v repozitáři
|
||||||
|
<a id="links" href="https://codeberg.org/kankys/doxa" target="_blank"> Codeberg</a>. Pokud chceš přispět,
|
||||||
|
otevři issue nebo PR.</p>
|
||||||
|
<p data-i18n-key="about.author.text" style="white-space: pre-wrap;">Web autora, další projekty a podpora: <a id="links" href="https://lukaskanka.cz/" target="_blank">Lukáš Kaňka</a></p>
|
||||||
|
<p data-i18n-key="about.license.text">Aplikace je open-source a licencována pod MIT licencí.</p>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<p class="copyright" data-i18n-key="footer.copyright">© 2026 Doxa Notes | Local-First & Freedom 🦅</p>
|
||||||
|
</footer>
|
||||||
|
<script src="assets/js/i18n.js"></script>
|
||||||
|
<script src="assets/js/script.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M184 120C184 89.1 209.1 64 240 64L264 64C281.7 64 296 78.3 296 96L296 544C296 561.7 281.7 576 264 576L232 576C202.2 576 177.1 555.6 170 528C169.3 528 168.7 528 168 528C123.8 528 88 492.2 88 448C88 430 94 413.4 104 400C84.6 385.4 72 362.2 72 336C72 305.1 89.6 278.2 115.2 264.9C108.1 252.9 104 238.9 104 224C104 179.8 139.8 144 184 144L184 120zM456 120L456 144C500.2 144 536 179.8 536 224C536 239 531.9 253 524.8 264.9C550.5 278.2 568 305 568 336C568 362.2 555.4 385.4 536 400C546 413.4 552 430 552 448C552 492.2 516.2 528 472 528C471.3 528 470.7 528 470 528C462.9 555.6 437.8 576 408 576L376 576C358.3 576 344 561.7 344 544L344 96C344 78.3 358.3 64 376 64L400 64C430.9 64 456 89.1 456 120z"/></svg>
|
||||||
|
After Width: | Height: | Size: 920 B |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 658 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
// Tento skript by měl být v HTML vložen PŘED hlavním script.js
|
||||||
|
|
||||||
|
// Globální překladová funkce, kterou bude používat i script.js
|
||||||
|
let translations = {};
|
||||||
|
window.t = (key, params = {}) => {
|
||||||
|
let text = translations[key] || key;
|
||||||
|
for (const p in params) {
|
||||||
|
text = text.replace(`{${p}}`, params[p]);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Načte překlady pro daný jazyk a aplikuje je na DOM.
|
||||||
|
* @param {string} lang - Kód jazyka (např. 'en', 'cs').
|
||||||
|
*/
|
||||||
|
window.setLanguage = async function(lang) {
|
||||||
|
// 1. Uloží preferenci uživatele
|
||||||
|
localStorage.setItem('doxa_lang', lang);
|
||||||
|
|
||||||
|
// Nastaví atribut 'lang' na <html> elementu
|
||||||
|
document.documentElement.lang = lang;
|
||||||
|
|
||||||
|
// 2. Načte a uloží překlady
|
||||||
|
try {
|
||||||
|
const response = await fetch(`assets/locales/${lang}.json`);
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(`Chyba: Nepodařilo se načíst soubor s překlady: ${lang}.json`);
|
||||||
|
translations = {};
|
||||||
|
} else {
|
||||||
|
translations = await response.json();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Chyba při načítání překladů pro jazyk ${lang}:`, error);
|
||||||
|
translations = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Přeloží všechny statické prvky v DOM
|
||||||
|
document.querySelectorAll('[data-i18n-key]').forEach(el => {
|
||||||
|
const key = el.getAttribute('data-i18n-key');
|
||||||
|
const translation = window.t(key);
|
||||||
|
|
||||||
|
// Přeloží specifický atribut (např. placeholder, title), pokud je definován
|
||||||
|
const attr = el.getAttribute('data-i18n-attr');
|
||||||
|
if (attr) {
|
||||||
|
el.setAttribute(attr, translation);
|
||||||
|
} else {
|
||||||
|
// Jinak přeloží obsah elementu
|
||||||
|
el.innerHTML = translation;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. DŮLEŽITÉ: Znovu inicializuje části aplikace, které používají dynamický text
|
||||||
|
if (typeof window.updateDynamicUIText === 'function') {
|
||||||
|
window.updateDynamicUIText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const userLang = localStorage.getItem('doxa_lang') || navigator.language.split('-')[0];
|
||||||
|
const langToLoad = ['en', 'cs'].includes(userLang) ? userLang : 'cs';
|
||||||
|
window.setLanguage(langToLoad);
|
||||||
|
|
||||||
|
const langToggleBtn = document.getElementById('langToggle');
|
||||||
|
if (langToggleBtn) {
|
||||||
|
langToggleBtn.addEventListener('click', () => {
|
||||||
|
const currentLang = localStorage.getItem('doxa_lang') || 'cs';
|
||||||
|
const nextLang = currentLang === 'cs' ? 'en' : 'cs';
|
||||||
|
window.setLanguage(nextLang);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const THEME_KEY = "doxa_theme";
|
||||||
|
let systemMediaQuery = null;
|
||||||
|
let systemMediaListener = null;
|
||||||
|
|
||||||
|
function applyTheme(theme) {
|
||||||
|
try {
|
||||||
|
if (systemMediaQuery && systemMediaListener) {
|
||||||
|
try {
|
||||||
|
if (systemMediaQuery.removeEventListener)
|
||||||
|
systemMediaQuery.removeEventListener("change", systemMediaListener);
|
||||||
|
else systemMediaQuery.removeListener(systemMediaListener);
|
||||||
|
} catch (e) {}
|
||||||
|
systemMediaQuery = null;
|
||||||
|
systemMediaListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme === "dark") {
|
||||||
|
document.body.classList.add("dark");
|
||||||
|
} else if (theme === "light") {
|
||||||
|
document.body.classList.remove("dark");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const mql = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
if (mql.matches) document.body.classList.add("dark");
|
||||||
|
else document.body.classList.remove("dark");
|
||||||
|
systemMediaQuery = mql;
|
||||||
|
systemMediaListener = function (e) {
|
||||||
|
if (e.matches) document.body.classList.add("dark");
|
||||||
|
else document.body.classList.remove("dark");
|
||||||
|
updateThemeButton();
|
||||||
|
};
|
||||||
|
if (mql.addEventListener)
|
||||||
|
mql.addEventListener("change", systemMediaListener);
|
||||||
|
else mql.addListener(systemMediaListener);
|
||||||
|
} catch (e) {
|
||||||
|
document.body.classList.remove("dark");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
updateThemeButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateThemeButton() {
|
||||||
|
const btn = document.getElementById("themeToggle");
|
||||||
|
if (!btn) return;
|
||||||
|
const theme = getStoredTheme() || "system";
|
||||||
|
let icon = "🌓";
|
||||||
|
if (theme === "dark") {
|
||||||
|
icon = "🌙";
|
||||||
|
} else if (theme === "light") {
|
||||||
|
icon = "☀️";
|
||||||
|
}
|
||||||
|
btn.textContent = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStoredTheme() {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(THEME_KEY);
|
||||||
|
} catch (e) { return null; }
|
||||||
|
}
|
||||||
|
function setStoredTheme(value) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(THEME_KEY, value);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTheme() {
|
||||||
|
const stored = getStoredTheme() || "system";
|
||||||
|
const order = ["light", "dark", "system"];
|
||||||
|
let idx = order.indexOf(stored);
|
||||||
|
if (idx === -1) idx = 2;
|
||||||
|
const next = order[(idx + 1) % order.length];
|
||||||
|
setStoredTheme(next);
|
||||||
|
applyTheme(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const themeBtn = document.getElementById("themeToggle");
|
||||||
|
if (themeBtn) themeBtn.addEventListener("click", toggleTheme);
|
||||||
|
applyTheme(getStoredTheme() || "system");
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"ogLocale": "cs_CZ",
|
||||||
|
"nav.home": "Domů",
|
||||||
|
"nav.wiki": "Wiki",
|
||||||
|
"nav.privacy": "Ochrana soukromí",
|
||||||
|
"nav.about": "O projektu",
|
||||||
|
"nav.themeToggle": "Přepnout motiv",
|
||||||
|
"footer.copyright": "Doxa Notes © 2026 | Local-First & Svoboda 🦅",
|
||||||
|
"index.pageTitle": "DOXA Notes — Local-First poznámky a minimalistický Markdown editor",
|
||||||
|
"index.pageDescription": "DOXA Notes je minimalistická, local‑first poznámková aplikace s podporou Markdownu a otevřeným formátem JSON. Absolutní soukromí pro vaše myšlenky.",
|
||||||
|
"index.ogTitle": "DOXA Notes — Local-First poznámky a minimalistický Markdown editor",
|
||||||
|
"index.ogDescription": "DOXA Notes je minimalistická, local‑first poznámková aplikace založená na otevřeném JSON formátu. Data zůstávají plně pod vaší kontrolou.",
|
||||||
|
"index.twitterTitle": "DOXA Notes — Local-First poznámky a minimalistický Markdown editor",
|
||||||
|
"index.twitterDescription": "DOXA Notes je minimalistická, local‑first poznámková aplikace založená na otevřeném JSON formátu.",
|
||||||
|
"index.mainTitle": "DOXA Notes",
|
||||||
|
"index.subtitle": "Local-First Notes & Sovereignty",
|
||||||
|
"index.intro": "DOXA Notes: Markdown editor, nezávislé, bezpečné a minimalistické prostředí pro uchovávání lidských myšlenek, poznámek a životních reflexí. V souladu se základními principy osobní svobody a soběstačnosti aplikace vědomě odmítá cloudové databáze, servery třetích stran a centralizovaná API. Doxa věří, že lidská mysl nepotřebuje být sledována algoritmy, aby mohla růst.",
|
||||||
|
"index.philosophy": "„Základní myšlenka zůstává nekompromisní: Radikální Local-First přístup, čistá data v JSON formátu a absolutní digitální soukromí bez korporátního cloudu a API.“",
|
||||||
|
"index.appButton": "Vstoupit do DOXA Notes →",
|
||||||
|
"about.pageTitle": "O projektu — DOXA Notes",
|
||||||
|
"about.pageDescription": "Přečtěte si manifest a filozofické i technické cíle projektu DOXA Notes. Technická suverenita, local-first přístup a bezpečné ukládání myšlenek.",
|
||||||
|
"about.ogTitle": "O projektu — DOXA Notes",
|
||||||
|
"about.ogDescription": "Manifest a filozofické cíle projektu DOXA Notes. Technická suverenita, local-first přístup a bezpečné ukládání myšlenek.",
|
||||||
|
"about.twitterTitle": "O projektu — DOXA Notes",
|
||||||
|
"about.twitterDescription": "Manifest a filozofické cíle projektu DOXA Notes.",
|
||||||
|
"about.mainTitle": "O projektu — Doxa",
|
||||||
|
"about.architecture.title": "Architektura",
|
||||||
|
"about.architecture.item1": "Frontend: čisté HTML/CSS/JS (bez frameworků).",
|
||||||
|
"about.architecture.item2": "Datový formát: otevřený JSON — trvanlivý a přenosný.",
|
||||||
|
"about.architecture.item3": "Local-first: data jsou lokální (localStorage nebo soubor).",
|
||||||
|
"about.architecture.item4": "Desktop aplikace pro Linux je zabalena do instalátorů AppImage.",
|
||||||
|
"about.recent.title": "Co bylo přidáno naposledy",
|
||||||
|
"about.recent.item1": "Podpora více poznámek v jednom JSON (název + obsah).",
|
||||||
|
"about.recent.item2": "Přidání, editace (inline + modal), mazání poznámek.",
|
||||||
|
"about.recent.item3": "Přesouvání pořadí (šípky + drag & drop).",
|
||||||
|
"about.recent.item4": "Import / Export JSON a fallback mezi localStorage a lokálním souborem (Electron).",
|
||||||
|
"about.recent.item5": "Přidána Markdown podpora",
|
||||||
|
"about.recent.item6": "Desktop aplikace pro Linux",
|
||||||
|
"about.recent.item7": "Přidání tagů do poznámek",
|
||||||
|
"about.plans.title": "Další plány",
|
||||||
|
"about.plans.item1": "Rozšíření UI (hledání, historie/undo).",
|
||||||
|
"about.repo.text": "Projekt najdeš v repozitáři <a id=\"links\" href=\"https://codeberg.org/kankys/doxa\" target=\"_blank\">Codeberg</a>. Pokud chceš přispět,\n otevři issue nebo PR.",
|
||||||
|
"about.author.text": "Web autora, další projekty a podpora: <a id=\"links\" href=\"https://lukaskanka.cz/\" target=\"_blank\">Lukáš Kaňka</a>",
|
||||||
|
"about.license.text": "Aplikace je open-source a licencována pod MIT licencí.",
|
||||||
|
"privacy.pageTitle": "Ochrana soukromí — DOXA Notes",
|
||||||
|
"privacy.pageDescription": "Zásady ochrany osobních údajů a soukromí v aplikaci DOXA Notes. Žádná data neodcházejí na servery třetích stran, vše zůstává u vás.",
|
||||||
|
"privacy.ogTitle": "Ochrana soukromí — DOXA Notes",
|
||||||
|
"privacy.ogDescription": "Zásady ochrany osobních údajů a soukromí v aplikaci DOXA Notes. Žádná data neodcházejí na servery třetích stran.",
|
||||||
|
"privacy.twitterTitle": "Ochrana soukromí — DOXA Notes",
|
||||||
|
"privacy.twitterDescription": "Zásady ochrany osobních údajů a soukromí v aplikaci DOXA Notes.",
|
||||||
|
"privacy.mainTitle": "Ochrana soukromí",
|
||||||
|
"privacy.intro": "Doxa je navržena jako <strong>local‑first</strong> aplikace\n — soukromí je primární designový princip. Níže shrnutí, jak\n zacházím s daty v aktuálním stavu projektu.",
|
||||||
|
"privacy.stores.title": "Co aplikace ukládá",
|
||||||
|
"privacy.stores.item1": "Poznámky: <code>id</code>, <code>name</code>,\n <code>content</code>, <code>created_at</code>,\n <code>modified_at</code> (pokud existuje).",
|
||||||
|
"privacy.stores.item2": "Žádné automatické telemetry ani sledování není\n implementováno.",
|
||||||
|
"privacy.where.title": "Kde jsou data uložena",
|
||||||
|
"privacy.where.item1": "V prohlížeči: <strong>localStorage</strong> (klíč\n <code>doxa_notes_v1</code>).",
|
||||||
|
"privacy.where.item2": "V Electron/Node režimu: soubor na disku (např.\n <code>assets/js/data/doxa.json</code>).",
|
||||||
|
"privacy.where.item3": "Export/import: uživatel může stáhnout JSON a nahrát ho na\n jiném zařízení — je to manuální proces.",
|
||||||
|
"privacy.notSent.title": "Co aplikace NEODESÍLÁ",
|
||||||
|
"privacy.notSent.item1": "Výchozí instalace neposílá poznámky na žádný vzdálený\n server.",
|
||||||
|
"privacy.notSent.item2": "Bez implementace serverové synchronizace nejsou data\n odesílána ani zálohována vůči třetím stranám.",
|
||||||
|
"privacy.responsibility.title": "Odpovědnost uživatele",
|
||||||
|
"privacy.responsibility.body": "Protože data jsou lokální, odpovědnost za zálohy a bezpečnou\n konfiguraci serveru (pokud použita) nese vlastník instalace.\n Doporučuji pravidelné exporty a bezpečné úložiště záloh.",
|
||||||
|
"manifest.title": "Manifest digitální suverenity a minimalistického myšlení",
|
||||||
|
"manifest.projectIntent.title": "Záměr projektu:",
|
||||||
|
"manifest.projectIntent.body": "Vytvořit absolutně nezávislé, bezpečné a minimalistické prostředí pro uchovávání lidských myšlenek, poznámek a životních reflexí. V souladu se základními principy osobní svobody a soběstačnosti aplikace vědomě odmítá cloudové databáze, servery třetích stran a centralizovaná API. Doxa věří, že lidská mysl nepotřebuje být sledována algoritmy, aby mohla růst.",
|
||||||
|
"manifest.localFirst.title": "1. Radikální Local-First Filozofie",
|
||||||
|
"manifest.localFirst.subtitle": "Aplikace funguje na principu absolutní decentralizace. Běží na tvém zařízení a tam také umírá i žije.",
|
||||||
|
"manifest.localFirst.noBackend": "Žádný backend, žádné API: Doxa vědomě neobsahuje žádný kód pro odesílání dat na vzdálené servery. Tím je eliminováno riziko úniku dat, výpadku serveru nebo změny obchodních podmínek poskytovatele.",
|
||||||
|
"manifest.localFirst.privacy": "Absolutní soukromí: Data jsou uložena výhradně v tvém prohlížeči (localStorage) nebo lokálním souboru. Co napíšeš, to zůstává tvým výhradním vlastnictvím.",
|
||||||
|
"manifest.jsonFormat.title": "2. Formát JSON: Trvanlivost napříč dekádami",
|
||||||
|
"manifest.jsonFormat.subtitle": "Data jsou strukturována do otevřeného, čistého a lidsky čitelného formátu JSON. Žádné proprietární databáze.",
|
||||||
|
"manifest.jsonFormat.ownership": "Tvá data ti patří. Díky možnosti manuálního exportu si můžeš svůj myšlenkový fond odnést kamkoli – uložit si ho na svůj bezpečný lokální disk, nahrát na vlastní Git (GitHub/Gitea), nebo jej synchronizovat skrze cloudovou službu, kterou ty sám uznáš za bezpečnou.",
|
||||||
|
"manifest.jsonFormat.longevity": "Čistota formátu zaručuje, že tvé poznámky budou čitelné i za dvacet let na jakémkoli zařízení, bez ohledu na to, zda Doxa bude stále existovat.",
|
||||||
|
"manifest.vanillaStack.title": "3. Technická čistota a minimalismus (Vanilla Stack)",
|
||||||
|
"manifest.vanillaStack.subtitle": "Pro zachování maximální rychlosti, nezávislosti a nulových externích závislostí je aplikace postavena výhradně na čistých webových standardech:",
|
||||||
|
"manifest.vanillaStack.frontend": "Frontend (HTML5/CSS3): Minimalistické rozhraní, které nezatěžuje mysl barevným chaosem a vizuálním balastem moderního internetu.",
|
||||||
|
"manifest.vanillaStack.logic": "Logika (Vanilla JavaScript ES6+): Žádné těžkopádné frameworky (React/Vue), které podléhají módním trendům a neustálým změnám.",
|
||||||
|
"manifest.quote": "„Nezáleží na tom, jak složité jsou okolnosti kolem tebe. Když stavíš vlastní chrám myšlenek, začínáš od prvního kamene. Tento kód a tvá data jsou tvým vlastním, nedobytným hradem.“",
|
||||||
|
"manifest.quote.author": "— Stoický odkaz pro svobodné myslitele"
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"ogLocale": "en_US",
|
||||||
|
"nav.home": "Home",
|
||||||
|
"nav.wiki": "Wiki",
|
||||||
|
"nav.privacy": "Privacy",
|
||||||
|
"nav.about": "About",
|
||||||
|
"nav.themeToggle": "Toggle theme",
|
||||||
|
"footer.copyright": "Doxa Notes © 2026 | Local-First & Freedom 🦅",
|
||||||
|
"index.pageTitle": "DOXA Notes — Local-First Notes & Minimalist Markdown Editor",
|
||||||
|
"index.pageDescription": "DOXA Notes is a minimalist, local-first note-taking app with Markdown support and an open JSON format. Absolute privacy for your thoughts.",
|
||||||
|
"index.ogTitle": "DOXA Notes — Local-First Notes & Minimalist Markdown Editor",
|
||||||
|
"index.ogDescription": "DOXA Notes is a minimalist, local-first note-taking app based on an open JSON format. Your data remains fully under your control.",
|
||||||
|
"index.twitterTitle": "DOXA Notes — Local-First Notes & Minimalist Markdown Editor",
|
||||||
|
"index.twitterDescription": "DOXA Notes is a minimalist, local-first note-taking app based on an open JSON format.",
|
||||||
|
"index.mainTitle": "DOXA Notes",
|
||||||
|
"index.subtitle": "Local-First Notes & Sovereignty",
|
||||||
|
"index.intro": "DOXA Notes: A Markdown editor, an independent, secure, and minimalist environment for storing human thoughts, notes, and life reflections. In line with the fundamental principles of personal freedom and self-sufficiency, the application consciously rejects cloud databases, third-party servers, and centralized APIs. Doxa believes that the human mind does not need to be monitored by algorithms to grow.",
|
||||||
|
"index.philosophy": "\"The core idea remains uncompromising: A radical Local-First approach, clean data in JSON format, and absolute digital privacy without corporate clouds and APIs.\"",
|
||||||
|
"index.appButton": "Enter DOXA Notes →",
|
||||||
|
"about.pageTitle": "About — DOXA Notes",
|
||||||
|
"about.pageDescription": "Read the manifesto and the philosophical and technical goals of the DOXA Notes project. Technical sovereignty, local-first approach, and secure storage of thoughts.",
|
||||||
|
"about.ogTitle": "About — DOXA Notes",
|
||||||
|
"about.ogDescription": "The manifesto and philosophical goals of the DOXA Notes project. Technical sovereignty, local-first approach, and secure storage of thoughts.",
|
||||||
|
"about.twitterTitle": "About — DOXA Notes",
|
||||||
|
"about.twitterDescription": "The manifesto and philosophical goals of the DOXA Notes project.",
|
||||||
|
"about.mainTitle": "About — Doxa",
|
||||||
|
"about.architecture.title": "Architecture",
|
||||||
|
"about.architecture.item1": "Frontend: pure HTML/CSS/JS (no frameworks).",
|
||||||
|
"about.architecture.item2": "Data format: open JSON — durable and portable.",
|
||||||
|
"about.architecture.item3": "Local-first: data is local (localStorage or file).",
|
||||||
|
"about.architecture.item4": "The desktop application for Linux is packaged in AppImage installers.",
|
||||||
|
"about.recent.title": "What was added recently",
|
||||||
|
"about.recent.item1": "Support for multiple notes in one JSON (title + content).",
|
||||||
|
"about.recent.item2": "Adding, editing (inline + modal), and deleting notes.",
|
||||||
|
"about.recent.item3": "Reordering (arrows + drag & drop).",
|
||||||
|
"about.recent.item4": "Import / Export JSON and fallback between localStorage and local file (Electron).",
|
||||||
|
"about.recent.item5": "Markdown support added",
|
||||||
|
"about.recent.item6": "Desktop application for Linux",
|
||||||
|
"about.recent.item7": "Added tags to notes",
|
||||||
|
"about.plans.title": "Future plans",
|
||||||
|
"about.plans.item1": "UI enhancements (search, history/undo).",
|
||||||
|
"about.repo.text": "You can find the project in the <a id=\"links\" href=\"https://codeberg.org/kankys/doxa\" target=\"_blank\">Codeberg</a> repository. If you want to contribute,\n open an issue or PR.",
|
||||||
|
"about.author.text": "Author's website, other projects, and support: <a id=\"links\" href=\"https://lukaskanka.cz/\" target=\"_blank\">Lukáš Kaňka</a>",
|
||||||
|
"about.license.text": "The application is open-source and licensed under the MIT license.",
|
||||||
|
"privacy.pageTitle": "Privacy Policy — DOXA Notes",
|
||||||
|
"privacy.pageDescription": "Privacy policy for the DOXA Notes application. No data is sent to third-party servers; everything stays with you.",
|
||||||
|
"privacy.ogTitle": "Privacy Policy — DOXA Notes",
|
||||||
|
"privacy.ogDescription": "Privacy policy for the DOXA Notes application. No data is sent to third-party servers.",
|
||||||
|
"privacy.twitterTitle": "Privacy Policy — DOXA Notes",
|
||||||
|
"privacy.twitterDescription": "Privacy policy for the DOXA Notes application.",
|
||||||
|
"privacy.mainTitle": "Privacy Policy",
|
||||||
|
"privacy.intro": "Doxa is designed as a <strong>local-first</strong> application\n — privacy is a primary design principle. Below is a summary of how\n I handle data in the current state of the project.",
|
||||||
|
"privacy.stores.title": "What the application stores",
|
||||||
|
"privacy.stores.item1": "Notes: <code>id</code>, <code>name</code>,\n <code>content</code>, <code>created_at</code>,\n <code>modified_at</code> (if it exists).",
|
||||||
|
"privacy.stores.item2": "No automatic telemetry or tracking is\n implemented.",
|
||||||
|
"privacy.where.title": "Where the data is stored",
|
||||||
|
"privacy.where.item1": "In the browser: <strong>localStorage</strong> (key\n <code>doxa_notes_v1</code>).",
|
||||||
|
"privacy.where.item2": "In Electron/Node mode: a file on the disk (e.g.,\n <code>assets/js/data/doxa.json</code>).",
|
||||||
|
"privacy.where.item3": "Export/import: the user can download a JSON file and upload it on\n another device — this is a manual process.",
|
||||||
|
"privacy.notSent.title": "What the application DOES NOT SEND",
|
||||||
|
"privacy.notSent.item1": "The default installation does not send notes to any remote\n server.",
|
||||||
|
"privacy.notSent.item2": "Without server-side synchronization, data is not\n sent to or backed up by third parties.",
|
||||||
|
"privacy.responsibility.title": "User's responsibility",
|
||||||
|
"privacy.responsibility.body": "Because the data is local, the responsibility for backups and secure\n server configuration (if used) lies with the owner of the installation.\n I recommend regular exports and secure storage of backups.",
|
||||||
|
"manifest.title": "Manifesto of Digital Sovereignty and Minimalist Thinking",
|
||||||
|
"manifest.projectIntent.title": "Project Intent:",
|
||||||
|
"manifest.projectIntent.body": "To create a completely independent, secure, and minimalist environment for storing human thoughts, notes, and life reflections. In line with the fundamental principles of personal freedom and self-sufficiency, the application consciously rejects cloud databases, third-party servers, and centralized APIs. DOXA believes that the human mind does not need to be monitored by algorithms to grow.",
|
||||||
|
"manifest.localFirst.title": "1. Radical Local-First Philosophy",
|
||||||
|
"manifest.localFirst.subtitle": "The application operates on the principle of absolute decentralization. It lives and dies on your device.",
|
||||||
|
"manifest.localFirst.noBackend": "No backend, no API: DOXA intentionally contains no code for sending data to remote servers. This eliminates the risk of data leaks, server outages, or changes in the provider's terms of service.",
|
||||||
|
"manifest.localFirst.privacy": "Absolute Privacy: Data is stored exclusively in your browser (localStorage) or a local file. What you write remains your exclusive property.",
|
||||||
|
"manifest.jsonFormat.title": "2. JSON Format: Durability Across Decades",
|
||||||
|
"manifest.jsonFormat.subtitle": "Data is structured in an open, clean, and human-readable JSON format. No proprietary databases.",
|
||||||
|
"manifest.jsonFormat.ownership": "Your data belongs to you. Thanks to the manual export option, you can take your repository of thoughts anywhere – save it to your secure local drive, upload it to your own Git (GitHub/Gitea), or synchronize it through a cloud service that you yourself deem secure.",
|
||||||
|
"manifest.jsonFormat.longevity": "The purity of the format ensures that your notes will be readable even in twenty years on any device, regardless of whether DOXA still exists.",
|
||||||
|
"manifest.vanillaStack.title": "3. Technical Purity and Minimalism (Vanilla Stack)",
|
||||||
|
"manifest.vanillaStack.subtitle": "To maintain maximum speed, independence, and zero external dependencies, the application is built exclusively on clean web standards:",
|
||||||
|
"manifest.vanillaStack.frontend": "Frontend (HTML5/CSS3): A minimalist interface that does not burden the mind with the colorful chaos and visual clutter of the modern internet.",
|
||||||
|
"manifest.vanillaStack.logic": "Logic (Vanilla JavaScript ES6+): No cumbersome frameworks (React/Vue) that are subject to fashion trends and constant changes.",
|
||||||
|
"manifest.quote": "\"It does not matter how complex the circumstances around you are. When you build your own temple of thoughts, you start with the first stone. This code and your data are your own, impregnable castle.\"",
|
||||||
|
"manifest.quote.author": "— A Stoic message for free thinkers"
|
||||||
|
}
|
||||||
@@ -0,0 +1,500 @@
|
|||||||
|
/* --- STOICKÝ DESIGN PRO DOXA NOTES --- */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Světlý motiv - Inspirovaný antickým mramorem a pergamenem */
|
||||||
|
--bg: #f5f4f0;
|
||||||
|
--text: #2c2e30;
|
||||||
|
--muted: #73797e;
|
||||||
|
|
||||||
|
--button-bg: #e5e2da;
|
||||||
|
--button-text: #2c2e30;
|
||||||
|
|
||||||
|
--note-bg: #ffffff;
|
||||||
|
--note-border: #dcd8cd;
|
||||||
|
--note-hover: #edeae2;
|
||||||
|
|
||||||
|
--accent: #b3893e; /* Tlumená bronzová */
|
||||||
|
--accent-hover: #967131;
|
||||||
|
|
||||||
|
--modal-bg: #ffffff;
|
||||||
|
--modal-overlay: rgba(44, 46, 48, 0.4);
|
||||||
|
|
||||||
|
--active-bg: #fdfbf7;
|
||||||
|
--active-border: #b3893e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tmavý motiv - Hluboká noc a bronz (Výchozí pro landing page) */
|
||||||
|
body.dark {
|
||||||
|
--bg: #1a1c1e;
|
||||||
|
--text: #e2e4e9;
|
||||||
|
--muted: #9ba1a6;
|
||||||
|
|
||||||
|
--button-bg: #2d3135;
|
||||||
|
--button-text: #e2e4e9;
|
||||||
|
|
||||||
|
--note-bg: #222529;
|
||||||
|
--note-border: #2e3238;
|
||||||
|
--note-hover: #2b2e33;
|
||||||
|
|
||||||
|
--accent: #c5a059; /* Jasnější stoická bronzová */
|
||||||
|
--accent-hover: #dfba73;
|
||||||
|
|
||||||
|
--modal-bg: #222529;
|
||||||
|
--modal-overlay: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
|
--active-bg: rgba(197, 160, 89, 0.05);
|
||||||
|
--active-border: #c5a059;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Základní nastavení */
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, monospace, sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 40px 20px;
|
||||||
|
line-height: 1.6;
|
||||||
|
transition: background 250ms ease, color 250ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigace */
|
||||||
|
.top-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
border-bottom: 1px solid var(--note-border);
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a,
|
||||||
|
.top-nav .theme-toggle {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a:hover,
|
||||||
|
.top-nav .theme-toggle:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nadpisy */
|
||||||
|
h1 {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1::after {
|
||||||
|
content: " 🦅";
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Formulářové prvky */
|
||||||
|
#noteTitle {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteTitle:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 220px;
|
||||||
|
padding: 14px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
resize: vertical;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tlačítka */
|
||||||
|
button {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
padding: 10px 18px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Editor toolbar */
|
||||||
|
.editor-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background: var(--note-bg);
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text);
|
||||||
|
padding: 6px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Layout editoru a náhledu */
|
||||||
|
.editor-wrap {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-wrap textarea {
|
||||||
|
flex: 1 1 55%;
|
||||||
|
min-height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
flex: 1 1 45%;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
background: var(--note-bg);
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
/*overflow-auto;*/
|
||||||
|
min-height: 250px;
|
||||||
|
max-height: 400px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Akce pod editorem */
|
||||||
|
.editor-actions {
|
||||||
|
margin: 15px 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newNoteButton {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newNoteButton:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-status {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seznam poznámek */
|
||||||
|
#notesContainer {
|
||||||
|
margin-top: 25px;
|
||||||
|
border-top: 1px solid var(--note-border);
|
||||||
|
padding-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--note-bg);
|
||||||
|
transition: transform 0.15s, border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: rgba(197, 160, 89, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.active {
|
||||||
|
background: var(--active-bg);
|
||||||
|
border-color: var(--active-border);
|
||||||
|
box-shadow: 0 0 10px rgba(197, 160, 89, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-left {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding-right: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-preview {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 10px;
|
||||||
|
color: var(--text);
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: opacity 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button:hover {
|
||||||
|
background: var(--button-bg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button.delete:hover {
|
||||||
|
color: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-notes {
|
||||||
|
color: var(--muted);
|
||||||
|
font-style: italic;
|
||||||
|
padding: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notes-hint {
|
||||||
|
margin-top: 10px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drag & Drop styly */
|
||||||
|
.note-item.drag-over {
|
||||||
|
border-style: dashed;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.drag-after {
|
||||||
|
box-shadow: inset 0 -3px 0 0 var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.dragging {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modální okno */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--modal-overlay);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
background: var(--modal-bg);
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 95%;
|
||||||
|
max-width: 800px;
|
||||||
|
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal h2 {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 16px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--muted);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-close:hover {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modalSaveBtn {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modalSaveBtn:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styly pro renderovaný Markdown */
|
||||||
|
.preview h1, .preview h2, .preview h3 {
|
||||||
|
margin: 14px 0 8px 0;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
.preview p {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.preview pre {
|
||||||
|
background: rgba(0, 0, 0, 0.15);
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
body:not(.dark) .preview pre {
|
||||||
|
background: #f0ede6;
|
||||||
|
}
|
||||||
|
.preview code {
|
||||||
|
font-family: monospace;
|
||||||
|
background: rgba(197, 160, 89, 0.15);
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
.preview ul, .preview ol {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.preview blockquote {
|
||||||
|
border-left: 3px solid var(--accent);
|
||||||
|
padding-left: 12px;
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--muted);
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
.preview table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
.preview th, .preview td {
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.preview th {
|
||||||
|
background: var(--button-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responzivita */
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
body {
|
||||||
|
padding: 20px 10px;
|
||||||
|
}
|
||||||
|
.editor-wrap {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.preview {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
/* Theme variables */
|
||||||
|
:root {
|
||||||
|
--bg: #fafafa;
|
||||||
|
--text: #333333;
|
||||||
|
--muted: #666666;
|
||||||
|
|
||||||
|
--button-bg: #333333;
|
||||||
|
--button-text: #ffffff;
|
||||||
|
|
||||||
|
--note-bg: #ffffff;
|
||||||
|
--note-border: #dddddd;
|
||||||
|
--note-hover: #f5f5f5;
|
||||||
|
|
||||||
|
--accent: #0b63d6;
|
||||||
|
|
||||||
|
--modal-bg: #ffffff;
|
||||||
|
--modal-overlay: rgba(0, 0, 0, 0.45);
|
||||||
|
|
||||||
|
--active-bg: #e6f0ff;
|
||||||
|
--active-border: #a6c8ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme overrides */
|
||||||
|
body.dark {
|
||||||
|
--bg: #0b1220;
|
||||||
|
--text: #e6eef8;
|
||||||
|
--muted: #9aa6b2;
|
||||||
|
|
||||||
|
--button-bg: #1f2937;
|
||||||
|
--button-text: #ffffff;
|
||||||
|
|
||||||
|
--note-bg: #071022;
|
||||||
|
--note-border: #263046;
|
||||||
|
--note-hover: #092033;
|
||||||
|
|
||||||
|
--accent: #3b82f6;
|
||||||
|
|
||||||
|
--modal-bg: #0b1220;
|
||||||
|
--modal-overlay: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
|
--active-bg: rgba(59, 130, 246, 0.14);
|
||||||
|
--active-border: #60a5fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base */
|
||||||
|
body {
|
||||||
|
font-family: monospace;
|
||||||
|
background: var(--bg);
|
||||||
|
padding: 40px;
|
||||||
|
color: var(--text);
|
||||||
|
transition:
|
||||||
|
background 200ms ease,
|
||||||
|
color 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a,
|
||||||
|
.top-nav .theme-toggle {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a:hover,
|
||||||
|
.top-nav .theme-toggle:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a.active {
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1::after {
|
||||||
|
content: " 🦅";
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteTitle {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 150px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notesContainer {
|
||||||
|
margin-top: 10px;
|
||||||
|
border-top: 1px solid var(--note-border);
|
||||||
|
padding-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item {
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--note-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.active {
|
||||||
|
background: var(--active-bg);
|
||||||
|
border-color: var(--active-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-notes {
|
||||||
|
color: var(--muted);
|
||||||
|
font-style: italic;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note list layout */
|
||||||
|
.note-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-left {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding-right: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-title {
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-preview {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: monospace;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.drag-over {
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.drag-after {
|
||||||
|
box-shadow: inset 0 -3px 0 0 rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.dragging {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--modal-overlay);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-overlay.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
background: var(--modal-bg);
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 92%;
|
||||||
|
max-width: 720px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 8px;
|
||||||
|
top: 8px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal input,
|
||||||
|
.modal textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
.container {
|
||||||
|
max-width: 900px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notes-hint {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Editor toolbar & preview */
|
||||||
|
.editor-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 6px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* active toolbar button */
|
||||||
|
.editor-toolbar button.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--button-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-wrap {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-wrap textarea {
|
||||||
|
flex: 1 1 60%;
|
||||||
|
min-height: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
flex: 1 1 40%;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
background: var(--note-bg);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-actions {
|
||||||
|
margin-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-status {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rendered markdown styles */
|
||||||
|
.preview h1,
|
||||||
|
.preview h2,
|
||||||
|
.preview h3 {
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
.preview pre {
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
padding: 8px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.preview code {
|
||||||
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.editor-wrap {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.preview {
|
||||||
|
max-height: 240px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 16px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,604 @@
|
|||||||
|
/* --- STOICKÝ DESIGN PRO DOXA NOTES --- */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Světlý motiv - Inspirovaný antickým mramorem a pergamenem */
|
||||||
|
--bg: #f5f4f0;
|
||||||
|
--text: #2c2e30;
|
||||||
|
--muted: #73797e;
|
||||||
|
|
||||||
|
--button-bg: #e5e2da;
|
||||||
|
--button-text: #2c2e30;
|
||||||
|
|
||||||
|
--note-bg: #ffffff;
|
||||||
|
--note-border: #dcd8cd;
|
||||||
|
--note-hover: #edeae2;
|
||||||
|
|
||||||
|
--accent: #b3893e; /* Tlumená bronzová */
|
||||||
|
--accent-hover: #967131;
|
||||||
|
|
||||||
|
--modal-bg: #ffffff;
|
||||||
|
--modal-overlay: rgba(44, 46, 48, 0.4);
|
||||||
|
|
||||||
|
--active-bg: #fdfbf7;
|
||||||
|
--active-border: #b3893e;
|
||||||
|
|
||||||
|
/* --card-bg: #222529;*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tmavý motiv - Hluboká noc a bronz (Výchozí pro landing page) */
|
||||||
|
body.dark {
|
||||||
|
--bg: #1a1c1e;
|
||||||
|
--text: #e2e4e9;
|
||||||
|
--muted: #9ba1a6;
|
||||||
|
|
||||||
|
--button-bg: #2d3135;
|
||||||
|
--button-text: #e2e4e9;
|
||||||
|
|
||||||
|
--note-bg: #222529;
|
||||||
|
--note-border: #2e3238;
|
||||||
|
--note-hover: #2b2e33;
|
||||||
|
|
||||||
|
--accent: #c5a059; /* Jasnější stoická bronzová */
|
||||||
|
--accent-hover: #dfba73;
|
||||||
|
|
||||||
|
--modal-bg: #222529;
|
||||||
|
--modal-overlay: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
|
--active-bg: rgba(197, 160, 89, 0.05);
|
||||||
|
--active-border: #c5a059;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Základní nastavení */
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, monospace, sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 40px 20px;
|
||||||
|
line-height: 1.6;
|
||||||
|
transition: background 250ms ease, color 250ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid rgba(197, 160, 89, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigace */
|
||||||
|
.top-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
border-bottom: 1px solid var(--note-border);
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a,
|
||||||
|
.top-nav .theme-toggle {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a:hover,
|
||||||
|
.top-nav .theme-toggle:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nadpisy */
|
||||||
|
h1 {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
/*margin-bottom: 20px;*/
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
/*display: flex;*/
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1::after {
|
||||||
|
content: " 🦅";
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Formulářové prvky */
|
||||||
|
#noteTitle {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteTitle:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteTags {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteTags:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 220px;
|
||||||
|
padding: 14px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
resize: vertical;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tlačítka */
|
||||||
|
button {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
padding: 10px 18px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Editor toolbar */
|
||||||
|
.editor-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background: var(--note-bg);
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text);
|
||||||
|
padding: 6px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Layout editoru a náhledu */
|
||||||
|
.editor-wrap {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-wrap textarea {
|
||||||
|
flex: 1 1 55%;
|
||||||
|
min-height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
flex: 1 1 45%;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
background: var(--note-bg);
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
min-height: 250px;
|
||||||
|
max-height: 400px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Akce pod editorem */
|
||||||
|
.editor-actions {
|
||||||
|
margin: 15px 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newNoteButton {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newNoteButton:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-status {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchTagInput {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchTagInput:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seznam poznámek */
|
||||||
|
#notesContainer {
|
||||||
|
margin-top: 25px;
|
||||||
|
border-top: 1px solid var(--note-border);
|
||||||
|
padding-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--note-bg);
|
||||||
|
transition: transform 0.15s, border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: rgba(197, 160, 89, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.active {
|
||||||
|
background: var(--active-bg);
|
||||||
|
border-color: var(--active-border);
|
||||||
|
box-shadow: 0 0 10px rgba(197, 160, 89, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-left {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding-right: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-preview {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 10px;
|
||||||
|
color: var(--text);
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: opacity 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button:hover {
|
||||||
|
background: var(--button-bg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button.delete:hover {
|
||||||
|
color: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-notes {
|
||||||
|
color: var(--muted);
|
||||||
|
font-style: italic;
|
||||||
|
padding: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notes-hint {
|
||||||
|
margin-top: 10px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drag & Drop styly */
|
||||||
|
.note-item.drag-over {
|
||||||
|
border-style: dashed;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.drag-after {
|
||||||
|
box-shadow: inset 0 -3px 0 0 var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.dragging {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modální okno */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--modal-overlay);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
background: var(--modal-bg);
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 95%;
|
||||||
|
max-width: 800px;
|
||||||
|
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal h2 {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 16px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--muted);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-close:hover {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modalSaveBtn {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modalSaveBtn:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styly pro renderovaný Markdown */
|
||||||
|
.preview h1, .preview h2, .preview h3 {
|
||||||
|
margin: 14px 0 8px 0;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
.preview p {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.preview pre {
|
||||||
|
background: rgba(0, 0, 0, 0.15);
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
body:not(.dark) .preview pre {
|
||||||
|
background: #f0ede6;
|
||||||
|
}
|
||||||
|
.preview code {
|
||||||
|
font-family: monospace;
|
||||||
|
background: rgba(197, 160, 89, 0.15);
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
.preview ul, .preview ol {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.preview blockquote {
|
||||||
|
border-left: 3px solid var(--accent);
|
||||||
|
padding-left: 12px;
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--muted);
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
.preview table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
.preview th, .preview td {
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.preview th {
|
||||||
|
background: var(--button-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responzivita */
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
body {
|
||||||
|
padding: 20px 10px;
|
||||||
|
}
|
||||||
|
.editor-wrap {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.preview {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#links {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineHR {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 26px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Homepage card*/
|
||||||
|
.new-name {
|
||||||
|
color: var(--accent-bronze);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.philosophy {
|
||||||
|
font-style: italic;
|
||||||
|
border-left: 2px solid var(--accent-bronze);
|
||||||
|
padding-left: 15px;
|
||||||
|
text-align: left;
|
||||||
|
margin: 30px 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btnHomePage {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: var(--accent-bronze);
|
||||||
|
color: #e0961f;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 14px 28px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
transition: background-color 0.2s ease, transform 0.1s ease;
|
||||||
|
box-shadow: 0 8px 16px rgba(197, 160, 89, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btnHomePage:hover {
|
||||||
|
background-color: var(--accent-hover);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btnHomePage:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="cs">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title data-i18n-key="index.pageTitle">DOXA Notes — Local-First poznámky a minimalistický Markdown editor</title>
|
||||||
|
<meta name="description" data-i18n-key="index.pageDescription" data-i18n-attr="content"
|
||||||
|
content="DOXA Notes je minimalistická, local‑first poznámková aplikace s podporou Markdownu a otevřeným formátem JSON. Absolutní soukromí pro vaše myšlenky." />
|
||||||
|
<link rel="canonical" href="https://doxa-notes.org/index.html" />
|
||||||
|
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="https://doxa-notes.org/" />
|
||||||
|
<meta property="og:title" data-i18n-key="index.ogTitle" data-i18n-attr="content" content="DOXA Notes — Local-First poznámky a minimalistický Markdown editor" />
|
||||||
|
<meta property="og:description" data-i18n-key="index.ogDescription" data-i18n-attr="content"
|
||||||
|
content="DOXA Notes je minimalistická, local‑first poznámková aplikace založená na otevřeném JSON formátu. Data zůstávají plně pod vaší kontrolou." />
|
||||||
|
<meta property="og:image" content="https://doxa-notes.org/assets/img/doxa.png" />
|
||||||
|
<meta property="og:image:alt" content="DOXA Notes - Local-First Notes & Sovereignty" />
|
||||||
|
<meta property="og:locale" data-i18n-key="ogLocale" data-i18n-attr="content" content="cs_CZ" />
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" data-i18n-key="index.twitterTitle" data-i18n-attr="content" content="DOXA Notes — Local-First poznámky a minimalistický Markdown editor" />
|
||||||
|
<meta name="twitter:description" data-i18n-key="index.twitterDescription" data-i18n-attr="content"
|
||||||
|
content="DOXA Notes je minimalistická, local‑first poznámková aplikace založená na otevřeném JSON formátu." />
|
||||||
|
<meta name="twitter:image" content="https://doxa-notes.org/assets/img/doxa.png" />
|
||||||
|
<meta name="twitter:image:alt" content="DOXA Notes - Local-First Notes & Sovereignty" />
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "SoftwareApplication",
|
||||||
|
"name": "DOXA Notes",
|
||||||
|
"operatingSystem": "Windows, Linux, Web",
|
||||||
|
"applicationCategory": "UtilitiesApplication",
|
||||||
|
"offers": {
|
||||||
|
"@type": "Offer",
|
||||||
|
"price": "0",
|
||||||
|
"priceCurrency": "CZK"
|
||||||
|
},
|
||||||
|
"license": "https://opensource.org/licenses/MIT",
|
||||||
|
"description": "Minimalistická local-first poznámková aplikace běžící plně v prohlížeči, s daty v otevřeném formátu JSON a podporou Markdownu.",
|
||||||
|
"mainEntityOfPage": "https://doxa-notes.org/"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="assets/style/style.css" />
|
||||||
|
<link rel="shortcut icon" href="assets/img/logo.jpg" />
|
||||||
|
<script defer src="https://analytics.lukan.cz/script.js"
|
||||||
|
data-website-id="3bfbabf3-f3ef-4275-86f5-3dc3e4e2084b"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<nav class="top-nav">
|
||||||
|
<a href="index.html" data-i18n-key="nav.home">Domů</a>
|
||||||
|
<a href="https://wiki.doxa-notes.org/index.html" target="_blank" data-i18n-key="nav.wiki">Wiki</a>
|
||||||
|
<a href="privacy.html" data-i18n-key="nav.privacy">Ochrana soukromí</a>
|
||||||
|
<a href="about.html" data-i18n-key="nav.about">O projektu</a>
|
||||||
|
<button id="langToggle" class="theme-toggle" title="Přepnout jazyk / Switch language">
|
||||||
|
🇬🇧 / 🇨🇿
|
||||||
|
</button>
|
||||||
|
<button id="themeToggle" class="theme-toggle" data-i18n-key="nav.themeToggle" data-i18n-attr="aria-label" aria-label="Přepnout motiv">
|
||||||
|
🌙
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
<h1 data-i18n-key="index.mainTitle">DOXA Notes</h1>
|
||||||
|
<p style="color: var(--muted); margin-bottom: 30px;margin-top: 2px; font-size: 0.95rem;" data-i18n-key="index.subtitle">Local-First Notes & Sovereignty</p>
|
||||||
|
<p data-i18n-key="index.intro">DOXA Notes: Markdown editor, nezávislé, bezpečné a minimalistické prostředí pro uchovávání lidských myšlenek, poznámek a životních reflexí. V souladu se základními principy osobní svobody a soběstačnosti aplikace vědomě odmítá cloudové databáze, servery třetích stran a centralizovaná API. Doxa věří, že lidská mysl nepotřebuje být sledována algoritmy, aby mohla růst.</p>
|
||||||
|
<div class="philosophy" data-i18n-key="index.philosophy">
|
||||||
|
„Základní myšlenka zůstává nekompromisní: Radikální Local-First přístup, čistá data v JSON formátu a absolutní digitální soukromí bez korporátního cloudu a API.“
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<a href="https://app.doxa-notes.org" class="btnHomePage" target="_blank" data-i18n-key="index.appButton">Vstoupit do DOXA Notes →</a>
|
||||||
|
<p style="margin-bottom: 30px;margin-top: 35px; font-size: 0.95rem;"><a id="links" href="https://cli.doxa-notes.org" target="_blank">DOXA-CLI (Notes) - terminal version in GO language</a></p>
|
||||||
|
<p style="margin-bottom: 30px;margin-top: 35px; font-size: 0.95rem;"><a id="links" href="https://secret.doxa-notes.org" target="_blank">DOXA-SECRET - secure offline command-line tool for encrypting and decrypting messages</a></p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<p class="copyright" data-i18n-key="footer.copyright">Doxa Notes © 2026 | Local-First & Freedom 🦅</p>
|
||||||
|
</footer>
|
||||||
|
<script src="assets/js/i18n.js"></script>
|
||||||
|
<script src="assets/js/script.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="cs">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title data-i18n-key="privacy.pageTitle">Ochrana soukromí — DOXA Notes</title>
|
||||||
|
<meta name="description" data-i18n-key="privacy.pageDescription" data-i18n-attr="content"
|
||||||
|
content="Zásady ochrany osobních údajů a soukromí v aplikaci DOXA Notes. Žádná data neodcházejí na servery třetích stran, vše zůstává u vás." />
|
||||||
|
<link rel="canonical" href="https://doxa-notes.org/privacy.html" />
|
||||||
|
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="https://doxa-notes.org/privacy.html" />
|
||||||
|
<meta property="og:title" data-i18n-key="privacy.ogTitle" data-i18n-attr="content" content="Ochrana soukromí — DOXA Notes" />
|
||||||
|
<meta property="og:description" data-i18n-key="privacy.ogDescription" data-i18n-attr="content" content="Zásady ochrany osobních údajů a soukromí v aplikaci DOXA Notes. Žádná data neodcházejí na servery třetích stran." />
|
||||||
|
<meta property="og:image" content="https://doxa-notes.org/assets/img/doxa.png" />
|
||||||
|
<meta property="og:image:alt" content="DOXA Notes - Local-First Notes & Sovereignty" />
|
||||||
|
<meta property="og:locale" data-i18n-key="ogLocale" data-i18n-attr="content" content="cs_CZ" />
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" data-i18n-key="privacy.twitterTitle" data-i18n-attr="content" content="Ochrana soukromí — DOXA Notes" />
|
||||||
|
<meta name="twitter:description" data-i18n-key="privacy.twitterDescription" data-i18n-attr="content" content="Zásady ochrany osobních údajů a soukromí v aplikaci DOXA Notes." />
|
||||||
|
<meta name="twitter:image" content="https://doxa-notes.org/assets/img/doxa.png" />
|
||||||
|
<meta name="twitter:image:alt" content="DOXA Notes - Local-First Notes & Sovereignty" />
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="assets/style/style.css" />
|
||||||
|
<link rel="shortcut icon" href="assets/img/brain-solid-full.svg" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<nav class="top-nav">
|
||||||
|
<a href="index.html" data-i18n-key="nav.home">Domů</a>
|
||||||
|
<a href="https://wiki.doxa-notes.org/index.html" target="_blank" data-i18n-key="nav.wiki">Wiki</a>
|
||||||
|
<a href="privacy.html" data-i18n-key="nav.privacy">Ochrana soukromí</a>
|
||||||
|
<a href="about.html" data-i18n-key="nav.about">O projektu</a>
|
||||||
|
<button id="langToggle" class="theme-toggle" title="Přepnout jazyk / Switch language">
|
||||||
|
🇬🇧 / 🇨🇿
|
||||||
|
</button>
|
||||||
|
<button id="themeToggle" class="theme-toggle" data-i18n-key="nav.themeToggle" data-i18n-attr="aria-label" aria-label="Přepnout motiv">
|
||||||
|
🌙
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<h1 data-i18n-key="privacy.mainTitle">Ochrana soukromí</h1>
|
||||||
|
|
||||||
|
<p data-i18n-key="privacy.intro">
|
||||||
|
Doxa je navržena jako <strong>local‑first</strong> aplikace
|
||||||
|
— soukromí je primární designový princip. Níže shrnutí, jak
|
||||||
|
zacházím s daty v aktuálním stavu projektu.
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<h2 data-i18n-key="privacy.stores.title">Co aplikace ukládá</h2>
|
||||||
|
<ul>
|
||||||
|
<li data-i18n-key="privacy.stores.item1">
|
||||||
|
Poznámky: <code>id</code>, <code>name</code>,
|
||||||
|
<code>content</code>, <code>created_at</code>,
|
||||||
|
<code>modified_at</code> (pokud existuje).
|
||||||
|
</li>
|
||||||
|
<li data-i18n-key="privacy.stores.item2">
|
||||||
|
Žádné automatické telemetry ani sledování není
|
||||||
|
implementováno.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<h2 data-i18n-key="privacy.where.title">Kde jsou data uložena</h2>
|
||||||
|
<ul>
|
||||||
|
<li data-i18n-key="privacy.where.item1">
|
||||||
|
V prohlížeči: <strong>localStorage</strong> (klíč
|
||||||
|
<code>doxa_notes_v1</code>).
|
||||||
|
</li>
|
||||||
|
<li data-i18n-key="privacy.where.item2">
|
||||||
|
V Electron/Node režimu: soubor na disku (např.
|
||||||
|
<code>assets/js/data/doxa.json</code>).
|
||||||
|
</li>
|
||||||
|
<li data-i18n-key="privacy.where.item3">
|
||||||
|
Export/import: uživatel může stáhnout JSON a nahrát ho na
|
||||||
|
jiném zařízení — je to manuální proces.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<h2 data-i18n-key="privacy.notSent.title">Co aplikace NEODESÍLÁ</h2>
|
||||||
|
<ul>
|
||||||
|
<li data-i18n-key="privacy.notSent.item1">
|
||||||
|
Výchozí instalace neposílá poznámky na žádný vzdálený
|
||||||
|
server.
|
||||||
|
</li>
|
||||||
|
<li data-i18n-key="privacy.notSent.item2">
|
||||||
|
Bez implementace serverové synchronizace nejsou data
|
||||||
|
odesílána ani zálohována vůči třetím stranám.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<h2 data-i18n-key="privacy.responsibility.title">Odpovědnost uživatele</h2>
|
||||||
|
<p data-i18n-key="privacy.responsibility.body">
|
||||||
|
Protože data jsou lokální, odpovědnost za zálohy a bezpečnou
|
||||||
|
konfiguraci serveru (pokud použita) nese vlastník instalace.
|
||||||
|
Doporučuji pravidelné exporty a bezpečné úložiště záloh.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<p class="copyright" data-i18n-key="footer.copyright">© 2026 Doxa Notes | Local-First & Freedom 🦅</p>
|
||||||
|
</footer>
|
||||||
|
<script src="assets/js/i18n.js"></script>
|
||||||
|
<script src="assets/js/script.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
Allow: /index.html
|
||||||
|
Allow: /about.html
|
||||||
|
Allow: /privacy.html
|
||||||
|
Allow: /wiki/
|
||||||
|
Disallow: /web_app/
|
||||||
|
Disallow: /desktop_app_dev/
|
||||||
|
Disallow: /test/
|
||||||
|
Disallow: /docs/
|
||||||
|
|
||||||
|
Sitemap: https://doxa-notes.org/sitemap.xml
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
<url>
|
||||||
|
<loc>https://doxa-notes.org/</loc>
|
||||||
|
<lastmod>2026-07-04</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://doxa-notes.org/index.html</loc>
|
||||||
|
<lastmod>2026-07-04</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://doxa-notes.org/about.html</loc>
|
||||||
|
<lastmod>2026-07-04</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://doxa-notes.org/privacy.html</loc>
|
||||||
|
<lastmod>2026-07-04</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.7</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://doxa-notes.org/navod.html</loc>
|
||||||
|
<lastmod>2026-07-04</lastmod>
|
||||||
|
<changefreq>weekly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
</urlset>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M184 120C184 89.1 209.1 64 240 64L264 64C281.7 64 296 78.3 296 96L296 544C296 561.7 281.7 576 264 576L232 576C202.2 576 177.1 555.6 170 528C169.3 528 168.7 528 168 528C123.8 528 88 492.2 88 448C88 430 94 413.4 104 400C84.6 385.4 72 362.2 72 336C72 305.1 89.6 278.2 115.2 264.9C108.1 252.9 104 238.9 104 224C104 179.8 139.8 144 184 144L184 120zM456 120L456 144C500.2 144 536 179.8 536 224C536 239 531.9 253 524.8 264.9C550.5 278.2 568 305 568 336C568 362.2 555.4 385.4 536 400C546 413.4 552 430 552 448C552 492.2 516.2 528 472 528C471.3 528 470.7 528 470 528C462.9 555.6 437.8 576 408 576L376 576C358.3 576 344 561.7 344 544L344 96C344 78.3 358.3 64 376 64L400 64C430.9 64 456 89.1 456 120z"/></svg>
|
||||||
|
After Width: | Height: | Size: 920 B |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 658 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
// Tento skript by měl být v HTML vložen PŘED hlavním script.js
|
||||||
|
|
||||||
|
// Globální překladová funkce, kterou bude používat i script.js
|
||||||
|
let translations = {};
|
||||||
|
window.t = (key, params = {}) => {
|
||||||
|
let text = translations[key] || key;
|
||||||
|
for (const p in params) {
|
||||||
|
text = text.replace(`{${p}}`, params[p]);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Načte překlady pro daný jazyk a aplikuje je na DOM.
|
||||||
|
* @param {string} lang - Kód jazyka (např. 'en', 'cs').
|
||||||
|
*/
|
||||||
|
window.setLanguage = async function(lang) {
|
||||||
|
// 1. Uloží preferenci uživatele
|
||||||
|
localStorage.setItem('doxa_lang', lang);
|
||||||
|
|
||||||
|
// Nastaví atribut 'lang' na <html> elementu
|
||||||
|
document.documentElement.lang = lang;
|
||||||
|
|
||||||
|
// 2. Načte a uloží překlady
|
||||||
|
try {
|
||||||
|
const response = await fetch(`assets/locales/${lang}.json`);
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(`Chyba: Nepodařilo se načíst soubor s překlady: ${lang}.json`);
|
||||||
|
translations = {};
|
||||||
|
} else {
|
||||||
|
translations = await response.json();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Chyba při načítání překladů pro jazyk ${lang}:`, error);
|
||||||
|
translations = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Přeloží všechny statické prvky v DOM
|
||||||
|
document.querySelectorAll('[data-i18n-key]').forEach(el => {
|
||||||
|
const key = el.getAttribute('data-i18n-key');
|
||||||
|
const translation = window.t(key);
|
||||||
|
|
||||||
|
// Přeloží specifický atribut (např. placeholder, title), pokud je definován
|
||||||
|
const attr = el.getAttribute('data-i18n-attr');
|
||||||
|
if (attr) {
|
||||||
|
el.setAttribute(attr, translation);
|
||||||
|
} else {
|
||||||
|
// Jinak přeloží obsah elementu
|
||||||
|
el.innerHTML = translation;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. DŮLEŽITÉ: Znovu inicializuje části aplikace, které používají dynamický text
|
||||||
|
if (typeof window.updateDynamicUIText === 'function') {
|
||||||
|
window.updateDynamicUIText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const userLang = localStorage.getItem('doxa_lang') || navigator.language.split('-')[0];
|
||||||
|
const langToLoad = ['en', 'cs'].includes(userLang) ? userLang : 'cs';
|
||||||
|
window.setLanguage(langToLoad);
|
||||||
|
|
||||||
|
const langToggleBtn = document.getElementById('langToggle');
|
||||||
|
if (langToggleBtn) {
|
||||||
|
langToggleBtn.addEventListener('click', () => {
|
||||||
|
const currentLang = localStorage.getItem('doxa_lang') || 'cs';
|
||||||
|
const nextLang = currentLang === 'cs' ? 'en' : 'cs';
|
||||||
|
window.setLanguage(nextLang);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"webTitle": "DOXA Notes — Workspace",
|
||||||
|
"ogLocale": "cs_CZ",
|
||||||
|
"navLink": "Web DOXA Notes, Návody, Manifest",
|
||||||
|
"themeToggleLabel": "Přepnout motiv",
|
||||||
|
"mainTitle": "DOXA Notes 🦅",
|
||||||
|
"mainSubtitle": "Local-First Notes & Sovereignty",
|
||||||
|
"noteTitlePlaceholder": "Název poznámky",
|
||||||
|
"noteTagsPlaceholder": "Tagy (oddělujte čárkou, např: práce, citaty)",
|
||||||
|
"noteFolderPlaceholder": "Složka (nepovinné)",
|
||||||
|
"toolbarH1": "Nadpis H1",
|
||||||
|
"toolbarH2": "Nadpis H2",
|
||||||
|
"toolbarH3": "Nadpis H3",
|
||||||
|
"toolbarBold": "Tučné (Ctrl+B)",
|
||||||
|
"toolbarItalic": "Kurzíva (Ctrl+I)",
|
||||||
|
"toolbarStrike": "Přeškrtnutí",
|
||||||
|
"toolbarUl": "Odrážkový seznam",
|
||||||
|
"toolbarOl": "Číslovaný seznam",
|
||||||
|
"toolbarTask": "Úkol (checkbox)",
|
||||||
|
"toolbarQuote": "Citace",
|
||||||
|
"toolbarHr": "Vodorovná čára",
|
||||||
|
"toolbarCode": "Inline kód",
|
||||||
|
"toolbarCodeblock": "Blok kódu",
|
||||||
|
"toolbarLink": "Vložit odkaz",
|
||||||
|
"toolbarImage": "Vložit obrázek",
|
||||||
|
"toolbarTable": "Vložit tabulku",
|
||||||
|
"toolbarPreview": "Náhled",
|
||||||
|
"noteInputPlaceholder": "Zapiš poznámku... Použij / pro rychlé vyhledávání příkazů",
|
||||||
|
"saveNowButton": "Uložit nyní",
|
||||||
|
"exportJsonButton": "Exportovat JSON",
|
||||||
|
"exportMarkdownButton": "Exportovat do Markdown",
|
||||||
|
"importJsonButton": "Importovat JSON",
|
||||||
|
"newNoteButton": "➕ Nová poznámka",
|
||||||
|
"filterAllFolders": "📁 Všechny složky",
|
||||||
|
"searchInputPlaceholder": "🔍 Vyhledat podle názvu, obsahu, tagu nebo složky...",
|
||||||
|
"dragHint": "Přetáhněte poznámky pro změnu pořadí nebo použijte šipky vedle položek.",
|
||||||
|
"slashDate": "📅 Vložit datum",
|
||||||
|
"slashTime": "🕚 Vložit čas",
|
||||||
|
"slashCode": "📝 Vložit kód",
|
||||||
|
"slashCodeblock": "📄 Vložit blok kódu",
|
||||||
|
"slashLink": "🔗 Vložit odkaz",
|
||||||
|
"slashImage": "🖼️ Vložit obrázek",
|
||||||
|
"slashTable": "📊 Vložit tabulku",
|
||||||
|
"slashQuote": "❝ Vložit citaci",
|
||||||
|
"slashHr": "🖋️ Vodorovná čára",
|
||||||
|
"slashTask": "☑️ Vložit úkol",
|
||||||
|
"slashList": "📋 Vložit seznam",
|
||||||
|
"slashH1": "Nadpis H1",
|
||||||
|
"slashH2": "Nadpis H2",
|
||||||
|
"slashH3": "Nadpis H3",
|
||||||
|
"notesSavedAlert": "Poznámky byly úspěšně uloženy.",
|
||||||
|
"saveErrorAlert": "Chyba při ukládání: {message}",
|
||||||
|
"confirmDeleteNote": "Opravdu chcete smazat poznámku \"{noteName}\"?",
|
||||||
|
"alertNoTitle": "Poznámka musí mít název!",
|
||||||
|
"noNotes": "Žádné poznámky k zobrazení.",
|
||||||
|
"noNotesForFilter": "Žádné poznámky neodpovídají vašemu hledání.",
|
||||||
|
"noteNameDefault": "Poznámka {index}",
|
||||||
|
"noteMoveUp": "Posunout nahoru",
|
||||||
|
"noteMoveDown": "Posunout dolů",
|
||||||
|
"noteEditModal": "Upravit v okně",
|
||||||
|
"noteDelete": "Smazat",
|
||||||
|
"promptNewNote": "Zadejte název nové poznámky:",
|
||||||
|
"importInvalidJson": "Neplatný formát JSON souboru.",
|
||||||
|
"importReadError": "Chyba při čtení souboru: {message}",
|
||||||
|
"exportEmptyAlert": "Nelze exportovat prázdnou poznámku!",
|
||||||
|
"previewUnavailable": "Náhled není k dispozici.",
|
||||||
|
"promptUrl": "Zadejte URL adresu:",
|
||||||
|
"promptImageUrl": "Zadejte URL adresu obrázku:",
|
||||||
|
"promptImageAlt": "Popis obrázku (alt):",
|
||||||
|
"promptTableCols": "Počet sloupců:",
|
||||||
|
"promptTableRows": "Počet řádků:",
|
||||||
|
"tableHeaderDefault": "Hlavička {index}",
|
||||||
|
"modalClose": "Zavřít",
|
||||||
|
"modalTitle": "Upravit poznámku",
|
||||||
|
"modalSave": "Uložit změny",
|
||||||
|
"modalCancel": "Zrušit",
|
||||||
|
"statusSaved": "Uloženo",
|
||||||
|
"themeCurrentDark": "tmavý",
|
||||||
|
"themeCurrentLight": "světlý",
|
||||||
|
"themeAriaSystem": "Motiv: systémový (aktuálně {current})",
|
||||||
|
"themeAriaDark": "Motiv: tmavý",
|
||||||
|
"themeAriaLight": "Motiv: světlý"
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"webTitle": "DOXA Notes — Workspace",
|
||||||
|
"ogLocale": "en_US",
|
||||||
|
"navLink": "Web DOXA Notes, Guides, Manifesto",
|
||||||
|
"themeToggleLabel": "Toggle theme",
|
||||||
|
"mainTitle": "DOXA Notes 🦅",
|
||||||
|
"mainSubtitle": "Local-First Notes & Sovereignty",
|
||||||
|
"noteTitlePlaceholder": "Note title",
|
||||||
|
"noteTagsPlaceholder": "Tags (comma separated, e.g: work, quotes)",
|
||||||
|
"noteFolderPlaceholder": "Folder (optional)",
|
||||||
|
"toolbarH1": "Heading H1",
|
||||||
|
"toolbarH2": "Heading H2",
|
||||||
|
"toolbarH3": "Heading H3",
|
||||||
|
"toolbarBold": "Bold (Ctrl+B)",
|
||||||
|
"toolbarItalic": "Italic (Ctrl+I)",
|
||||||
|
"toolbarStrike": "Strikethrough",
|
||||||
|
"toolbarUl": "Unordered list",
|
||||||
|
"toolbarOl": "Ordered list",
|
||||||
|
"toolbarTask": "Task (checkbox)",
|
||||||
|
"toolbarQuote": "Quote",
|
||||||
|
"toolbarHr": "Horizontal line",
|
||||||
|
"toolbarCode": "Inline code",
|
||||||
|
"toolbarCodeblock": "Code block",
|
||||||
|
"toolbarLink": "Insert link",
|
||||||
|
"toolbarImage": "Insert image",
|
||||||
|
"toolbarTable": "Insert table",
|
||||||
|
"toolbarPreview": "Preview",
|
||||||
|
"noteInputPlaceholder": "Write a note... Use / for quick command search",
|
||||||
|
"saveNowButton": "Save now",
|
||||||
|
"exportJsonButton": "Export JSON",
|
||||||
|
"exportMarkdownButton": "Export to Markdown",
|
||||||
|
"importJsonButton": "Import JSON",
|
||||||
|
"newNoteButton": "➕ New note",
|
||||||
|
"filterAllFolders": "📁 All folders",
|
||||||
|
"searchInputPlaceholder": "🔍 Search by title, content, tag, or folder...",
|
||||||
|
"dragHint": "Drag and drop notes to reorder or use the arrows next to the items.",
|
||||||
|
"slashDate": "📅 Insert date",
|
||||||
|
"slashTime": "🕚 Insert time",
|
||||||
|
"slashCode": "📝 Insert code",
|
||||||
|
"slashCodeblock": "📄 Insert code block",
|
||||||
|
"slashLink": "🔗 Insert link",
|
||||||
|
"slashImage": "🖼️ Insert image",
|
||||||
|
"slashTable": "📊 Insert table",
|
||||||
|
"slashQuote": "❝ Insert quote",
|
||||||
|
"slashHr": "🖋️ Horizontal line",
|
||||||
|
"slashTask": "☑️ Insert task",
|
||||||
|
"slashList": "📋 Insert list",
|
||||||
|
"slashH1": "Heading H1",
|
||||||
|
"slashH2": "Heading H2",
|
||||||
|
"slashH3": "Heading H3",
|
||||||
|
"notesSavedAlert": "Notes successfully saved.",
|
||||||
|
"saveErrorAlert": "Error saving: {message}",
|
||||||
|
"confirmDeleteNote": "Are you sure you want to delete note \"{noteName}\"?",
|
||||||
|
"alertNoTitle": "Note must have a title!",
|
||||||
|
"noNotes": "No notes to display.",
|
||||||
|
"noNotesForFilter": "No notes match your search.",
|
||||||
|
"noteNameDefault": "Note {index}",
|
||||||
|
"noteMoveUp": "Move up",
|
||||||
|
"noteMoveDown": "Move down",
|
||||||
|
"noteEditModal": "Edit in window",
|
||||||
|
"noteDelete": "Delete",
|
||||||
|
"promptNewNote": "Enter name for the new note:",
|
||||||
|
"importInvalidJson": "Invalid JSON file format.",
|
||||||
|
"importReadError": "Error reading file: {message}",
|
||||||
|
"exportEmptyAlert": "Cannot export an empty note!",
|
||||||
|
"previewUnavailable": "Preview unavailable.",
|
||||||
|
"promptUrl": "Enter URL address:",
|
||||||
|
"promptImageUrl": "Enter image URL address:",
|
||||||
|
"promptImageAlt": "Image description (alt):",
|
||||||
|
"promptTableCols": "Number of columns:",
|
||||||
|
"promptTableRows": "Number of rows:",
|
||||||
|
"tableHeaderDefault": "Header {index}",
|
||||||
|
"modalClose": "Close",
|
||||||
|
"modalTitle": "Edit note",
|
||||||
|
"modalSave": "Save changes",
|
||||||
|
"modalCancel": "Cancel",
|
||||||
|
"statusSaved": "Saved",
|
||||||
|
"themeCurrentDark": "dark",
|
||||||
|
"themeCurrentLight": "light",
|
||||||
|
"themeAriaSystem": "Theme: system (currently {current})",
|
||||||
|
"themeAriaDark": "Theme: dark",
|
||||||
|
"themeAriaLight": "Theme: light"
|
||||||
|
}
|
||||||
@@ -0,0 +1,500 @@
|
|||||||
|
/* --- STOICKÝ DESIGN PRO DOXA NOTES --- */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Světlý motiv - Inspirovaný antickým mramorem a pergamenem */
|
||||||
|
--bg: #f5f4f0;
|
||||||
|
--text: #2c2e30;
|
||||||
|
--muted: #73797e;
|
||||||
|
|
||||||
|
--button-bg: #e5e2da;
|
||||||
|
--button-text: #2c2e30;
|
||||||
|
|
||||||
|
--note-bg: #ffffff;
|
||||||
|
--note-border: #dcd8cd;
|
||||||
|
--note-hover: #edeae2;
|
||||||
|
|
||||||
|
--accent: #b3893e; /* Tlumená bronzová */
|
||||||
|
--accent-hover: #967131;
|
||||||
|
|
||||||
|
--modal-bg: #ffffff;
|
||||||
|
--modal-overlay: rgba(44, 46, 48, 0.4);
|
||||||
|
|
||||||
|
--active-bg: #fdfbf7;
|
||||||
|
--active-border: #b3893e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tmavý motiv - Hluboká noc a bronz (Výchozí pro landing page) */
|
||||||
|
body.dark {
|
||||||
|
--bg: #1a1c1e;
|
||||||
|
--text: #e2e4e9;
|
||||||
|
--muted: #9ba1a6;
|
||||||
|
|
||||||
|
--button-bg: #2d3135;
|
||||||
|
--button-text: #e2e4e9;
|
||||||
|
|
||||||
|
--note-bg: #222529;
|
||||||
|
--note-border: #2e3238;
|
||||||
|
--note-hover: #2b2e33;
|
||||||
|
|
||||||
|
--accent: #c5a059; /* Jasnější stoická bronzová */
|
||||||
|
--accent-hover: #dfba73;
|
||||||
|
|
||||||
|
--modal-bg: #222529;
|
||||||
|
--modal-overlay: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
|
--active-bg: rgba(197, 160, 89, 0.05);
|
||||||
|
--active-border: #c5a059;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Základní nastavení */
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, monospace, sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 40px 20px;
|
||||||
|
line-height: 1.6;
|
||||||
|
transition: background 250ms ease, color 250ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigace */
|
||||||
|
.top-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
border-bottom: 1px solid var(--note-border);
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a,
|
||||||
|
.top-nav .theme-toggle {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a:hover,
|
||||||
|
.top-nav .theme-toggle:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nadpisy */
|
||||||
|
h1 {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1::after {
|
||||||
|
content: " 🦅";
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Formulářové prvky */
|
||||||
|
#noteTitle {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteTitle:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 220px;
|
||||||
|
padding: 14px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
resize: vertical;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tlačítka */
|
||||||
|
button {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
padding: 10px 18px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Editor toolbar */
|
||||||
|
.editor-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background: var(--note-bg);
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text);
|
||||||
|
padding: 6px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Layout editoru a náhledu */
|
||||||
|
.editor-wrap {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-wrap textarea {
|
||||||
|
flex: 1 1 55%;
|
||||||
|
min-height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
flex: 1 1 45%;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
background: var(--note-bg);
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
/*overflow-auto;*/
|
||||||
|
min-height: 250px;
|
||||||
|
max-height: 400px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Akce pod editorem */
|
||||||
|
.editor-actions {
|
||||||
|
margin: 15px 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newNoteButton {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newNoteButton:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-status {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seznam poznámek */
|
||||||
|
#notesContainer {
|
||||||
|
margin-top: 25px;
|
||||||
|
border-top: 1px solid var(--note-border);
|
||||||
|
padding-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--note-bg);
|
||||||
|
transition: transform 0.15s, border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: rgba(197, 160, 89, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.active {
|
||||||
|
background: var(--active-bg);
|
||||||
|
border-color: var(--active-border);
|
||||||
|
box-shadow: 0 0 10px rgba(197, 160, 89, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-left {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding-right: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-preview {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 10px;
|
||||||
|
color: var(--text);
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: opacity 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button:hover {
|
||||||
|
background: var(--button-bg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button.delete:hover {
|
||||||
|
color: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-notes {
|
||||||
|
color: var(--muted);
|
||||||
|
font-style: italic;
|
||||||
|
padding: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notes-hint {
|
||||||
|
margin-top: 10px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drag & Drop styly */
|
||||||
|
.note-item.drag-over {
|
||||||
|
border-style: dashed;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.drag-after {
|
||||||
|
box-shadow: inset 0 -3px 0 0 var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.dragging {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modální okno */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--modal-overlay);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
background: var(--modal-bg);
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 95%;
|
||||||
|
max-width: 800px;
|
||||||
|
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal h2 {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 16px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--muted);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-close:hover {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modalSaveBtn {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modalSaveBtn:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styly pro renderovaný Markdown */
|
||||||
|
.preview h1, .preview h2, .preview h3 {
|
||||||
|
margin: 14px 0 8px 0;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
.preview p {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.preview pre {
|
||||||
|
background: rgba(0, 0, 0, 0.15);
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
body:not(.dark) .preview pre {
|
||||||
|
background: #f0ede6;
|
||||||
|
}
|
||||||
|
.preview code {
|
||||||
|
font-family: monospace;
|
||||||
|
background: rgba(197, 160, 89, 0.15);
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
.preview ul, .preview ol {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.preview blockquote {
|
||||||
|
border-left: 3px solid var(--accent);
|
||||||
|
padding-left: 12px;
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--muted);
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
.preview table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
.preview th, .preview td {
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.preview th {
|
||||||
|
background: var(--button-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responzivita */
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
body {
|
||||||
|
padding: 20px 10px;
|
||||||
|
}
|
||||||
|
.editor-wrap {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.preview {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
/* Theme variables */
|
||||||
|
:root {
|
||||||
|
--bg: #fafafa;
|
||||||
|
--text: #333333;
|
||||||
|
--muted: #666666;
|
||||||
|
|
||||||
|
--button-bg: #333333;
|
||||||
|
--button-text: #ffffff;
|
||||||
|
|
||||||
|
--note-bg: #ffffff;
|
||||||
|
--note-border: #dddddd;
|
||||||
|
--note-hover: #f5f5f5;
|
||||||
|
|
||||||
|
--accent: #0b63d6;
|
||||||
|
|
||||||
|
--modal-bg: #ffffff;
|
||||||
|
--modal-overlay: rgba(0, 0, 0, 0.45);
|
||||||
|
|
||||||
|
--active-bg: #e6f0ff;
|
||||||
|
--active-border: #a6c8ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme overrides */
|
||||||
|
body.dark {
|
||||||
|
--bg: #0b1220;
|
||||||
|
--text: #e6eef8;
|
||||||
|
--muted: #9aa6b2;
|
||||||
|
|
||||||
|
--button-bg: #1f2937;
|
||||||
|
--button-text: #ffffff;
|
||||||
|
|
||||||
|
--note-bg: #071022;
|
||||||
|
--note-border: #263046;
|
||||||
|
--note-hover: #092033;
|
||||||
|
|
||||||
|
--accent: #3b82f6;
|
||||||
|
|
||||||
|
--modal-bg: #0b1220;
|
||||||
|
--modal-overlay: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
|
--active-bg: rgba(59, 130, 246, 0.14);
|
||||||
|
--active-border: #60a5fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base */
|
||||||
|
body {
|
||||||
|
font-family: monospace;
|
||||||
|
background: var(--bg);
|
||||||
|
padding: 40px;
|
||||||
|
color: var(--text);
|
||||||
|
transition:
|
||||||
|
background 200ms ease,
|
||||||
|
color 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a,
|
||||||
|
.top-nav .theme-toggle {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a:hover,
|
||||||
|
.top-nav .theme-toggle:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a.active {
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1::after {
|
||||||
|
content: " 🦅";
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteTitle {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 150px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notesContainer {
|
||||||
|
margin-top: 10px;
|
||||||
|
border-top: 1px solid var(--note-border);
|
||||||
|
padding-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item {
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--note-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.active {
|
||||||
|
background: var(--active-bg);
|
||||||
|
border-color: var(--active-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-notes {
|
||||||
|
color: var(--muted);
|
||||||
|
font-style: italic;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note list layout */
|
||||||
|
.note-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-left {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding-right: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-title {
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-preview {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: monospace;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.drag-over {
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.drag-after {
|
||||||
|
box-shadow: inset 0 -3px 0 0 rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.dragging {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--modal-overlay);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-overlay.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
background: var(--modal-bg);
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 92%;
|
||||||
|
max-width: 720px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 8px;
|
||||||
|
top: 8px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal input,
|
||||||
|
.modal textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
.container {
|
||||||
|
max-width: 900px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notes-hint {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Editor toolbar & preview */
|
||||||
|
.editor-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 6px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* active toolbar button */
|
||||||
|
.editor-toolbar button.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--button-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-wrap {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-wrap textarea {
|
||||||
|
flex: 1 1 60%;
|
||||||
|
min-height: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
flex: 1 1 40%;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
background: var(--note-bg);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-actions {
|
||||||
|
margin-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-status {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rendered markdown styles */
|
||||||
|
.preview h1,
|
||||||
|
.preview h2,
|
||||||
|
.preview h3 {
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
.preview pre {
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
padding: 8px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.preview code {
|
||||||
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.editor-wrap {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.preview {
|
||||||
|
max-height: 240px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 16px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,725 @@
|
|||||||
|
/* --- STOICKÝ DESIGN PRO DOXA NOTES --- */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Světlý motiv - Inspirovaný antickým mramorem a pergamenem */
|
||||||
|
--bg: #f5f4f0;
|
||||||
|
--text: #2c2e30;
|
||||||
|
--muted: #73797e;
|
||||||
|
|
||||||
|
--button-bg: #e5e2da;
|
||||||
|
--button-text: #2c2e30;
|
||||||
|
|
||||||
|
--note-bg: #ffffff;
|
||||||
|
--note-border: #dcd8cd;
|
||||||
|
--note-hover: #edeae2;
|
||||||
|
|
||||||
|
--accent: #b3893e; /* Tlumená bronzová */
|
||||||
|
--accent-hover: #967131;
|
||||||
|
|
||||||
|
--modal-bg: #ffffff;
|
||||||
|
--modal-overlay: rgba(44, 46, 48, 0.4);
|
||||||
|
|
||||||
|
--active-bg: #fdfbf7;
|
||||||
|
--active-border: #b3893e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tmavý motiv - Hluboká noc a bronz (Výchozí pro landing page) */
|
||||||
|
body.dark {
|
||||||
|
--bg: #1a1c1e;
|
||||||
|
--text: #e2e4e9;
|
||||||
|
--muted: #9ba1a6;
|
||||||
|
|
||||||
|
--button-bg: #2d3135;
|
||||||
|
--button-text: #e2e4e9;
|
||||||
|
|
||||||
|
--note-bg: #222529;
|
||||||
|
--note-border: #2e3238;
|
||||||
|
--note-hover: #2b2e33;
|
||||||
|
|
||||||
|
--accent: #c5a059; /* Jasnější stoická bronzová */
|
||||||
|
--accent-hover: #dfba73;
|
||||||
|
|
||||||
|
--modal-bg: #222529;
|
||||||
|
--modal-overlay: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
|
--active-bg: rgba(197, 160, 89, 0.05);
|
||||||
|
--active-border: #c5a059;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Základní nastavení */
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, monospace, sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 40px 20px;
|
||||||
|
line-height: 1.6;
|
||||||
|
transition: background 250ms ease, color 250ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigace */
|
||||||
|
.top-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
border-bottom: 1px solid var(--note-border);
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a,
|
||||||
|
.top-nav .theme-toggle {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a:hover,
|
||||||
|
.top-nav .theme-toggle:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-nav a.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nadpisy */
|
||||||
|
h1 {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
/*margin-bottom: 20px;*/
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Formulářové prvky */
|
||||||
|
#noteTitle {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteTitle:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteTags {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding: 10px 14px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteTags:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 220px;
|
||||||
|
padding: 14px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
resize: vertical;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Složka - malé pole vedle tagů */
|
||||||
|
#noteFolder {
|
||||||
|
flex: 0 0 180px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text);
|
||||||
|
background: linear-gradient(180deg, var(--button-bg), var(--note-bg));
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteFolder:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filtr složek jako tlačítko (velké jako ostatní tlačítka) */
|
||||||
|
#folderFilter {
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
height: 44px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#folderFilter:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure filter-row alignment */
|
||||||
|
.filter-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove bottom margin for search input inside filter-row */
|
||||||
|
.filter-row #searchTagInput {
|
||||||
|
margin-bottom: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zobrazení složky v seznamu poznámek - ikona vždy viditelná */
|
||||||
|
.note-preview-folder {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-top: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
.note-preview-folder .folder-icon {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
.note-preview-folder .folder-name {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.meta-row {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteFolder {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#folderFilter {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.meta-row {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteFolder {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#folderFilter {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tlačítka */
|
||||||
|
button {
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
padding: 10px 18px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Editor toolbar */
|
||||||
|
.editor-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background: var(--note-bg);
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text);
|
||||||
|
padding: 6px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar button.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Layout editoru a náhledu */
|
||||||
|
.editor-wrap {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-wrap textarea {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
min-height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
background: var(--note-bg);
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
min-height: 250px;
|
||||||
|
max-height: 600px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Akce pod editorem */
|
||||||
|
.editor-actions {
|
||||||
|
margin: 15px 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newNoteButton {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newNoteButton:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-status {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchTagInput {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchTagInput:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seznam poznámek */
|
||||||
|
#notesContainer {
|
||||||
|
margin-top: 25px;
|
||||||
|
border-top: 1px solid var(--note-border);
|
||||||
|
padding-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--note-bg);
|
||||||
|
transition: transform 0.15s, border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item:hover {
|
||||||
|
background: var(--note-hover);
|
||||||
|
border-color: rgba(197, 160, 89, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.active {
|
||||||
|
background: var(--active-bg);
|
||||||
|
border-color: var(--active-border);
|
||||||
|
box-shadow: 0 0 10px rgba(197, 160, 89, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-left {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding-right: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-preview {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 10px;
|
||||||
|
color: var(--text);
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: opacity 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button:hover {
|
||||||
|
background: var(--button-bg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-controls button.delete:hover {
|
||||||
|
color: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-notes {
|
||||||
|
color: var(--muted);
|
||||||
|
font-style: italic;
|
||||||
|
padding: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notes-hint {
|
||||||
|
margin-top: 10px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drag & Drop styly */
|
||||||
|
.note-item.drag-over {
|
||||||
|
border-style: dashed;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.drag-after {
|
||||||
|
box-shadow: inset 0 -3px 0 0 var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-item.dragging {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modální okno */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--modal-overlay);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
background: var(--modal-bg);
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 95%;
|
||||||
|
max-width: 800px;
|
||||||
|
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal h2 {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 16px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--muted);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .modal-close:hover {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modalSaveBtn {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modalSaveBtn:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styly pro renderovaný Markdown */
|
||||||
|
.preview h1, .preview h2, .preview h3 {
|
||||||
|
margin: 14px 0 8px 0;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
.preview p {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.preview pre {
|
||||||
|
background: rgba(0, 0, 0, 0.15);
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: auto;
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
body:not(.dark) .preview pre {
|
||||||
|
background: #f0ede6;
|
||||||
|
}
|
||||||
|
.preview code {
|
||||||
|
font-family: monospace;
|
||||||
|
background: rgba(197, 160, 89, 0.15);
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
.preview ul, .preview ol {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.preview blockquote {
|
||||||
|
border-left: 3px solid var(--accent);
|
||||||
|
padding-left: 12px;
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--muted);
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
.preview table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
.preview th, .preview td {
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.preview th {
|
||||||
|
background: var(--button-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responzivita */
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
body {
|
||||||
|
padding: 20px 10px;
|
||||||
|
}
|
||||||
|
.editor-wrap {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.preview {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#links {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
#lineHR {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slash command menu */
|
||||||
|
#slashCommandMenu {
|
||||||
|
position: fixed;
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);
|
||||||
|
z-index: 1000;
|
||||||
|
max-height: 280px;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#slashCommandMenu.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slash-command-item {
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 13px;
|
||||||
|
border-bottom: 1px solid var(--note-border);
|
||||||
|
transition: background 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slash-command-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slash-command-item:hover,
|
||||||
|
.slash-command-item.active {
|
||||||
|
background: var(--note-hover);
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Suggestions dropdown (tags & folders) */
|
||||||
|
.suggestions {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: var(--note-bg);
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 6px 20px rgba(0,0,0,0.12);
|
||||||
|
z-index: 1200;
|
||||||
|
max-height: 220px;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion-item {
|
||||||
|
padding: 8px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 14px;
|
||||||
|
border-bottom: 1px solid var(--note-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion-item:last-child { border-bottom: none; }
|
||||||
|
.suggestion-item:hover, .suggestion-item.active { background: var(--note-hover); color: var(--accent); font-weight: 600; }
|
||||||
|
|
||||||
|
/* ensure suggestions look ok on small screens */
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.suggestions { max-height: 160px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 26px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title data-i18n-key="webTitle">DOXA Notes — Workspace</title>
|
||||||
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
|
<meta name="description" data-i18n-key="webDescription" data-i18n-attr="content" content="DOXA Notes Workspace — Local-first poznámková aplikace s podporou Markdown. Vaše data zůstávají pod vaší kontrolou." />
|
||||||
|
<link rel="canonical" href="https://app.doxa-notes.org/" />
|
||||||
|
|
||||||
|
<meta property="og:type" content="webapp" />
|
||||||
|
<meta property="og:url" content="https://app.doxa-notes.org/" />
|
||||||
|
<meta property="og:title" data-i18n-key="ogTitle" data-i18n-attr="content" content="DOXA Notes — Workspace" />
|
||||||
|
<meta property="og:description" data-i18n-key="ogDescription" data-i18n-attr="content" content="Local-first poznámková aplikace s podporou Markdown. Vaše data zůstávají pod vaší kontrolou." />
|
||||||
|
<meta property="og:image" content="https://doxa-notes.org/assets/img/doxa.png" />
|
||||||
|
<meta property="og:image:alt" content="DOXA Notes - Local-First Notes & Sovereignty" />
|
||||||
|
<meta property="og:locale" data-i18n-key="ogLocale" data-i18n-attr="content" content="cs_CZ" />
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" data-i18n-key="twitterTitle" data-i18n-attr="content" content="DOXA Notes — Workspace" />
|
||||||
|
<meta name="twitter:description" data-i18n-key="twitterDescription" data-i18n-attr="content" content="Local-first poznámková aplikace s podporou Markdown." />
|
||||||
|
<meta name="twitter:image" content="https://doxa-notes.org/assets/img/doxa.png" />
|
||||||
|
<meta name="twitter:image:alt" content="DOXA Notes - Local-First Notes & Sovereignty" />
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="assets/style/style.css" />
|
||||||
|
<style>
|
||||||
|
#notesContainer {
|
||||||
|
max-height: 520px; /* Výška pro cca 6 náhledů, aby se stránka neprodlužovala */
|
||||||
|
overflow-y: auto; /* Povolí vertikální rolování, pokud je obsah vyšší */
|
||||||
|
border: 1px solid var(--note-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 1rem; /* Malý odstup pod kontejnerem */
|
||||||
|
transition: border-color 0.2s ease-in-out; /* Plynulý přechod pro zvýraznění */
|
||||||
|
}
|
||||||
|
|
||||||
|
#notesContainer:hover {
|
||||||
|
border-color: var(--accent); /* Zvýraznění při najetí myší */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="shortcut icon" href="assets/img/brain-solid-full.svg" />
|
||||||
|
<!-- Markdown and sanitization libraries (used for preview). CDN fallback; if offline, preview will be disabled. -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<nav class="top-nav">
|
||||||
|
<a href="https://doxa-notes.org/index.html" target="_blank" data-i18n-key="navLink">Web DOXA Notes, Návody, Manifest</a>
|
||||||
|
<button id="langToggle" class="theme-toggle" title="Přepnout jazyk / Switch language">
|
||||||
|
🇬🇧 / 🇨🇿 </button>
|
||||||
|
<button id="themeToggle" class="theme-toggle" data-i18n-key="themeToggleLabel" data-i18n-attr="aria-label" aria-label="Přepnout motiv">
|
||||||
|
🌙
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
<h1 data-i18n-key="mainTitle">DOXA Notes 🦅</h1>
|
||||||
|
<p style="color: var(--muted); margin-bottom: 30px;margin-top: 2px; font-size: 0.95rem;" data-i18n-key="mainSubtitle">Local-First Notes &
|
||||||
|
Sovereignty</p>
|
||||||
|
<div id="slashCommandMenu" class="hidden"></div>
|
||||||
|
<input id="noteTitle" data-i18n-key="noteTitlePlaceholder" data-i18n-attr="placeholder" placeholder="Název poznámky" />
|
||||||
|
<div class="meta-row">
|
||||||
|
<input id="noteTags" data-i18n-key="noteTagsPlaceholder" data-i18n-attr="placeholder" placeholder="Tagy (oddělujte čárkou, např: práce, citaty)" autocomplete="off" />
|
||||||
|
<div style="position:relative; display:inline-block;">
|
||||||
|
<input id="noteFolder" data-i18n-key="noteFolderPlaceholder" data-i18n-attr="placeholder" placeholder="Složka (nepovinné)" list="foldersList" autocomplete="off" />
|
||||||
|
<datalist id="foldersList"></datalist>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="editor-toolbar" id="editorToolbar">
|
||||||
|
<button type="button" data-action="h1" data-i18n-key="toolbarH1" data-i18n-attr="title" title="Nadpis H1">
|
||||||
|
H1
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="h2" data-i18n-key="toolbarH2" data-i18n-attr="title" title="Nadpis H2">
|
||||||
|
H2
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="h3" data-i18n-key="toolbarH3" data-i18n-attr="title" title="Nadpis H3">
|
||||||
|
H3
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" data-action="bold" data-i18n-key="toolbarBold" data-i18n-attr="title" title="Tučné (Ctrl+B)">
|
||||||
|
<strong>B</strong>
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="italic" data-i18n-key="toolbarItalic" data-i18n-attr="title" title="Kurzíva (Ctrl+I)">
|
||||||
|
<em>I</em>
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="strike" data-i18n-key="toolbarStrike" data-i18n-attr="title" title="Přeškrtnutí">
|
||||||
|
S
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" data-action="ul" data-i18n-key="toolbarUl" data-i18n-attr="title" title="Odrážkový seznam">
|
||||||
|
•
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="ol" data-i18n-key="toolbarOl" data-i18n-attr="title" title="Číslovaný seznam">
|
||||||
|
1.
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="task" data-i18n-key="toolbarTask" data-i18n-attr="title" title="Úkol (checkbox)">
|
||||||
|
☐
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" data-action="quote" data-i18n-key="toolbarQuote" data-i18n-attr="title" title="Citace">
|
||||||
|
❝
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="hr" data-i18n-key="toolbarHr" data-i18n-attr="title" title="Vodorovná čára">
|
||||||
|
—
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" data-action="code" data-i18n-key="toolbarCode" data-i18n-attr="title" title="Inline kód">
|
||||||
|
`kód`
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="codeblock" data-i18n-key="toolbarCodeblock" data-i18n-attr="title" title="Blok kódu">
|
||||||
|
</>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" data-action="link" data-i18n-key="toolbarLink" data-i18n-attr="title" title="Vložit odkaz">
|
||||||
|
🔗
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="image" data-i18n-key="toolbarImage" data-i18n-attr="title" title="Vložit obrázek">
|
||||||
|
🖼
|
||||||
|
</button>
|
||||||
|
<button type="button" data-action="table" data-i18n-key="toolbarTable" data-i18n-attr="title" title="Vložit tabulku">
|
||||||
|
▦
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" id="togglePreviewMain" data-i18n-key="toolbarPreview" data-i18n-attr="title" title="Náhled">
|
||||||
|
Náhled
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-wrap">
|
||||||
|
<textarea id="noteInput" data-i18n-key="noteInputPlaceholder" data-i18n-attr="placeholder" placeholder="Zapiš poznámku... Použij / pro rychlé vyhledávání příkazů"></textarea>
|
||||||
|
<div id="notePreview" class="preview hidden" aria-hidden="true"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-actions">
|
||||||
|
<button onclick="saveCurrentNote()" data-i18n-key="saveNowButton">
|
||||||
|
Uložit nyní
|
||||||
|
</button>
|
||||||
|
<button onclick="saveNotesToFile(true)" data-i18n-key="exportJsonButton">Exportovat JSON</button>
|
||||||
|
<button onclick="exportToMarkdown()" data-i18n-key="exportMarkdownButton">Exportovat do Markdown</button>
|
||||||
|
<button onclick="importujPoznamky()" data-i18n-key="importJsonButton">Importovat JSON</button>
|
||||||
|
<button id="newNoteButton" data-i18n-key="newNoteButton">➕ Nová poznámka</button>
|
||||||
|
<span id="saveStatus" class="save-status" aria-live="polite"></span>
|
||||||
|
</div>
|
||||||
|
<div class="filter-row">
|
||||||
|
<select id="folderFilter" aria-label="Filtrovat podle složky">
|
||||||
|
<option value="all" data-i18n-key="filterAllFolders">📁 Všechny složky</option>
|
||||||
|
</select>
|
||||||
|
<input id="searchTagInput" data-i18n-key="searchInputPlaceholder" data-i18n-attr="placeholder" placeholder="🔍 Vyhledat podle názvu, obsahu, tagu nebo složky..." />
|
||||||
|
</div>
|
||||||
|
<div id="notesContainer"></div>
|
||||||
|
<div id="notesHint" class="notes-hint" data-i18n-key="dragHint">
|
||||||
|
Přetáhněte poznámky pro změnu pořadí nebo použijte šipky vedle
|
||||||
|
položek.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="assets/js/i18n.js"></script>
|
||||||
|
<script src="assets/js/script.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-bs-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="/img/favicon.ico">
|
||||||
|
<title>DOXA Notes 🦅</title>
|
||||||
|
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="/css/fontawesome.min.css" rel="stylesheet">
|
||||||
|
<link href="/css/brands.min.css" rel="stylesheet">
|
||||||
|
<link href="/css/solid.min.css" rel="stylesheet">
|
||||||
|
<link href="/css/v4-font-face.min.css" rel="stylesheet">
|
||||||
|
<link href="/css/base.css" rel="stylesheet">
|
||||||
|
<link id="hljs-light" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github.min.css" >
|
||||||
|
<link id="hljs-dark" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css" disabled>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
|
||||||
|
<script>hljs.highlightAll();</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="navbar fixed-top navbar-expand-lg navbar-dark bg-primary">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand" href="/.">DOXA Notes 🦅</a>
|
||||||
|
<!-- Expander button -->
|
||||||
|
<button type="button" class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#navbar-collapse" aria-controls="navbar-collapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Expanded navigation -->
|
||||||
|
<div id="navbar-collapse" class="navbar-collapse collapse">
|
||||||
|
<!-- Main navigation -->
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/." class="nav-link">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a href="#" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">Wiki - Česky</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="/install/" class="dropdown-item">Instalace</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="/editor/" class="dropdown-item">DOXA editor</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a href="#" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">Wiki - English</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="/install_en/" class="dropdown-item">Install</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="/editor_en/" class="dropdown-item">DOXA editor</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="nav navbar-nav ms-md-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#mkdocs_search_modal">
|
||||||
|
<i class="fa fa-search"></i> Search
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div id="main-content" class="span12">
|
||||||
|
<h1 id="404-page-not-found" style="text-align: center">404</h1>
|
||||||
|
<p style="text-align: center"><strong>Page not found</strong></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="col-md-12">
|
||||||
|
<hr>
|
||||||
|
<p>Documentation built with <a href="https://www.mkdocs.org/">MkDocs</a>.</p>
|
||||||
|
</footer>
|
||||||
|
<script src="/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script>
|
||||||
|
var base_url = "/",
|
||||||
|
shortcuts = {"help": 191, "next": 78, "previous": 80, "search": 83};
|
||||||
|
</script>
|
||||||
|
<script src="/js/base.js"></script>
|
||||||
|
<script src="/search/main.js"></script>
|
||||||
|
|
||||||
|
<div class="modal" id="mkdocs_search_modal" tabindex="-1" role="dialog" aria-labelledby="searchModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title" id="searchModalLabel">Search</h4>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>From here you can search these documents. Enter your search terms below.</p>
|
||||||
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="search" class="form-control" placeholder="Search..." id="mkdocs-search-query" title="Type search term here">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div id="mkdocs-search-results" data-no-results-text="No results found"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><div class="modal" id="mkdocs_keyboard_modal" tabindex="-1" role="dialog" aria-labelledby="keyboardModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title" id="keyboardModalLabel">Keyboard Shortcuts</h4>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 20%;">Keys</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="help shortcut"><kbd>?</kbd></td>
|
||||||
|
<td>Open this help</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="next shortcut"><kbd>n</kbd></td>
|
||||||
|
<td>Next page</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="prev shortcut"><kbd>p</kbd></td>
|
||||||
|
<td>Previous page</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="search shortcut"><kbd>s</kbd></td>
|
||||||
|
<td>Search</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||