Compare commits
213 Commits
release/v0
...
renovate/c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3a3b3cb5c | ||
|
|
28f88f2407 | ||
|
|
e525a7a0ff | ||
|
|
3415f38e0a | ||
|
|
910c0faade | ||
|
|
4ad1bb5e3a | ||
|
|
97f6f0a945 | ||
|
|
bc78c17a11 | ||
|
|
b8e813f7bd | ||
|
|
db3581eb26 | ||
|
|
d23230df68 | ||
|
|
b29781b614 | ||
|
|
7d7c3e7cdb | ||
|
|
cbd8cb80ab | ||
|
|
bfcdc34faf | ||
|
|
c728e6047d | ||
|
|
4c53a9ba8c | ||
|
|
e10a7da7e3 | ||
|
|
5cc431b1bf | ||
|
|
734aa2fcb5 | ||
|
|
5e37319d9b | ||
|
|
2e9eb6e3e9 | ||
|
|
9ce57b123a | ||
|
|
e793168afa | ||
|
|
d1513424e7 | ||
|
|
1436a01dbe | ||
|
|
b9b936b92a | ||
|
|
adf14bec31 | ||
|
|
ca1403ffea | ||
|
|
06672e439e | ||
|
|
e851701a9e | ||
|
|
9589164008 | ||
|
|
a88b067081 | ||
|
|
b3777e6900 | ||
|
|
d2646e291d | ||
|
|
99ab9ee66b | ||
|
|
08678e74e6 | ||
|
|
62de52ab17 | ||
|
|
d9820d9725 | ||
|
|
fe8a8eeac9 | ||
|
|
dfeb414aff | ||
|
|
69f12a2916 | ||
|
|
2b062e938e | ||
|
|
e0299bd1ae | ||
|
|
ac2f1b56fe | ||
|
|
06d98f6fcf | ||
|
|
bb660d15b2 | ||
|
|
4d73cdefef | ||
|
|
313ba3df80 | ||
|
|
15377c32c2 | ||
|
|
22b52f7c4a | ||
|
|
7055f77c91 | ||
|
|
051fe67176 | ||
|
|
90accfcc48 | ||
|
|
4f99db0c90 | ||
|
|
aeb356bf54 | ||
|
|
0dffa0f333 | ||
|
|
d17f5b8447 | ||
|
|
b5a57b3c66 | ||
|
|
987a3404a9 | ||
|
|
eddc30769f | ||
|
|
4d455650ba | ||
|
|
e2157aab26 | ||
|
|
b277f4bf3f | ||
|
|
4047452b0f | ||
|
|
cb37724879 | ||
|
|
8890893412 | ||
|
|
d0cbda7c0d | ||
|
|
60e7b9ffb0 | ||
|
|
45457c6f76 | ||
|
|
737f41d92b | ||
|
|
180841f364 | ||
|
|
bea40d4c2f | ||
|
|
5f9a054441 | ||
|
|
f90bf1ce7c | ||
|
|
8c4ed2d4da | ||
|
|
0e590a1bbf | ||
|
|
218a096135 | ||
|
|
8407bce370 | ||
|
|
43229f0b99 | ||
|
|
84fa0002b9 | ||
|
|
e79c705b20 | ||
|
|
894d7ce15d | ||
|
|
5830880582 | ||
|
|
caab0f70ff | ||
|
|
641966fcdd | ||
|
|
24c22e9bbf | ||
|
|
795f597bda | ||
|
|
2228663a7e | ||
|
|
0c97df357d | ||
|
|
19f63f1be0 | ||
|
|
fc000caf73 | ||
|
|
78929e0293 | ||
|
|
71e22da987 | ||
|
|
24e99d9654 | ||
|
|
98299da424 | ||
|
|
7014af66b6 | ||
|
|
659bd90027 | ||
|
|
146b0c284b | ||
|
|
4a0ac8807f | ||
|
|
d67734832e | ||
|
|
1673bf026a | ||
|
|
1f29b000a9 | ||
|
|
a6d024123e | ||
|
|
fb1a7239ce | ||
|
|
4f71d508cb | ||
|
|
2072bd61d1 | ||
|
|
6021178b7d | ||
|
|
179b0be2bb | ||
|
|
bf2b45dd4a | ||
|
|
513561234c | ||
|
|
33da990ae7 | ||
|
|
4003946e68 | ||
|
|
21f8d40789 | ||
|
|
d6c698e1d6 | ||
|
|
6c227852ae | ||
|
|
29cb22c4fd | ||
|
|
d040bc9e2d | ||
|
|
abb92f23a6 | ||
|
|
da5c86bb69 | ||
|
|
a0d428b12c | ||
|
|
e22fe20e23 | ||
|
|
1e6659aff9 | ||
|
|
60b32d5b05 | ||
|
|
e2ee9053a0 | ||
|
|
d2f0422ecc | ||
|
|
bfd97da626 | ||
|
|
1fd163f0bb | ||
|
|
d15ce575df | ||
|
|
9999ff5a89 | ||
|
|
4653941082 | ||
|
|
fa509661ab | ||
|
|
d9a289bf18 | ||
|
|
98c76b713d | ||
|
|
05ed917a56 | ||
|
|
b833806ec7 | ||
|
|
7fdef3418a | ||
|
|
49e14ec542 | ||
|
|
efd9244684 | ||
|
|
318f2d1f8c | ||
|
|
92fa1cf052 | ||
|
|
17c6eb1680 | ||
|
|
7c6af568d8 | ||
|
|
23c9c6826e | ||
|
|
b08fda5e10 | ||
|
|
5ec3a49377 | ||
|
|
1c728ae432 | ||
|
|
ec021be16c | ||
|
|
8b6826ffa4 | ||
|
|
00cc1ffe74 | ||
|
|
2384fdbaad | ||
|
|
08a93d81d7 | ||
|
|
86911100df | ||
|
|
ff01656268 | ||
|
|
d0ea6d9e8d | ||
|
|
96ca3d5e38 | ||
|
|
3a569499cb | ||
|
|
545b19f978 | ||
|
|
d98be19c9a | ||
|
|
4826898c55 | ||
|
|
482b592f77 | ||
|
|
939ebfe47b | ||
|
|
c6dee1339b | ||
|
|
23f8c3ad3c | ||
|
|
81c1b88376 | ||
|
|
c4a85db698 | ||
|
|
e6eda45c04 | ||
|
|
eb76362de4 | ||
|
|
1cde14859b | ||
|
|
c752b98995 | ||
|
|
1f792ca418 | ||
|
|
b22e08b1eb | ||
|
|
2b5029cc38 | ||
|
|
9e936cb57b | ||
|
|
e8fd2c1b3c | ||
|
|
977fbf54ee | ||
|
|
3e5c91415d | ||
|
|
d60b855f74 | ||
|
|
4146192b6d | ||
|
|
26ee0ff48f | ||
|
|
1763d80d5f | ||
|
|
a594e5147c | ||
|
|
e51ea1a619 | ||
|
|
37c9260dca | ||
|
|
e1a8f4f5db | ||
|
|
b7b0b39afc | ||
|
|
af797489e8 | ||
|
|
b1b756b179 | ||
|
|
9e3372df72 | ||
|
|
657df7a728 | ||
|
|
944f0b694b | ||
|
|
efd409da17 | ||
|
|
08d60c554c | ||
|
|
a428ea7beb | ||
|
|
f69878b082 | ||
|
|
c5ffc2882b | ||
|
|
765691751a | ||
|
|
f19e5977c2 | ||
|
|
8f8b9af862 | ||
|
|
3e7dc71995 | ||
|
|
2a25cd8686 | ||
|
|
7664839135 | ||
|
|
47daebc65a | ||
|
|
0d18b944b6 | ||
|
|
951b5384a3 | ||
|
|
11547ecaa3 | ||
|
|
d4aaf4ca9b | ||
|
|
e7450b5143 | ||
|
|
fd90454eb6 | ||
|
|
f327b54c0e | ||
|
|
f38105ef05 | ||
|
|
6f6041ee7b | ||
|
|
2c1517d259 |
40
.github/instructions/nx.instructions.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
applyTo: '**'
|
||||
---
|
||||
|
||||
// This file is automatically generated by Nx Console
|
||||
|
||||
You are in an nx workspace using Nx 21.3.5 and pnpm as the package manager.
|
||||
|
||||
You have access to the Nx MCP server and the tools it provides. Use them. Follow these guidelines in order to best help the user:
|
||||
|
||||
# General Guidelines
|
||||
- When answering questions, use the nx_workspace tool first to gain an understanding of the workspace architecture
|
||||
- For questions around nx configuration, best practices or if you're unsure, use the nx_docs tool to get relevant, up-to-date docs!! Always use this instead of assuming things about nx configuration
|
||||
- If the user needs help with an Nx configuration or project graph error, use the 'nx_workspace' tool to get any errors
|
||||
- To help answer questions about the workspace structure or simply help with demonstrating how tasks depend on each other, use the 'nx_visualize_graph' tool
|
||||
|
||||
# Generation Guidelines
|
||||
If the user wants to generate something, use the following flow:
|
||||
|
||||
- learn about the nx workspace and any specifics the user needs by using the 'nx_workspace' tool and the 'nx_project_details' tool if applicable
|
||||
- get the available generators using the 'nx_generators' tool
|
||||
- decide which generator to use. If no generators seem relevant, check the 'nx_available_plugins' tool to see if the user could install a plugin to help them
|
||||
- get generator details using the 'nx_generator_schema' tool
|
||||
- you may use the 'nx_docs' tool to learn more about a specific generator or technology if you're unsure
|
||||
- decide which options to provide in order to best complete the user's request. Don't make any assumptions and keep the options minimalistic
|
||||
- open the generator UI using the 'nx_open_generate_ui' tool
|
||||
- wait for the user to finish the generator
|
||||
- read the generator log file using the 'nx_read_generator_log' tool
|
||||
- use the information provided in the log file to answer the user's question or continue with what they were doing
|
||||
|
||||
# Running Tasks Guidelines
|
||||
If the user wants help with tasks or commands (which include keywords like "test", "build", "lint", or other similar actions), use the following flow:
|
||||
- Use the 'nx_current_running_tasks_details' tool to get the list of tasks (this can include tasks that were completed, stopped or failed).
|
||||
- If there are any tasks, ask the user if they would like help with a specific task then use the 'nx_current_running_task_output' tool to get the terminal output for that task/command
|
||||
- Use the terminal output from 'nx_current_running_task_output' to see what's wrong and help the user fix their problem. Use the appropriate tools if necessary
|
||||
- If the user would like to rerun the task or command, always use `nx run <taskId>` to rerun in the terminal. This will ensure that the task will run in the nx context and will be run the same way it originally executed
|
||||
- If the task was marked as "continuous" do not offer to rerun the task. This task is already running and the user can see the output in the terminal. You can use 'nx_current_running_task_output' to get the output of the task to verify the output.
|
||||
|
||||
|
||||
|
||||
8
.vscode/mcp.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"servers": {
|
||||
"nx-mcp": {
|
||||
"type": "http",
|
||||
"url": "http://localhost:9461/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
3
.vscode/settings.json
vendored
@@ -35,5 +35,6 @@
|
||||
"docs/**/*.png": true,
|
||||
"apps/server/src/assets/doc_notes/**": true,
|
||||
"apps/edit-docs/demo/**": true
|
||||
}
|
||||
},
|
||||
"nxConsole.generateAiAgentRules": true
|
||||
}
|
||||
161
CLAUDE.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Overview
|
||||
|
||||
Trilium Notes is a hierarchical note-taking application with advanced features like synchronization, scripting, and rich text editing. It's built as a TypeScript monorepo using NX, with multiple applications and shared packages.
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Setup
|
||||
- `pnpm install` - Install all dependencies
|
||||
- `corepack enable` - Enable pnpm if not available
|
||||
|
||||
### Running Applications
|
||||
- `pnpm run server:start` - Start development server (http://localhost:8080)
|
||||
- `pnpm nx run server:serve` - Alternative server start command
|
||||
- `pnpm nx run desktop:serve` - Run desktop Electron app
|
||||
- `pnpm run server:start-prod` - Run server in production mode
|
||||
|
||||
### Building
|
||||
- `pnpm nx build <project>` - Build specific project (server, client, desktop, etc.)
|
||||
- `pnpm run client:build` - Build client application
|
||||
- `pnpm run server:build` - Build server application
|
||||
- `pnpm run electron:build` - Build desktop application
|
||||
|
||||
### Testing
|
||||
- `pnpm test:all` - Run all tests (parallel + sequential)
|
||||
- `pnpm test:parallel` - Run tests that can run in parallel
|
||||
- `pnpm test:sequential` - Run tests that must run sequentially (server, ckeditor5-mermaid, ckeditor5-math)
|
||||
- `pnpm nx test <project>` - Run tests for specific project
|
||||
- `pnpm coverage` - Generate coverage reports
|
||||
|
||||
### Linting & Type Checking
|
||||
- `pnpm nx run <project>:lint` - Lint specific project
|
||||
- `pnpm nx run <project>:typecheck` - Type check specific project
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Monorepo Structure
|
||||
- **apps/**: Runnable applications
|
||||
- `client/` - Frontend application (shared by server and desktop)
|
||||
- `server/` - Node.js server with web interface
|
||||
- `desktop/` - Electron desktop application
|
||||
- `web-clipper/` - Browser extension for saving web content
|
||||
- Additional tools: `db-compare`, `dump-db`, `edit-docs`
|
||||
|
||||
- **packages/**: Shared libraries
|
||||
- `commons/` - Shared interfaces and utilities
|
||||
- `ckeditor5/` - Custom rich text editor with Trilium-specific plugins
|
||||
- `codemirror/` - Code editor customizations
|
||||
- `highlightjs/` - Syntax highlighting
|
||||
- Custom CKEditor plugins: `ckeditor5-admonition`, `ckeditor5-footnotes`, `ckeditor5-math`, `ckeditor5-mermaid`
|
||||
|
||||
### Core Architecture Patterns
|
||||
|
||||
#### Three-Layer Cache System
|
||||
- **Becca** (Backend Cache): Server-side entity cache (`apps/server/src/becca/`)
|
||||
- **Froca** (Frontend Cache): Client-side mirror of backend data (`apps/client/src/services/froca.ts`)
|
||||
- **Shaca** (Share Cache): Optimized cache for shared/published notes (`apps/server/src/share/`)
|
||||
|
||||
#### Entity System
|
||||
Core entities are defined in `apps/server/src/becca/entities/`:
|
||||
- `BNote` - Notes with content and metadata
|
||||
- `BBranch` - Hierarchical relationships between notes (allows multiple parents)
|
||||
- `BAttribute` - Key-value metadata attached to notes
|
||||
- `BRevision` - Note version history
|
||||
- `BOption` - Application configuration
|
||||
|
||||
#### Widget-Based UI
|
||||
Frontend uses a widget system (`apps/client/src/widgets/`):
|
||||
- `BasicWidget` - Base class for all UI components
|
||||
- `NoteContextAwareWidget` - Widgets that respond to note changes
|
||||
- `RightPanelWidget` - Widgets displayed in the right panel
|
||||
- Type-specific widgets in `type_widgets/` directory
|
||||
|
||||
#### API Architecture
|
||||
- **Internal API**: REST endpoints in `apps/server/src/routes/api/`
|
||||
- **ETAPI**: External API for third-party integrations (`apps/server/src/etapi/`)
|
||||
- **WebSocket**: Real-time synchronization (`apps/server/src/services/ws.ts`)
|
||||
|
||||
### Key Files for Understanding Architecture
|
||||
|
||||
1. **Application Entry Points**:
|
||||
- `apps/server/src/main.ts` - Server startup
|
||||
- `apps/client/src/desktop.ts` - Client initialization
|
||||
|
||||
2. **Core Services**:
|
||||
- `apps/server/src/becca/becca.ts` - Backend data management
|
||||
- `apps/client/src/services/froca.ts` - Frontend data synchronization
|
||||
- `apps/server/src/services/backend_script_api.ts` - Scripting API
|
||||
|
||||
3. **Database Schema**:
|
||||
- `apps/server/src/assets/db/schema.sql` - Core database structure
|
||||
|
||||
4. **Configuration**:
|
||||
- `nx.json` - NX workspace configuration
|
||||
- `package.json` - Project dependencies and scripts
|
||||
|
||||
## Note Types and Features
|
||||
|
||||
Trilium supports multiple note types, each with specialized widgets:
|
||||
- **Text**: Rich text with CKEditor5 (markdown import/export)
|
||||
- **Code**: Syntax-highlighted code editing with CodeMirror
|
||||
- **File**: Binary file attachments
|
||||
- **Image**: Image display with editing capabilities
|
||||
- **Canvas**: Drawing/diagramming with Excalidraw
|
||||
- **Mermaid**: Diagram generation
|
||||
- **Relation Map**: Visual note relationship mapping
|
||||
- **Web View**: Embedded web pages
|
||||
- **Doc/Book**: Hierarchical documentation structure
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
### Testing Strategy
|
||||
- Server tests run sequentially due to shared database
|
||||
- Client tests can run in parallel
|
||||
- E2E tests use Playwright for both server and desktop apps
|
||||
- Build validation tests check artifact integrity
|
||||
|
||||
### Scripting System
|
||||
Trilium provides powerful user scripting capabilities:
|
||||
- Frontend scripts run in browser context
|
||||
- Backend scripts run in Node.js context with full API access
|
||||
- Script API documentation available in `docs/Script API/`
|
||||
|
||||
### Internationalization
|
||||
- Translation files in `apps/client/src/translations/`
|
||||
- Supported languages: English, German, Spanish, French, Romanian, Chinese
|
||||
|
||||
### Security Considerations
|
||||
- Per-note encryption with granular protected sessions
|
||||
- CSRF protection for API endpoints
|
||||
- OpenID and TOTP authentication support
|
||||
- Sanitization of user-generated content
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Adding New Note Types
|
||||
1. Create widget in `apps/client/src/widgets/type_widgets/`
|
||||
2. Register in `apps/client/src/services/note_types.ts`
|
||||
3. Add backend handling in `apps/server/src/services/notes.ts`
|
||||
|
||||
### Extending Search
|
||||
- Search expressions handled in `apps/server/src/services/search/`
|
||||
- Add new search operators in search context files
|
||||
|
||||
### Custom CKEditor Plugins
|
||||
- Create new package in `packages/` following existing plugin structure
|
||||
- Register in `packages/ckeditor5/src/plugins.ts`
|
||||
|
||||
### Database Migrations
|
||||
- Add migration scripts in `apps/server/src/migrations/`
|
||||
- Update schema in `apps/server/src/assets/db/schema.sql`
|
||||
|
||||
## Build System Notes
|
||||
- Uses NX for monorepo management with build caching
|
||||
- Vite for fast development builds
|
||||
- ESBuild for production optimization
|
||||
- pnpm workspaces for dependency management
|
||||
- Docker support with multi-stage builds
|
||||
@@ -36,12 +36,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.54.1",
|
||||
"@stylistic/eslint-plugin": "5.2.0",
|
||||
"@stylistic/eslint-plugin": "5.2.2",
|
||||
"@types/express": "5.0.3",
|
||||
"@types/node": "22.16.5",
|
||||
"@types/yargs": "17.0.33",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"eslint": "9.31.0",
|
||||
"eslint": "9.32.0",
|
||||
"eslint-plugin-simple-import-sort": "12.1.1",
|
||||
"esm": "3.2.25",
|
||||
"jsdoc": "4.0.4",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"url": "https://github.com/TriliumNext/Notes"
|
||||
},
|
||||
"dependencies": {
|
||||
"@eslint/js": "9.31.0",
|
||||
"@eslint/js": "9.32.0",
|
||||
"@excalidraw/excalidraw": "0.18.0",
|
||||
"@fullcalendar/core": "6.1.18",
|
||||
"@fullcalendar/daygrid": "6.1.18",
|
||||
@@ -18,6 +18,7 @@
|
||||
"@fullcalendar/list": "6.1.18",
|
||||
"@fullcalendar/multimonth": "6.1.18",
|
||||
"@fullcalendar/timegrid": "6.1.18",
|
||||
"@maplibre/maplibre-gl-leaflet": "0.1.2",
|
||||
"@mermaid-js/layout-elk": "0.1.8",
|
||||
"@mind-elixir/node-menu": "5.0.0",
|
||||
"@popperjs/core": "2.11.8",
|
||||
@@ -48,7 +49,7 @@
|
||||
"mark.js": "8.11.1",
|
||||
"marked": "16.1.1",
|
||||
"mermaid": "11.9.0",
|
||||
"mind-elixir": "5.0.2",
|
||||
"mind-elixir": "5.0.4",
|
||||
"normalize.css": "8.0.1",
|
||||
"panzoom": "9.4.3",
|
||||
"preact": "10.26.9",
|
||||
@@ -64,7 +65,7 @@
|
||||
"@types/leaflet": "1.9.20",
|
||||
"@types/leaflet-gpx": "1.3.7",
|
||||
"@types/mark.js": "8.11.12",
|
||||
"@types/tabulator-tables": "6.2.7",
|
||||
"@types/tabulator-tables": "6.2.8",
|
||||
"copy-webpack-plugin": "13.0.0",
|
||||
"happy-dom": "18.0.1",
|
||||
"script-loader": "0.7.2",
|
||||
|
||||
@@ -325,8 +325,9 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded">
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some book types must always display a note list, even if no children.
|
||||
if (["calendar", "table", "geoMap"].includes(note.getLabelValue("viewType") ?? "")) {
|
||||
// Collections must always display a note list, even if no children.
|
||||
const viewType = note.getLabelValue("viewType") ?? "grid";
|
||||
if (!["list", "grid"].includes(viewType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,10 +91,10 @@ function parseActions(note: FNote) {
|
||||
.filter((action) => !!action);
|
||||
}
|
||||
|
||||
export async function executeBulkActions(parentNoteId: string, actions: BulkAction[]) {
|
||||
export async function executeBulkActions(targetNoteIds: string[], actions: BulkAction[], includeDescendants = false) {
|
||||
await server.post("bulk-action/execute", {
|
||||
noteIds: [ parentNoteId ],
|
||||
includeDescendants: true,
|
||||
noteIds: targetNoteIds,
|
||||
includeDescendants,
|
||||
actions
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type FNote from "../entities/fnote.js";
|
||||
import BoardView from "../widgets/view_widgets/board_view/index.js";
|
||||
import CalendarView from "../widgets/view_widgets/calendar_view.js";
|
||||
import GeoView from "../widgets/view_widgets/geo_view/index.js";
|
||||
import ListOrGridView from "../widgets/view_widgets/list_or_grid_view.js";
|
||||
@@ -6,8 +7,9 @@ import TableView from "../widgets/view_widgets/table_view/index.js";
|
||||
import type { ViewModeArgs } from "../widgets/view_widgets/view_mode.js";
|
||||
import type ViewMode from "../widgets/view_widgets/view_mode.js";
|
||||
|
||||
const allViewTypes = ["list", "grid", "calendar", "table", "geoMap", "board"] as const;
|
||||
export type ArgsWithoutNoteId = Omit<ViewModeArgs, "noteIds">;
|
||||
export type ViewTypeOptions = "list" | "grid" | "calendar" | "table" | "geoMap";
|
||||
export type ViewTypeOptions = typeof allViewTypes[number];
|
||||
|
||||
export default class NoteListRenderer {
|
||||
|
||||
@@ -23,7 +25,7 @@ export default class NoteListRenderer {
|
||||
#getViewType(parentNote: FNote): ViewTypeOptions {
|
||||
const viewType = parentNote.getLabelValue("viewType");
|
||||
|
||||
if (!["list", "grid", "calendar", "table", "geoMap"].includes(viewType || "")) {
|
||||
if (!(allViewTypes as readonly string[]).includes(viewType || "")) {
|
||||
// when not explicitly set, decide based on the note type
|
||||
return parentNote.type === "search" ? "list" : "grid";
|
||||
} else {
|
||||
@@ -57,6 +59,8 @@ export default class NoteListRenderer {
|
||||
return new TableView(args);
|
||||
case "geoMap":
|
||||
return new GeoView(args);
|
||||
case "board":
|
||||
return new BoardView(args);
|
||||
case "list":
|
||||
case "grid":
|
||||
default:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type LabelType = "text" | "number" | "boolean" | "date" | "datetime" | "time" | "url";
|
||||
export type LabelType = "text" | "number" | "boolean" | "date" | "datetime" | "time" | "url" | "color";
|
||||
type Multiplicity = "single" | "multi";
|
||||
|
||||
export interface DefinitionObject {
|
||||
@@ -17,7 +17,7 @@ function parse(value: string) {
|
||||
for (const token of tokens) {
|
||||
if (token === "promoted") {
|
||||
defObj.isPromoted = true;
|
||||
} else if (["text", "number", "boolean", "date", "datetime", "time", "url"].includes(token)) {
|
||||
} else if (["text", "number", "boolean", "date", "datetime", "time", "url", "color"].includes(token)) {
|
||||
defObj.labelType = token as LabelType;
|
||||
} else if (["single", "multi"].includes(token)) {
|
||||
defObj.multiplicity = token as Multiplicity;
|
||||
|
||||
@@ -51,6 +51,14 @@ export default class SpacedUpdate {
|
||||
this.lastUpdated = Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the update interval for the spaced update.
|
||||
* @param interval The update interval in milliseconds.
|
||||
*/
|
||||
setUpdateInterval(interval: number) {
|
||||
this.updateInterval = interval;
|
||||
}
|
||||
|
||||
triggerUpdate() {
|
||||
if (!this.changed) {
|
||||
return;
|
||||
|
||||
@@ -29,6 +29,14 @@ async function formatCodeBlocks() {
|
||||
await formatCodeBlocks($("#content"));
|
||||
}
|
||||
|
||||
async function setupTextNote() {
|
||||
formatCodeBlocks();
|
||||
applyMath();
|
||||
|
||||
const setupMermaid = (await import("./share/mermaid.js")).default;
|
||||
setupMermaid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch note with given ID from backend
|
||||
*
|
||||
@@ -47,8 +55,11 @@ async function fetchNote(noteId: string | null = null) {
|
||||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
() => {
|
||||
formatCodeBlocks();
|
||||
applyMath();
|
||||
const noteType = determineNoteType();
|
||||
|
||||
if (noteType === "text") {
|
||||
setupTextNote();
|
||||
}
|
||||
|
||||
const toggleMenuButton = document.getElementById("toggleMenuButton");
|
||||
const layout = document.getElementById("layout");
|
||||
@@ -60,6 +71,12 @@ document.addEventListener(
|
||||
false
|
||||
);
|
||||
|
||||
function determineNoteType() {
|
||||
const bodyClass = document.body.className;
|
||||
const match = bodyClass.match(/type-([^\s]+)/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
// workaround to prevent webpack from removing "fetchNote" as dead code:
|
||||
// add fetchNote as property to the window object
|
||||
Object.defineProperty(window, "fetchNote", {
|
||||
|
||||
17
apps/client/src/share/mermaid.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import mermaid from "mermaid";
|
||||
|
||||
export default function setupMermaid() {
|
||||
for (const codeBlock of document.querySelectorAll("#content pre code.language-mermaid")) {
|
||||
const parentPre = codeBlock.parentElement;
|
||||
if (!parentPre) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mermaidDiv = document.createElement("div");
|
||||
mermaidDiv.classList.add("mermaid");
|
||||
mermaidDiv.innerHTML = codeBlock.innerHTML;
|
||||
parentPre.replaceWith(mermaidDiv);
|
||||
}
|
||||
|
||||
mermaid.init();
|
||||
}
|
||||
@@ -139,12 +139,6 @@ textarea,
|
||||
color: var(--muted-text-color);
|
||||
}
|
||||
|
||||
/* Restore default apperance */
|
||||
input[type="number"],
|
||||
input[type="checkbox"] {
|
||||
appearance: auto !important;
|
||||
}
|
||||
|
||||
/* Add a gap between consecutive radios / check boxes */
|
||||
label.tn-radio + label.tn-radio,
|
||||
label.tn-checkbox + label.tn-checkbox {
|
||||
|
||||
@@ -1678,4 +1678,42 @@ div.find-replace-widget div.find-widget-found-wrapper > span {
|
||||
#right-pane .highlights-list li:active {
|
||||
background: transparent;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
/** Canvas **/
|
||||
|
||||
.excalidraw {
|
||||
--border-radius-lg: 6px;
|
||||
}
|
||||
|
||||
.excalidraw .Island {
|
||||
backdrop-filter: var(--dropdown-backdrop-filter);
|
||||
}
|
||||
|
||||
.excalidraw .Island.App-toolbar {
|
||||
--island-bg-color: var(--floating-button-background-color);
|
||||
--shadow-island: 1px 1px 1px var(--floating-button-shadow-color);
|
||||
}
|
||||
|
||||
.excalidraw .dropdown-menu {
|
||||
border: unset !important;
|
||||
box-shadow: unset !important;
|
||||
background-color: transparent !important;
|
||||
--island-bg-color: var(--menu-background-color);
|
||||
--shadow-island: 0px 10px 20px rgba(0, 0, 0, var(--dropdown-shadow-opacity));
|
||||
--default-border-color: var(--bs-dropdown-divider-bg);
|
||||
--button-hover-bg: var(--hover-item-background-color);
|
||||
}
|
||||
|
||||
.excalidraw .dropdown-menu .dropdown-menu-container {
|
||||
border-radius: var(--dropdown-border-radius);
|
||||
}
|
||||
|
||||
.excalidraw .dropdown-menu .dropdown-menu-container > div:not([class]):not(:last-child) {
|
||||
margin-left: calc(var(--padding) * var(--space-factor) * -1) !important;
|
||||
margin-right: calc(var(--padding) * var(--space-factor) * -1) !important;
|
||||
}
|
||||
|
||||
.excalidraw .dropdown-menu:before {
|
||||
content: unset !important;
|
||||
}
|
||||
@@ -1449,7 +1449,7 @@
|
||||
"relation-map": "关系图",
|
||||
"note-map": "笔记地图",
|
||||
"render-note": "渲染笔记",
|
||||
"book": "书",
|
||||
"book": "",
|
||||
"mermaid-diagram": "Mermaid 图",
|
||||
"canvas": "画布",
|
||||
"web-view": "网页视图",
|
||||
|
||||
@@ -1403,7 +1403,7 @@
|
||||
"relation-map": "Beziehungskarte",
|
||||
"note-map": "Notizkarte",
|
||||
"render-note": "Render Notiz",
|
||||
"book": "Buch",
|
||||
"book": "",
|
||||
"mermaid-diagram": "Mermaid Diagram",
|
||||
"canvas": "Canvas",
|
||||
"web-view": "Webansicht",
|
||||
|
||||
@@ -443,7 +443,8 @@
|
||||
"other_notes_with_name": "Other notes with {{attributeType}} name \"{{attributeName}}\"",
|
||||
"and_more": "... and {{count}} more.",
|
||||
"print_landscape": "When exporting to PDF, changes the orientation of the page to landscape instead of portrait.",
|
||||
"print_page_size": "When exporting to PDF, changes the size of the page. Supported values: <code>A0</code>, <code>A1</code>, <code>A2</code>, <code>A3</code>, <code>A4</code>, <code>A5</code>, <code>A6</code>, <code>Legal</code>, <code>Letter</code>, <code>Tabloid</code>, <code>Ledger</code>."
|
||||
"print_page_size": "When exporting to PDF, changes the size of the page. Supported values: <code>A0</code>, <code>A1</code>, <code>A2</code>, <code>A3</code>, <code>A4</code>, <code>A5</code>, <code>A6</code>, <code>Legal</code>, <code>Letter</code>, <code>Tabloid</code>, <code>Ledger</code>.",
|
||||
"color_type": "Color"
|
||||
},
|
||||
"attribute_editor": {
|
||||
"help_text_body1": "To add label, just type e.g. <code>#rock</code> or if you want to add also value then e.g. <code>#year = 2020</code>",
|
||||
@@ -762,7 +763,8 @@
|
||||
"invalid_view_type": "Invalid view type '{{type}}'",
|
||||
"calendar": "Calendar",
|
||||
"table": "Table",
|
||||
"geo-map": "Geo Map"
|
||||
"geo-map": "Geo Map",
|
||||
"board": "Board"
|
||||
},
|
||||
"edited_notes": {
|
||||
"no_edited_notes_found": "No edited notes on this day yet...",
|
||||
@@ -839,7 +841,8 @@
|
||||
"unknown_label_type": "Unknown label type '{{type}}'",
|
||||
"unknown_attribute_type": "Unknown attribute type '{{type}}'",
|
||||
"add_new_attribute": "Add new attribute",
|
||||
"remove_this_attribute": "Remove this attribute"
|
||||
"remove_this_attribute": "Remove this attribute",
|
||||
"remove_color": "Remove the color label"
|
||||
},
|
||||
"script_executor": {
|
||||
"query": "Query",
|
||||
@@ -1615,7 +1618,7 @@
|
||||
"relation-map": "Relation Map",
|
||||
"note-map": "Note Map",
|
||||
"render-note": "Render Note",
|
||||
"book": "Book",
|
||||
"book": "Collection",
|
||||
"mermaid-diagram": "Mermaid Diagram",
|
||||
"canvas": "Canvas",
|
||||
"web-view": "Web View",
|
||||
@@ -1964,9 +1967,25 @@
|
||||
},
|
||||
"book_properties_config": {
|
||||
"hide-weekends": "Hide weekends",
|
||||
"display-week-numbers": "Display week numbers"
|
||||
"display-week-numbers": "Display week numbers",
|
||||
"map-style": "Map style:",
|
||||
"max-nesting-depth": "Max nesting depth:",
|
||||
"raster": "Raster",
|
||||
"vector_light": "Vector (Light)",
|
||||
"vector_dark": "Vector (Dark)",
|
||||
"show-scale": "Show scale"
|
||||
},
|
||||
"table_context_menu": {
|
||||
"delete_row": "Delete row"
|
||||
},
|
||||
"board_view": {
|
||||
"delete-note": "Delete Note",
|
||||
"move-to": "Move to",
|
||||
"insert-above": "Insert above",
|
||||
"insert-below": "Insert below",
|
||||
"delete-column": "Delete column",
|
||||
"delete-column-confirmation": "Are you sure you want to delete this column? The corresponding attribute will be deleted in the notes under this column as well.",
|
||||
"new-item": "New item",
|
||||
"add-column": "Add Column"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1612,7 +1612,7 @@
|
||||
"relation-map": "Mapa de Relaciones",
|
||||
"note-map": "Mapa de Notas",
|
||||
"render-note": "Nota de Renderizado",
|
||||
"book": "Libro",
|
||||
"book": "",
|
||||
"mermaid-diagram": "Diagrama Mermaid",
|
||||
"canvas": "Lienzo",
|
||||
"web-view": "Vista Web",
|
||||
|
||||
@@ -1408,7 +1408,7 @@
|
||||
"relation-map": "Carte des relations",
|
||||
"note-map": "Carte de notes",
|
||||
"render-note": "Rendu Html",
|
||||
"book": "Livre",
|
||||
"book": "",
|
||||
"mermaid-diagram": "Diagramme Mermaid",
|
||||
"canvas": "Canevas",
|
||||
"web-view": "Affichage Web",
|
||||
|
||||
@@ -1377,7 +1377,7 @@
|
||||
"shared_publicly": "Această notiță este partajată public la"
|
||||
},
|
||||
"note_types": {
|
||||
"book": "Carte",
|
||||
"book": "Colecție",
|
||||
"canvas": "Schiță",
|
||||
"code": "Cod sursă",
|
||||
"mermaid-diagram": "Diagramă Mermaid",
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
"relation-map": "關係圖",
|
||||
"note-map": "筆記地圖",
|
||||
"render-note": "渲染筆記",
|
||||
"book": "書",
|
||||
"book": "",
|
||||
"mermaid-diagram": "美人魚圖(Mermaid)",
|
||||
"canvas": "畫布",
|
||||
"web-view": "網頁視圖",
|
||||
|
||||
5
apps/client/src/types-assets.d.ts
vendored
@@ -3,6 +3,11 @@ declare module "*.png" {
|
||||
export default path;
|
||||
}
|
||||
|
||||
declare module "*.json" {
|
||||
var content: any;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module "*?url" {
|
||||
var path: string;
|
||||
export default path;
|
||||
|
||||
@@ -142,6 +142,7 @@ const TPL = /*html*/`
|
||||
<option value="datetime">${t("attribute_detail.date_time")}</option>
|
||||
<option value="time">${t("attribute_detail.time")}</option>
|
||||
<option value="url">${t("attribute_detail.url")}</option>
|
||||
<option value="color">${t("attribute_detail.color_type")}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -106,7 +106,11 @@ export default class PopupEditorDialog extends Container<BasicWidget> {
|
||||
focus: false
|
||||
});
|
||||
|
||||
await this.noteContext.setNote(noteIdOrPath);
|
||||
await this.noteContext.setNote(noteIdOrPath, {
|
||||
viewScope: {
|
||||
readOnlyTemporarilyDisabled: true
|
||||
}
|
||||
});
|
||||
|
||||
const activeEl = document.activeElement;
|
||||
if (activeEl && "blur" in activeEl) {
|
||||
|
||||
@@ -35,7 +35,8 @@ export const byBookType: Record<ViewTypeOptions, string | null> = {
|
||||
grid: "8QqnMzx393bx",
|
||||
calendar: "xWbu3jpNWapp",
|
||||
table: "2FvYrpmOXm29",
|
||||
geoMap: "81SGnPGMk7Xc"
|
||||
geoMap: "81SGnPGMk7Xc",
|
||||
board: "CtBQqbwXDx1w"
|
||||
};
|
||||
|
||||
export default class ContextualHelpButton extends NoteContextAwareWidget {
|
||||
|
||||
@@ -5,6 +5,16 @@ import type FNote from "../../entities/fnote.js";
|
||||
import type { EventData } from "../../components/app_context.js";
|
||||
import { bookPropertiesConfig, BookProperty } from "./book_properties_config.js";
|
||||
import attributes from "../../services/attributes.js";
|
||||
import type { ViewTypeOptions } from "../../services/note_list_renderer.js";
|
||||
|
||||
const VIEW_TYPE_MAPPINGS: Record<ViewTypeOptions, string> = {
|
||||
grid: t("book_properties.grid"),
|
||||
list: t("book_properties.list"),
|
||||
calendar: t("book_properties.calendar"),
|
||||
table: t("book_properties.table"),
|
||||
geoMap: t("book_properties.geo-map"),
|
||||
board: t("book_properties.board")
|
||||
};
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="book-properties-widget">
|
||||
@@ -35,17 +45,25 @@ const TPL = /*html*/`
|
||||
.book-properties-container input[type="checkbox"] {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.book-properties-container label {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-overflow: clip;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div style="display: flex; align-items: baseline">
|
||||
<span style="white-space: nowrap">${t("book_properties.view_type")}: </span>
|
||||
|
||||
<select class="view-type-select form-select form-select-sm">
|
||||
<option value="grid">${t("book_properties.grid")}</option>
|
||||
<option value="list">${t("book_properties.list")}</option>
|
||||
<option value="calendar">${t("book_properties.calendar")}</option>
|
||||
<option value="table">${t("book_properties.table")}</option>
|
||||
<option value="geoMap">${t("book_properties.geo-map")}</option>
|
||||
${Object.entries(VIEW_TYPE_MAPPINGS)
|
||||
.filter(([type]) => type !== "raster")
|
||||
.map(([type, label]) => `
|
||||
<option value="${type}">${label}</option>
|
||||
`).join("")}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -115,7 +133,7 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!["list", "grid", "calendar", "table", "geoMap"].includes(type)) {
|
||||
if (!VIEW_TYPE_MAPPINGS.hasOwnProperty(type)) {
|
||||
throw new Error(t("book_properties.invalid_view_type", { type }));
|
||||
}
|
||||
|
||||
@@ -195,6 +213,35 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
|
||||
.append(" ".repeat(2))
|
||||
.append($numberInput));
|
||||
break;
|
||||
case "combobox":
|
||||
const $select = $("<select>", {
|
||||
class: "form-select form-select-sm"
|
||||
});
|
||||
const actualValue = note.getLabelValue(property.bindToLabel) ?? property.defaultValue ?? "";
|
||||
for (const option of property.options) {
|
||||
if ("items" in option) {
|
||||
const $optGroup = $("<optgroup>", { label: option.name });
|
||||
for (const item of option.items) {
|
||||
buildComboBoxItem(item, actualValue).appendTo($optGroup);
|
||||
}
|
||||
$optGroup.appendTo($select);
|
||||
} else {
|
||||
buildComboBoxItem(option, actualValue).appendTo($select);
|
||||
}
|
||||
}
|
||||
$select.on("change", () => {
|
||||
const value = $select.val();
|
||||
if (value === null || value === "") {
|
||||
attributes.removeOwnedLabelByName(note, property.bindToLabel);
|
||||
} else {
|
||||
attributes.setLabel(note.noteId, property.bindToLabel, String(value));
|
||||
}
|
||||
});
|
||||
$container.append($("<label>")
|
||||
.text(property.label)
|
||||
.append(" ".repeat(2))
|
||||
.append($select));
|
||||
break;
|
||||
}
|
||||
|
||||
return $container;
|
||||
@@ -202,3 +249,14 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
|
||||
|
||||
|
||||
}
|
||||
|
||||
function buildComboBoxItem({ value, label }: { value: string, label: string }, actualValue: string) {
|
||||
const $option = $("<option>", {
|
||||
value,
|
||||
text: label
|
||||
});
|
||||
if (actualValue === value) {
|
||||
$option.prop("selected", true);
|
||||
}
|
||||
return $option;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import FNote from "../../entities/fnote";
|
||||
import attributes from "../../services/attributes";
|
||||
import { ViewTypeOptions } from "../../services/note_list_renderer"
|
||||
import NoteContextAwareWidget from "../note_context_aware_widget";
|
||||
import { DEFAULT_MAP_LAYER_NAME, MAP_LAYERS, type MapLayer } from "../view_widgets/geo_view/map_layer";
|
||||
|
||||
interface BookConfig {
|
||||
properties: BookProperty[];
|
||||
@@ -30,7 +31,28 @@ interface NumberProperty {
|
||||
min?: number;
|
||||
}
|
||||
|
||||
export type BookProperty = CheckBoxProperty | ButtonProperty | NumberProperty;
|
||||
interface ComboBoxItem {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface ComboBoxGroup {
|
||||
name: string;
|
||||
items: ComboBoxItem[];
|
||||
}
|
||||
|
||||
interface ComboBoxProperty {
|
||||
type: "combobox",
|
||||
label: string;
|
||||
bindToLabel: string;
|
||||
/**
|
||||
* The default value is used when the label is not set.
|
||||
*/
|
||||
defaultValue?: string;
|
||||
options: (ComboBoxItem | ComboBoxGroup)[];
|
||||
}
|
||||
|
||||
export type BookProperty = CheckBoxProperty | ButtonProperty | NumberProperty | ComboBoxProperty;
|
||||
|
||||
interface BookContext {
|
||||
note: FNote;
|
||||
@@ -90,16 +112,58 @@ export const bookPropertiesConfig: Record<ViewTypeOptions, BookConfig> = {
|
||||
]
|
||||
},
|
||||
geoMap: {
|
||||
properties: []
|
||||
properties: [
|
||||
{
|
||||
label: t("book_properties_config.map-style"),
|
||||
type: "combobox",
|
||||
bindToLabel: "map:style",
|
||||
defaultValue: DEFAULT_MAP_LAYER_NAME,
|
||||
options: [
|
||||
{
|
||||
name: t("book_properties_config.raster"),
|
||||
items: Object.entries(MAP_LAYERS)
|
||||
.filter(([_, layer]) => layer.type === "raster")
|
||||
.map(buildMapLayer)
|
||||
},
|
||||
{
|
||||
name: t("book_properties_config.vector_light"),
|
||||
items: Object.entries(MAP_LAYERS)
|
||||
.filter(([_, layer]) => layer.type === "vector" && !layer.isDarkTheme)
|
||||
.map(buildMapLayer)
|
||||
},
|
||||
{
|
||||
name: t("book_properties_config.vector_dark"),
|
||||
items: Object.entries(MAP_LAYERS)
|
||||
.filter(([_, layer]) => layer.type === "vector" && layer.isDarkTheme)
|
||||
.map(buildMapLayer)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: t("book_properties_config.show-scale"),
|
||||
type: "checkbox",
|
||||
bindToLabel: "map:scale"
|
||||
}
|
||||
]
|
||||
},
|
||||
table: {
|
||||
properties: [
|
||||
{
|
||||
label: "Max nesting depth:",
|
||||
label: t("book_properties_config.max-nesting-depth"),
|
||||
type: "number",
|
||||
bindToLabel: "maxNestingDepth",
|
||||
width: 65
|
||||
}
|
||||
]
|
||||
},
|
||||
board: {
|
||||
properties: []
|
||||
}
|
||||
};
|
||||
|
||||
function buildMapLayer([ id, layer ]: [ string, MapLayer ]): ComboBoxItem {
|
||||
return {
|
||||
value: id,
|
||||
label: layer.name
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,12 +53,56 @@ const TPL = /*html*/`
|
||||
word-break:keep-all;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.promoted-attribute-cell input[type="checkbox"] {
|
||||
width: 22px !important;
|
||||
flex-grow: 0;
|
||||
width: unset;
|
||||
}
|
||||
|
||||
/* Restore default apperance */
|
||||
.promoted-attribute-cell input[type="number"],
|
||||
.promoted-attribute-cell input[type="checkbox"] {
|
||||
appearance: auto;
|
||||
}
|
||||
|
||||
.promoted-attribute-cell input[type="color"] {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-top: 2px;
|
||||
appearance: none;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: none;
|
||||
border-radius: 25% !important;
|
||||
}
|
||||
|
||||
.promoted-attribute-cell input[type="color"]::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.promoted-attribute-cell input[type="color"]::-webkit-color-swatch {
|
||||
border: none;
|
||||
border-radius: 25%;
|
||||
}
|
||||
|
||||
.promoted-attribute-label-color input[type="hidden"][value=""] + input[type="color"] {
|
||||
position: relative;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.promoted-attribute-label-color input[type="hidden"][value=""] + input[type="color"]:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0px;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
transform: rotate(45deg);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="promoted-attributes-container"></div>
|
||||
@@ -258,6 +302,35 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
||||
.on("click", () => window.open($input.val() as string, "_blank"));
|
||||
|
||||
$input.after($openButton);
|
||||
} else if (definition.labelType === "color") {
|
||||
const defaultColor = "#ffffff";
|
||||
$input.prop("type", "hidden");
|
||||
$input.val(valueAttr.value ?? "");
|
||||
|
||||
// We insert a separate input since the color input does not support empty value.
|
||||
// This is a workaround to allow clearing the color input.
|
||||
const $colorInput = $("<input>")
|
||||
.prop("type", "color")
|
||||
.prop("value", valueAttr.value || defaultColor)
|
||||
.addClass("form-control promoted-attribute-input")
|
||||
.on("change", e => setValue((e.target as HTMLInputElement).value, e));
|
||||
$input.after($colorInput);
|
||||
|
||||
const $clearButton = $("<span>")
|
||||
.addClass("input-group-text bx bxs-tag-x")
|
||||
.prop("title", t("promoted_attributes.remove_color"))
|
||||
.on("click", e => setValue("", e));
|
||||
|
||||
const setValue = (color: string, event: JQuery.TriggeredEvent<HTMLElement, undefined, HTMLElement, HTMLElement>) => {
|
||||
$input.val(color);
|
||||
if (!color) {
|
||||
$colorInput.val(defaultColor);
|
||||
}
|
||||
event.target = $input[0]; // Set the event target to the main input
|
||||
this.promotedAttributeChanged(event);
|
||||
};
|
||||
|
||||
$colorInput.after($clearButton);
|
||||
} else {
|
||||
ws.logError(t("promoted_attributes.unknown_label_type", { type: definition.labelType }));
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import appContext, { type CommandNames, type CommandListenerData, type EventData
|
||||
import froca from "../services/froca.js";
|
||||
import attributeService from "../services/attributes.js";
|
||||
import type NoteContext from "../components/note_context.js";
|
||||
import { setupHorizontalScrollViaWheel } from "./widget_utils.js";
|
||||
|
||||
const isDesktop = utils.isDesktop();
|
||||
|
||||
@@ -386,15 +387,7 @@ export default class TabRowWidget extends BasicWidget {
|
||||
};
|
||||
|
||||
setupScrollEvents() {
|
||||
this.$tabScrollingContainer.on('wheel', (event) => {
|
||||
const wheelEvent = event.originalEvent as WheelEvent;
|
||||
if (utils.isCtrlKey(event) || event.altKey || event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
event.currentTarget.scrollLeft += wheelEvent.deltaY + wheelEvent.deltaX;
|
||||
});
|
||||
setupHorizontalScrollViaWheel(this.$tabScrollingContainer);
|
||||
|
||||
this.$scrollButtonLeft[0].addEventListener('click', () => this.scrollTabContainer(-210));
|
||||
this.$scrollButtonRight[0].addEventListener('click', () => this.scrollTabContainer(210));
|
||||
|
||||
@@ -130,7 +130,8 @@ export default abstract class AbstractSplitTypeWidget extends TypeWidget {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.editorTypeWidget = new EditableCodeTypeWidget();
|
||||
|
||||
this.editorTypeWidget = new EditableCodeTypeWidget(true);
|
||||
this.editorTypeWidget.updateBackgroundColor = () => {};
|
||||
this.editorTypeWidget.isEnabled = () => true;
|
||||
|
||||
@@ -146,6 +147,8 @@ export default abstract class AbstractSplitTypeWidget extends TypeWidget {
|
||||
doRender(): void {
|
||||
this.$widget = $(TPL);
|
||||
|
||||
this.spacedUpdate.setUpdateInterval(750);
|
||||
|
||||
// Preview pane
|
||||
this.$previewCol = this.$widget.find(".note-detail-split-preview-col");
|
||||
this.$preview = this.$widget.find(".note-detail-split-preview");
|
||||
|
||||
@@ -45,12 +45,11 @@ export default class BookTypeWidget extends TypeWidget {
|
||||
}
|
||||
|
||||
switch (this.note?.getAttributeValue("label", "viewType")) {
|
||||
case "calendar":
|
||||
case "table":
|
||||
case "geoMap":
|
||||
return false;
|
||||
default:
|
||||
case "list":
|
||||
case "grid":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,6 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
onChange: () => this.onChangeHandler(),
|
||||
viewModeEnabled: options.is("databaseReadonly"),
|
||||
zenModeEnabled: false,
|
||||
gridModeEnabled: false,
|
||||
isCollaborating: false,
|
||||
detectScroll: false,
|
||||
handleKeyboardGlobally: false,
|
||||
|
||||
@@ -153,7 +153,8 @@ export default class Canvas {
|
||||
appState: {
|
||||
scrollX: appState.scrollX,
|
||||
scrollY: appState.scrollY,
|
||||
zoom: appState.zoom
|
||||
zoom: appState.zoom,
|
||||
gridModeEnabled: appState.gridModeEnabled
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,16 @@ const TPL = /*html*/`
|
||||
|
||||
export default class EditableCodeTypeWidget extends AbstractCodeTypeWidget {
|
||||
|
||||
private debounceUpdate: boolean;
|
||||
|
||||
/**
|
||||
* @param debounceUpdate if true, the update will be debounced to prevent excessive updates. Especially useful if the editor is linked to a live preview.
|
||||
*/
|
||||
constructor(debounceUpdate: boolean = false) {
|
||||
super();
|
||||
this.debounceUpdate = debounceUpdate;
|
||||
}
|
||||
|
||||
static getType() {
|
||||
return "editableCode";
|
||||
}
|
||||
@@ -46,7 +56,13 @@ export default class EditableCodeTypeWidget extends AbstractCodeTypeWidget {
|
||||
return {
|
||||
placeholder: t("editable_code.placeholder"),
|
||||
vimKeybindings: options.is("vimKeymapEnabled"),
|
||||
onContentChanged: () => this.spacedUpdate.scheduleUpdate(),
|
||||
onContentChanged: () => {
|
||||
if (this.debounceUpdate) {
|
||||
this.spacedUpdate.resetUpdateTimer();
|
||||
}
|
||||
|
||||
this.spacedUpdate.scheduleUpdate();
|
||||
},
|
||||
tabIndex: 300
|
||||
}
|
||||
}
|
||||
|
||||
180
apps/client/src/widgets/view_widgets/board_view/api.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import appContext from "../../../components/app_context";
|
||||
import FNote from "../../../entities/fnote";
|
||||
import attributes from "../../../services/attributes";
|
||||
import { executeBulkActions } from "../../../services/bulk_action";
|
||||
import note_create from "../../../services/note_create";
|
||||
import ViewModeStorage from "../view_mode_storage";
|
||||
import { BoardData } from "./config";
|
||||
import { ColumnMap, getBoardData } from "./data";
|
||||
|
||||
export default class BoardApi {
|
||||
|
||||
private constructor(
|
||||
private _columns: string[],
|
||||
private _parentNoteId: string,
|
||||
private viewStorage: ViewModeStorage<BoardData>,
|
||||
private byColumn: ColumnMap,
|
||||
private persistedData: BoardData,
|
||||
private _statusAttribute: string) {}
|
||||
|
||||
get columns() {
|
||||
return this._columns;
|
||||
}
|
||||
|
||||
get statusAttribute() {
|
||||
return this._statusAttribute;
|
||||
}
|
||||
|
||||
getColumn(column: string) {
|
||||
return this.byColumn.get(column);
|
||||
}
|
||||
|
||||
async changeColumn(noteId: string, newColumn: string) {
|
||||
await attributes.setLabel(noteId, this._statusAttribute, newColumn);
|
||||
}
|
||||
|
||||
openNote(noteId: string) {
|
||||
appContext.triggerCommand("openInPopup", { noteIdOrPath: noteId });
|
||||
}
|
||||
|
||||
async insertRowAtPosition(
|
||||
column: string,
|
||||
relativeToBranchId: string,
|
||||
direction: "before" | "after",
|
||||
open: boolean = true) {
|
||||
const { note } = await note_create.createNote(this._parentNoteId, {
|
||||
activate: false,
|
||||
targetBranchId: relativeToBranchId,
|
||||
target: direction,
|
||||
title: "New item"
|
||||
});
|
||||
|
||||
if (!note) {
|
||||
throw new Error("Failed to create note");
|
||||
}
|
||||
|
||||
const { noteId } = note;
|
||||
await this.changeColumn(noteId, column);
|
||||
if (open) {
|
||||
this.openNote(noteId);
|
||||
}
|
||||
|
||||
return note;
|
||||
}
|
||||
|
||||
async renameColumn(oldValue: string, newValue: string, noteIds: string[]) {
|
||||
// Change the value in the notes.
|
||||
await executeBulkActions(noteIds, [
|
||||
{
|
||||
name: "updateLabelValue",
|
||||
labelName: this._statusAttribute,
|
||||
labelValue: newValue
|
||||
}
|
||||
]);
|
||||
|
||||
// Rename the column in the persisted data.
|
||||
for (const column of this.persistedData.columns || []) {
|
||||
if (column.value === oldValue) {
|
||||
column.value = newValue;
|
||||
}
|
||||
}
|
||||
await this.viewStorage.store(this.persistedData);
|
||||
}
|
||||
|
||||
async removeColumn(column: string) {
|
||||
// Remove the value from the notes.
|
||||
const noteIds = this.byColumn.get(column)?.map(item => item.note.noteId) || [];
|
||||
await executeBulkActions(noteIds, [
|
||||
{
|
||||
name: "deleteLabel",
|
||||
labelName: this._statusAttribute
|
||||
}
|
||||
]);
|
||||
|
||||
this.persistedData.columns = (this.persistedData.columns ?? []).filter(col => col.value !== column);
|
||||
this.viewStorage.store(this.persistedData);
|
||||
}
|
||||
|
||||
async createColumn(columnValue: string) {
|
||||
// Add the new column to persisted data if it doesn't exist
|
||||
if (!this.persistedData.columns) {
|
||||
this.persistedData.columns = [];
|
||||
}
|
||||
|
||||
const existingColumn = this.persistedData.columns.find(col => col.value === columnValue);
|
||||
if (!existingColumn) {
|
||||
this.persistedData.columns.push({ value: columnValue });
|
||||
await this.viewStorage.store(this.persistedData);
|
||||
}
|
||||
|
||||
return columnValue;
|
||||
}
|
||||
|
||||
async reorderColumns(newColumnOrder: string[]) {
|
||||
// Update the column order in persisted data
|
||||
if (!this.persistedData.columns) {
|
||||
this.persistedData.columns = [];
|
||||
}
|
||||
|
||||
// Create a map of existing column data
|
||||
const columnDataMap = new Map();
|
||||
this.persistedData.columns.forEach(col => {
|
||||
columnDataMap.set(col.value, col);
|
||||
});
|
||||
|
||||
// Reorder columns based on new order
|
||||
this.persistedData.columns = newColumnOrder.map(columnValue => {
|
||||
return columnDataMap.get(columnValue) || { value: columnValue };
|
||||
});
|
||||
|
||||
// Update internal columns array
|
||||
this._columns = newColumnOrder;
|
||||
|
||||
await this.viewStorage.store(this.persistedData);
|
||||
}
|
||||
|
||||
async refresh(parentNote: FNote) {
|
||||
// Refresh the API data by re-fetching from the parent note
|
||||
const statusAttribute = parentNote.getLabelValue("board:groupBy") ?? "status";
|
||||
this._statusAttribute = statusAttribute;
|
||||
|
||||
// Use the current in-memory persisted data instead of restoring from storage
|
||||
// This ensures we don't lose recent updates like column renames
|
||||
const { byColumn, newPersistedData } = await getBoardData(parentNote, statusAttribute, this.persistedData);
|
||||
|
||||
// Update internal state
|
||||
this.byColumn = byColumn;
|
||||
|
||||
if (newPersistedData) {
|
||||
this.persistedData = newPersistedData;
|
||||
this.viewStorage.store(this.persistedData);
|
||||
}
|
||||
|
||||
// Use the order from persistedData.columns, then add any new columns found
|
||||
const orderedColumns = this.persistedData.columns?.map(col => col.value) || [];
|
||||
const allColumns = Array.from(byColumn.keys());
|
||||
const newColumns = allColumns.filter(col => !orderedColumns.includes(col));
|
||||
this._columns = [...orderedColumns, ...newColumns];
|
||||
}
|
||||
|
||||
static async build(parentNote: FNote, viewStorage: ViewModeStorage<BoardData>) {
|
||||
const statusAttribute = parentNote.getLabelValue("board:groupBy") ?? "status";
|
||||
|
||||
let persistedData = await viewStorage.restore() ?? {};
|
||||
const { byColumn, newPersistedData } = await getBoardData(parentNote, statusAttribute, persistedData);
|
||||
|
||||
// Use the order from persistedData.columns, then add any new columns found
|
||||
const orderedColumns = persistedData.columns?.map(col => col.value) || [];
|
||||
const allColumns = Array.from(byColumn.keys());
|
||||
const newColumns = allColumns.filter(col => !orderedColumns.includes(col));
|
||||
const columns = [...orderedColumns, ...newColumns];
|
||||
|
||||
if (newPersistedData) {
|
||||
persistedData = newPersistedData;
|
||||
viewStorage.store(persistedData);
|
||||
}
|
||||
|
||||
return new BoardApi(columns, parentNote.noteId, viewStorage, byColumn, persistedData, statusAttribute);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
import BoardApi from "./api";
|
||||
import { DragContext, BaseDragHandler } from "./drag_types";
|
||||
|
||||
export class ColumnDragHandler implements BaseDragHandler {
|
||||
private $container: JQuery<HTMLElement>;
|
||||
private api: BoardApi;
|
||||
private context: DragContext;
|
||||
|
||||
constructor(
|
||||
$container: JQuery<HTMLElement>,
|
||||
api: BoardApi,
|
||||
context: DragContext,
|
||||
) {
|
||||
this.$container = $container;
|
||||
this.api = api;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
setupColumnDrag($columnEl: JQuery<HTMLElement>, columnValue: string) {
|
||||
const $titleEl = $columnEl.find('h3[data-column-value]');
|
||||
|
||||
$titleEl.attr("draggable", "true");
|
||||
|
||||
// Delay drag start to allow click detection
|
||||
let dragStartTimer: number | null = null;
|
||||
|
||||
$titleEl.on("mousedown", (e) => {
|
||||
// Don't interfere with editing mode or input field interactions
|
||||
if ($titleEl.hasClass('editing') || $(e.target).is('input')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear any existing timer
|
||||
if (dragStartTimer) {
|
||||
clearTimeout(dragStartTimer);
|
||||
dragStartTimer = null;
|
||||
}
|
||||
|
||||
// Set a short delay before enabling dragging
|
||||
dragStartTimer = window.setTimeout(() => {
|
||||
$titleEl.attr("draggable", "true");
|
||||
dragStartTimer = null;
|
||||
}, 150);
|
||||
});
|
||||
|
||||
$titleEl.on("mouseup mouseleave", (e) => {
|
||||
// Don't interfere with editing mode
|
||||
if ($titleEl.hasClass('editing') || $(e.target).is('input')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel drag start timer on mouse up or leave
|
||||
if (dragStartTimer) {
|
||||
clearTimeout(dragStartTimer);
|
||||
dragStartTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
$titleEl.on("dragstart", (e) => {
|
||||
// Only start dragging if the target is not an input (for inline editing)
|
||||
if ($(e.target).is('input') || $titleEl.hasClass('editing')) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
this.context.draggedColumn = columnValue;
|
||||
this.context.draggedColumnElement = $columnEl;
|
||||
$columnEl.addClass("column-dragging");
|
||||
|
||||
const originalEvent = e.originalEvent as DragEvent;
|
||||
if (originalEvent.dataTransfer) {
|
||||
originalEvent.dataTransfer.effectAllowed = "move";
|
||||
originalEvent.dataTransfer.setData("text/plain", columnValue);
|
||||
}
|
||||
|
||||
// Prevent note dragging when column is being dragged
|
||||
e.stopPropagation();
|
||||
|
||||
// Setup global drag tracking for better drop indicator positioning
|
||||
this.setupGlobalColumnDragTracking();
|
||||
});
|
||||
|
||||
$titleEl.on("dragend", () => {
|
||||
$columnEl.removeClass("column-dragging");
|
||||
this.context.draggedColumn = null;
|
||||
this.context.draggedColumnElement = null;
|
||||
this.cleanupColumnDropIndicators();
|
||||
this.cleanupGlobalColumnDragTracking();
|
||||
|
||||
// Re-enable draggable
|
||||
$titleEl.attr("draggable", "true");
|
||||
});
|
||||
}
|
||||
|
||||
setupColumnDropZone($columnEl: JQuery<HTMLElement>) {
|
||||
$columnEl.on("dragover", (e) => {
|
||||
// Only handle column drops when a column is being dragged
|
||||
if (this.context.draggedColumn && !this.context.draggedNote) {
|
||||
e.preventDefault();
|
||||
const originalEvent = e.originalEvent as DragEvent;
|
||||
if (originalEvent.dataTransfer) {
|
||||
originalEvent.dataTransfer.dropEffect = "move";
|
||||
}
|
||||
|
||||
// Don't highlight columns - we only care about the drop indicator position
|
||||
}
|
||||
});
|
||||
|
||||
$columnEl.on("drop", async (e) => {
|
||||
if (this.context.draggedColumn && !this.context.draggedNote) {
|
||||
e.preventDefault();
|
||||
console.log("Column drop event triggered for column:", this.context.draggedColumn);
|
||||
|
||||
// Use the drop indicator position to determine where to place the column
|
||||
await this.handleColumnDrop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.cleanupColumnDropIndicators();
|
||||
this.context.draggedColumn = null;
|
||||
this.context.draggedColumnElement = null;
|
||||
this.cleanupGlobalColumnDragTracking();
|
||||
}
|
||||
|
||||
private setupGlobalColumnDragTracking() {
|
||||
// Add container-level drag tracking for better indicator positioning
|
||||
this.$container.on("dragover.columnDrag", (e) => {
|
||||
if (this.context.draggedColumn) {
|
||||
e.preventDefault();
|
||||
const originalEvent = e.originalEvent as DragEvent;
|
||||
this.showColumnDropIndicator(originalEvent.clientX);
|
||||
}
|
||||
});
|
||||
|
||||
// Add container-level drop handler for column reordering
|
||||
this.$container.on("drop.columnDrag", async (e) => {
|
||||
if (this.context.draggedColumn) {
|
||||
e.preventDefault();
|
||||
console.log("Container drop event triggered for column:", this.context.draggedColumn);
|
||||
await this.handleColumnDrop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private cleanupGlobalColumnDragTracking() {
|
||||
this.$container.off("dragover.columnDrag");
|
||||
this.$container.off("drop.columnDrag");
|
||||
}
|
||||
|
||||
private cleanupColumnDropIndicators() {
|
||||
// Remove column drop indicators
|
||||
this.$container.find(".column-drop-indicator").remove();
|
||||
}
|
||||
|
||||
private showColumnDropIndicator(mouseX: number) {
|
||||
// Clean up existing indicators
|
||||
this.cleanupColumnDropIndicators();
|
||||
|
||||
// Get all columns (excluding the dragged one if it exists)
|
||||
let $allColumns = this.$container.find('.board-column');
|
||||
if (this.context.draggedColumnElement) {
|
||||
$allColumns = $allColumns.not(this.context.draggedColumnElement);
|
||||
}
|
||||
|
||||
let $targetColumn: JQuery<HTMLElement> = $();
|
||||
let insertBefore = false;
|
||||
|
||||
// Find which column the mouse is closest to
|
||||
$allColumns.each((_, columnEl) => {
|
||||
const $column = $(columnEl);
|
||||
const rect = columnEl.getBoundingClientRect();
|
||||
const columnMiddle = rect.left + rect.width / 2;
|
||||
|
||||
if (mouseX >= rect.left && mouseX <= rect.right) {
|
||||
// Mouse is over this column
|
||||
$targetColumn = $column;
|
||||
insertBefore = mouseX < columnMiddle;
|
||||
return false; // Break the loop
|
||||
}
|
||||
});
|
||||
|
||||
// If no column found under mouse, find the closest one
|
||||
if ($targetColumn.length === 0) {
|
||||
let closestDistance = Infinity;
|
||||
$allColumns.each((_, columnEl) => {
|
||||
const $column = $(columnEl);
|
||||
const rect = columnEl.getBoundingClientRect();
|
||||
const columnCenter = rect.left + rect.width / 2;
|
||||
const distance = Math.abs(mouseX - columnCenter);
|
||||
|
||||
if (distance < closestDistance) {
|
||||
closestDistance = distance;
|
||||
$targetColumn = $column;
|
||||
insertBefore = mouseX < columnCenter;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ($targetColumn.length > 0) {
|
||||
const $dropIndicator = $("<div>").addClass("column-drop-indicator");
|
||||
|
||||
if (insertBefore) {
|
||||
$targetColumn.before($dropIndicator);
|
||||
} else {
|
||||
$targetColumn.after($dropIndicator);
|
||||
}
|
||||
|
||||
$dropIndicator.addClass("show");
|
||||
}
|
||||
}
|
||||
|
||||
private async handleColumnDrop() {
|
||||
console.log("handleColumnDrop called for:", this.context.draggedColumn);
|
||||
|
||||
if (!this.context.draggedColumn || !this.context.draggedColumnElement) {
|
||||
console.log("No dragged column or element found");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Find the drop indicator to determine insert position
|
||||
const $dropIndicator = this.$container.find(".column-drop-indicator.show");
|
||||
console.log("Drop indicator found:", $dropIndicator.length > 0);
|
||||
|
||||
if ($dropIndicator.length > 0) {
|
||||
// Get current column order from the API (source of truth)
|
||||
const currentOrder = [...this.api.columns];
|
||||
|
||||
let newOrder = [...currentOrder];
|
||||
|
||||
// Remove dragged column from current position
|
||||
newOrder = newOrder.filter(col => col !== this.context.draggedColumn);
|
||||
|
||||
// Determine insertion position based on drop indicator position
|
||||
const $nextColumn = $dropIndicator.next('.board-column');
|
||||
const $prevColumn = $dropIndicator.prev('.board-column');
|
||||
|
||||
let insertIndex = -1;
|
||||
|
||||
if ($nextColumn.length > 0) {
|
||||
// Insert before the next column
|
||||
const nextColumnValue = $nextColumn.attr('data-column');
|
||||
if (nextColumnValue) {
|
||||
insertIndex = newOrder.indexOf(nextColumnValue);
|
||||
}
|
||||
} else if ($prevColumn.length > 0) {
|
||||
// Insert after the previous column
|
||||
const prevColumnValue = $prevColumn.attr('data-column');
|
||||
if (prevColumnValue) {
|
||||
insertIndex = newOrder.indexOf(prevColumnValue) + 1;
|
||||
}
|
||||
} else {
|
||||
// Insert at the beginning
|
||||
insertIndex = 0;
|
||||
}
|
||||
|
||||
// Insert the dragged column at the determined position
|
||||
if (insertIndex >= 0 && insertIndex <= newOrder.length) {
|
||||
newOrder.splice(insertIndex, 0, this.context.draggedColumn);
|
||||
} else {
|
||||
// Fallback: insert at the end
|
||||
newOrder.push(this.context.draggedColumn);
|
||||
}
|
||||
|
||||
// Update column order in API
|
||||
await this.api.reorderColumns(newOrder);
|
||||
} else {
|
||||
console.warn("No drop indicator found for column drop");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to reorder columns:", error);
|
||||
} finally {
|
||||
this.cleanupColumnDropIndicators();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface BoardColumnData {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface BoardData {
|
||||
columns?: BoardColumnData[];
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import contextMenu, { ContextMenuEvent } from "../../../menus/context_menu.js";
|
||||
import link_context_menu from "../../../menus/link_context_menu.js";
|
||||
import branches from "../../../services/branches.js";
|
||||
import dialog from "../../../services/dialog.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import BoardApi from "./api.js";
|
||||
import type BoardView from "./index.js";
|
||||
|
||||
interface ShowNoteContextMenuArgs {
|
||||
$container: JQuery<HTMLElement>;
|
||||
api: BoardApi;
|
||||
boardView: BoardView;
|
||||
}
|
||||
|
||||
export function setupContextMenu({ $container, api, boardView }: ShowNoteContextMenuArgs) {
|
||||
$container.on("contextmenu", ".board-note", showNoteContextMenu);
|
||||
$container.on("contextmenu", ".board-column h3", showColumnContextMenu);
|
||||
|
||||
function showColumnContextMenu(event: ContextMenuEvent) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const $el = $(event.currentTarget);
|
||||
const column = $el.closest(".board-column").data("column");
|
||||
|
||||
contextMenu.show({
|
||||
x: event.pageX,
|
||||
y: event.pageY,
|
||||
items: [
|
||||
{
|
||||
title: t("board_view.delete-column"),
|
||||
uiIcon: "bx bx-trash",
|
||||
async handler() {
|
||||
const confirmed = await dialog.confirm(t("board_view.delete-column-confirmation"));
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
await api.removeColumn(column);
|
||||
}
|
||||
}
|
||||
],
|
||||
selectMenuItemHandler() {}
|
||||
});
|
||||
}
|
||||
|
||||
function showNoteContextMenu(event: ContextMenuEvent) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const $el = $(event.currentTarget);
|
||||
const noteId = $el.data("note-id");
|
||||
const branchId = $el.data("branch-id");
|
||||
const column = $el.closest(".board-column").data("column");
|
||||
if (!noteId) return;
|
||||
|
||||
contextMenu.show({
|
||||
x: event.pageX,
|
||||
y: event.pageY,
|
||||
items: [
|
||||
...link_context_menu.getItems(),
|
||||
{ title: "----" },
|
||||
{
|
||||
title: t("board_view.move-to"),
|
||||
uiIcon: "bx bx-transfer",
|
||||
items: api.columns.map(columnToMoveTo => ({
|
||||
title: columnToMoveTo,
|
||||
enabled: columnToMoveTo !== column,
|
||||
handler: () => api.changeColumn(noteId, columnToMoveTo)
|
||||
}))
|
||||
},
|
||||
{ title: "----" },
|
||||
{
|
||||
title: t("board_view.insert-above"),
|
||||
uiIcon: "bx bx-list-plus",
|
||||
handler: () => boardView.insertItemAtPosition(column, branchId, "before")
|
||||
},
|
||||
{
|
||||
title: t("board_view.insert-below"),
|
||||
uiIcon: "bx bx-empty",
|
||||
handler: () => boardView.insertItemAtPosition(column, branchId, "after")
|
||||
},
|
||||
{ title: "----" },
|
||||
{
|
||||
title: t("board_view.delete-note"),
|
||||
uiIcon: "bx bx-trash",
|
||||
handler: () => branches.deleteNotes([ branchId ], false, false)
|
||||
}
|
||||
],
|
||||
selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, noteId),
|
||||
});
|
||||
}
|
||||
}
|
||||
84
apps/client/src/widgets/view_widgets/board_view/data.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import FBranch from "../../../entities/fbranch";
|
||||
import FNote from "../../../entities/fnote";
|
||||
import { BoardData } from "./config";
|
||||
|
||||
export type ColumnMap = Map<string, {
|
||||
branch: FBranch;
|
||||
note: FNote;
|
||||
}[]>;
|
||||
|
||||
export async function getBoardData(parentNote: FNote, groupByColumn: string, persistedData: BoardData) {
|
||||
const byColumn: ColumnMap = new Map();
|
||||
|
||||
// First, scan all notes to find what columns actually exist
|
||||
await recursiveGroupBy(parentNote.getChildBranches(), byColumn, groupByColumn);
|
||||
|
||||
// Get all columns that exist in the notes
|
||||
const columnsFromNotes = [...byColumn.keys()];
|
||||
|
||||
// Get existing persisted columns and preserve their order
|
||||
const existingPersistedColumns = persistedData.columns || [];
|
||||
const existingColumnValues = existingPersistedColumns.map(c => c.value);
|
||||
|
||||
// Find truly new columns (exist in notes but not in persisted data)
|
||||
const newColumnValues = columnsFromNotes.filter(col => !existingColumnValues.includes(col));
|
||||
|
||||
// Build the complete correct column list: existing + new
|
||||
const allColumns = [
|
||||
...existingPersistedColumns, // Preserve existing order
|
||||
...newColumnValues.map(value => ({ value })) // Add new columns
|
||||
];
|
||||
|
||||
// Remove duplicates (just in case) and ensure we only keep columns that exist in notes or are explicitly preserved
|
||||
const deduplicatedColumns = allColumns.filter((column, index) => {
|
||||
const firstIndex = allColumns.findIndex(c => c.value === column.value);
|
||||
return firstIndex === index; // Keep only the first occurrence
|
||||
});
|
||||
|
||||
// Ensure all persisted columns have empty arrays in byColumn (even if no notes use them)
|
||||
for (const column of deduplicatedColumns) {
|
||||
if (!byColumn.has(column.value)) {
|
||||
byColumn.set(column.value, []);
|
||||
}
|
||||
}
|
||||
|
||||
// Return updated persisted data only if there were changes
|
||||
let newPersistedData: BoardData | undefined;
|
||||
const hasChanges = newColumnValues.length > 0 ||
|
||||
existingPersistedColumns.length !== deduplicatedColumns.length ||
|
||||
!existingPersistedColumns.every((col, idx) => deduplicatedColumns[idx]?.value === col.value);
|
||||
|
||||
if (hasChanges) {
|
||||
newPersistedData = {
|
||||
...persistedData,
|
||||
columns: deduplicatedColumns
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
byColumn,
|
||||
newPersistedData
|
||||
};
|
||||
}
|
||||
|
||||
async function recursiveGroupBy(branches: FBranch[], byColumn: ColumnMap, groupByColumn: string) {
|
||||
for (const branch of branches) {
|
||||
const note = await branch.getNote();
|
||||
if (!note) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const group = note.getLabelValue(groupByColumn);
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!byColumn.has(group)) {
|
||||
byColumn.set(group, []);
|
||||
}
|
||||
byColumn.get(group)!.push({
|
||||
branch,
|
||||
note
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,539 @@
|
||||
import { BoardDragHandler } from "./drag_handler";
|
||||
import BoardApi from "./api";
|
||||
import appContext from "../../../components/app_context";
|
||||
import FNote from "../../../entities/fnote";
|
||||
import ViewModeStorage from "../view_mode_storage";
|
||||
import { BoardData } from "./config";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
|
||||
export interface BoardState {
|
||||
columns: { [key: string]: { note: any; branch: any }[] };
|
||||
columnOrder: string[];
|
||||
}
|
||||
|
||||
export class DifferentialBoardRenderer {
|
||||
private $container: JQuery<HTMLElement>;
|
||||
private api: BoardApi;
|
||||
private dragHandler: BoardDragHandler;
|
||||
private lastState: BoardState | null = null;
|
||||
private onCreateNewItem: (column: string) => void;
|
||||
private updateTimeout: number | null = null;
|
||||
private pendingUpdate = false;
|
||||
private parentNote: FNote;
|
||||
private viewStorage: ViewModeStorage<BoardData>;
|
||||
private onRefreshApi: () => Promise<void>;
|
||||
|
||||
constructor(
|
||||
$container: JQuery<HTMLElement>,
|
||||
api: BoardApi,
|
||||
dragHandler: BoardDragHandler,
|
||||
onCreateNewItem: (column: string) => void,
|
||||
parentNote: FNote,
|
||||
viewStorage: ViewModeStorage<BoardData>,
|
||||
onRefreshApi: () => Promise<void>
|
||||
) {
|
||||
this.$container = $container;
|
||||
this.api = api;
|
||||
this.dragHandler = dragHandler;
|
||||
this.onCreateNewItem = onCreateNewItem;
|
||||
this.parentNote = parentNote;
|
||||
this.viewStorage = viewStorage;
|
||||
this.onRefreshApi = onRefreshApi;
|
||||
}
|
||||
|
||||
async renderBoard(refreshApi = false): Promise<void> {
|
||||
// Refresh API data if requested
|
||||
if (refreshApi) {
|
||||
await this.onRefreshApi();
|
||||
}
|
||||
|
||||
// Debounce rapid updates
|
||||
if (this.updateTimeout) {
|
||||
clearTimeout(this.updateTimeout);
|
||||
}
|
||||
|
||||
this.updateTimeout = window.setTimeout(async () => {
|
||||
await this.performUpdate();
|
||||
this.updateTimeout = null;
|
||||
}, 16); // ~60fps
|
||||
}
|
||||
|
||||
private async performUpdate(): Promise<void> {
|
||||
// Clean up any stray drag indicators before updating
|
||||
this.dragHandler.cleanup();
|
||||
|
||||
const currentState = this.getCurrentState();
|
||||
|
||||
if (!this.lastState) {
|
||||
// First render - do full render
|
||||
await this.fullRender(currentState);
|
||||
} else {
|
||||
// Differential render - only update what changed
|
||||
await this.differentialRender(this.lastState, currentState);
|
||||
}
|
||||
|
||||
this.lastState = currentState;
|
||||
}
|
||||
|
||||
private getCurrentState(): BoardState {
|
||||
const columns: { [key: string]: { note: any; branch: any }[] } = {};
|
||||
const columnOrder: string[] = [];
|
||||
|
||||
for (const column of this.api.columns) {
|
||||
columnOrder.push(column);
|
||||
columns[column] = this.api.getColumn(column) || [];
|
||||
}
|
||||
|
||||
return { columns, columnOrder };
|
||||
}
|
||||
|
||||
private async fullRender(state: BoardState): Promise<void> {
|
||||
this.$container.empty();
|
||||
|
||||
for (const column of state.columnOrder) {
|
||||
const columnItems = state.columns[column];
|
||||
const $columnEl = this.createColumn(column, columnItems);
|
||||
this.$container.append($columnEl);
|
||||
}
|
||||
|
||||
this.addAddColumnButton();
|
||||
}
|
||||
|
||||
private async differentialRender(oldState: BoardState, newState: BoardState): Promise<void> {
|
||||
// Store scroll positions before making changes
|
||||
const scrollPositions = this.saveScrollPositions();
|
||||
|
||||
// Handle column additions/removals
|
||||
this.updateColumns(oldState, newState);
|
||||
|
||||
// Handle card updates within existing columns
|
||||
for (const column of newState.columnOrder) {
|
||||
this.updateColumnCards(column, oldState.columns[column] || [], newState.columns[column]);
|
||||
}
|
||||
|
||||
// Restore scroll positions
|
||||
this.restoreScrollPositions(scrollPositions);
|
||||
}
|
||||
|
||||
private saveScrollPositions(): { [column: string]: number } {
|
||||
const positions: { [column: string]: number } = {};
|
||||
this.$container.find('.board-column').each((_, el) => {
|
||||
const column = $(el).attr('data-column');
|
||||
if (column) {
|
||||
positions[column] = el.scrollTop;
|
||||
}
|
||||
});
|
||||
return positions;
|
||||
}
|
||||
|
||||
private restoreScrollPositions(positions: { [column: string]: number }): void {
|
||||
this.$container.find('.board-column').each((_, el) => {
|
||||
const column = $(el).attr('data-column');
|
||||
if (column && positions[column] !== undefined) {
|
||||
el.scrollTop = positions[column];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateColumns(oldState: BoardState, newState: BoardState): void {
|
||||
// Check if column order has changed
|
||||
const orderChanged = !this.arraysEqual(oldState.columnOrder, newState.columnOrder);
|
||||
|
||||
if (orderChanged) {
|
||||
// If order changed, we need to reorder the columns in the DOM
|
||||
this.reorderColumns(newState.columnOrder);
|
||||
}
|
||||
|
||||
// Remove columns that no longer exist
|
||||
for (const oldColumn of oldState.columnOrder) {
|
||||
if (!newState.columnOrder.includes(oldColumn)) {
|
||||
this.$container.find(`[data-column="${oldColumn}"]`).remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Add new columns
|
||||
for (const newColumn of newState.columnOrder) {
|
||||
if (!oldState.columnOrder.includes(newColumn)) {
|
||||
const columnItems = newState.columns[newColumn];
|
||||
const $columnEl = this.createColumn(newColumn, columnItems);
|
||||
|
||||
// Insert at correct position
|
||||
const insertIndex = newState.columnOrder.indexOf(newColumn);
|
||||
const $existingColumns = this.$container.find('.board-column');
|
||||
|
||||
if (insertIndex === 0) {
|
||||
this.$container.prepend($columnEl);
|
||||
} else if (insertIndex >= $existingColumns.length) {
|
||||
this.$container.find('.board-add-column').before($columnEl);
|
||||
} else {
|
||||
$($existingColumns[insertIndex - 1]).after($columnEl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private arraysEqual(a: string[], b: string[]): boolean {
|
||||
return a.length === b.length && a.every((val, index) => val === b[index]);
|
||||
}
|
||||
|
||||
private reorderColumns(newOrder: string[]): void {
|
||||
// Get all existing column elements
|
||||
const $columns = this.$container.find('.board-column');
|
||||
const $addColumnButton = this.$container.find('.board-add-column');
|
||||
|
||||
// Create a map of column elements by their data-column attribute
|
||||
const columnElements = new Map<string, JQuery<HTMLElement>>();
|
||||
$columns.each((_, el) => {
|
||||
const $el = $(el);
|
||||
const columnValue = $el.attr('data-column');
|
||||
if (columnValue) {
|
||||
columnElements.set(columnValue, $el);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove all columns from DOM (but keep references)
|
||||
$columns.detach();
|
||||
|
||||
// Re-insert columns in the new order
|
||||
let $insertAfter: JQuery<HTMLElement> | null = null;
|
||||
for (const columnValue of newOrder) {
|
||||
const $columnEl = columnElements.get(columnValue);
|
||||
if ($columnEl) {
|
||||
if ($insertAfter) {
|
||||
$insertAfter.after($columnEl);
|
||||
} else {
|
||||
// Insert at the beginning
|
||||
this.$container.prepend($columnEl);
|
||||
}
|
||||
$insertAfter = $columnEl;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure add column button is at the end
|
||||
if ($addColumnButton.length) {
|
||||
this.$container.append($addColumnButton);
|
||||
}
|
||||
}
|
||||
|
||||
private updateColumnCards(column: string, oldCards: { note: any; branch: any }[], newCards: { note: any; branch: any }[]): void {
|
||||
const $column = this.$container.find(`[data-column="${column}"]`);
|
||||
if (!$column.length) return;
|
||||
|
||||
const $cardContainer = $column;
|
||||
const oldCardIds = oldCards.map(item => item.note.noteId);
|
||||
const newCardIds = newCards.map(item => item.note.noteId);
|
||||
|
||||
// Remove cards that no longer exist
|
||||
$cardContainer.find('.board-note').each((_, el) => {
|
||||
const noteId = $(el).attr('data-note-id');
|
||||
if (noteId && !newCardIds.includes(noteId)) {
|
||||
$(el).addClass('fade-out');
|
||||
setTimeout(() => $(el).remove(), 150);
|
||||
}
|
||||
});
|
||||
|
||||
// Add or update cards
|
||||
for (let i = 0; i < newCards.length; i++) {
|
||||
const item = newCards[i];
|
||||
const noteId = item.note.noteId;
|
||||
const $existingCard = $cardContainer.find(`[data-note-id="${noteId}"]`);
|
||||
const isNewCard = !oldCardIds.includes(noteId);
|
||||
|
||||
if ($existingCard.length) {
|
||||
// Check for changes in title, icon, or color
|
||||
const currentTitle = $existingCard.text().trim();
|
||||
const currentIconClass = $existingCard.attr('data-icon-class');
|
||||
const currentColorClass = $existingCard.attr('data-color-class') || '';
|
||||
|
||||
const newIconClass = item.note.getIcon();
|
||||
const newColorClass = item.note.getColorClass() || '';
|
||||
|
||||
let hasChanges = false;
|
||||
|
||||
// Update title if changed
|
||||
if (currentTitle !== item.note.title) {
|
||||
$existingCard.contents().filter(function() {
|
||||
return this.nodeType === 3; // Text nodes
|
||||
}).remove();
|
||||
$existingCard.append(document.createTextNode(item.note.title));
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
// Update icon if changed
|
||||
if (currentIconClass !== newIconClass) {
|
||||
const $icon = $existingCard.find('.icon');
|
||||
$icon.removeClass().addClass('icon').addClass(newIconClass);
|
||||
$existingCard.attr('data-icon-class', newIconClass);
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
// Update color if changed
|
||||
if (currentColorClass !== newColorClass) {
|
||||
// Remove old color class if it exists
|
||||
if (currentColorClass) {
|
||||
$existingCard.removeClass(currentColorClass);
|
||||
}
|
||||
// Add new color class if it exists
|
||||
if (newColorClass) {
|
||||
$existingCard.addClass(newColorClass);
|
||||
}
|
||||
$existingCard.attr('data-color-class', newColorClass);
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
// Add subtle animation if there were changes
|
||||
if (hasChanges) {
|
||||
$existingCard.addClass('card-updated');
|
||||
setTimeout(() => $existingCard.removeClass('card-updated'), 300);
|
||||
}
|
||||
|
||||
// Ensure card is in correct position
|
||||
this.ensureCardPosition($existingCard, i, $cardContainer);
|
||||
} else {
|
||||
// Create new card
|
||||
const $newCard = this.createCard(item.note, item.branch, column);
|
||||
$newCard.addClass('fade-in').css('opacity', '0');
|
||||
|
||||
// Insert at correct position
|
||||
if (i === 0) {
|
||||
$cardContainer.find('h3').after($newCard);
|
||||
} else {
|
||||
const $prevCard = $cardContainer.find('.board-note').eq(i - 1);
|
||||
if ($prevCard.length) {
|
||||
$prevCard.after($newCard);
|
||||
} else {
|
||||
$cardContainer.find('.board-new-item').before($newCard);
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger fade in animation
|
||||
setTimeout(() => $newCard.css('opacity', '1'), 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ensureCardPosition($card: JQuery<HTMLElement>, targetIndex: number, $container: JQuery<HTMLElement>): void {
|
||||
const $allCards = $container.find('.board-note');
|
||||
const currentIndex = $allCards.index($card);
|
||||
|
||||
if (currentIndex !== targetIndex) {
|
||||
if (targetIndex === 0) {
|
||||
$container.find('h3').after($card);
|
||||
} else {
|
||||
const $targetPrev = $allCards.eq(targetIndex - 1);
|
||||
if ($targetPrev.length) {
|
||||
$targetPrev.after($card);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private createColumn(column: string, columnItems: { note: any; branch: any }[]): JQuery<HTMLElement> {
|
||||
const $columnEl = $("<div>")
|
||||
.addClass("board-column")
|
||||
.attr("data-column", column);
|
||||
|
||||
// Create header
|
||||
const $titleEl = $("<h3>").attr("data-column-value", column);
|
||||
|
||||
// Create title text
|
||||
const $titleText = $("<span>").text(column);
|
||||
|
||||
// Create edit icon
|
||||
const $editIcon = $("<span>")
|
||||
.addClass("edit-icon icon bx bx-edit-alt")
|
||||
.attr("title", "Click to edit column title");
|
||||
|
||||
$titleEl.append($titleText, $editIcon);
|
||||
$columnEl.append($titleEl);
|
||||
|
||||
// Setup column dragging
|
||||
this.dragHandler.setupColumnDrag($columnEl, column);
|
||||
|
||||
// Handle wheel events for scrolling
|
||||
$columnEl.on("wheel", (event) => {
|
||||
const el = $columnEl[0];
|
||||
const needsScroll = el.scrollHeight > el.clientHeight;
|
||||
if (needsScroll) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
// Setup drop zones for both notes and columns
|
||||
this.dragHandler.setupNoteDropZone($columnEl, column);
|
||||
this.dragHandler.setupColumnDropZone($columnEl);
|
||||
|
||||
// Add cards
|
||||
for (const item of columnItems) {
|
||||
if (item.note) {
|
||||
const $noteEl = this.createCard(item.note, item.branch, column);
|
||||
$columnEl.append($noteEl);
|
||||
}
|
||||
}
|
||||
|
||||
// Add "New item" button
|
||||
const $newItemEl = $("<div>")
|
||||
.addClass("board-new-item")
|
||||
.attr("data-column", column)
|
||||
.html(`<span class="icon bx bx-plus"></span> ${t("board_view.new-item")}`);
|
||||
|
||||
$newItemEl.on("click", () => this.onCreateNewItem(column));
|
||||
$columnEl.append($newItemEl);
|
||||
|
||||
return $columnEl;
|
||||
}
|
||||
|
||||
private createCard(note: any, branch: any, column: string): JQuery<HTMLElement> {
|
||||
const $iconEl = $("<span>")
|
||||
.addClass("icon")
|
||||
.addClass(note.getIcon());
|
||||
|
||||
const colorClass = note.getColorClass() || '';
|
||||
|
||||
const $noteEl = $("<div>")
|
||||
.addClass("board-note")
|
||||
.attr("data-note-id", note.noteId)
|
||||
.attr("data-branch-id", branch.branchId)
|
||||
.attr("data-current-column", column)
|
||||
.attr("data-icon-class", note.getIcon())
|
||||
.attr("data-color-class", colorClass)
|
||||
.text(note.title);
|
||||
|
||||
// Add color class to the card if it exists
|
||||
if (colorClass) {
|
||||
$noteEl.addClass(colorClass);
|
||||
}
|
||||
|
||||
$noteEl.prepend($iconEl);
|
||||
$noteEl.on("click", () => appContext.triggerCommand("openInPopup", { noteIdOrPath: note.noteId }));
|
||||
|
||||
// Setup drag functionality
|
||||
this.dragHandler.setupNoteDrag($noteEl, note, branch);
|
||||
|
||||
return $noteEl;
|
||||
}
|
||||
|
||||
private addAddColumnButton(): void {
|
||||
if (this.$container.find('.board-add-column').length === 0) {
|
||||
const $addColumnEl = $("<div>")
|
||||
.addClass("board-add-column")
|
||||
.html(`<span class="icon bx bx-plus"></span> ${t("board_view.add-column")}`);
|
||||
|
||||
this.$container.append($addColumnEl);
|
||||
}
|
||||
}
|
||||
|
||||
forceFullRender(): void {
|
||||
this.lastState = null;
|
||||
if (this.updateTimeout) {
|
||||
clearTimeout(this.updateTimeout);
|
||||
this.updateTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
async flushPendingUpdates(): Promise<void> {
|
||||
if (this.updateTimeout) {
|
||||
clearTimeout(this.updateTimeout);
|
||||
this.updateTimeout = null;
|
||||
await this.performUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
startInlineEditing(noteId: string): void {
|
||||
// Use setTimeout to ensure the card is rendered before trying to edit it
|
||||
setTimeout(() => {
|
||||
const $card = this.$container.find(`[data-note-id="${noteId}"]`);
|
||||
if ($card.length) {
|
||||
this.makeCardEditable($card, noteId);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
private makeCardEditable($card: JQuery<HTMLElement>, noteId: string): void {
|
||||
if ($card.hasClass('editing')) {
|
||||
return; // Already editing
|
||||
}
|
||||
|
||||
// Get the current title (get text without icon)
|
||||
const $icon = $card.find('.icon');
|
||||
const currentTitle = $card.text().trim();
|
||||
|
||||
// Add editing class and store original click handler
|
||||
$card.addClass('editing');
|
||||
$card.off('click'); // Remove any existing click handlers temporarily
|
||||
|
||||
// Create input element
|
||||
const $input = $('<input>')
|
||||
.attr('type', 'text')
|
||||
.val(currentTitle)
|
||||
.css({
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
outline: 'none',
|
||||
fontFamily: 'inherit',
|
||||
fontSize: 'inherit',
|
||||
color: 'inherit',
|
||||
flex: '1',
|
||||
minWidth: '0',
|
||||
padding: '0',
|
||||
marginLeft: '0.25em'
|
||||
});
|
||||
|
||||
// Create a flex container to keep icon and input inline
|
||||
const $editContainer = $('<div>')
|
||||
.css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%'
|
||||
});
|
||||
|
||||
// Replace content with icon + input in flex container
|
||||
$editContainer.append($icon.clone(), $input);
|
||||
$card.empty().append($editContainer);
|
||||
$input.focus().select();
|
||||
|
||||
const finishEdit = async (save = true) => {
|
||||
if (!$card.hasClass('editing')) {
|
||||
return; // Already finished
|
||||
}
|
||||
|
||||
$card.removeClass('editing');
|
||||
|
||||
let finalTitle = currentTitle;
|
||||
if (save) {
|
||||
const newTitle = $input.val() as string;
|
||||
if (newTitle.trim() && newTitle !== currentTitle) {
|
||||
try {
|
||||
// Update the note title using the board view's server call
|
||||
import('../../../services/server').then(async ({ default: server }) => {
|
||||
await server.put(`notes/${noteId}/title`, { title: newTitle.trim() });
|
||||
finalTitle = newTitle.trim();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to update note title:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the card content
|
||||
const iconClass = $card.attr('data-icon-class') || 'bx bx-file';
|
||||
const $newIcon = $('<span>').addClass('icon').addClass(iconClass);
|
||||
$card.text(finalTitle);
|
||||
$card.prepend($newIcon);
|
||||
|
||||
// Re-attach click handler for quick edit (for existing cards)
|
||||
$card.on('click', () => appContext.triggerCommand("openInPopup", { noteIdOrPath: noteId }));
|
||||
};
|
||||
|
||||
$input.on('blur', () => finishEdit(true));
|
||||
$input.on('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
finishEdit(true);
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
finishEdit(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import BoardApi from "./api";
|
||||
import { DragContext } from "./drag_types";
|
||||
import { NoteDragHandler } from "./note_drag_handler";
|
||||
import { ColumnDragHandler } from "./column_drag_handler";
|
||||
|
||||
export class BoardDragHandler {
|
||||
private noteDragHandler: NoteDragHandler;
|
||||
private columnDragHandler: ColumnDragHandler;
|
||||
|
||||
constructor(
|
||||
$container: JQuery<HTMLElement>,
|
||||
api: BoardApi,
|
||||
context: DragContext,
|
||||
) {
|
||||
// Initialize specialized drag handlers
|
||||
this.noteDragHandler = new NoteDragHandler($container, api, context);
|
||||
this.columnDragHandler = new ColumnDragHandler($container, api, context);
|
||||
}
|
||||
|
||||
// Note drag methods - delegate to NoteDragHandler
|
||||
setupNoteDrag($noteEl: JQuery<HTMLElement>, note: any, branch: any) {
|
||||
this.noteDragHandler.setupNoteDrag($noteEl, note, branch);
|
||||
}
|
||||
|
||||
setupNoteDropZone($columnEl: JQuery<HTMLElement>, column: string) {
|
||||
this.noteDragHandler.setupNoteDropZone($columnEl, column);
|
||||
}
|
||||
|
||||
// Column drag methods - delegate to ColumnDragHandler
|
||||
setupColumnDrag($columnEl: JQuery<HTMLElement>, columnValue: string) {
|
||||
this.columnDragHandler.setupColumnDrag($columnEl, columnValue);
|
||||
}
|
||||
|
||||
setupColumnDropZone($columnEl: JQuery<HTMLElement>) {
|
||||
this.columnDragHandler.setupColumnDropZone($columnEl);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.noteDragHandler.cleanup();
|
||||
this.columnDragHandler.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
// Export the drag context type for external use
|
||||
export type { DragContext } from "./drag_types";
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface DragContext {
|
||||
draggedNote: any;
|
||||
draggedBranch: any;
|
||||
draggedNoteElement: JQuery<HTMLElement> | null;
|
||||
draggedColumn: string | null;
|
||||
draggedColumnElement: JQuery<HTMLElement> | null;
|
||||
}
|
||||
|
||||
export interface BaseDragHandler {
|
||||
cleanup(): void;
|
||||
}
|
||||
650
apps/client/src/widgets/view_widgets/board_view/index.ts
Normal file
@@ -0,0 +1,650 @@
|
||||
import { setupHorizontalScrollViaWheel } from "../../widget_utils";
|
||||
import ViewMode, { ViewModeArgs } from "../view_mode";
|
||||
import noteCreateService from "../../../services/note_create";
|
||||
import { EventData } from "../../../components/app_context";
|
||||
import { BoardData } from "./config";
|
||||
import SpacedUpdate from "../../../services/spaced_update";
|
||||
import { setupContextMenu } from "./context_menu";
|
||||
import BoardApi from "./api";
|
||||
import { BoardDragHandler, DragContext } from "./drag_handler";
|
||||
import { DifferentialBoardRenderer } from "./differential_renderer";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="board-view">
|
||||
<style>
|
||||
.board-view {
|
||||
overflow-x: auto;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.board-view-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
padding: 1em;
|
||||
padding-bottom: 0;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.board-view-container .board-column {
|
||||
width: 250px;
|
||||
flex-shrink: 0;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 8px;
|
||||
padding: 0.5em;
|
||||
background-color: var(--accented-background-color);
|
||||
transition: border-color 0.2s ease;
|
||||
overflow-y: auto;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.board-view-container .board-column.drag-over {
|
||||
border-color: var(--main-text-color);
|
||||
background-color: var(--hover-item-background-color);
|
||||
}
|
||||
|
||||
.board-view-container .board-column h3 {
|
||||
font-size: 1em;
|
||||
margin-bottom: 0.75em;
|
||||
padding: 0.5em 0.5em 0.5em 0.5em;
|
||||
border-bottom: 1px solid var(--main-border-color);
|
||||
cursor: grab;
|
||||
position: relative;
|
||||
transition: background-color 0.2s ease, border-radius 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.board-view-container .board-column h3:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.board-view-container .board-column h3.editing {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.board-view-container .board-column h3:hover {
|
||||
background-color: var(--hover-item-background-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.board-view-container .board-column h3.editing {
|
||||
background-color: var(--main-background-color);
|
||||
border: 1px solid var(--main-text-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.board-view-container .board-column.column-dragging {
|
||||
opacity: 0.6;
|
||||
transform: scale(0.98);
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.board-view-container .board-column h3 input {
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
color: inherit;
|
||||
width: 100%;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.board-view-container .board-column h3 .edit-icon {
|
||||
opacity: 0;
|
||||
margin-left: 0.5em;
|
||||
transition: opacity 0.2s ease;
|
||||
color: var(--muted-text-color);
|
||||
}
|
||||
|
||||
.board-view-container .board-column h3:hover .edit-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.board-view-container .board-column h3.editing .edit-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.board-view-container .board-note {
|
||||
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.25);
|
||||
margin: 0.65em 0;
|
||||
padding: 0.5em;
|
||||
border-radius: 5px;
|
||||
cursor: move;
|
||||
position: relative;
|
||||
background-color: var(--main-background-color);
|
||||
border: 1px solid var(--main-border-color);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.15s ease;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.board-view-container .board-note.fade-in {
|
||||
animation: fadeIn 0.15s ease-in;
|
||||
}
|
||||
|
||||
.board-view-container .board-note.fade-out {
|
||||
animation: fadeOut 0.15s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from { opacity: 1; transform: translateY(0); }
|
||||
to { opacity: 0; transform: translateY(-10px); }
|
||||
}
|
||||
|
||||
.board-view-container .board-note.card-updated {
|
||||
animation: cardUpdate 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes cardUpdate {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.02); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
|
||||
.board-view-container .board-note:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
.board-view-container .board-note.dragging {
|
||||
opacity: 0.8;
|
||||
transform: rotate(5deg);
|
||||
z-index: 1000;
|
||||
box-shadow: 4px 8px 16px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.board-view-container .board-note.editing {
|
||||
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.35);
|
||||
border-color: var(--main-text-color);
|
||||
}
|
||||
|
||||
.board-view-container .board-note.editing input {
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.board-view-container .board-note .icon {
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.board-drop-indicator {
|
||||
height: 3px;
|
||||
background-color: var(--main-text-color);
|
||||
border-radius: 2px;
|
||||
margin: 0.25em 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.board-drop-indicator.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.column-drop-indicator {
|
||||
width: 4px;
|
||||
background-color: var(--main-text-color);
|
||||
border-radius: 2px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
height: 100%;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.3);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.column-drop-indicator.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.board-new-item {
|
||||
margin-top: 0.5em;
|
||||
padding: 0.5em;
|
||||
border-radius: 5px;
|
||||
color: var(--muted-text-color);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.board-new-item:hover {
|
||||
border-color: var(--main-text-color);
|
||||
color: var(--main-text-color);
|
||||
background-color: var(--hover-item-background-color);
|
||||
}
|
||||
|
||||
.board-new-item .icon {
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.board-add-column {
|
||||
width: 180px;
|
||||
flex-shrink: 0;
|
||||
height: 60px;
|
||||
border-radius: 8px;
|
||||
padding: 0.5em;
|
||||
background-color: var(--accented-background-color);
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: var(--muted-text-color);
|
||||
font-size: 0.9em;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.board-add-column:hover {
|
||||
border-color: var(--main-text-color);
|
||||
color: var(--main-text-color);
|
||||
background-color: var(--hover-item-background-color);
|
||||
}
|
||||
|
||||
.board-add-column .icon {
|
||||
margin-right: 0.5em;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.board-drag-preview {
|
||||
position: fixed;
|
||||
z-index: 10000;
|
||||
pointer-events: none;
|
||||
opacity: 0.8;
|
||||
transform: rotate(5deg);
|
||||
box-shadow: 4px 8px 16px rgba(0, 0, 0, 0.5);
|
||||
background-color: var(--main-background-color);
|
||||
border: 1px solid var(--main-border-color);
|
||||
border-radius: 5px;
|
||||
padding: 0.5em;
|
||||
font-size: 0.9em;
|
||||
max-width: 200px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="board-view-container"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
export default class BoardView extends ViewMode<BoardData> {
|
||||
|
||||
private $root: JQuery<HTMLElement>;
|
||||
private $container: JQuery<HTMLElement>;
|
||||
private spacedUpdate: SpacedUpdate;
|
||||
private dragContext: DragContext;
|
||||
private persistentData: BoardData;
|
||||
private api?: BoardApi;
|
||||
private dragHandler?: BoardDragHandler;
|
||||
private renderer?: DifferentialBoardRenderer;
|
||||
|
||||
constructor(args: ViewModeArgs) {
|
||||
super(args, "board");
|
||||
|
||||
this.$root = $(TPL);
|
||||
setupHorizontalScrollViaWheel(this.$root);
|
||||
this.$container = this.$root.find(".board-view-container");
|
||||
this.spacedUpdate = new SpacedUpdate(() => this.onSave(), 5_000);
|
||||
this.persistentData = {
|
||||
columns: []
|
||||
};
|
||||
this.dragContext = {
|
||||
draggedNote: null,
|
||||
draggedBranch: null,
|
||||
draggedNoteElement: null,
|
||||
draggedColumn: null,
|
||||
draggedColumnElement: null
|
||||
};
|
||||
|
||||
args.$parent.append(this.$root);
|
||||
}
|
||||
|
||||
async renderList(): Promise<JQuery<HTMLElement> | undefined> {
|
||||
if (!this.renderer) {
|
||||
// First time setup
|
||||
this.$container.empty();
|
||||
await this.initializeRenderer();
|
||||
}
|
||||
|
||||
await this.renderer!.renderBoard();
|
||||
return this.$root;
|
||||
}
|
||||
|
||||
private async initializeRenderer() {
|
||||
this.api = await BoardApi.build(this.parentNote, this.viewStorage);
|
||||
this.dragHandler = new BoardDragHandler(
|
||||
this.$container,
|
||||
this.api,
|
||||
this.dragContext
|
||||
);
|
||||
|
||||
this.renderer = new DifferentialBoardRenderer(
|
||||
this.$container,
|
||||
this.api,
|
||||
this.dragHandler,
|
||||
(column: string) => this.createNewItem(column),
|
||||
this.parentNote,
|
||||
this.viewStorage,
|
||||
() => this.refreshApi()
|
||||
);
|
||||
|
||||
setupContextMenu({
|
||||
$container: this.$container,
|
||||
api: this.api,
|
||||
boardView: this
|
||||
});
|
||||
|
||||
// Setup column title editing and add column functionality
|
||||
this.setupBoardInteractions();
|
||||
}
|
||||
|
||||
private async refreshApi(): Promise<void> {
|
||||
if (!this.api) {
|
||||
throw new Error("API not initialized");
|
||||
}
|
||||
|
||||
await this.api.refresh(this.parentNote);
|
||||
}
|
||||
|
||||
private setupBoardInteractions() {
|
||||
// Handle column title editing with click detection that works with dragging
|
||||
this.$container.on('mousedown', 'h3[data-column-value]', (e) => {
|
||||
const $titleEl = $(e.currentTarget);
|
||||
|
||||
// Don't interfere with editing mode
|
||||
if ($titleEl.hasClass('editing') || $(e.target).is('input')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
let hasMoved = false;
|
||||
const startX = e.clientX;
|
||||
const startY = e.clientY;
|
||||
|
||||
const handleMouseMove = (moveEvent: JQuery.MouseMoveEvent) => {
|
||||
const deltaX = Math.abs(moveEvent.clientX - startX);
|
||||
const deltaY = Math.abs(moveEvent.clientY - startY);
|
||||
if (deltaX > 5 || deltaY > 5) {
|
||||
hasMoved = true;
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = (upEvent: JQuery.MouseUpEvent) => {
|
||||
const duration = Date.now() - startTime;
|
||||
$(document).off('mousemove', handleMouseMove);
|
||||
$(document).off('mouseup', handleMouseUp);
|
||||
|
||||
// If it was a quick click without much movement, treat as edit request
|
||||
if (duration < 500 && !hasMoved && upEvent.button === 0) {
|
||||
const columnValue = $titleEl.attr('data-column-value');
|
||||
if (columnValue) {
|
||||
const columnItems = this.api?.getColumn(columnValue) || [];
|
||||
this.startEditingColumnTitle($titleEl, columnValue, columnItems);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$(document).on('mousemove', handleMouseMove);
|
||||
$(document).on('mouseup', handleMouseUp);
|
||||
});
|
||||
|
||||
// Handle add column button
|
||||
this.$container.on('click', '.board-add-column', (e) => {
|
||||
e.stopPropagation();
|
||||
this.startCreatingNewColumn($(e.currentTarget));
|
||||
});
|
||||
}
|
||||
|
||||
private createTitleStructure(title: string): { $titleText: JQuery<HTMLElement>; $editIcon: JQuery<HTMLElement> } {
|
||||
const $titleText = $("<span>").text(title);
|
||||
const $editIcon = $("<span>")
|
||||
.addClass("edit-icon icon bx bx-edit-alt")
|
||||
.attr("title", "Click to edit column title");
|
||||
|
||||
return { $titleText, $editIcon };
|
||||
}
|
||||
|
||||
private startEditingColumnTitle($titleEl: JQuery<HTMLElement>, columnValue: string, columnItems: { branch: any; note: any; }[]) {
|
||||
if ($titleEl.hasClass("editing")) {
|
||||
return; // Already editing
|
||||
}
|
||||
|
||||
const $titleSpan = $titleEl.find("span").first(); // Get the text span
|
||||
const currentTitle = $titleSpan.text();
|
||||
$titleEl.addClass("editing");
|
||||
|
||||
// Disable dragging while editing
|
||||
$titleEl.attr("draggable", "false");
|
||||
|
||||
const $input = $("<input>")
|
||||
.attr("type", "text")
|
||||
.val(currentTitle)
|
||||
.attr("placeholder", "Column title");
|
||||
|
||||
// Prevent events from bubbling to parent drag handlers
|
||||
$input.on('mousedown mouseup click', (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$titleEl.empty().append($input);
|
||||
$input.focus().select();
|
||||
|
||||
const finishEdit = async (save: boolean = true) => {
|
||||
if (!$titleEl.hasClass("editing")) {
|
||||
return; // Already finished
|
||||
}
|
||||
|
||||
$titleEl.removeClass("editing");
|
||||
|
||||
// Re-enable dragging after editing
|
||||
$titleEl.attr("draggable", "true");
|
||||
|
||||
let finalTitle = currentTitle;
|
||||
if (save) {
|
||||
const newTitle = $input.val() as string;
|
||||
if (newTitle.trim() && newTitle !== currentTitle) {
|
||||
await this.renameColumn(columnValue, newTitle.trim(), columnItems);
|
||||
finalTitle = newTitle.trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Recreate the title structure
|
||||
const { $titleText, $editIcon } = this.createTitleStructure(finalTitle);
|
||||
$titleEl.empty().append($titleText, $editIcon);
|
||||
};
|
||||
|
||||
$input.on("blur", () => finishEdit(true));
|
||||
$input.on("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
finishEdit(true);
|
||||
} else if (e.key === "Escape") {
|
||||
e.preventDefault();
|
||||
finishEdit(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async renameColumn(oldValue: string, newValue: string, columnItems: { branch: any; note: any; }[]) {
|
||||
try {
|
||||
// Get all note IDs in this column
|
||||
const noteIds = columnItems.map(item => item.note.noteId);
|
||||
|
||||
// Use the API to rename the column (update all notes)
|
||||
// This will trigger onEntitiesReloaded which will automatically refresh the board
|
||||
await this.api?.renameColumn(oldValue, newValue, noteIds);
|
||||
} catch (error) {
|
||||
console.error("Failed to rename column:", error);
|
||||
}
|
||||
}
|
||||
|
||||
private async createNewItem(column: string) {
|
||||
try {
|
||||
// Get the parent note path
|
||||
const parentNotePath = this.parentNote.noteId;
|
||||
|
||||
// Create a new note as a child of the parent note
|
||||
const { note: newNote } = await noteCreateService.createNote(parentNotePath, {
|
||||
activate: false,
|
||||
title: "New item"
|
||||
});
|
||||
|
||||
if (newNote) {
|
||||
// Set the status label to place it in the correct column
|
||||
await this.api?.changeColumn(newNote.noteId, column);
|
||||
|
||||
// Refresh the board to show the new item
|
||||
await this.renderList();
|
||||
|
||||
// Start inline editing of the newly created card
|
||||
this.startInlineEditingCard(newNote.noteId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to create new item:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async insertItemAtPosition(column: string, relativeToBranchId: string, direction: "before" | "after"): Promise<void> {
|
||||
try {
|
||||
// Create the note without opening it
|
||||
const newNote = await this.api?.insertRowAtPosition(column, relativeToBranchId, direction, false);
|
||||
|
||||
if (newNote) {
|
||||
// Refresh the board to show the new item
|
||||
await this.renderList();
|
||||
|
||||
// Start inline editing of the newly created card
|
||||
this.startInlineEditingCard(newNote.noteId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to insert new item:", error);
|
||||
}
|
||||
}
|
||||
|
||||
private startInlineEditingCard(noteId: string) {
|
||||
this.renderer?.startInlineEditing(noteId);
|
||||
}
|
||||
|
||||
forceFullRefresh() {
|
||||
this.renderer?.forceFullRender();
|
||||
return this.renderList();
|
||||
}
|
||||
|
||||
private startCreatingNewColumn($addColumnEl: JQuery<HTMLElement>) {
|
||||
if ($addColumnEl.hasClass("editing")) {
|
||||
return; // Already editing
|
||||
}
|
||||
|
||||
$addColumnEl.addClass("editing");
|
||||
|
||||
const $input = $("<input>")
|
||||
.attr("type", "text")
|
||||
.attr("placeholder", "Enter column name...")
|
||||
.css({
|
||||
background: "var(--main-background-color)",
|
||||
border: "1px solid var(--main-text-color)",
|
||||
borderRadius: "4px",
|
||||
padding: "0.5em",
|
||||
color: "var(--main-text-color)",
|
||||
fontFamily: "inherit",
|
||||
fontSize: "inherit",
|
||||
width: "100%",
|
||||
textAlign: "center"
|
||||
});
|
||||
|
||||
$addColumnEl.empty().append($input);
|
||||
$input.focus();
|
||||
|
||||
const finishEdit = async (save: boolean = true) => {
|
||||
if (!$addColumnEl.hasClass("editing")) {
|
||||
return; // Already finished
|
||||
}
|
||||
|
||||
$addColumnEl.removeClass("editing");
|
||||
|
||||
if (save) {
|
||||
const columnName = $input.val() as string;
|
||||
if (columnName.trim()) {
|
||||
await this.createNewColumn(columnName.trim());
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the add button
|
||||
$addColumnEl.html('<span class="icon bx bx-plus"></span>Add Column');
|
||||
};
|
||||
|
||||
$input.on("blur", () => finishEdit(true));
|
||||
$input.on("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
finishEdit(true);
|
||||
} else if (e.key === "Escape") {
|
||||
e.preventDefault();
|
||||
finishEdit(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async createNewColumn(columnName: string) {
|
||||
try {
|
||||
// Check if column already exists
|
||||
if (this.api?.columns.includes(columnName)) {
|
||||
console.warn("A column with this name already exists.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the new column
|
||||
await this.api?.createColumn(columnName);
|
||||
|
||||
// Refresh the board to show the new column
|
||||
await this.renderList();
|
||||
} catch (error) {
|
||||
console.error("Failed to create new column:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async onEntitiesReloaded({ loadResults }: EventData<"entitiesReloaded">) {
|
||||
// Check if any changes affect our board
|
||||
const hasRelevantChanges =
|
||||
// React to changes in status attribute for notes in this board
|
||||
loadResults.getAttributeRows().some(attr => attr.name === this.api?.statusAttribute && this.noteIds.includes(attr.noteId!)) ||
|
||||
// React to changes in note title
|
||||
loadResults.getNoteIds().some(noteId => this.noteIds.includes(noteId)) ||
|
||||
// React to changes in branches for subchildren (e.g., moved, added, or removed notes)
|
||||
loadResults.getBranchRows().some(branch => this.noteIds.includes(branch.noteId!)) ||
|
||||
// React to changes in note icon or color.
|
||||
loadResults.getAttributeRows().some(attr => [ "iconClass", "color" ].includes(attr.name ?? "") && this.noteIds.includes(attr.noteId ?? "")) ||
|
||||
// React to attachment change
|
||||
loadResults.getAttachmentRows().some(att => att.ownerId === this.parentNote.noteId && att.title === "board.json") ||
|
||||
// React to changes in "groupBy"
|
||||
loadResults.getAttributeRows().some(attr => attr.name === "board:groupBy" && attr.noteId === this.parentNote.noteId);
|
||||
|
||||
if (hasRelevantChanges && this.renderer) {
|
||||
// Use differential rendering with API refresh
|
||||
await this.renderer.renderBoard(true);
|
||||
}
|
||||
|
||||
// Don't trigger full view refresh - let differential renderer handle it
|
||||
return false;
|
||||
}
|
||||
|
||||
private onSave() {
|
||||
this.viewStorage.store(this.persistentData);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
import branchService from "../../../services/branches";
|
||||
import BoardApi from "./api";
|
||||
import { DragContext, BaseDragHandler } from "./drag_types";
|
||||
|
||||
export class NoteDragHandler implements BaseDragHandler {
|
||||
private $container: JQuery<HTMLElement>;
|
||||
private api: BoardApi;
|
||||
private context: DragContext;
|
||||
|
||||
constructor(
|
||||
$container: JQuery<HTMLElement>,
|
||||
api: BoardApi,
|
||||
context: DragContext,
|
||||
) {
|
||||
this.$container = $container;
|
||||
this.api = api;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
setupNoteDrag($noteEl: JQuery<HTMLElement>, note: any, branch: any) {
|
||||
$noteEl.attr("draggable", "true");
|
||||
|
||||
// Mouse drag events
|
||||
this.setupMouseDrag($noteEl, note, branch);
|
||||
|
||||
// Touch drag events
|
||||
this.setupTouchDrag($noteEl, note, branch);
|
||||
}
|
||||
|
||||
setupNoteDropZone($columnEl: JQuery<HTMLElement>, column: string) {
|
||||
$columnEl.on("dragover", (e) => {
|
||||
// Only handle note drops when a note is being dragged
|
||||
if (this.context.draggedNote && !this.context.draggedColumn) {
|
||||
e.preventDefault();
|
||||
const originalEvent = e.originalEvent as DragEvent;
|
||||
if (originalEvent.dataTransfer) {
|
||||
originalEvent.dataTransfer.dropEffect = "move";
|
||||
}
|
||||
|
||||
$columnEl.addClass("drag-over");
|
||||
this.showDropIndicator($columnEl, e);
|
||||
}
|
||||
});
|
||||
|
||||
$columnEl.on("dragleave", (e) => {
|
||||
// Only remove drag-over if we're leaving the column entirely
|
||||
const rect = $columnEl[0].getBoundingClientRect();
|
||||
const originalEvent = e.originalEvent as DragEvent;
|
||||
const x = originalEvent.clientX;
|
||||
const y = originalEvent.clientY;
|
||||
|
||||
if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
|
||||
$columnEl.removeClass("drag-over");
|
||||
this.cleanupNoteDropIndicators($columnEl);
|
||||
}
|
||||
});
|
||||
|
||||
$columnEl.on("drop", async (e) => {
|
||||
if (this.context.draggedNote && !this.context.draggedColumn) {
|
||||
e.preventDefault();
|
||||
$columnEl.removeClass("drag-over");
|
||||
|
||||
if (this.context.draggedNote && this.context.draggedNoteElement && this.context.draggedBranch) {
|
||||
await this.handleNoteDrop($columnEl, column);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.cleanupAllDropIndicators();
|
||||
this.$container.find('.board-column').removeClass('drag-over');
|
||||
}
|
||||
|
||||
private setupMouseDrag($noteEl: JQuery<HTMLElement>, note: any, branch: any) {
|
||||
$noteEl.on("dragstart", (e) => {
|
||||
this.context.draggedNote = note;
|
||||
this.context.draggedBranch = branch;
|
||||
this.context.draggedNoteElement = $noteEl;
|
||||
$noteEl.addClass("dragging");
|
||||
|
||||
// Set drag data
|
||||
const originalEvent = e.originalEvent as DragEvent;
|
||||
if (originalEvent.dataTransfer) {
|
||||
originalEvent.dataTransfer.effectAllowed = "move";
|
||||
originalEvent.dataTransfer.setData("text/plain", note.noteId);
|
||||
}
|
||||
});
|
||||
|
||||
$noteEl.on("dragend", () => {
|
||||
$noteEl.removeClass("dragging");
|
||||
this.context.draggedNote = null;
|
||||
this.context.draggedBranch = null;
|
||||
this.context.draggedNoteElement = null;
|
||||
|
||||
// Clean up all drop indicators properly
|
||||
this.cleanupAllDropIndicators();
|
||||
});
|
||||
}
|
||||
|
||||
private setupTouchDrag($noteEl: JQuery<HTMLElement>, note: any, branch: any) {
|
||||
let isDragging = false;
|
||||
let startY = 0;
|
||||
let startX = 0;
|
||||
let dragThreshold = 10; // Minimum distance to start dragging
|
||||
let $dragPreview: JQuery<HTMLElement> | null = null;
|
||||
|
||||
$noteEl.on("touchstart", (e) => {
|
||||
const touch = (e.originalEvent as TouchEvent).touches[0];
|
||||
startX = touch.clientX;
|
||||
startY = touch.clientY;
|
||||
isDragging = false;
|
||||
$dragPreview = null;
|
||||
});
|
||||
|
||||
$noteEl.on("touchmove", (e) => {
|
||||
e.preventDefault(); // Prevent scrolling
|
||||
const touch = (e.originalEvent as TouchEvent).touches[0];
|
||||
const deltaX = Math.abs(touch.clientX - startX);
|
||||
const deltaY = Math.abs(touch.clientY - startY);
|
||||
|
||||
// Start dragging if we've moved beyond threshold
|
||||
if (!isDragging && (deltaX > dragThreshold || deltaY > dragThreshold)) {
|
||||
isDragging = true;
|
||||
this.context.draggedNote = note;
|
||||
this.context.draggedBranch = branch;
|
||||
this.context.draggedNoteElement = $noteEl;
|
||||
$noteEl.addClass("dragging");
|
||||
|
||||
// Create drag preview
|
||||
$dragPreview = this.createDragPreview($noteEl, touch.clientX, touch.clientY);
|
||||
}
|
||||
|
||||
if (isDragging && $dragPreview) {
|
||||
// Update drag preview position
|
||||
$dragPreview.css({
|
||||
left: touch.clientX - ($dragPreview.outerWidth() || 0) / 2,
|
||||
top: touch.clientY - ($dragPreview.outerHeight() || 0) / 2
|
||||
});
|
||||
|
||||
// Find element under touch point
|
||||
const elementBelow = document.elementFromPoint(touch.clientX, touch.clientY);
|
||||
if (elementBelow) {
|
||||
const $columnEl = $(elementBelow).closest('.board-column');
|
||||
|
||||
if ($columnEl.length > 0) {
|
||||
// Remove drag-over from all columns
|
||||
this.$container.find('.board-column').removeClass('drag-over');
|
||||
$columnEl.addClass('drag-over');
|
||||
|
||||
// Show drop indicator
|
||||
this.showDropIndicatorAtPoint($columnEl, touch.clientY);
|
||||
} else {
|
||||
// Remove all drag indicators if not over a column
|
||||
this.$container.find('.board-column').removeClass('drag-over');
|
||||
this.cleanupAllDropIndicators();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$noteEl.on("touchend", async (e) => {
|
||||
if (isDragging) {
|
||||
const touch = (e.originalEvent as TouchEvent).changedTouches[0];
|
||||
const elementBelow = document.elementFromPoint(touch.clientX, touch.clientY);
|
||||
if (elementBelow) {
|
||||
const $columnEl = $(elementBelow).closest('.board-column');
|
||||
|
||||
if ($columnEl.length > 0) {
|
||||
const column = $columnEl.attr('data-column');
|
||||
if (column && this.context.draggedNote && this.context.draggedNoteElement && this.context.draggedBranch) {
|
||||
await this.handleNoteDrop($columnEl, column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up
|
||||
$noteEl.removeClass("dragging");
|
||||
this.context.draggedNote = null;
|
||||
this.context.draggedBranch = null;
|
||||
this.context.draggedNoteElement = null;
|
||||
this.$container.find('.board-column').removeClass('drag-over');
|
||||
this.cleanupAllDropIndicators();
|
||||
|
||||
// Remove drag preview
|
||||
if ($dragPreview) {
|
||||
$dragPreview.remove();
|
||||
$dragPreview = null;
|
||||
}
|
||||
}
|
||||
isDragging = false;
|
||||
});
|
||||
}
|
||||
|
||||
private createDragPreview($noteEl: JQuery<HTMLElement>, x: number, y: number): JQuery<HTMLElement> {
|
||||
// Clone the note element for the preview
|
||||
const $preview = $noteEl.clone();
|
||||
|
||||
$preview
|
||||
.addClass('board-drag-preview')
|
||||
.css({
|
||||
position: 'fixed',
|
||||
left: x - ($noteEl.outerWidth() || 0) / 2,
|
||||
top: y - ($noteEl.outerHeight() || 0) / 2,
|
||||
pointerEvents: 'none',
|
||||
zIndex: 10000
|
||||
})
|
||||
.appendTo('body');
|
||||
|
||||
return $preview;
|
||||
}
|
||||
|
||||
private showDropIndicator($columnEl: JQuery<HTMLElement>, e: JQuery.DragOverEvent) {
|
||||
const originalEvent = e.originalEvent as DragEvent;
|
||||
const mouseY = originalEvent.clientY;
|
||||
this.showDropIndicatorAtY($columnEl, mouseY);
|
||||
}
|
||||
|
||||
private showDropIndicatorAtPoint($columnEl: JQuery<HTMLElement>, touchY: number) {
|
||||
this.showDropIndicatorAtY($columnEl, touchY);
|
||||
}
|
||||
|
||||
private showDropIndicatorAtY($columnEl: JQuery<HTMLElement>, y: number) {
|
||||
const columnRect = $columnEl[0].getBoundingClientRect();
|
||||
const relativeY = y - columnRect.top;
|
||||
|
||||
// Clean up any existing drop indicators in this column first
|
||||
this.cleanupNoteDropIndicators($columnEl);
|
||||
|
||||
// Create a new drop indicator
|
||||
const $dropIndicator = $("<div>").addClass("board-drop-indicator");
|
||||
|
||||
// Find the best position to insert the note
|
||||
const $notes = this.context.draggedNoteElement ?
|
||||
$columnEl.find(".board-note").not(this.context.draggedNoteElement) :
|
||||
$columnEl.find(".board-note");
|
||||
let insertAfterElement: HTMLElement | null = null;
|
||||
|
||||
$notes.each((_, noteEl) => {
|
||||
const noteRect = noteEl.getBoundingClientRect();
|
||||
const noteMiddle = noteRect.top + noteRect.height / 2 - columnRect.top;
|
||||
|
||||
if (relativeY > noteMiddle) {
|
||||
insertAfterElement = noteEl;
|
||||
}
|
||||
});
|
||||
|
||||
// Position the drop indicator
|
||||
if (insertAfterElement) {
|
||||
$(insertAfterElement).after($dropIndicator);
|
||||
} else {
|
||||
// Insert at the beginning (after the header)
|
||||
const $header = $columnEl.find("h3");
|
||||
$header.after($dropIndicator);
|
||||
}
|
||||
|
||||
$dropIndicator.addClass("show");
|
||||
}
|
||||
|
||||
private async handleNoteDrop($columnEl: JQuery<HTMLElement>, column: string) {
|
||||
const draggedNoteElement = this.context.draggedNoteElement;
|
||||
const draggedNote = this.context.draggedNote;
|
||||
const draggedBranch = this.context.draggedBranch;
|
||||
|
||||
if (draggedNote && draggedNoteElement && draggedBranch) {
|
||||
const currentColumn = draggedNoteElement.attr("data-current-column");
|
||||
|
||||
// Capture drop indicator position BEFORE removing it
|
||||
const dropIndicator = $columnEl.find(".board-drop-indicator.show");
|
||||
let targetBranchId: string | null = null;
|
||||
let moveType: "before" | "after" | null = null;
|
||||
|
||||
if (dropIndicator.length > 0) {
|
||||
// Find the note element that the drop indicator is positioned relative to
|
||||
const nextNote = dropIndicator.next(".board-note");
|
||||
const prevNote = dropIndicator.prev(".board-note");
|
||||
|
||||
if (nextNote.length > 0) {
|
||||
targetBranchId = nextNote.attr("data-branch-id") || null;
|
||||
moveType = "before";
|
||||
} else if (prevNote.length > 0) {
|
||||
targetBranchId = prevNote.attr("data-branch-id") || null;
|
||||
moveType = "after";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Handle column change
|
||||
if (currentColumn !== column) {
|
||||
await this.api.changeColumn(draggedNote.noteId, column);
|
||||
}
|
||||
|
||||
// Handle position change (works for both same column and different column moves)
|
||||
if (targetBranchId && moveType) {
|
||||
if (moveType === "before") {
|
||||
await branchService.moveBeforeBranch([draggedBranch.branchId], targetBranchId);
|
||||
} else if (moveType === "after") {
|
||||
await branchService.moveAfterBranch([draggedBranch.branchId], targetBranchId);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the data attributes
|
||||
draggedNoteElement.attr("data-current-column", column);
|
||||
} catch (error) {
|
||||
console.error("Failed to update note position:", error);
|
||||
} finally {
|
||||
// Always clean up drop indicators after drop operation
|
||||
this.cleanupAllDropIndicators();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private cleanupAllDropIndicators() {
|
||||
// Remove all drop indicators from the DOM to prevent layout issues
|
||||
this.$container.find(".board-drop-indicator").remove();
|
||||
}
|
||||
|
||||
private cleanupNoteDropIndicators($columnEl: JQuery<HTMLElement>) {
|
||||
// Remove note drop indicators from a specific column
|
||||
$columnEl.find(".board-drop-indicator").remove();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import ViewMode, { ViewModeArgs } from "../view_mode.js";
|
||||
import L from "leaflet";
|
||||
import type { GPX, LatLng, LeafletMouseEvent, Map, Marker } from "leaflet";
|
||||
import type { GPX, LatLng, Layer, LeafletMouseEvent, Map, Marker } from "leaflet";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
@@ -10,6 +10,8 @@ import toast from "../../../services/toast.js";
|
||||
import { CommandListenerData, EventData } from "../../../components/app_context.js";
|
||||
import { createNewNote, moveMarker, setupDragging } from "./editing.js";
|
||||
import { openMapContextMenu } from "./context_menu.js";
|
||||
import attributes from "../../../services/attributes.js";
|
||||
import { DEFAULT_MAP_LAYER_NAME, MAP_LAYERS } from "./map_layer.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="geo-view">
|
||||
@@ -83,6 +85,11 @@ const TPL = /*html*/`
|
||||
white-space: no-wrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.geo-map-container.dark .leaflet-div-icon .title-label {
|
||||
color: white;
|
||||
text-shadow: -1px -1px 0 black, 1px -1px 0 black, -1px 1px 0 black, 1px 1px 0 black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="geo-map-container"></div>
|
||||
@@ -138,10 +145,32 @@ export default class GeoView extends ViewMode<MapData> {
|
||||
const map = L.map(this.$container[0], {
|
||||
worldCopyJump: true
|
||||
});
|
||||
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
detectRetina: true
|
||||
}).addTo(map);
|
||||
|
||||
const layerName = this.parentNote.getLabelValue("map:style") ?? DEFAULT_MAP_LAYER_NAME;
|
||||
let layer: Layer;
|
||||
const layerData = MAP_LAYERS[layerName];
|
||||
|
||||
if (layerData.type === "vector") {
|
||||
const style = (typeof layerData.style === "string" ? layerData.style : await layerData.style());
|
||||
await import("@maplibre/maplibre-gl-leaflet");
|
||||
|
||||
layer = L.maplibreGL({
|
||||
style: style as any
|
||||
});
|
||||
} else {
|
||||
layer = L.tileLayer(layerData.url, {
|
||||
attribution: layerData.attribution,
|
||||
detectRetina: true
|
||||
});
|
||||
}
|
||||
|
||||
if (this.parentNote.hasLabel("map:scale")) {
|
||||
L.control.scale().addTo(map);
|
||||
}
|
||||
|
||||
this.$container.toggleClass("dark", !!layerData.isDarkTheme);
|
||||
|
||||
layer.addTo(map);
|
||||
|
||||
this.map = map;
|
||||
|
||||
@@ -261,9 +290,14 @@ export default class GeoView extends ViewMode<MapData> {
|
||||
// If any of note has its location attribute changed.
|
||||
// TODO: Should probably filter by parent here as well.
|
||||
const attributeRows = loadResults.getAttributeRows();
|
||||
if (attributeRows.find((at) => [LOCATION_ATTRIBUTE, "color"].includes(at.name ?? ""))) {
|
||||
if (attributeRows.find((at) => [LOCATION_ATTRIBUTE, "color", "iconClass"].includes(at.name ?? ""))) {
|
||||
this.#reloadMarkers();
|
||||
}
|
||||
|
||||
// Full reload if map layer is changed.
|
||||
if (loadResults.getAttributeRows().some(attr => (attr.name?.startsWith("map:") && attributes.isAffecting(attr, this.parentNote)))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
async geoMapCreateChildNoteEvent({ ntxId }: EventData<"geoMapCreateChildNote">) {
|
||||
@@ -330,3 +364,4 @@ export default class GeoView extends ViewMode<MapData> {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
53
apps/client/src/widgets/view_widgets/geo_view/map_layer.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
export interface MapLayer {
|
||||
name: string;
|
||||
isDarkTheme?: boolean;
|
||||
}
|
||||
|
||||
interface VectorLayer extends MapLayer {
|
||||
type: "vector";
|
||||
style: string | (() => Promise<{}>)
|
||||
}
|
||||
|
||||
interface RasterLayer extends MapLayer {
|
||||
type: "raster";
|
||||
url: string;
|
||||
attribution: string;
|
||||
}
|
||||
|
||||
export const MAP_LAYERS: Record<string, VectorLayer | RasterLayer> = {
|
||||
"openstreetmap": {
|
||||
name: "OpenStreetMap",
|
||||
type: "raster",
|
||||
url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
},
|
||||
"versatiles-colorful": {
|
||||
name: "VersaTiles Colorful",
|
||||
type: "vector",
|
||||
style: async () => (await import("./styles/colorful/en.json")).default
|
||||
},
|
||||
"versatiles-eclipse": {
|
||||
name: "VersaTiles Eclipse",
|
||||
type: "vector",
|
||||
style: async () => (await import("./styles/eclipse/en.json")).default,
|
||||
isDarkTheme: true
|
||||
},
|
||||
"versatiles-graybeard": {
|
||||
name: "VersaTiles Graybeard",
|
||||
type: "vector",
|
||||
style: async () => (await import("./styles/graybeard/en.json")).default
|
||||
},
|
||||
"versatiles-neutrino": {
|
||||
name: "VersaTiles Neutrino",
|
||||
type: "vector",
|
||||
style: async () => (await import("./styles/neutrino/en.json")).default
|
||||
},
|
||||
"versatiles-shadow": {
|
||||
name: "VersaTiles Shadow",
|
||||
type: "vector",
|
||||
style: async () => (await import("./styles/shadow/en.json")).default,
|
||||
isDarkTheme: true
|
||||
}
|
||||
};
|
||||
|
||||
export const DEFAULT_MAP_LAYER_NAME: keyof typeof MAP_LAYERS = "versatiles-colorful";
|
||||
4811
apps/client/src/widgets/view_widgets/geo_view/styles/eclipse/de.json
Normal file
4811
apps/client/src/widgets/view_widgets/geo_view/styles/eclipse/en.json
Normal file
4811
apps/client/src/widgets/view_widgets/geo_view/styles/shadow/de.json
Normal file
4811
apps/client/src/widgets/view_widgets/geo_view/styles/shadow/en.json
Normal file
@@ -2,30 +2,30 @@ import { executeBulkActions } from "../../../services/bulk_action.js";
|
||||
|
||||
export async function renameColumn(parentNoteId: string, type: "label" | "relation", originalName: string, newName: string) {
|
||||
if (type === "label") {
|
||||
return executeBulkActions(parentNoteId, [{
|
||||
return executeBulkActions([parentNoteId], [{
|
||||
name: "renameLabel",
|
||||
oldLabelName: originalName,
|
||||
newLabelName: newName
|
||||
}]);
|
||||
}], true);
|
||||
} else {
|
||||
return executeBulkActions(parentNoteId, [{
|
||||
return executeBulkActions([parentNoteId], [{
|
||||
name: "renameRelation",
|
||||
oldRelationName: originalName,
|
||||
newRelationName: newName
|
||||
}]);
|
||||
}], true);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteColumn(parentNoteId: string, type: "label" | "relation", columnName: string) {
|
||||
if (type === "label") {
|
||||
return executeBulkActions(parentNoteId, [{
|
||||
return executeBulkActions([parentNoteId], [{
|
||||
name: "deleteLabel",
|
||||
labelName: columnName
|
||||
}]);
|
||||
}], true);
|
||||
} else {
|
||||
return executeBulkActions(parentNoteId, [{
|
||||
return executeBulkActions([parentNoteId], [{
|
||||
name: "deleteRelation",
|
||||
relationName: columnName
|
||||
}]);
|
||||
}], true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,15 @@ const labelTypeMappings: Record<ColumnType, Partial<ColumnDefinition>> = {
|
||||
formatter: "link",
|
||||
editor: "input"
|
||||
},
|
||||
color: {
|
||||
editor: "input",
|
||||
formatter: "color",
|
||||
editorParams: {
|
||||
elementAttributes: {
|
||||
type: "color"
|
||||
}
|
||||
}
|
||||
},
|
||||
relation: {
|
||||
editor: RelationEditor,
|
||||
formatter: NoteFormatter
|
||||
|
||||
18
apps/client/src/widgets/widget_utils.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import utils from "../services/utils.js";
|
||||
|
||||
/**
|
||||
* Enables scrolling of a container horizontally using the mouse wheel, instead of having to use the scrollbar or keep Shift pressed.
|
||||
*
|
||||
* @param $container the jQuery-wrapped container element to enable horizontal scrolling for.
|
||||
*/
|
||||
export function setupHorizontalScrollViaWheel($container: JQuery<HTMLElement>) {
|
||||
$container.on("wheel", (event) => {
|
||||
const wheelEvent = event.originalEvent as WheelEvent;
|
||||
if (utils.isCtrlKey(event) || event.altKey || event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
event.currentTarget.scrollLeft += wheelEvent.deltaY + wheelEvent.deltaX;
|
||||
});
|
||||
}
|
||||
@@ -32,7 +32,8 @@
|
||||
"eslint.config.mjs"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
"src/**/*.ts",
|
||||
"src/**/*.json"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"dotenv": "17.2.0",
|
||||
"electron": "37.2.3"
|
||||
"dotenv": "17.2.1",
|
||||
"electron": "37.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"@types/electron-squirrel-startup": "1.0.2",
|
||||
"@triliumnext/server": "workspace:*",
|
||||
"copy-webpack-plugin": "13.0.0",
|
||||
"electron": "37.2.3",
|
||||
"electron": "37.2.4",
|
||||
"@electron-forge/cli": "7.8.1",
|
||||
"@electron-forge/maker-deb": "7.8.1",
|
||||
"@electron-forge/maker-dmg": "7.8.1",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"@triliumnext/desktop": "workspace:*",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"copy-webpack-plugin": "13.0.0",
|
||||
"electron": "37.2.3",
|
||||
"electron": "37.2.4",
|
||||
"fs-extra": "11.3.0"
|
||||
},
|
||||
"nx": {
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"dotenv": "17.2.0"
|
||||
"dotenv": "17.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,27 +39,27 @@
|
||||
"@types/ws": "8.18.1",
|
||||
"@types/xml2js": "0.4.14",
|
||||
"express-http-proxy": "2.1.1",
|
||||
"@anthropic-ai/sdk": "0.56.0",
|
||||
"@anthropic-ai/sdk": "0.57.0",
|
||||
"@braintree/sanitize-url": "7.1.1",
|
||||
"@triliumnext/commons": "workspace:*",
|
||||
"@triliumnext/express-partial-content": "workspace:*",
|
||||
"@triliumnext/turndown-plugin-gfm": "workspace:*",
|
||||
"archiver": "7.0.1",
|
||||
"async-mutex": "0.5.0",
|
||||
"axios": "1.10.0",
|
||||
"axios": "1.11.0",
|
||||
"bindings": "1.5.0",
|
||||
"chardet": "2.1.0",
|
||||
"cheerio": "1.1.0",
|
||||
"cheerio": "1.1.2",
|
||||
"chokidar": "4.0.3",
|
||||
"cls-hooked": "4.2.2",
|
||||
"compression": "1.8.1",
|
||||
"cookie-parser": "1.4.7",
|
||||
"csrf-csrf": "3.2.2",
|
||||
"csrf-csrf": "4.0.2",
|
||||
"dayjs": "1.11.13",
|
||||
"debounce": "2.2.0",
|
||||
"debug": "4.4.1",
|
||||
"ejs": "3.1.10",
|
||||
"electron": "37.2.3",
|
||||
"electron": "37.2.4",
|
||||
"electron-debug": "4.1.0",
|
||||
"electron-window-state": "5.0.3",
|
||||
"escape-html": "1.0.3",
|
||||
@@ -88,7 +88,7 @@
|
||||
"multer": "2.0.2",
|
||||
"normalize-strings": "1.1.1",
|
||||
"ollama": "0.5.16",
|
||||
"openai": "5.10.1",
|
||||
"openai": "5.10.2",
|
||||
"rand-token": "1.0.1",
|
||||
"safe-compare": "1.1.4",
|
||||
"sanitize-filename": "1.6.3",
|
||||
@@ -98,7 +98,7 @@
|
||||
"stream-throttle": "0.1.3",
|
||||
"strip-bom": "5.0.0",
|
||||
"striptags": "3.2.0",
|
||||
"supertest": "7.1.3",
|
||||
"supertest": "7.1.4",
|
||||
"swagger-jsdoc": "6.2.8",
|
||||
"swagger-ui-express": "5.0.1",
|
||||
"time2fa": "^1.3.0",
|
||||
|
||||
2
apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json
generated
vendored
@@ -52,334 +52,332 @@
|
||||
are multiple labels with the same prefix, consult the specific page linked
|
||||
in the description of that label for more information.</p>
|
||||
</aside>
|
||||
<figure class="table" style="width:100%;">
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:33.82%;">
|
||||
<col style="width:66.18%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Label</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>disableVersioning</code>
|
||||
</td>
|
||||
<td>Disables automatic creation of <a class="reference-link" href="#root/_help_vZWERwf8U3nx">Note Revisions</a> for
|
||||
a particular note. Useful for e.g. large, but unimportant notes - e.g.
|
||||
large JS libraries used for scripting.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>versioningLimit</code>
|
||||
</td>
|
||||
<td>Limits the maximum number of <a class="reference-link" href="#root/_help_vZWERwf8U3nx">Note Revisions</a> for
|
||||
a particular note, overriding the global settings.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>calendarRoot</code>
|
||||
</td>
|
||||
<td>Marks the note which should be used as root for <a class="reference-link"
|
||||
href="#root/_help_l0tKav7yLHGF">Day Notes</a>. Only one should be marked
|
||||
as such.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>archived</code>
|
||||
</td>
|
||||
<td>Hides notes from default search results and dialogs. Archived notes can
|
||||
optionally be hidden in the <a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>excludeFromExport</code>
|
||||
</td>
|
||||
<td>Excludes this note and its children when exporting.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>run</code>, <code>runOnInstance</code>, <code>runAtHour</code>
|
||||
</td>
|
||||
<td>See <a class="reference-link" href="#root/_help_GPERMystNGTB">Events</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>disableInclusion</code>
|
||||
</td>
|
||||
<td>Scripts with this label won't be included into parent script execution.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>sorted</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>Keeps child notes sorted by title alphabetically.</p>
|
||||
<p>When given a value, it will sort by the value of another label instead.
|
||||
If one of the child notes doesn't have the specified label, the title will
|
||||
be used for them instead.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>sortDirection</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>If <code>sorted</code> is applied, specifies the direction of the sort:</p>
|
||||
<ul>
|
||||
<li><code>ASC</code>, ascending (default)</li>
|
||||
<li><code>DESC</code>, descending</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>sortFoldersFirst</code>
|
||||
</td>
|
||||
<td>If <code>sorted</code> is applied, folders (notes with children) will be
|
||||
sorted as a group at the top, and the rest will be sorted.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>top</code>
|
||||
</td>
|
||||
<td>If <code>sorted</code> is applied to the parent note, keeps given note on
|
||||
top in its parent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>hidePromotedAttributes</code>
|
||||
</td>
|
||||
<td>Hide <a class="reference-link" href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a> on
|
||||
this note. Generally useful when defining inherited attributes, but the
|
||||
parent note doesn't need them.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>readOnly</code>
|
||||
</td>
|
||||
<td>Marks a note to be always be <a href="#root/_help_CoFPLs3dRlXc">read-only</a>,
|
||||
if it's a supported note (text, code, mermaid).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>autoReadOnlyDisabled</code>
|
||||
</td>
|
||||
<td>Disables automatic <a href="#root/_help_CoFPLs3dRlXc">read-only mode</a> for
|
||||
the given note.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>appCss</code>
|
||||
</td>
|
||||
<td>Marks CSS notes which are loaded into the Trilium application and can
|
||||
thus be used to modify Trilium's looks. See <a class="reference-link"
|
||||
href="#root/_help_AlhDUqhENtH7">Custom app-wide CSS</a> for more info.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>appTheme</code>
|
||||
</td>
|
||||
<td>Marks CSS notes which are full Trilium themes and are thus available in
|
||||
Trilium options. See <a class="reference-link" href="#root/_help_pKK96zzmvBGf">Theme development</a> for
|
||||
more information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>appThemeBase</code>
|
||||
</td>
|
||||
<td>Set to <code>next</code>, <code>next-light</code>, or <code>next-dark</code> to
|
||||
use the corresponding TriliumNext theme (auto, light or dark) as the base
|
||||
for a custom theme, instead of the legacy one. See <a class="reference-link"
|
||||
href="#root/_help_WFGzWeUK6arS">Customize the Next theme</a> for more
|
||||
information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>cssClass</code>
|
||||
</td>
|
||||
<td>Value of this label is then added as CSS class to the node representing
|
||||
given note in the <a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.
|
||||
This can be useful for advanced theming. Can be used in template notes.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>iconClass</code>
|
||||
</td>
|
||||
<td>value of this label is added as a CSS class to the icon on the tree which
|
||||
can help visually distinguish the notes in the tree. Example might be bx
|
||||
bx-home - icons are taken from boxicons. Can be used in template notes.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>pageSize</code>
|
||||
</td>
|
||||
<td>Specifies the number of items per page in <a class="reference-link"
|
||||
href="#root/_help_0ESUbbAxVnoK">Note List</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>customRequestHandler</code>
|
||||
</td>
|
||||
<td>See <a class="reference-link" href="#root/_help_J5Ex1ZrMbyJ6">Custom Request Handler</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>customResourceProvider</code>
|
||||
</td>
|
||||
<td>See <a class="reference-link" href="#root/_help_d3fAXQ2diepH">Custom Resource Providers</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>widget</code>
|
||||
</td>
|
||||
<td>Marks this note as a custom widget which will be added to the Trilium
|
||||
component tree. See <a class="reference-link" href="#root/_help_MgibgPcfeuGz">Custom Widgets</a> for
|
||||
more information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>searchHome</code>
|
||||
</td>
|
||||
<td>New search notes will be created as children of this note (see
|
||||
<a
|
||||
class="reference-link" href="#root/_help_m523cpzocqaD">Saved Search</a>).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspace</code> and related attributes</td>
|
||||
<td>See <a class="reference-link" href="#root/_help_9sRHySam5fXb">Workspaces</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>inbox</code>
|
||||
</td>
|
||||
<td>default inbox location for new notes - when you create a note using <em>new note</em> button
|
||||
in the sidebar, notes will be created as child notes in the note marked
|
||||
as with <code>#inbox</code> label.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>sqlConsoleHome</code>
|
||||
</td>
|
||||
<td>Default location of <a class="reference-link" href="#root/_hidden/_help/_help_tC7s2alapj8V/_help_wX4HbRucYSDD/_help_oyIAJ9PvvwHX/_help__help_YKWqdJhzi2VY">SQL Console</a> notes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>bookmarked</code>
|
||||
</td>
|
||||
<td>Indicates this note is a <a href="#root/_help_u3YFHC9tQlpm">bookmark</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>bookmarkFolder</code>
|
||||
</td>
|
||||
<td>Note with this label will appear in bookmarks as folder (allowing access
|
||||
to its children). See <a class="reference-link" href="#root/_help_u3YFHC9tQlpm">Bookmarks</a> for
|
||||
more information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>share*</code>
|
||||
</td>
|
||||
<td>See the attribute reference in <a class="reference-link" href="#root/_help_R9pX4DGra2Vt">Sharing</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>displayRelations</code>, <code>hideRelations</code>
|
||||
</td>
|
||||
<td>Comma delimited names of relations which should be displayed/hidden in
|
||||
a <a class="reference-link" href="#root/_help_iRwzGnHPzonm">Relation Map</a> (both
|
||||
the note type and the <a class="reference-link" href="#root/_help_BCkXAVs63Ttv">Note Map (Link map, Tree map)</a> general
|
||||
functionality).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>titleTemplate</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>Default title of notes created as children of this note. This value is
|
||||
evaluated as a JavaScript string and thus can be enriched with dynamic
|
||||
content via the injected <code>now</code> and <code>parentNote</code> variables.</p>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li><code><span class="math-tex">\({parentNote.getLabel('authorName')}'s literary works</span></code>
|
||||
</li>
|
||||
<li><code>Log for \){now.format('YYYY-MM-DD HH:mm:ss')}</code>
|
||||
</li>
|
||||
<li>to mirror the parent's template.</li>
|
||||
</ul>
|
||||
<p>See <a class="reference-link" href="#root/_help_47ZrP6FNuoG8">Default Note Title</a> for
|
||||
more info.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>template</code>
|
||||
</td>
|
||||
<td>This note will appear in the selection of available template when creating
|
||||
new note. See <a class="reference-link" href="#root/_help_KC1HB96bqqHX">Templates</a> for
|
||||
more information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>toc</code>
|
||||
</td>
|
||||
<td>Controls the display of the <a class="reference-link" href="#root/_help_BFvAtE74rbP6">Table of contents</a> for
|
||||
a given note. <code>#toc</code> or <code>#toc=show</code> to always display
|
||||
the table of contents, <code>#toc=false</code> to always hide it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>color</code>
|
||||
</td>
|
||||
<td>defines color of the note in note tree, links etc. Use any valid CSS color
|
||||
value like 'red' or #a13d5f</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>keyboardShortcut</code>
|
||||
</td>
|
||||
<td>Defines a keyboard shortcut which will immediately jump to this note.
|
||||
Example: 'ctrl+alt+e'. Requires frontend reload for the change to take
|
||||
effect.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>keepCurrentHoisting</code>
|
||||
</td>
|
||||
<td>Opening this link won't change hoisting even if the note is not displayable
|
||||
in the current hoisted subtree.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>executeButton</code>
|
||||
</td>
|
||||
<td>Title of the button which will execute the current code note</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>executeDescription</code>
|
||||
</td>
|
||||
<td>Longer description of the current code note displayed together with the
|
||||
execute button</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>excludeFromNoteMap</code>
|
||||
</td>
|
||||
<td>Notes with this label will be hidden from the <a class="reference-link"
|
||||
href="#root/_help_bdUJEHsAPYQR">Note Map</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>newNotesOnTop</code>
|
||||
</td>
|
||||
<td>New notes will be created at the top of the parent note, not on the bottom.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>hideHighlightWidget</code>
|
||||
</td>
|
||||
<td>Hides the <a class="reference-link" href="#root/_help_AxshuNRegLAv">Highlights list</a> widget</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>hideChildrenOverview</code>
|
||||
</td>
|
||||
<td>Hides the <a class="reference-link" href="#root/_help_0ESUbbAxVnoK">Note List</a> for
|
||||
that particular note.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>printLandscape</code>
|
||||
</td>
|
||||
<td>When exporting to PDF, changes the orientation of the page to landscape
|
||||
instead of portrait.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>printPageSize</code>
|
||||
</td>
|
||||
<td>When exporting to PDF, changes the size of the page. Supported values: <code>A0</code>, <code>A1</code>, <code>A2</code>, <code>A3</code>, <code>A4</code>, <code>A5</code>, <code>A6</code>, <code>Legal</code>, <code>Letter</code>, <code>Tabloid</code>, <code>Ledger</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>geolocation</code>
|
||||
</td>
|
||||
<td>Indicates the latitude and longitude of a note, to be displayed in a
|
||||
<a
|
||||
class="reference-link" href="#root/_help_81SGnPGMk7Xc">Geo Map</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>calendar:*</code>
|
||||
</td>
|
||||
<td>Defines specific options for the <a class="reference-link" href="#root/_help_xWbu3jpNWapp">Calendar View</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>viewType</code>
|
||||
</td>
|
||||
<td>Sets the view of child notes (e.g. grid or list). See <a class="reference-link"
|
||||
href="#root/_help_0ESUbbAxVnoK">Note List</a> for more information.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:33.82%;">
|
||||
<col style="width:66.18%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Label</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>disableVersioning</code>
|
||||
</td>
|
||||
<td>Disables automatic creation of <a class="reference-link" href="#root/_help_vZWERwf8U3nx">Note Revisions</a> for
|
||||
a particular note. Useful for e.g. large, but unimportant notes - e.g.
|
||||
large JS libraries used for scripting.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>versioningLimit</code>
|
||||
</td>
|
||||
<td>Limits the maximum number of <a class="reference-link" href="#root/_help_vZWERwf8U3nx">Note Revisions</a> for
|
||||
a particular note, overriding the global settings.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>calendarRoot</code>
|
||||
</td>
|
||||
<td>Marks the note which should be used as root for <a class="reference-link"
|
||||
href="#root/_help_l0tKav7yLHGF">Day Notes</a>. Only one should be marked
|
||||
as such.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>archived</code>
|
||||
</td>
|
||||
<td>Hides notes from default search results and dialogs. Archived notes can
|
||||
optionally be hidden in the <a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>excludeFromExport</code>
|
||||
</td>
|
||||
<td>Excludes this note and its children when exporting.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>run</code>, <code>runOnInstance</code>, <code>runAtHour</code>
|
||||
</td>
|
||||
<td>See <a class="reference-link" href="#root/_help_GPERMystNGTB">Events</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>disableInclusion</code>
|
||||
</td>
|
||||
<td>Scripts with this label won't be included into parent script execution.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>sorted</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>Keeps child notes sorted by title alphabetically.</p>
|
||||
<p>When given a value, it will sort by the value of another label instead.
|
||||
If one of the child notes doesn't have the specified label, the title will
|
||||
be used for them instead.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>sortDirection</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>If <code>sorted</code> is applied, specifies the direction of the sort:</p>
|
||||
<ul>
|
||||
<li><code>ASC</code>, ascending (default)</li>
|
||||
<li><code>DESC</code>, descending</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>sortFoldersFirst</code>
|
||||
</td>
|
||||
<td>If <code>sorted</code> is applied, folders (notes with children) will be
|
||||
sorted as a group at the top, and the rest will be sorted.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>top</code>
|
||||
</td>
|
||||
<td>If <code>sorted</code> is applied to the parent note, keeps given note on
|
||||
top in its parent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>hidePromotedAttributes</code>
|
||||
</td>
|
||||
<td>Hide <a class="reference-link" href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a> on
|
||||
this note. Generally useful when defining inherited attributes, but the
|
||||
parent note doesn't need them.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>readOnly</code>
|
||||
</td>
|
||||
<td>Marks a note to be always be <a href="#root/_help_CoFPLs3dRlXc">read-only</a>,
|
||||
if it's a supported note (text, code, mermaid).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>autoReadOnlyDisabled</code>
|
||||
</td>
|
||||
<td>Disables automatic <a href="#root/_help_CoFPLs3dRlXc">read-only mode</a> for
|
||||
the given note.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>appCss</code>
|
||||
</td>
|
||||
<td>Marks CSS notes which are loaded into the Trilium application and can
|
||||
thus be used to modify Trilium's looks. See <a class="reference-link"
|
||||
href="#root/_help_AlhDUqhENtH7">Custom app-wide CSS</a> for more info.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>appTheme</code>
|
||||
</td>
|
||||
<td>Marks CSS notes which are full Trilium themes and are thus available in
|
||||
Trilium options. See <a class="reference-link" href="#root/_help_pKK96zzmvBGf">Theme development</a> for
|
||||
more information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>appThemeBase</code>
|
||||
</td>
|
||||
<td>Set to <code>next</code>, <code>next-light</code>, or <code>next-dark</code> to
|
||||
use the corresponding TriliumNext theme (auto, light or dark) as the base
|
||||
for a custom theme, instead of the legacy one. See <a class="reference-link"
|
||||
href="#root/_help_WFGzWeUK6arS">Customize the Next theme</a> for more
|
||||
information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>cssClass</code>
|
||||
</td>
|
||||
<td>Value of this label is then added as CSS class to the node representing
|
||||
given note in the <a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.
|
||||
This can be useful for advanced theming. Can be used in template notes.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>iconClass</code>
|
||||
</td>
|
||||
<td>value of this label is added as a CSS class to the icon on the tree which
|
||||
can help visually distinguish the notes in the tree. Example might be bx
|
||||
bx-home - icons are taken from boxicons. Can be used in template notes.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>pageSize</code>
|
||||
</td>
|
||||
<td>Specifies the number of items per page in <a class="reference-link"
|
||||
href="#root/_help_0ESUbbAxVnoK">Note List</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>customRequestHandler</code>
|
||||
</td>
|
||||
<td>See <a class="reference-link" href="#root/_help_J5Ex1ZrMbyJ6">Custom Request Handler</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>customResourceProvider</code>
|
||||
</td>
|
||||
<td>See <a class="reference-link" href="#root/_help_d3fAXQ2diepH">Custom Resource Providers</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>widget</code>
|
||||
</td>
|
||||
<td>Marks this note as a custom widget which will be added to the Trilium
|
||||
component tree. See <a class="reference-link" href="#root/_help_MgibgPcfeuGz">Custom Widgets</a> for
|
||||
more information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>searchHome</code>
|
||||
</td>
|
||||
<td>New search notes will be created as children of this note (see
|
||||
<a
|
||||
class="reference-link" href="#root/_help_m523cpzocqaD">Saved Search</a>).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspace</code> and related attributes</td>
|
||||
<td>See <a class="reference-link" href="#root/_help_9sRHySam5fXb">Workspaces</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>inbox</code>
|
||||
</td>
|
||||
<td>default inbox location for new notes - when you create a note using <em>new note</em> button
|
||||
in the sidebar, notes will be created as child notes in the note marked
|
||||
as with <code>#inbox</code> label.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>sqlConsoleHome</code>
|
||||
</td>
|
||||
<td>Default location of <a class="reference-link" href="#root/_hidden/_help/_help_tC7s2alapj8V/_help_wX4HbRucYSDD/_help_oyIAJ9PvvwHX/_help__help_YKWqdJhzi2VY">SQL Console</a> notes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>bookmarked</code>
|
||||
</td>
|
||||
<td>Indicates this note is a <a href="#root/_help_u3YFHC9tQlpm">bookmark</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>bookmarkFolder</code>
|
||||
</td>
|
||||
<td>Note with this label will appear in bookmarks as folder (allowing access
|
||||
to its children). See <a class="reference-link" href="#root/_help_u3YFHC9tQlpm">Bookmarks</a> for
|
||||
more information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>share*</code>
|
||||
</td>
|
||||
<td>See the attribute reference in <a class="reference-link" href="#root/_help_R9pX4DGra2Vt">Sharing</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>displayRelations</code>, <code>hideRelations</code>
|
||||
</td>
|
||||
<td>Comma delimited names of relations which should be displayed/hidden in
|
||||
a <a class="reference-link" href="#root/_help_iRwzGnHPzonm">Relation Map</a> (both
|
||||
the note type and the <a class="reference-link" href="#root/_help_BCkXAVs63Ttv">Note Map (Link map, Tree map)</a> general
|
||||
functionality).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>titleTemplate</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>Default title of notes created as children of this note. This value is
|
||||
evaluated as a JavaScript string and thus can be enriched with dynamic
|
||||
content via the injected <code>now</code> and <code>parentNote</code> variables.</p>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li><code><span class="math-tex">\({parentNote.getLabel('authorName')}'s literary works</span></code>
|
||||
</li>
|
||||
<li><code>Log for \){now.format('YYYY-MM-DD HH:mm:ss')}</code>
|
||||
</li>
|
||||
<li>to mirror the parent's template.</li>
|
||||
</ul>
|
||||
<p>See <a class="reference-link" href="#root/_help_47ZrP6FNuoG8">Default Note Title</a> for
|
||||
more info.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>template</code>
|
||||
</td>
|
||||
<td>This note will appear in the selection of available template when creating
|
||||
new note. See <a class="reference-link" href="#root/_help_KC1HB96bqqHX">Templates</a> for
|
||||
more information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>toc</code>
|
||||
</td>
|
||||
<td>Controls the display of the <a class="reference-link" href="#root/_help_BFvAtE74rbP6">Table of contents</a> for
|
||||
a given note. <code>#toc</code> or <code>#toc=show</code> to always display
|
||||
the table of contents, <code>#toc=false</code> to always hide it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>color</code>
|
||||
</td>
|
||||
<td>defines color of the note in note tree, links etc. Use any valid CSS color
|
||||
value like 'red' or #a13d5f</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>keyboardShortcut</code>
|
||||
</td>
|
||||
<td>Defines a keyboard shortcut which will immediately jump to this note.
|
||||
Example: 'ctrl+alt+e'. Requires frontend reload for the change to take
|
||||
effect.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>keepCurrentHoisting</code>
|
||||
</td>
|
||||
<td>Opening this link won't change hoisting even if the note is not displayable
|
||||
in the current hoisted subtree.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>executeButton</code>
|
||||
</td>
|
||||
<td>Title of the button which will execute the current code note</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>executeDescription</code>
|
||||
</td>
|
||||
<td>Longer description of the current code note displayed together with the
|
||||
execute button</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>excludeFromNoteMap</code>
|
||||
</td>
|
||||
<td>Notes with this label will be hidden from the <a class="reference-link"
|
||||
href="#root/_help_bdUJEHsAPYQR">Note Map</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>newNotesOnTop</code>
|
||||
</td>
|
||||
<td>New notes will be created at the top of the parent note, not on the bottom.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>hideHighlightWidget</code>
|
||||
</td>
|
||||
<td>Hides the <a class="reference-link" href="#root/_help_AxshuNRegLAv">Highlights list</a> widget</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>hideChildrenOverview</code>
|
||||
</td>
|
||||
<td>Hides the <a class="reference-link" href="#root/_help_0ESUbbAxVnoK">Note List</a> for
|
||||
that particular note.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>printLandscape</code>
|
||||
</td>
|
||||
<td>When exporting to PDF, changes the orientation of the page to landscape
|
||||
instead of portrait.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>printPageSize</code>
|
||||
</td>
|
||||
<td>When exporting to PDF, changes the size of the page. Supported values: <code>A0</code>, <code>A1</code>, <code>A2</code>, <code>A3</code>, <code>A4</code>, <code>A5</code>, <code>A6</code>, <code>Legal</code>, <code>Letter</code>, <code>Tabloid</code>, <code>Ledger</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>geolocation</code>
|
||||
</td>
|
||||
<td>Indicates the latitude and longitude of a note, to be displayed in a
|
||||
<a
|
||||
class="reference-link" href="#root/_help_81SGnPGMk7Xc">Geo Map</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>calendar:*</code>
|
||||
</td>
|
||||
<td>Defines specific options for the <a class="reference-link" href="#root/_help_xWbu3jpNWapp">Calendar View</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>viewType</code>
|
||||
</td>
|
||||
<td>Sets the view of child notes (e.g. grid or list). See <a class="reference-link"
|
||||
href="#root/_help_0ESUbbAxVnoK">Note List</a> for more information.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -61,79 +61,73 @@ class="admonition tip">
|
||||
there are multiple relations with the same prefix, consult the specific
|
||||
page linked in the description of that relation for more information.</p>
|
||||
</aside>
|
||||
<figure class="table" style="width:100%;">
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:33.95%;">
|
||||
<col style="width:66.05%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Label</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>runOn*</code>
|
||||
</td>
|
||||
<td>See <a class="reference-link" href="#root/_help_GPERMystNGTB">Events</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>template</code>
|
||||
</td>
|
||||
<td>note's attributes will be inherited even without a parent-child relationship,
|
||||
note's content and subtree will be added to instance notes if empty. See
|
||||
documentation for details.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>inherit</code>
|
||||
</td>
|
||||
<td>note's attributes will be inherited even without a parent-child relationship.
|
||||
See <a class="reference-link" href="#root/_help_KC1HB96bqqHX">Templates</a> for
|
||||
a similar concept. See <a class="reference-link" href="#root/_help_bwZpz2ajCEwO">Attribute Inheritance</a> in
|
||||
the documentation.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>renderNote</code>
|
||||
</td>
|
||||
<td>notes of type <a class="reference-link" href="#root/_help_HcABDtFCkbFN">Render Note</a> will
|
||||
be rendered using a code note (HTML or script) and it is necessary to point
|
||||
using this relation to which note should be rendered</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>widget_relation</code>
|
||||
</td>
|
||||
<td>target of this relation will be executed and rendered as a widget in the
|
||||
sidebar</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareCss</code>
|
||||
</td>
|
||||
<td>CSS note which will be injected into the share page. CSS note must be
|
||||
in the shared sub-tree as well. Consider using <code>share_hidden_from_tree</code> and <code>share_omit_default_css</code> as
|
||||
well.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareJs</code>
|
||||
</td>
|
||||
<td>JavaScript note which will be injected into the share page. JS note must
|
||||
be in the shared sub-tree as well. Consider using <code>share_hidden_from_tree</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareTemplate</code>
|
||||
</td>
|
||||
<td>Embedded JavaScript note that will be used as the template for displaying
|
||||
the shared note. Falls back to the default template. Consider using <code>share_hidden_from_tree</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareFavicon</code>
|
||||
</td>
|
||||
<td>Favicon note to be set in the shared page. Typically you want to set it
|
||||
to share root and make it inheritable. Favicon note must be in the shared
|
||||
sub-tree as well. Consider using <code>share_hidden_from_tree</code>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Label</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>runOn*</code>
|
||||
</td>
|
||||
<td>See <a class="reference-link" href="#root/_help_GPERMystNGTB">Events</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>template</code>
|
||||
</td>
|
||||
<td>note's attributes will be inherited even without a parent-child relationship,
|
||||
note's content and subtree will be added to instance notes if empty. See
|
||||
documentation for details.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>inherit</code>
|
||||
</td>
|
||||
<td>note's attributes will be inherited even without a parent-child relationship.
|
||||
See <a class="reference-link" href="#root/_help_KC1HB96bqqHX">Templates</a> for
|
||||
a similar concept. See <a class="reference-link" href="#root/_help_bwZpz2ajCEwO">Attribute Inheritance</a> in
|
||||
the documentation.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>renderNote</code>
|
||||
</td>
|
||||
<td>notes of type <a class="reference-link" href="#root/_help_HcABDtFCkbFN">Render Note</a> will
|
||||
be rendered using a code note (HTML or script) and it is necessary to point
|
||||
using this relation to which note should be rendered</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>widget_relation</code>
|
||||
</td>
|
||||
<td>target of this relation will be executed and rendered as a widget in the
|
||||
sidebar</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareCss</code>
|
||||
</td>
|
||||
<td>CSS note which will be injected into the share page. CSS note must be
|
||||
in the shared sub-tree as well. Consider using <code>share_hidden_from_tree</code> and <code>share_omit_default_css</code> as
|
||||
well.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareJs</code>
|
||||
</td>
|
||||
<td>JavaScript note which will be injected into the share page. JS note must
|
||||
be in the shared sub-tree as well. Consider using <code>share_hidden_from_tree</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareTemplate</code>
|
||||
</td>
|
||||
<td>Embedded JavaScript note that will be used as the template for displaying
|
||||
the shared note. Falls back to the default template. Consider using <code>share_hidden_from_tree</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareFavicon</code>
|
||||
</td>
|
||||
<td>Favicon note to be set in the shared page. Typically you want to set it
|
||||
to share root and make it inheritable. Favicon note must be in the shared
|
||||
sub-tree as well. Consider using <code>share_hidden_from_tree</code>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -3,44 +3,39 @@
|
||||
<p>However, it is possible to manually configure <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS">Cross-Origin Resource Sharing (CORS)</a> since
|
||||
Trilium v0.93.0 using environment variables or <code>config.ini</code>,
|
||||
as follows:</p>
|
||||
<figure class="table" style="width:100%;">
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:26.93%;">
|
||||
<col style="width:32.46%;">
|
||||
<col style="width:40.61%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>CORS Header</th>
|
||||
<th>Corresponding option in <code>config.ini</code>
|
||||
</th>
|
||||
<th>Corresponding option in environment variables in the <code>Network</code> section</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>Access-Control-Allow-Origin</code>
|
||||
</td>
|
||||
<td><code>TRILIUM_NETWORK_CORS_ALLOW_ORIGIN</code>
|
||||
</td>
|
||||
<td><code>corsAllowOrigin</code> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Access-Control-Allow-Methods</code>
|
||||
</td>
|
||||
<td><code>TRILIUM_NETWORK_CORS_ALLOW_METHODS</code>
|
||||
</td>
|
||||
<td><code>corsAllowMethods</code> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Access-Control-Allow-Headers</code>
|
||||
</td>
|
||||
<td><code>TRILIUM_NETWORK_CORS_ALLOW_HEADERS</code>
|
||||
</td>
|
||||
<td><code>corsAllowHeaders</code>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>CORS Header</th>
|
||||
<th>Corresponding option in <code>config.ini</code>
|
||||
</th>
|
||||
<th>Corresponding option in environment variables in the <code>Network</code> section</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>Access-Control-Allow-Origin</code>
|
||||
</td>
|
||||
<td><code>TRILIUM_NETWORK_CORS_ALLOW_ORIGIN</code>
|
||||
</td>
|
||||
<td><code>corsAllowOrigin</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Access-Control-Allow-Methods</code>
|
||||
</td>
|
||||
<td><code>TRILIUM_NETWORK_CORS_ALLOW_METHODS</code>
|
||||
</td>
|
||||
<td><code>corsAllowMethods</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Access-Control-Allow-Headers</code>
|
||||
</td>
|
||||
<td><code>TRILIUM_NETWORK_CORS_ALLOW_HEADERS</code>
|
||||
</td>
|
||||
<td><code>corsAllowHeaders</code>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
314
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Advanced Usage/Hidden Notes.html
generated
vendored
@@ -22,161 +22,159 @@
|
||||
select <em>Advanced</em> → <em>Show Hidden Subtree</em>.</p>
|
||||
<h2>Contents of the hidden note tree</h2>
|
||||
<p>Here is a brief summary of all the notes within the hidden tree:</p>
|
||||
<figure
|
||||
class="table" style="width:100%;">
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:19.93%;">
|
||||
<col style="width:80.07%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Note</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__globalNoteMap">Note Map</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This note is actually opened when the <a class="reference-link" href="#root/_help_bdUJEHsAPYQR">Note Map</a> feature
|
||||
that is accessed from the <a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>.</p>
|
||||
<p>It is possible to create any child notes in it without any additional
|
||||
meaning. For example, it can be used to store a list of note maps which
|
||||
can be linked to from other notes or <a href="#root/_help_u3YFHC9tQlpm">bookmarked</a>.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__sqlConsole">SQL Console History</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>When SQL queries or commands are executed in the <a class="reference-link"
|
||||
href="#root/_help_YKWqdJhzi2VY">SQL Console</a>, they are stored here,
|
||||
grouped by month. Only the query is stored and not the results.</p>
|
||||
<p>This section can be accessed without going to the hidden tree by simply
|
||||
going to the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting Advanced → Open SQL Console History.</p>
|
||||
<p>Notes can be added as children of this tree, but it's generally not recommended
|
||||
to do so to not interfere with the normal history process.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__search">Search History</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>Whenever a search is executed from the full <a class="reference-link"
|
||||
href="#root/_help_eIg8jdvaoNNd">Search</a>, the query will be stored here,
|
||||
grouped by month. Only the search parameters are stored and not the results
|
||||
themselves.</p>
|
||||
<p>This section can be accessed without going to the hidden tree by simply
|
||||
going to the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting Advanced → Open Search History.</p>
|
||||
<p>Notes can be added as children of this tree, but it's generally not recommended
|
||||
to do so to not interfere with the normal history process.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__bulkAction">Bulk Action</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This section is used for <a class="reference-link" href="#root/_help_ivYnonVFBxbQ">Bulk Actions</a>.
|
||||
The last configuration for bulk actions will be stored as part of this
|
||||
note, each action in its own <code>action</code> label.</p>
|
||||
<p>Notes can be added as children of this tree, but there won't be any benefit
|
||||
in doing so.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__backendLog">Backend Log</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This note corresponds to the backend log feature (see <a class="reference-link"
|
||||
href="#root/_help_qzNzp9LYQyPT">Error logs</a>).</p>
|
||||
<p>This item can be accessed without going to the hidden try by going to
|
||||
the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting Advanced → Show backend log.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__userHidden">User Hidden</a>
|
||||
</td>
|
||||
<td>This section can be used by <a href="#root/_help_CdNpE2pqjmI6">scripts</a> to
|
||||
create their own notes that should not be directly visible to the user.
|
||||
The note can be identified by scripts by its unique ID: <code>_userHidden</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__lbTplRoot">Launch Bar Templates</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This section contains the templates for the creation of launchers in the
|
||||
<a
|
||||
class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>. It is not possible to create child notes here.</p>
|
||||
<p>Theoretically some of the notes here can be customized, but there's not
|
||||
much benefit to be had in doing so.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__share">Shared Notes</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This tree lists all of the notes that are <a href="#root/_help_R9pX4DGra2Vt">shared</a> publicly.
|
||||
It can be useful to track down which notes are shared regardless of their
|
||||
position in the note tree.</p>
|
||||
<p>This section can be accessed without going to the hidden tree simply by
|
||||
going to the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting <em>Show Shared Notes Subtree</em>.</p>
|
||||
<p>Sub-notes cannot be created here.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__lbRoot">Launch Bar</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>The tree contains both available and displayed items of the <a class="reference-link"
|
||||
href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>.</p>
|
||||
<p>This section can be accessed without going to the hidden tree by:</p>
|
||||
<ul>
|
||||
<li>Going to the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting <em>Configure Launchbar</em>.</li>
|
||||
<li>Right-clicking an empty space on the <a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a> and
|
||||
selecting <em>Configure Launchbar</em>.</li>
|
||||
</ul>
|
||||
<p>Sub-notes cannot be created here.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__options">Options</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This section stores the list of <a class="reference-link" href="#root/_help_4TIF1oA4VQRO">Options</a>.</p>
|
||||
<p>This section can be accessed without going to the hidden tree by:</p>
|
||||
<ul>
|
||||
<li>Going to the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting <em>Options</em>.</li>
|
||||
<li>Pressing the dedicated Options icon in the <a class="reference-link"
|
||||
href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__lbMobileRoot">Mobile Launch Bar</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This is very similar to the <em>Launch Bar</em>, but is dedicated for the
|
||||
mobile UI only.</p>
|
||||
<p>Accessing it outside the <em>Launch Bar</em> is the same as the Launch Bar,
|
||||
but needs to be done so from the mobile interface.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__help">User Guide</a>
|
||||
</td>
|
||||
<td>This is where the note structure for the User Guide is actually stored.
|
||||
Only the metadata is stored, as the help itself is present as actual files
|
||||
in the application directory.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<table
|
||||
class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:19.93%;">
|
||||
<col style="width:80.07%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Note</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__globalNoteMap">Note Map</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This note is actually opened when the <a class="reference-link" href="#root/_help_bdUJEHsAPYQR">Note Map</a> feature
|
||||
that is accessed from the <a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>.</p>
|
||||
<p>It is possible to create any child notes in it without any additional
|
||||
meaning. For example, it can be used to store a list of note maps which
|
||||
can be linked to from other notes or <a href="#root/_help_u3YFHC9tQlpm">bookmarked</a>.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__sqlConsole">SQL Console History</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>When SQL queries or commands are executed in the <a class="reference-link"
|
||||
href="#root/_help_YKWqdJhzi2VY">SQL Console</a>, they are stored here,
|
||||
grouped by month. Only the query is stored and not the results.</p>
|
||||
<p>This section can be accessed without going to the hidden tree by simply
|
||||
going to the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting Advanced → Open SQL Console History.</p>
|
||||
<p>Notes can be added as children of this tree, but it's generally not recommended
|
||||
to do so to not interfere with the normal history process.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__search">Search History</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>Whenever a search is executed from the full <a class="reference-link"
|
||||
href="#root/_help_eIg8jdvaoNNd">Search</a>, the query will be stored here,
|
||||
grouped by month. Only the search parameters are stored and not the results
|
||||
themselves.</p>
|
||||
<p>This section can be accessed without going to the hidden tree by simply
|
||||
going to the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting Advanced → Open Search History.</p>
|
||||
<p>Notes can be added as children of this tree, but it's generally not recommended
|
||||
to do so to not interfere with the normal history process.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__bulkAction">Bulk Action</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This section is used for <a class="reference-link" href="#root/_help_ivYnonVFBxbQ">Bulk Actions</a>.
|
||||
The last configuration for bulk actions will be stored as part of this
|
||||
note, each action in its own <code>action</code> label.</p>
|
||||
<p>Notes can be added as children of this tree, but there won't be any benefit
|
||||
in doing so.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__backendLog">Backend Log</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This note corresponds to the backend log feature (see <a class="reference-link"
|
||||
href="#root/_help_qzNzp9LYQyPT">Error logs</a>).</p>
|
||||
<p>This item can be accessed without going to the hidden try by going to
|
||||
the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting Advanced → Show backend log.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__userHidden">User Hidden</a>
|
||||
</td>
|
||||
<td>This section can be used by <a href="#root/_help_CdNpE2pqjmI6">scripts</a> to
|
||||
create their own notes that should not be directly visible to the user.
|
||||
The note can be identified by scripts by its unique ID: <code>_userHidden</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__lbTplRoot">Launch Bar Templates</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This section contains the templates for the creation of launchers in the
|
||||
<a
|
||||
class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>. It is not possible to create child notes here.</p>
|
||||
<p>Theoretically some of the notes here can be customized, but there's not
|
||||
much benefit to be had in doing so.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__share">Shared Notes</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This tree lists all of the notes that are <a href="#root/_help_R9pX4DGra2Vt">shared</a> publicly.
|
||||
It can be useful to track down which notes are shared regardless of their
|
||||
position in the note tree.</p>
|
||||
<p>This section can be accessed without going to the hidden tree simply by
|
||||
going to the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting <em>Show Shared Notes Subtree</em>.</p>
|
||||
<p>Sub-notes cannot be created here.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__lbRoot">Launch Bar</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>The tree contains both available and displayed items of the <a class="reference-link"
|
||||
href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>.</p>
|
||||
<p>This section can be accessed without going to the hidden tree by:</p>
|
||||
<ul>
|
||||
<li>Going to the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting <em>Configure Launchbar</em>.</li>
|
||||
<li>Right-clicking an empty space on the <a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a> and
|
||||
selecting <em>Configure Launchbar</em>.</li>
|
||||
</ul>
|
||||
<p>Sub-notes cannot be created here.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__options">Options</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This section stores the list of <a class="reference-link" href="#root/_help_4TIF1oA4VQRO">Options</a>.</p>
|
||||
<p>This section can be accessed without going to the hidden tree by:</p>
|
||||
<ul>
|
||||
<li>Going to the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> and
|
||||
selecting <em>Options</em>.</li>
|
||||
<li>Pressing the dedicated Options icon in the <a class="reference-link"
|
||||
href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__lbMobileRoot">Mobile Launch Bar</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>This is very similar to the <em>Launch Bar</em>, but is dedicated for the
|
||||
mobile UI only.</p>
|
||||
<p>Accessing it outside the <em>Launch Bar</em> is the same as the Launch Bar,
|
||||
but needs to be done so from the mobile interface.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_hidden/_help__help">User Guide</a>
|
||||
</td>
|
||||
<td>This is where the note structure for the User Guide is actually stored.
|
||||
Only the metadata is stored, as the help itself is present as actual files
|
||||
in the application directory.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
446
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Advanced Usage/Sharing.html
generated
vendored
@@ -19,154 +19,152 @@ class="image">
|
||||
<li>Displaying the date of the last update of the note.</li>
|
||||
</ul>
|
||||
<h3>By note type</h3>
|
||||
<figure class="table" style="width:100%;">
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:19.92%;">
|
||||
<col style="width:41.66%;">
|
||||
<col style="width:38.42%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th>Supported features</th>
|
||||
<th>Limitations</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_iPIMuisry3hd">Text</a>
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Table of contents.</li>
|
||||
<li>Syntax highlight of code blocks, provided a language is selected (does
|
||||
not work if “Auto-detected” is enabled).</li>
|
||||
<li>Rendering for math equations.</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Including notes is not supported.</li>
|
||||
<li>Inline Mermaid diagrams are not rendered.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_6f9hih2hXXZk">Code</a>
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Basic support (displaying the contents of the note in a monospace font).</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>No syntax highlight.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_m523cpzocqaD">Saved Search</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_iRwzGnHPzonm">Relation Map</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_bdUJEHsAPYQR">Note Map</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_HcABDtFCkbFN">Render Note</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_GTwFsgaA0lCt">Collections</a>
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li>The child notes are displayed in a fixed format. </li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>More advanced view types such as the calendar view are not supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_s1aBHPd79XYj">Mermaid Diagrams</a>
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li>The diagram is displayed as a vector image.</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_grjYqerjn243">Canvas</a>
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li>The diagram is displayed as a vector image.</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_1vHRoWCEjj0L">Web View</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_gBbsAeiuUxI5">Mind Map</a>
|
||||
</th>
|
||||
<td>The diagram is displayed as a vector image.</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_81SGnPGMk7Xc">Geo Map View</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_W8vYD3Q1zjCR">File</a>
|
||||
</th>
|
||||
<td>Basic interaction (downloading the file).</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:19.92%;">
|
||||
<col style="width:41.66%;">
|
||||
<col style="width:38.42%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th>Supported features</th>
|
||||
<th>Limitations</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_iPIMuisry3hd">Text</a>
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Table of contents.</li>
|
||||
<li>Syntax highlight of code blocks, provided a language is selected (does
|
||||
not work if “Auto-detected” is enabled).</li>
|
||||
<li>Rendering for math equations.</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Including notes is not supported.</li>
|
||||
<li>Inline Mermaid diagrams are not rendered.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_6f9hih2hXXZk">Code</a>
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Basic support (displaying the contents of the note in a monospace font).</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>No syntax highlight.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_m523cpzocqaD">Saved Search</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_iRwzGnHPzonm">Relation Map</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_bdUJEHsAPYQR">Note Map</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_HcABDtFCkbFN">Render Note</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_GTwFsgaA0lCt">Collections</a>
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li>The child notes are displayed in a fixed format. </li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>More advanced view types such as the calendar view are not supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_s1aBHPd79XYj">Mermaid Diagrams</a>
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li>The diagram is displayed as a vector image.</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_grjYqerjn243">Canvas</a>
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li>The diagram is displayed as a vector image.</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_1vHRoWCEjj0L">Web View</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_gBbsAeiuUxI5">Mind Map</a>
|
||||
</th>
|
||||
<td>The diagram is displayed as a vector image.</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_81SGnPGMk7Xc">Geo Map View</a>
|
||||
</th>
|
||||
<td>Not supported.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><a class="reference-link" href="#root/_help_W8vYD3Q1zjCR">File</a>
|
||||
</th>
|
||||
<td>Basic interaction (downloading the file).</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>While the sharing feature is powerful, it has some limitations:</p>
|
||||
<ul>
|
||||
<li><strong>Code Notes</strong>: No syntax highlighting.</li>
|
||||
@@ -269,83 +267,81 @@ for (const attr of parentNote.attributes) {
|
||||
this with the <code>#shareIndex</code> label, which will display a list of
|
||||
all shared notes.</p>
|
||||
<h2>Attribute reference</h2>
|
||||
<figure class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Attribute</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>shareHiddenFromTree</code>
|
||||
</td>
|
||||
<td>this note is hidden from left navigation tree, but still accessible with
|
||||
its URL</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareExternalLink</code>
|
||||
</td>
|
||||
<td>note will act as a link to an external website in the share tree</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareAlias</code>
|
||||
</td>
|
||||
<td>define an alias using which the note will be available under <code>https://your_trilium_host/share/[your_alias]</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareOmitDefaultCss</code>
|
||||
</td>
|
||||
<td>default share page CSS will be omitted. Use when you make extensive styling
|
||||
changes.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareRoot</code>
|
||||
</td>
|
||||
<td>marks note which is served on /share root.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareDescription</code>
|
||||
</td>
|
||||
<td>define text to be added to the HTML meta tag for description</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareRaw</code>
|
||||
</td>
|
||||
<td>Note will be served in its raw format, without HTML wrapper. See also
|
||||
<a
|
||||
class="reference-link" href="#root/_help_Qjt68inQ2bRj">Serving directly the content of a note</a> for an alternative method
|
||||
without setting an attribute.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareDisallowRobotIndexing</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>Indicates to web crawlers that the page should not be indexed of this
|
||||
note by:</p>
|
||||
<ul>
|
||||
<li>Setting the <code>X-Robots-Tag: noindex</code> HTTP header.</li>
|
||||
<li>Setting the <code>noindex, follow</code> meta tag.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareCredentials</code>
|
||||
</td>
|
||||
<td>require credentials to access this shared note. Value is expected to be
|
||||
in format <code>username:password</code>. Don't forget to make this inheritable
|
||||
to apply to child-notes/images.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareIndex</code>
|
||||
</td>
|
||||
<td>Note with this label will list all roots of shared notes.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Attribute</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>shareHiddenFromTree</code>
|
||||
</td>
|
||||
<td>this note is hidden from left navigation tree, but still accessible with
|
||||
its URL</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareExternalLink</code>
|
||||
</td>
|
||||
<td>note will act as a link to an external website in the share tree</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareAlias</code>
|
||||
</td>
|
||||
<td>define an alias using which the note will be available under <code>https://your_trilium_host/share/[your_alias]</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareOmitDefaultCss</code>
|
||||
</td>
|
||||
<td>default share page CSS will be omitted. Use when you make extensive styling
|
||||
changes.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareRoot</code>
|
||||
</td>
|
||||
<td>marks note which is served on /share root.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareDescription</code>
|
||||
</td>
|
||||
<td>define text to be added to the HTML meta tag for description</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareRaw</code>
|
||||
</td>
|
||||
<td>Note will be served in its raw format, without HTML wrapper. See also
|
||||
<a
|
||||
class="reference-link" href="#root/_help_Qjt68inQ2bRj">Serving directly the content of a note</a> for an alternative method
|
||||
without setting an attribute.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareDisallowRobotIndexing</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>Indicates to web crawlers that the page should not be indexed of this
|
||||
note by:</p>
|
||||
<ul>
|
||||
<li>Setting the <code>X-Robots-Tag: noindex</code> HTTP header.</li>
|
||||
<li>Setting the <code>noindex, follow</code> meta tag.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareCredentials</code>
|
||||
</td>
|
||||
<td>require credentials to access this shared note. Value is expected to be
|
||||
in format <code>username:password</code>. Don't forget to make this inheritable
|
||||
to apply to child-notes/images.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shareIndex</code>
|
||||
</td>
|
||||
<td>Note with this label will list all roots of shared notes.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Credits</h2>
|
||||
<p>Since v0.95.0, a new theme was introduced (and enabled by default) which
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
<p>When accessing a shared note, Trilium will render it as a web page. Sometimes
|
||||
it's desirable to serve the content directly so that it can be used in
|
||||
a script or downloaded by the user.</p>
|
||||
<figure class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>A note displayed as a web page (HTML)</th>
|
||||
<th>A note displayed as a raw format</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:738/275;" src="1_Serving directly the conte.png"
|
||||
width="738" height="275">
|
||||
</figure>
|
||||
</td>
|
||||
<td>
|
||||
<img src="Serving directly the conte.png">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>A note displayed as a web page (HTML)</th>
|
||||
<th>A note displayed as a raw format</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:738/275;" src="1_Serving directly the conte.png"
|
||||
width="738" height="275">
|
||||
</figure>
|
||||
</td>
|
||||
<td>
|
||||
<img src="Serving directly the conte.png">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>By adding an attribute to the note</h2>
|
||||
<p>Simply add the <code>#shareRaw</code> attribute and the note will always
|
||||
|
||||
@@ -23,58 +23,56 @@
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Configuration</h3>
|
||||
<figure class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Label</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>workspace</code>
|
||||
</td>
|
||||
<td>Marks this note as a workspace, button to enter the workspace is controlled
|
||||
by this</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceIconClass</code>
|
||||
</td>
|
||||
<td>defines box icon CSS class which will be used in tab when hoisted to this
|
||||
note</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceTabBackgroundColor</code>
|
||||
</td>
|
||||
<td>CSS color used in the note tab when hoisted to this note, use any CSS
|
||||
color format, e.g. "lightblue" or "#ddd". See <a href="https://www.w3schools.com/cssref/css_colors.asp">https://www.w3schools.com/cssref/css_colors.asp</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceCalendarRoot</code>
|
||||
</td>
|
||||
<td>Marking a note with this label will define a new per-workspace calendar
|
||||
for <a class="reference-link" href="#root/_help_l0tKav7yLHGF">Day Notes</a>.
|
||||
If there's no such note, the global calendar will be used.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceTemplate</code>
|
||||
</td>
|
||||
<td>This note will appear in the selection of available template when creating
|
||||
new note, but only when hoisted into a workspace containing this template</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceSearchHome</code>
|
||||
</td>
|
||||
<td>new search notes will be created as children of this note when hoisted
|
||||
to some ancestor of this workspace note</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceInbox</code>
|
||||
</td>
|
||||
<td>default inbox location for new notes when hoisted to some ancestor of
|
||||
this workspace note</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Label</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>workspace</code>
|
||||
</td>
|
||||
<td>Marks this note as a workspace, button to enter the workspace is controlled
|
||||
by this</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceIconClass</code>
|
||||
</td>
|
||||
<td>defines box icon CSS class which will be used in tab when hoisted to this
|
||||
note</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceTabBackgroundColor</code>
|
||||
</td>
|
||||
<td>CSS color used in the note tab when hoisted to this note, use any CSS
|
||||
color format, e.g. "lightblue" or "#ddd". See <a href="https://www.w3schools.com/cssref/css_colors.asp">https://www.w3schools.com/cssref/css_colors.asp</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceCalendarRoot</code>
|
||||
</td>
|
||||
<td>Marking a note with this label will define a new per-workspace calendar
|
||||
for <a class="reference-link" href="#root/_help_l0tKav7yLHGF">Day Notes</a>.
|
||||
If there's no such note, the global calendar will be used.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceTemplate</code>
|
||||
</td>
|
||||
<td>This note will appear in the selection of available template when creating
|
||||
new note, but only when hoisted into a workspace containing this template</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceSearchHome</code>
|
||||
</td>
|
||||
<td>new search notes will be created as children of this note when hoisted
|
||||
to some ancestor of this workspace note</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>workspaceInbox</code>
|
||||
</td>
|
||||
<td>default inbox location for new notes when hoisted to some ancestor of
|
||||
this workspace note</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -15,9 +15,9 @@
|
||||
the desired number.</li>
|
||||
</ul>
|
||||
<h2>View types</h2>
|
||||
<p>The view types dictate how the child notes are represented.</p>
|
||||
<p>By default, the notes will be displayed in a grid, however there are also
|
||||
some other view types available.</p>
|
||||
<p>The view types dictate how the child notes are represented. By default,
|
||||
the notes will be displayed in a grid, however there are also some other
|
||||
view types available.</p>
|
||||
<p>Generally the view type can only be changed in a <a class="reference-link"
|
||||
href="#root/_help_GTwFsgaA0lCt">Collections</a> note from the
|
||||
<a
|
||||
|
||||
@@ -1,382 +0,0 @@
|
||||
<figure class="image image-style-align-center">
|
||||
<img style="aspect-ratio:767/606;" src="4_Calendar View_image.png" width="767"
|
||||
height="606">
|
||||
</figure>
|
||||
<p>The Calendar view will display each child note in a calendar that has
|
||||
a start date and optionally an end date, as an event.</p>
|
||||
<p>The Calendar view has multiple display modes:</p>
|
||||
<ul>
|
||||
<li>Week view, where all the 7 days of the week (or 5 if the weekends are
|
||||
hidden) are displayed in columns. This mode allows entering and displaying
|
||||
time-specific events, not just all-day events.</li>
|
||||
<li>Month view, where the entire month is displayed and all-day events can
|
||||
be inserted. Both time-specific events and all-day events are listed.</li>
|
||||
<li>Year view, which displays the entire year for quick reference.</li>
|
||||
<li>List view, which displays all the events of a given month in sequence.</li>
|
||||
</ul>
|
||||
<p>Unlike other Collection view types, the Calendar view also allows some
|
||||
kind of interaction, such as moving events around as well as creating new
|
||||
ones.</p>
|
||||
<h2>Creating a calendar</h2>
|
||||
<figure class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
<img src="2_Calendar View_image.png">
|
||||
</td>
|
||||
<td>The Calendar View works only for Collection note types. To create a new
|
||||
note, right click on the note tree on the left and select Insert note after,
|
||||
or Insert child note and then select <em>Collection</em>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
<img src="3_Calendar View_image.png">
|
||||
</td>
|
||||
<td>Once created, the “View type” of the Collection needs changed to “Calendar”,
|
||||
by selecting the “Collection Properties” tab in the ribbon.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
|
||||
<h2>Creating a new event/note</h2>
|
||||
<ul>
|
||||
<li>Clicking on a day will create a new child note and assign it to that particular
|
||||
day.
|
||||
<ul>
|
||||
<li>You will be asked for the name of the new note. If the popup is dismissed
|
||||
by pressing the close button or escape, then the note will not be created.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>It's possible to drag across multiple days to set both the start and end
|
||||
date of a particular note.
|
||||
<br>
|
||||
<img src="Calendar View_image.png">
|
||||
</li>
|
||||
<li>Creating new notes from the calendar will respect the <code>~child:template</code> relation
|
||||
if set on the Collection note.</li>
|
||||
</ul>
|
||||
<h2>Interacting with events</h2>
|
||||
<ul>
|
||||
<li>Hovering the mouse over an event will display information about the note.
|
||||
<br>
|
||||
<img src="7_Calendar View_image.png">
|
||||
</li>
|
||||
<li>Left clicking the event will open a <a class="reference-link" href="#root/_help_ZjLYv08Rp3qC">Quick edit</a> to
|
||||
edit the note in a popup while allowing easy return to the calendar by
|
||||
just dismissing the popup.
|
||||
<ul>
|
||||
<li>Middle clicking will open the note in a new tab.</li>
|
||||
<li>Right click will offer more options including opening the note in a new
|
||||
split or window.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Drag and drop an event on the calendar to move it to another day.</li>
|
||||
<li>The length of an event can be changed by placing the mouse to the right
|
||||
edge of the event and dragging the mouse around.</li>
|
||||
</ul>
|
||||
<h2>Configuring the calendar view</h2>
|
||||
<p>In the <em>Collections</em> tab in the <a class="reference-link" href="#root/_help_BlN9DFI679QC">Ribbon</a>,
|
||||
it's possible to adjust the following:</p>
|
||||
<ul>
|
||||
<li>Hide weekends from the week view.</li>
|
||||
<li>Display week numbers on the calendar.</li>
|
||||
</ul>
|
||||
<h2>Configuring the calendar using attributes</h2>
|
||||
<p>The following attributes can be added to the Collection type:</p>
|
||||
<figure
|
||||
class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>#calendar:hideWeekends</code>
|
||||
</td>
|
||||
<td>When present (regardless of value), it will hide Saturday and Sundays
|
||||
from the calendar.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:weekNumbers</code>
|
||||
</td>
|
||||
<td>When present (regardless of value), it will show the number of the week
|
||||
on the calendar.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:view</code>
|
||||
</td>
|
||||
<td>
|
||||
<p>Which view to display in the calendar:</p>
|
||||
<ul>
|
||||
<li><code>timeGridWeek</code> for the <em>week</em> view;</li>
|
||||
<li><code>dayGridMonth</code> for the <em>month</em> view;</li>
|
||||
<li><code>multiMonthYear</code> for the <em>year</em> view;</li>
|
||||
<li><code>listMonth</code> for the <em>list</em> view.</li>
|
||||
</ul>
|
||||
<p>Any other value will be dismissed and the default view (month) will be
|
||||
used instead.</p>
|
||||
<p>The value of this label is automatically updated when changing the view
|
||||
using the UI buttons.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>~child:template</code>
|
||||
</td>
|
||||
<td>Defines the template for newly created notes in the calendar (via dragging
|
||||
or clicking).</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>In addition, the first day of the week can be either Sunday or Monday
|
||||
and can be adjusted from the application settings.</p>
|
||||
<h2>Configuring the calendar events using attributes</h2>
|
||||
<p>For each note of the calendar, the following attributes can be used:</p>
|
||||
<figure
|
||||
class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>#startDate</code>
|
||||
</td>
|
||||
<td>The date the event starts, which will display it in the calendar. The
|
||||
format is <code>YYYY-MM-DD</code> (year, month and day separated by a minus
|
||||
sign).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#endDate</code>
|
||||
</td>
|
||||
<td>Similar to <code>startDate</code>, mentions the end date if the event spans
|
||||
across multiple days. The date is inclusive, so the end day is also considered.
|
||||
The attribute can be missing for single-day events.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#startTime</code>
|
||||
</td>
|
||||
<td>The time the event starts at. If this value is missing, then the event
|
||||
is considered a full-day event. The format is <code>HH:MM</code> (hours in
|
||||
24-hour format and minutes).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#endTime</code>
|
||||
</td>
|
||||
<td>Similar to <code>startTime</code>, it mentions the time at which the event
|
||||
ends (in relation with <code>endDate</code> if present, or <code>startDate</code>).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#color</code>
|
||||
</td>
|
||||
<td>Displays the event with a specified color (named such as <code>red</code>, <code>gray</code> or
|
||||
hex such as <code>#FF0000</code>). This will also change the color of the
|
||||
note in other places such as the note tree.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:color</code>
|
||||
</td>
|
||||
<td>Similar to <code>#color</code>, but applies the color only for the event
|
||||
in the calendar and not for other places such as the note tree.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#iconClass</code>
|
||||
</td>
|
||||
<td>If present, the icon of the note will be displayed to the left of the
|
||||
event title.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:title</code>
|
||||
</td>
|
||||
<td>Changes the title of an event to point to an attribute of the note other
|
||||
than the title, can either a label or a relation (without the <code>#</code> or <code>~</code> symbol).
|
||||
See <em>Use-cases</em> for more information.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:displayedAttributes</code>
|
||||
</td>
|
||||
<td>Allows displaying the value of one or more attributes in the calendar
|
||||
like this:
|
||||
<br>
|
||||
<br>
|
||||
<img src="9_Calendar View_image.png">
|
||||
<br>
|
||||
<br><code>#weight="70" #Mood="Good" #calendar:displayedAttributes="weight,Mood"</code>
|
||||
<br>
|
||||
<br>It can also be used with relations, case in which it will display the
|
||||
title of the target note:
|
||||
<br>
|
||||
<br><code>~assignee=@My assignee #calendar:displayedAttributes="assignee"</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:startDate</code>
|
||||
</td>
|
||||
<td>Allows using a different label to represent the start date, other than <code>startDate</code> (e.g. <code>expiryDate</code>).
|
||||
The label name <strong>must not be</strong> prefixed with <code>#</code>.
|
||||
If the label is not defined for a note, the default will be used instead.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:endDate</code>
|
||||
</td>
|
||||
<td>Similar to <code>#calendar:startDate</code>, allows changing the attribute
|
||||
which is being used to read the end date.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:startTime</code>
|
||||
</td>
|
||||
<td>Similar to <code>#calendar:startDate</code>, allows changing the attribute
|
||||
which is being used to read the start time.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:endTime</code>
|
||||
</td>
|
||||
<td>Similar to <code>#calendar:startDate</code>, allows changing the attribute
|
||||
which is being used to read the end time.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
|
||||
<h2>How the calendar works</h2>
|
||||
<p>
|
||||
<img src="11_Calendar View_image.png">
|
||||
</p>
|
||||
<p>The calendar displays all the child notes of the Collection that have
|
||||
a <code>#startDate</code>. An <code>#endDate</code> can optionally be added.</p>
|
||||
<p>If editing the start date and end date from the note itself is desirable,
|
||||
the following attributes can be added to the Collection note:</p><pre><code class="language-text-x-trilium-auto">#viewType=calendar #label:startDate(inheritable)="promoted,alias=Start Date,single,date"
|
||||
#label:endDate(inheritable)="promoted,alias=End Date,single,date"
|
||||
#hidePromotedAttributes </code></pre>
|
||||
<p>This will result in:</p>
|
||||
<p>
|
||||
<img src="10_Calendar View_image.png">
|
||||
</p>
|
||||
<p>When not used in a Journal, the calendar is recursive. That is, it will
|
||||
look for events not just in its child notes but also in the children of
|
||||
these child notes.</p>
|
||||
<h2>Use-cases</h2>
|
||||
<h3>Using with the Journal / calendar</h3>
|
||||
<p>It is possible to integrate the calendar view into the Journal with day
|
||||
notes. In order to do so change the note type of the Journal note (calendar
|
||||
root) to Collection and then select the Calendar View.</p>
|
||||
<p>Based on the <code>#calendarRoot</code> (or <code>#workspaceCalendarRoot</code>)
|
||||
attribute, the calendar will know that it's in a calendar and apply the
|
||||
following:</p>
|
||||
<ul>
|
||||
<li>The calendar events are now rendered based on their <code>dateNote</code> attribute
|
||||
rather than <code>startDate</code>.</li>
|
||||
<li>Interactive editing such as dragging over an empty era or resizing an
|
||||
event is no longer possible.</li>
|
||||
<li>Clicking on the empty space on a date will automatically open that day's
|
||||
note or create it if it does not exist.</li>
|
||||
<li>Direct children of a day note will be displayed on the calendar despite
|
||||
not having a <code>dateNote</code> attribute. Children of the child notes
|
||||
will not be displayed.</li>
|
||||
</ul>
|
||||
<img src="8_Calendar View_image.png" width="1217" height="724">
|
||||
|
||||
<h3>Using a different attribute as event title</h3>
|
||||
<p>By default, events are displayed on the calendar by their note title.
|
||||
However, it is possible to configure a different attribute to be displayed
|
||||
instead.</p>
|
||||
<p>To do so, assign <code>#calendar:title</code> to the child note (not the
|
||||
calendar/Collection note), with the value being <code>name</code> where <code>name</code> can
|
||||
be any label (make not to add the <code>#</code> prefix). The attribute can
|
||||
also come through inheritance such as a template attribute. If the note
|
||||
does not have the requested label, the title of the note will be used instead.</p>
|
||||
<figure
|
||||
class="table" style="width:100%;">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><pre><code class="language-text-x-trilium-auto">#startDate=2025-02-11 #endDate=2025-02-13 #name="My vacation" #calendar:title="name"</code></pre>
|
||||
</td>
|
||||
<td>
|
||||
<p> </p>
|
||||
<figure class="image image-style-align-center">
|
||||
<img style="aspect-ratio:445/124;" src="5_Calendar View_image.png" width="445"
|
||||
height="124">
|
||||
</figure>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
|
||||
<h3>Using a relation attribute as event title</h3>
|
||||
<p>Similarly to using an attribute, use <code>#calendar:title</code> and set
|
||||
it to <code>name</code> where <code>name</code> is the name of the relation
|
||||
to use.</p>
|
||||
<p>Moreover, if there are more relations of the same name, they will be displayed
|
||||
as multiple events coming from the same note.</p>
|
||||
<figure class="table"
|
||||
style="width:100%;">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><pre><code class="language-text-x-trilium-auto">#startDate=2025-02-14 #endDate=2025-02-15 ~for=@John Smith ~for=@Jane Doe #calendar:title="for"</code></pre>
|
||||
</td>
|
||||
<td>
|
||||
<img src="6_Calendar View_image.png" width="294" height="151">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Note that it's even possible to have a <code>#calendar:title</code> on the
|
||||
target note (e.g. “John Smith”) which will try to render an attribute of
|
||||
it. Note that it's not possible to use a relation here as well for safety
|
||||
reasons (an accidental recursion of attributes could cause the application
|
||||
to loop infinitely).</p>
|
||||
<figure class="table" style="width:100%;">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><pre><code class="language-text-x-trilium-auto">#calendar:title="shortName" #shortName="John S."</code></pre>
|
||||
</td>
|
||||
<td>
|
||||
<figure class="image image-style-align-center">
|
||||
<img style="aspect-ratio:296/150;" src="1_Calendar View_image.png" width="296"
|
||||
height="150">
|
||||
</figure>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
@@ -1,399 +0,0 @@
|
||||
<aside class="admonition important">
|
||||
<p>Starting with Trilium v0.97.0, the geo map has been converted from a standalone
|
||||
<a
|
||||
href="#root/_help_KSZ04uQ2D1St">note type</a>to a type of view for the <a class="reference-link"
|
||||
href="#root/_help_0ESUbbAxVnoK">Note List</a>. </p>
|
||||
</aside>
|
||||
<figure class="image image-style-align-center">
|
||||
<img style="aspect-ratio:892/675;" src="9_Geo Map View_image.png" width="892"
|
||||
height="675">
|
||||
</figure>
|
||||
<p>This note type displays the children notes on a geographical map, based
|
||||
on an attribute. It is also possible to add new notes at a specific location
|
||||
using the built-in interface.</p>
|
||||
<h2>Creating a new geo map</h2>
|
||||
<figure class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:483/413;" src="15_Geo Map View_image.png" width="483"
|
||||
height="413">
|
||||
</figure>
|
||||
</td>
|
||||
<td>Right click on any note on the note tree and select <em>Insert child note</em> → <em>Geo Map (beta)</em>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
<figure class="image image-style-align-center image_resized" style="width:53.44%;">
|
||||
<img style="aspect-ratio:1720/1396;" src="8_Geo Map View_image.png" width="1720"
|
||||
height="1396">
|
||||
</figure>
|
||||
</td>
|
||||
<td>By default the map will be empty and will show the entire world.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
|
||||
<h2>Repositioning the map</h2>
|
||||
<ul>
|
||||
<li>Click and drag the map in order to move across the map.</li>
|
||||
<li>Use the mouse wheel, two-finger gesture on a touchpad or the +/- buttons
|
||||
on the top-left to adjust the zoom.</li>
|
||||
</ul>
|
||||
<p>The position on the map and the zoom are saved inside the map note and
|
||||
restored when visiting again the note.</p>
|
||||
<h2>Adding a marker using the map</h2>
|
||||
<h3>Adding a new note using the plus button</h3>
|
||||
<figure class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>To create a marker, first navigate to the desired point on the map. Then
|
||||
press the
|
||||
<img src="10_Geo Map View_image.png">button in the <a href="#root/_help_XpOYSgsLkTJy">Floating buttons</a> (top-right)
|
||||
area.
|
||||
<br>
|
||||
<br>If the button is not visible, make sure the button section is visible
|
||||
by pressing the chevron button (
|
||||
<img src="17_Geo Map View_image.png">) in the top-right of the map.</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
<img class="image_resized" style="aspect-ratio:1730/416;width:100%;" src="2_Geo Map View_image.png"
|
||||
width="1730" height="416">
|
||||
</td>
|
||||
<td>Once pressed, the map will enter in the insert mode, as illustrated by
|
||||
the notification.
|
||||
<br>
|
||||
<br>Simply click the point on the map where to place the marker, or the Escape
|
||||
key to cancel.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>
|
||||
<img class="image_resized" style="aspect-ratio:1586/404;width:100%;" src="7_Geo Map View_image.png"
|
||||
width="1586" height="404">
|
||||
</td>
|
||||
<td>Enter the name of the marker/note to be created.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>
|
||||
<img class="image_resized" style="aspect-ratio:1696/608;width:100%;" src="16_Geo Map View_image.png"
|
||||
width="1696" height="608">
|
||||
</td>
|
||||
<td>Once confirmed, the marker will show up on the map and it will also be
|
||||
displayed as a child note of the map.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
|
||||
<h3>Adding a new note using the contextual menu</h3>
|
||||
<ol>
|
||||
<li>Right click anywhere on the map, where to place the newly created marker
|
||||
(and corresponding note).</li>
|
||||
<li>Select <em>Add a marker at this location</em>.</li>
|
||||
<li>Enter the name of the newly created note.</li>
|
||||
<li>The map should be updated with the new marker.</li>
|
||||
</ol>
|
||||
<h3>Adding an existing note on note from the note tree</h3>
|
||||
<ol>
|
||||
<li>Select the desired note in the <a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</li>
|
||||
<li>Hold the mouse on the note and drag it to the map to the desired location.</li>
|
||||
<li>The map should be updated with the new marker.</li>
|
||||
</ol>
|
||||
<p>This works for:</p>
|
||||
<ul>
|
||||
<li>Notes that are not part of the geo map, case in which a <a href="#root/_help_IakOLONlIfGI">clone</a> will
|
||||
be created.</li>
|
||||
<li>Notes that are a child of the geo map but not yet positioned on the map.</li>
|
||||
<li>Notes that are a child of the geo map and also positioned, case in which
|
||||
the marker will be relocated to the new position.</li>
|
||||
</ul>
|
||||
<h2>How the location of the markers is stored</h2>
|
||||
<p>The location of a marker is stored in the <code>#geolocation</code> attribute
|
||||
of the child notes:</p>
|
||||
<img src="18_Geo Map View_image.png" width="1288"
|
||||
height="278">
|
||||
<p>This value can be added manually if needed. The value of the attribute
|
||||
is made up of the latitude and longitude separated by a comma.</p>
|
||||
<h2>Repositioning markers</h2>
|
||||
<p>It's possible to reposition existing markers by simply drag and dropping
|
||||
them to the new destination.</p>
|
||||
<p>As soon as the mouse is released, the new position is saved.</p>
|
||||
<p>If moved by mistake, there is currently no way to undo the change. If
|
||||
the mouse was not yet released, it's possible to force a refresh of the
|
||||
page (<kbd>Ctrl</kbd>+<kbd>R</kbd> ) to cancel it.</p>
|
||||
<h2>Interaction with the markers</h2>
|
||||
<ul>
|
||||
<li>Hovering over a marker will display a <a class="reference-link" href="#root/_help_lgKX7r3aL30x">Note Tooltip</a> with
|
||||
the content of the note it belongs to.
|
||||
<ul>
|
||||
<li>Clicking on the note title in the tooltip will navigate to the note in
|
||||
the current view.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Middle-clicking the marker will open the note in a new tab.</li>
|
||||
<li>Right-clicking the marker will open a contextual menu (as described below).</li>
|
||||
<li>If the map is in read-only mode, clicking on a marker will open a
|
||||
<a
|
||||
class="reference-link" href="#root/_help_ZjLYv08Rp3qC">Quick edit</a> popup for the corresponding note.</li>
|
||||
</ul>
|
||||
<h2>Contextual menu</h2>
|
||||
<p>It's possible to press the right mouse button to display a contextual
|
||||
menu.</p>
|
||||
<ol>
|
||||
<li>If right-clicking an empty section of the map (not on a marker), it allows
|
||||
to:
|
||||
<ol>
|
||||
<li>Displays the latitude and longitude. Clicking this option will copy them
|
||||
to the clipboard.</li>
|
||||
<li>Open the location using an external application (if the operating system
|
||||
supports it).</li>
|
||||
<li>Adding a new marker at that location.</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>If right-clicking on a marker, it allows to:
|
||||
<ol>
|
||||
<li>Displays the latitude and longitude. Clicking this option will copy them
|
||||
to the clipboard.</li>
|
||||
<li>Open the location using an external application (if the operating system
|
||||
supports it).</li>
|
||||
<li>Open the note in a new tab, split or window.</li>
|
||||
<li>Remove the marker from the map, which will remove the <code>#geolocation</code> attribute
|
||||
of the note. To add it back again, the coordinates have to be manually
|
||||
added back in.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ol>
|
||||
<h2>Icon and color of the markers</h2>
|
||||
<figure class="image image-style-align-center">
|
||||
<img style="aspect-ratio:523/295;" src="Geo Map View_image.jpg" alt="image"
|
||||
width="523" height="295">
|
||||
</figure>
|
||||
<p>The markers will have the same icon as the note.</p>
|
||||
<p>It's possible to add a custom color to a marker by assigning them a <code>#color</code> attribute
|
||||
such as <code>#color=green</code>.</p>
|
||||
<h2>Adding the coordinates manually</h2>
|
||||
<p>In a nutshell, create a child note and set the <code>#geolocation</code> attribute
|
||||
to the coordinates.</p>
|
||||
<p>The value of the attribute is made up of the latitude and longitude separated
|
||||
by a comma.</p>
|
||||
<h3>Adding from Google Maps</h3>
|
||||
<figure class="table" style="width:100%;">
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:2.77%;">
|
||||
<col style="width:33.24%;">
|
||||
<col style="width:63.99%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
<figure class="image image-style-align-center image_resized" style="width:56.84%;">
|
||||
<img style="aspect-ratio:732/918;" src="12_Geo Map View_image.png" width="732"
|
||||
height="918">
|
||||
</figure>
|
||||
</td>
|
||||
<td>Go to Google Maps on the web and look for a desired location, right click
|
||||
on it and a context menu will show up.
|
||||
<br>
|
||||
<br>Simply click on the first item displaying the coordinates and they will
|
||||
be copied to clipboard.
|
||||
<br>
|
||||
<br>Then paste the value inside the text box into the <code>#geolocation</code> attribute
|
||||
of a child note of the map (don't forget to surround the value with a <code>"</code> character).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
<figure class="image image-style-align-center image_resized" style="width:100%;">
|
||||
<img style="aspect-ratio:518/84;" src="4_Geo Map View_image.png" width="518"
|
||||
height="84">
|
||||
</figure>
|
||||
</td>
|
||||
<td>In Trilium, create a child note under the map.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>
|
||||
<figure class="image image-style-align-center image_resized" style="width:100%;">
|
||||
<img style="aspect-ratio:1074/276;" src="11_Geo Map View_image.png" width="1074"
|
||||
height="276">
|
||||
</figure>
|
||||
</td>
|
||||
<td>And then go to Owned Attributes and type <code>#geolocation="</code>, then
|
||||
paste from the clipboard as-is and then add the ending <code>"</code> character.
|
||||
Press Enter to confirm and the map should now be updated to contain the
|
||||
new note.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
|
||||
<h3>Adding from OpenStreetMap</h3>
|
||||
<p>Similarly to the Google Maps approach:</p>
|
||||
<figure class="table" style="width:100%;">
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:2.77%;">
|
||||
<col style="width:33.42%;">
|
||||
<col style="width:63.81%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
<img class="image_resized" style="aspect-ratio:562/454;width:100%;" src="1_Geo Map View_image.png"
|
||||
width="562" height="454">
|
||||
</td>
|
||||
<td>Go to any location on openstreetmap.org and right click to bring up the
|
||||
context menu. Select the “Show address” item.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
<img class="image_resized" style="aspect-ratio:696/480;width:100%;" src="Geo Map View_image.png"
|
||||
width="696" height="480">
|
||||
</td>
|
||||
<td>The address will be visible in the top-left of the screen, in the place
|
||||
of the search bar.
|
||||
<br>
|
||||
<br>Select the coordinates and copy them into the clipboard.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>
|
||||
<img class="image_resized" style="aspect-ratio:640/276;width:100%;" src="5_Geo Map View_image.png"
|
||||
width="640" height="276">
|
||||
</td>
|
||||
<td>Simply paste the value inside the text box into the <code>#geolocation</code> attribute
|
||||
of a child note of the map and then it should be displayed on the map.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
|
||||
<h2>Adding GPS tracks (.gpx)</h2>
|
||||
<p>Trilium has basic support for displaying GPS tracks on the geo map.</p>
|
||||
<figure
|
||||
class="table" style="width:100%;">
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:2.77%;">
|
||||
<col style="width:30.22%;">
|
||||
<col style="width:67.01%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>
|
||||
<figure class="image image-style-align-center">
|
||||
<img style="aspect-ratio:226/74;" src="3_Geo Map View_image.png" width="226"
|
||||
height="74">
|
||||
</figure>
|
||||
</td>
|
||||
<td>To add a track, simply drag & drop a .gpx file inside the geo map
|
||||
in the note tree.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>
|
||||
<figure class="image image-style-align-center">
|
||||
<img style="aspect-ratio:322/222;" src="14_Geo Map View_image.png" width="322"
|
||||
height="222">
|
||||
</figure>
|
||||
</td>
|
||||
<td>In order for the file to be recognized as a GPS track, it needs to show
|
||||
up as <code>application/gpx+xml</code> in the <em>File type</em> field.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>
|
||||
<figure class="image image-style-align-center">
|
||||
<img style="aspect-ratio:620/530;" src="6_Geo Map View_image.png" width="620"
|
||||
height="530">
|
||||
</figure>
|
||||
</td>
|
||||
<td>When going back to the map, the track should now be visible.
|
||||
<br>
|
||||
<br>The start and end points of the track are indicated by the two blue markers.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<aside class="admonition note">
|
||||
<p>The starting point of the track will be displayed as a marker, with the
|
||||
name of the note underneath. The start marker will also respect the icon
|
||||
and the <code>color</code> of the note. The end marker is displayed with
|
||||
a distinct icon.</p>
|
||||
<p>If the GPX contains waypoints, they will also be displayed. If they have
|
||||
a name, it is displayed when hovering over it with the mouse.</p>
|
||||
</aside>
|
||||
<h2>Read-only mode</h2>
|
||||
<p>When a map is in read-only all editing features will be disabled such
|
||||
as:</p>
|
||||
<ul>
|
||||
<li>The add button in the <a class="reference-link" href="#root/_help_XpOYSgsLkTJy">Floating buttons</a>.</li>
|
||||
<li>Dragging markers.</li>
|
||||
<li>Editing from the contextual menu (removing locations or adding new items).</li>
|
||||
</ul>
|
||||
<p>To enable read-only mode simply press the <em>Lock</em> icon from the
|
||||
<a
|
||||
class="reference-link" href="#root/_help_XpOYSgsLkTJy">Floating buttons</a>. To disable it, press the button again.</p>
|
||||
<h2>Troubleshooting</h2>
|
||||
<figure class="image image-style-align-right image_resized" style="width:34.06%;">
|
||||
<img style="aspect-ratio:678/499;" src="13_Geo Map View_image.png" width="678"
|
||||
height="499">
|
||||
</figure>
|
||||
|
||||
<h3>Grid-like artifacts on the map</h3>
|
||||
<p>This occurs if the application is not at 100% zoom which causes the pixels
|
||||
of the map to not render correctly due to fractional scaling. The only
|
||||
possible solution is to set the UI zoom at 100% (default keyboard shortcut
|
||||
is <kbd>Ctrl</kbd>+<kbd>0</kbd>).</p>
|
||||
@@ -1,187 +0,0 @@
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:1050/259;" src="Table View_image.png" width="1050"
|
||||
height="259">
|
||||
</figure>
|
||||
<p>The table view displays information in a grid, where the rows are individual
|
||||
notes and the columns are <a class="reference-link" href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a>.
|
||||
In addition, values are editable.</p>
|
||||
<h2>How it works</h2>
|
||||
<p>The tabular structure is represented as such:</p>
|
||||
<ul>
|
||||
<li>Each child note is a row in the table.</li>
|
||||
<li>If child rows also have children, they will be displayed under an expander
|
||||
(nested notes).</li>
|
||||
<li>Each column is a <a href="#root/_help_OFXdgB2nNk1F">promoted attribute</a> that
|
||||
is defined on the Collection note.
|
||||
<ul>
|
||||
<li>Actually, both promoted and unpromoted attributes are supported, but it's
|
||||
a requirement to use a label/relation definition.</li>
|
||||
<li>The promoted attributes are usually defined as inheritable in order to
|
||||
show up in the child notes, but it's not a requirement.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>If there are multiple attribute definitions with the same <code>name</code>,
|
||||
only one will be displayed.</li>
|
||||
</ul>
|
||||
<p>There are also a few predefined columns:</p>
|
||||
<ul>
|
||||
<li>The current item number, identified by the <code>#</code> symbol.
|
||||
<ul>
|
||||
<li>This simply counts the note and is affected by sorting.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference-link" href="#root/_help_m1lbrzyKDaRB">Note ID</a>,
|
||||
representing the unique ID used internally by Trilium</li>
|
||||
<li>The title of the note.</li>
|
||||
</ul>
|
||||
<h2>Interaction</h2>
|
||||
<h3>Creating a new table</h3>
|
||||
<p>Right click the <a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a> and
|
||||
select <em>Insert child note</em> and look for the <em>Table item</em>.</p>
|
||||
<h3>Adding columns</h3>
|
||||
<p>Each column is a <a href="#root/_help_OFXdgB2nNk1F">promoted or unpromoted attribute</a> that
|
||||
is defined on the Collection note.</p>
|
||||
<p>To create a new column, either:</p>
|
||||
<ul>
|
||||
<li>Press <em>Add new column</em> at the bottom of the table.</li>
|
||||
<li>Right click on an existing column and select Add column to the left/right.</li>
|
||||
<li>Right click on the empty space of the column header and select <em>Label</em> or <em>Relation</em> in
|
||||
the <em>New column</em> section.</li>
|
||||
</ul>
|
||||
<h3>Adding new rows</h3>
|
||||
<p>Each row is actually a note that is a child of the Collection note.</p>
|
||||
<p>To create a new note, either:</p>
|
||||
<ul>
|
||||
<li>Press <em>Add new row</em> at the bottom of the table.</li>
|
||||
<li>Right click on an existing row and select <em>Insert row above, Insert child note</em> or <em>Insert row below</em>.</li>
|
||||
</ul>
|
||||
<p>By default it will try to edit the title of the newly created note.</p>
|
||||
<p>Alternatively, the note can be created from the <a class="reference-link"
|
||||
href="#root/_help_oPVyFC7WL2Lp">Note Tree</a> or <a href="#root/_help_CdNpE2pqjmI6">scripting</a>.</p>
|
||||
<h3>Context menu</h3>
|
||||
<p>There are multiple menus:</p>
|
||||
<ul>
|
||||
<li>Right clicking on a column, allows:
|
||||
<ul>
|
||||
<li>Sorting by the selected column and resetting the sort.</li>
|
||||
<li>Hiding the selected column or adjusting the visibility of every column.</li>
|
||||
<li>Adding new columns to the left or the right of the column.</li>
|
||||
<li>Editing the current column.</li>
|
||||
<li>Deleting the current column.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Right clicking on the space to the right of the columns, allows:
|
||||
<ul>
|
||||
<li>Adjusting the visibility of every column.</li>
|
||||
<li>Adding new columns.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Right clicking on a row, allows:
|
||||
<ul>
|
||||
<li>Opening the corresponding note of the row in a new tab, split, window
|
||||
or quick editing it.</li>
|
||||
<li>Inserting rows above, below or as a child note.</li>
|
||||
<li>Deleting the row.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Editing data</h3>
|
||||
<p>Simply click on a cell within a row to change its value. The change will
|
||||
not only reflect in the table, but also as an attribute of the corresponding
|
||||
note.</p>
|
||||
<ul>
|
||||
<li>The editing will respect the type of the promoted attribute, by presenting
|
||||
a normal text box, a number selector or a date selector for example.</li>
|
||||
<li>It also possible to change the title of a note.</li>
|
||||
<li>Editing relations is also possible
|
||||
<ul>
|
||||
<li>Simply click on a relation and it will become editable. Enter the text
|
||||
to look for a note and click on it.</li>
|
||||
<li>To remove a relation, remove the title of the note from the text box and
|
||||
click outside the cell.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Editing columns</h3>
|
||||
<p>It is possible to edit a column by right clicking it and selecting <em>Edit column.</em> This
|
||||
will basically change the label/relation definition at the collection level.</p>
|
||||
<p>If the <em>Name</em> field of a column is changed, this will trigger a batch
|
||||
operation in which the corresponding label/relation will be renamed in
|
||||
all the children.</p>
|
||||
<h2>Working with the data</h2>
|
||||
<h3>Sorting by column</h3>
|
||||
<p>By default, the order of the notes matches the order in the <a class="reference-link"
|
||||
href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>. However, it is possible
|
||||
to sort the data by the values of a column:</p>
|
||||
<ul>
|
||||
<li>To do so, simply click on a column.</li>
|
||||
<li>To switch between ascending or descending sort, simply click again on
|
||||
the same column. The arrow next to the column will indicate the direction
|
||||
of the sort.</li>
|
||||
<li>To disable sorting and fall back to the original order, right click any
|
||||
column on the header and select <em>Clear sorting.</em>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Reordering and hiding columns</h3>
|
||||
<ul>
|
||||
<li>Columns can be reordered by dragging the header of the columns.</li>
|
||||
<li>Columns can be hidden or shown by right clicking on a column and clicking
|
||||
the item corresponding to the column.</li>
|
||||
</ul>
|
||||
<h3>Reordering rows</h3>
|
||||
<p>Notes can be dragged around to change their order. To do so, move the
|
||||
mouse over the three vertical dots near the number row and drag the mouse
|
||||
to the desired position.</p>
|
||||
<p>This will also change the order of the note in the <a class="reference-link"
|
||||
href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</p>
|
||||
<p>Reordering does have some limitations:</p>
|
||||
<ul>
|
||||
<li>If the parent note has <code>#sorted</code>, reordering will be disabled.</li>
|
||||
<li>If using nested tables, then reordering will also be disabled.</li>
|
||||
<li>Currently, it's possible to reorder notes even if column sorting is used,
|
||||
but the result might be inconsistent.</li>
|
||||
</ul>
|
||||
<h3>Nested trees</h3>
|
||||
<p>If the child notes of the collection also have their own child notes,
|
||||
then they will be displayed in a hierarchy.</p>
|
||||
<p>Next to the title of each element there will be a button to expand or
|
||||
collapse. By default, all items are expanded.</p>
|
||||
<p>Since nesting is not always desirable, it is possible to limit the nesting
|
||||
to a certain number of levels or even disable it completely. To do so,
|
||||
either:</p>
|
||||
<ul>
|
||||
<li>Go to <em>Collection Properties</em> in the <a class="reference-link"
|
||||
href="#root/_help_BlN9DFI679QC">Ribbon</a> and look for the <em>Max nesting depth</em> section.
|
||||
<ul>
|
||||
<li>To disable nesting, type 0 and press Enter.</li>
|
||||
<li>To limit to a certain depth, type in the desired number (e.g. 2 to only
|
||||
display children and sub-children).</li>
|
||||
<li>To re-enable unlimited nesting, remove the number and press Enter.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Manually set <code>maxNestingDepth</code> to the desired value.</li>
|
||||
</ul>
|
||||
<p>Limitations:</p>
|
||||
<ul>
|
||||
<li>While in this mode, it's not possible to reorder notes.</li>
|
||||
</ul>
|
||||
<h2>Limitations</h2>
|
||||
<p>Multi-value labels and relations are not supported. If a <a class="reference-link"
|
||||
href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a> is defined
|
||||
with a <em>Multi value</em> specificity, they will be ignored.</p>
|
||||
<h2>Use in search</h2>
|
||||
<p>The table view can be used in a <a class="reference-link" href="#root/_help_m523cpzocqaD">Saved Search</a> by
|
||||
adding the <code>#viewType=table</code> attribute.</p>
|
||||
<p>Unlike when used in a Collection, saved searches are not limited to the
|
||||
sub-hierarchy of a note and allows for advanced queries thanks to the power
|
||||
of the <a class="reference-link" href="#root/_help_eIg8jdvaoNNd">Search</a>.</p>
|
||||
<p>However, there are also some limitations:</p>
|
||||
<ul>
|
||||
<li>It's not possible to reorder notes.</li>
|
||||
<li>It's not possible to add a new row.</li>
|
||||
</ul>
|
||||
<p>Columns are supported, by being defined as <a class="reference-link"
|
||||
href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a> to the
|
||||
<a
|
||||
class="reference-link" href="#root/_help_m523cpzocqaD">Saved Search</a> note.</p>
|
||||
<p>Editing is also supported.</p>
|
||||
@@ -2,124 +2,122 @@
|
||||
<h2>Legacy Themes</h2>
|
||||
<p>These themes may or may not be compatible with the latest versions of
|
||||
TriliumNext and are based on the original/legacy theme.</p>
|
||||
<figure class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Theme</th>
|
||||
<th>Author</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="https://github.com/tobealive/trilium-midnight-theme">Midnight</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/tobealive">tobealive</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/tobealive/trilum-eote-theme">EOTE</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/tobealive">tobealive</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Abourass/TriliumThemes">Trilium Themes</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/Abourass">Abourass</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/ZMonk91/Material-Dark-Trilium">MaterialDark</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/ZMonk91">ZMonk91</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/jaroet/trilium-theme-lightslategray">lightslategray</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/jaroet">jaroet</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/raphwriter/trilium-theme-melon">melon-4</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/raphwriter">raphwriter</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Engr-AllanG/trilium-themes">Neon_Dark</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/Engr-AllanG">Engr-AllanG</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Engr-AllanG/trilium-themes">Coder_Dark</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/Engr-AllanG">Engr-AllanG</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/idelem/trilium-theme-velvet">velvet</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/idelem">idelem</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/SADAVA/trilium-notes-theme-dark-plus">Dark Plus</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/SADAVA">SADAVA</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/WKSu/trilium-solarized-theme">Solarized</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/WKSu">WKSu</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/en3r0/Trilium-Nord-Theme">Nord</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/en3r0">en3r0</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/AllanZyne/trilium-bear-theme">Bear Note Light</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/AllanZyne">AllanZyne</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/AllanZyne/trilium-bear-theme">Bear Note Dark</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/AllanZyne">AllanZyne</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Sebiann/miku-hatsune-trilium-theme">Miku Hatsune</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/Sebiann">Sebiann</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/cwilliams5/Midnight-Trilium-Dark-Mode">Midnight</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/cwilliams5">cwilliams5</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/SiriusXT/trilium-theme-blue">Blue</a> (light)</td>
|
||||
<td><a href="https://github.com/SiriusXT">SiriusXT</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/SiriusXT/trilium-theme-blue">Blue</a> (dark)</td>
|
||||
<td><a href="https://github.com/SiriusXT">SiriusXT</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Theme</th>
|
||||
<th>Author</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="https://github.com/tobealive/trilium-midnight-theme">Midnight</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/tobealive">tobealive</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/tobealive/trilum-eote-theme">EOTE</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/tobealive">tobealive</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Abourass/TriliumThemes">Trilium Themes</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/Abourass">Abourass</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/ZMonk91/Material-Dark-Trilium">MaterialDark</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/ZMonk91">ZMonk91</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/jaroet/trilium-theme-lightslategray">lightslategray</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/jaroet">jaroet</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/raphwriter/trilium-theme-melon">melon-4</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/raphwriter">raphwriter</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Engr-AllanG/trilium-themes">Neon_Dark</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/Engr-AllanG">Engr-AllanG</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Engr-AllanG/trilium-themes">Coder_Dark</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/Engr-AllanG">Engr-AllanG</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/idelem/trilium-theme-velvet">velvet</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/idelem">idelem</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/SADAVA/trilium-notes-theme-dark-plus">Dark Plus</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/SADAVA">SADAVA</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/WKSu/trilium-solarized-theme">Solarized</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/WKSu">WKSu</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/en3r0/Trilium-Nord-Theme">Nord</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/en3r0">en3r0</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/AllanZyne/trilium-bear-theme">Bear Note Light</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/AllanZyne">AllanZyne</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/AllanZyne/trilium-bear-theme">Bear Note Dark</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/AllanZyne">AllanZyne</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Sebiann/miku-hatsune-trilium-theme">Miku Hatsune</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/Sebiann">Sebiann</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/cwilliams5/Midnight-Trilium-Dark-Mode">Midnight</a>
|
||||
</td>
|
||||
<td><a href="https://github.com/cwilliams5">cwilliams5</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/SiriusXT/trilium-theme-blue">Blue</a> (light)</td>
|
||||
<td><a href="https://github.com/SiriusXT">SiriusXT</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/SiriusXT/trilium-theme-blue">Blue</a> (dark)</td>
|
||||
<td><a href="https://github.com/SiriusXT">SiriusXT</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<aside class="admonition tip">
|
||||
<p>If you would like to add your theme to this gallery, write a new post
|
||||
in <a href="https://github.com/TriliumNext/Notes/discussions/categories/show-and-tell">👐 Show and tell</a>.</p>
|
||||
|
||||
190
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types.html
generated
vendored
@@ -31,102 +31,94 @@
|
||||
used to edit the <a href="#root/_help_4FahAwuGTAwC">source of a note</a>.</p>
|
||||
<h2>Supported note types</h2>
|
||||
<p>The following note types are supported by Trilium:</p>
|
||||
<figure class="table"
|
||||
style="width:100%;">
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:29.42%;">
|
||||
<col style="width:70.58%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Note Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_iPIMuisry3hd">Text</a>
|
||||
</td>
|
||||
<td>The default note type, which allows for rich text formatting, images,
|
||||
admonitions and right-to-left support.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_6f9hih2hXXZk">Code</a>
|
||||
</td>
|
||||
<td>Uses a mono-space font and can be used to store larger chunks of code
|
||||
or plain text than a text note, and has better syntax highlighting.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_m523cpzocqaD">Saved Search</a>
|
||||
</td>
|
||||
<td>Stores the information about a search (the search text, criteria, etc.)
|
||||
for later use. Can be used for quick filtering of a large amount of notes,
|
||||
for example. The search can easily be triggered.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_iRwzGnHPzonm">Relation Map</a>
|
||||
</td>
|
||||
<td>Allows easy creation of notes and relations between them. Can be used
|
||||
for mainly relational data such as a family tree.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_bdUJEHsAPYQR">Note Map</a>
|
||||
</td>
|
||||
<td>Displays the relationships between the notes, whether via relations or
|
||||
their hierarchical structure.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_HcABDtFCkbFN">Render Note</a>
|
||||
</td>
|
||||
<td>Used in <a class="reference-link" href="#root/_help_CdNpE2pqjmI6">Scripting</a>,
|
||||
it displays the HTML content of another note. This allows displaying any
|
||||
kind of content, provided there is a script behind it to generate it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_GTwFsgaA0lCt">Collections</a>
|
||||
</td>
|
||||
<td>
|
||||
<p>Displays the children of the note either as a grid, a list, or for a more
|
||||
specialized case: a calendar.</p>
|
||||
<p>Generally useful for easy reading of short notes.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_s1aBHPd79XYj">Mermaid Diagrams</a>
|
||||
</td>
|
||||
<td>Displays diagrams such as bar charts, flow charts, state diagrams, etc.
|
||||
Requires a bit of technical knowledge since the diagrams are written in
|
||||
a specialized format.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_grjYqerjn243">Canvas</a>
|
||||
</td>
|
||||
<td>Allows easy drawing of sketches, diagrams, handwritten content. Uses the
|
||||
same technology behind <a href="https://excalidraw.com">excalidraw.com</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_1vHRoWCEjj0L">Web View</a>
|
||||
</td>
|
||||
<td>Displays the content of an external web page, similar to a browser.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_gBbsAeiuUxI5">Mind Map</a>
|
||||
</td>
|
||||
<td>Easy for brainstorming ideas, by placing them in a hierarchical layout.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_81SGnPGMk7Xc">Geo Map View</a>
|
||||
</td>
|
||||
<td>Displays the children of the note as a geographical map, one use-case
|
||||
would be to plan vacations. It even has basic support for tracks. Notes
|
||||
can also be created from it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_W8vYD3Q1zjCR">File</a>
|
||||
</td>
|
||||
<td>Represents an uploaded file such as PDFs, images, video or audio files.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Note Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_iPIMuisry3hd">Text</a>
|
||||
</td>
|
||||
<td>The default note type, which allows for rich text formatting, images,
|
||||
admonitions and right-to-left support.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_6f9hih2hXXZk">Code</a>
|
||||
</td>
|
||||
<td>Uses a mono-space font and can be used to store larger chunks of code
|
||||
or plain text than a text note, and has better syntax highlighting.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_m523cpzocqaD">Saved Search</a>
|
||||
</td>
|
||||
<td>Stores the information about a search (the search text, criteria, etc.)
|
||||
for later use. Can be used for quick filtering of a large amount of notes,
|
||||
for example. The search can easily be triggered.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_iRwzGnHPzonm">Relation Map</a>
|
||||
</td>
|
||||
<td>Allows easy creation of notes and relations between them. Can be used
|
||||
for mainly relational data such as a family tree.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_bdUJEHsAPYQR">Note Map</a>
|
||||
</td>
|
||||
<td>Displays the relationships between the notes, whether via relations or
|
||||
their hierarchical structure.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_HcABDtFCkbFN">Render Note</a>
|
||||
</td>
|
||||
<td>Used in <a class="reference-link" href="#root/_help_CdNpE2pqjmI6">Scripting</a>,
|
||||
it displays the HTML content of another note. This allows displaying any
|
||||
kind of content, provided there is a script behind it to generate it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_GTwFsgaA0lCt">Collections</a>
|
||||
</td>
|
||||
<td>Displays the children of the note either as a grid, a list, or for a more
|
||||
specialized case: a calendar.
|
||||
<br>
|
||||
<br>Generally useful for easy reading of short notes.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_s1aBHPd79XYj">Mermaid Diagrams</a>
|
||||
</td>
|
||||
<td>Displays diagrams such as bar charts, flow charts, state diagrams, etc.
|
||||
Requires a bit of technical knowledge since the diagrams are written in
|
||||
a specialized format.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_grjYqerjn243">Canvas</a>
|
||||
</td>
|
||||
<td>Allows easy drawing of sketches, diagrams, handwritten content. Uses the
|
||||
same technology behind <a href="https://excalidraw.com">excalidraw.com</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_1vHRoWCEjj0L">Web View</a>
|
||||
</td>
|
||||
<td>Displays the content of an external web page, similar to a browser.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_gBbsAeiuUxI5">Mind Map</a>
|
||||
</td>
|
||||
<td>Easy for brainstorming ideas, by placing them in a hierarchical layout.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_81SGnPGMk7Xc">Geo Map View</a>
|
||||
</td>
|
||||
<td>Displays the children of the note as a geographical map, one use-case
|
||||
would be to plan vacations. It even has basic support for tracks. Notes
|
||||
can also be created from it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a class="reference-link" href="#root/_help_W8vYD3Q1zjCR">File</a>
|
||||
</td>
|
||||
<td>Represents an uploaded file such as PDFs, images, video or audio files.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
49
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Collections.html
generated
vendored
@@ -4,27 +4,31 @@
|
||||
child notes into one continuous view. This makes it ideal for reading extensive
|
||||
information broken into smaller, manageable segments.</p>
|
||||
<ul>
|
||||
<li><a class="reference-link" href="#root/_help_8QqnMzx393bx">Grid View</a> which
|
||||
<li data-list-item-id="e7f3117635b8c3e905c71f2839e331942"><a class="reference-link" href="#root/_help_8QqnMzx393bx">Grid View</a> which
|
||||
is the default presentation method for child notes (see <a class="reference-link"
|
||||
href="#root/_help_0ESUbbAxVnoK">Note List</a>), where the notes are displayed
|
||||
as tiles with their title and content being visible.</li>
|
||||
<li><a class="reference-link" href="#root/_help_mULW0Q3VojwY">List View</a> is
|
||||
<li data-list-item-id="e27a2ec6976e44512c9c52ba8d7a2ef76"><a class="reference-link" href="#root/_help_mULW0Q3VojwY">List View</a> is
|
||||
similar to <a class="reference-link" href="#root/_help_8QqnMzx393bx">Grid View</a>,
|
||||
but it displays the notes one under the other with the content being expandable/collapsible,
|
||||
but also works recursively.</li>
|
||||
</ul>
|
||||
<p>More specialized collections were introduced, such as the:</p>
|
||||
<ul>
|
||||
<li><a class="reference-link" href="#root/_help_xWbu3jpNWapp">Calendar View</a> which
|
||||
<li data-list-item-id="eae5c2f56f9a75e7ed813fe419d4a05a4"><a class="reference-link" href="#root/_help_xWbu3jpNWapp">Calendar View</a> which
|
||||
displays a week, month or year calendar with the notes being shown as events.
|
||||
New events can be added easily by dragging across the calendar.</li>
|
||||
<li><a class="reference-link" href="#root/_help_81SGnPGMk7Xc">Geo Map View</a> which
|
||||
<li
|
||||
data-list-item-id="e24642a7a4c2443497fd814d78f1ca784"><a class="reference-link" href="#root/_help_81SGnPGMk7Xc">Geo Map View</a> which
|
||||
displays a geographical map in which the notes are represented as markers/pins
|
||||
on the map. New events can be easily added by pointing on the map.</li>
|
||||
<li><a class="reference-link" href="#root/_help_2FvYrpmOXm29">Table View</a> displays
|
||||
each note as a row in a table, with <a class="reference-link" href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a> being
|
||||
shown as well. This makes it easy to visualize attributes of notes, as
|
||||
well as making them easily editable.</li>
|
||||
<li
|
||||
data-list-item-id="eaa80d024c07145aa8f4443d86b01295e"><a class="reference-link" href="#root/_help_2FvYrpmOXm29">Table View</a> displays
|
||||
each note as a row in a table, with <a class="reference-link" href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a> being
|
||||
shown as well. This makes it easy to visualize attributes of notes, as
|
||||
well as making them easily editable.</li>
|
||||
<li data-list-item-id="ec7243729048b57d173e3f7dfb232adca"><a class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/GTwFsgaA0lCt/_help_CtBQqbwXDx1w">Board View</a> (Kanban)
|
||||
displays notes in columns, grouped by the value of a label.</li>
|
||||
</ul>
|
||||
<p>For a quick presentation of all the supported view types, see the child
|
||||
notes of this help page, including screenshots.</p>
|
||||
@@ -32,12 +36,39 @@
|
||||
<p>To adjust the view type, see the dedicated <em>Collections</em> tab in the
|
||||
<a
|
||||
class="reference-link" href="#root/_help_BlN9DFI679QC">Ribbon</a>.</p>
|
||||
<h2>Use in saved search</h2>
|
||||
<h2>Use cases</h2>
|
||||
<h3>Creating a new collection</h3>
|
||||
<p>To create a new collections, right click in the <a class="reference-link"
|
||||
href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_oPVyFC7WL2Lp">Note Tree</a> and
|
||||
look for the <em>Collections</em> entry and select the desired type.</p>
|
||||
<h3>Adding a description to a collection</h3>
|
||||
<p>To add a text before the collection, for example to describe it:</p>
|
||||
<ol>
|
||||
<li data-list-item-id="e7eaebf201ba06033f72b0c556aa7bfb9">Create a new collection.</li>
|
||||
<li data-list-item-id="ecf21cdac7d4b10dbf7015fa4811cae6a">In the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_BlN9DFI679QC">Ribbon</a>,
|
||||
go to <em>Basic Properties</em> and change the note type from <em>Collection</em> to <em>Text</em>.</li>
|
||||
</ol>
|
||||
<p>Now the text will be displayed above while still maintaining the collection
|
||||
view.</p>
|
||||
<h3>Using saved search</h3>
|
||||
<p>Since collections are based on the <a class="reference-link" href="#root/_help_0ESUbbAxVnoK">Note List</a> mechanism,
|
||||
it's possible to apply the same configuration to <a class="reference-link"
|
||||
href="#root/_help_m523cpzocqaD">Saved Search</a> to do advanced querying
|
||||
and presenting the result in an adequate matter such as a calendar, a table
|
||||
or even a map.</p>
|
||||
<h3>Creating a collection from scratch</h3>
|
||||
<p>By default, collections come with a default configuration and sometimes
|
||||
even sample notes. To create a collection completely from scratch:</p>
|
||||
<ol>
|
||||
<li data-list-item-id="ec1bab7b7f39744ea42e187c10b7d7216">Create a new note of type <em>Text</em> (or any type).</li>
|
||||
<li data-list-item-id="edcd0acc96fef97ba37695cd69c19a1b6">In the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_BlN9DFI679QC">Ribbon</a>,
|
||||
go to <em>Basic Properties</em> and select <em>Collection</em> as the note
|
||||
type.</li>
|
||||
<li data-list-item-id="e48769aec6e389d541c275f59220f032e">Still in the ribbon, go to <em>Collection Properties</em> and select the
|
||||
desired view type.</li>
|
||||
<li data-list-item-id="e5d79e0729f8daaae2c32cce773898464">Consult the help page of the corresponding view type in order to understand
|
||||
how to configure them.</li>
|
||||
</ol>
|
||||
<h2>Under the hood</h2>
|
||||
<p>Collections by themselves are simply notes with no content that rely on
|
||||
the <a class="reference-link" href="#root/_help_0ESUbbAxVnoK">Note List</a> mechanism
|
||||
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 605 B After Width: | Height: | Size: 605 B |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 249 KiB |