mirror of
https://github.com/zadam/trilium.git
synced 2025-12-05 06:54:23 +01:00
Compare commits
72 Commits
4f6161cd5b
...
f25507efeb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f25507efeb | ||
|
|
a867a25d5f | ||
|
|
10910ac2ed | ||
|
|
514f5a0c81 | ||
|
|
6b18ed6477 | ||
|
|
e07b6cc409 | ||
|
|
5ae67fda7f | ||
|
|
1a03c0ca9f | ||
|
|
dc211b4d00 | ||
|
|
2e767ffde1 | ||
|
|
9f74a54c0d | ||
|
|
a7870495ac | ||
|
|
a8ec323ea8 | ||
|
|
4b7d243406 | ||
|
|
1855588270 | ||
|
|
e545e0c3a5 | ||
|
|
b24a0f1595 | ||
|
|
3f2bfc3050 | ||
|
|
66f100d534 | ||
|
|
e641d5ba0f | ||
|
|
6f8cebf954 | ||
|
|
2d2f2457cb | ||
|
|
68fa243af4 | ||
|
|
9cb133c8a6 | ||
|
|
33f6c75917 | ||
|
|
49acd5e8c2 | ||
|
|
85266adf20 | ||
|
|
27edea8380 | ||
|
|
dccddb8d43 | ||
|
|
5f9d7a223a | ||
|
|
29b71262d6 | ||
|
|
0cfcbcf5df | ||
|
|
e1258384d8 | ||
|
|
8837eddd40 | ||
|
|
f919d46e9a | ||
|
|
98094fac63 | ||
|
|
f94ccee252 | ||
|
|
7c4b338539 | ||
|
|
d88431f9e3 | ||
|
|
5f86068489 | ||
|
|
36bc226674 | ||
|
|
7947745218 | ||
|
|
19697aabfb | ||
|
|
87af23598d | ||
|
|
4ed7966a5a | ||
|
|
8d86b5c7e6 | ||
|
|
cdf9458962 | ||
|
|
83df37c2d1 | ||
|
|
bc7b3165c8 | ||
|
|
c332e9764a | ||
|
|
004d48d36c | ||
|
|
28b263a445 | ||
|
|
e0e9310907 | ||
|
|
68b9159b2b | ||
|
|
f5940cbf70 | ||
|
|
bfb143bb51 | ||
|
|
9682df6240 | ||
|
|
a718908385 | ||
|
|
309fbab2e6 | ||
|
|
99da145d65 | ||
|
|
2163334c4f | ||
|
|
f5d180af6b | ||
|
|
1d1639e5e1 | ||
|
|
6ab05fdb76 | ||
|
|
91ae8c0aaf | ||
|
|
fcb69c0190 | ||
|
|
2cf6fe4352 | ||
|
|
e3a2623a53 | ||
|
|
e0c0a423c1 | ||
|
|
fd99246c49 | ||
|
|
d247edd870 | ||
|
|
5d5fd2079a |
334
.github/copilot-instructions.md
vendored
Normal file
334
.github/copilot-instructions.md
vendored
Normal file
@ -0,0 +1,334 @@
|
||||
# Trilium Notes - AI Coding Agent Instructions
|
||||
|
||||
## Project Overview
|
||||
|
||||
Trilium Notes is a hierarchical note-taking application with advanced features like synchronization, scripting, and rich text editing. Built as a TypeScript monorepo using pnpm, it implements a three-layer caching architecture (Becca/Froca/Shaca) with a widget-based UI system and supports extensive user scripting capabilities.
|
||||
|
||||
## Essential Architecture Patterns
|
||||
|
||||
### Three-Layer Cache System (Critical to Understand)
|
||||
- **Becca** (`apps/server/src/becca/`): Server-side entity cache, primary data source
|
||||
- **Froca** (`apps/client/src/services/froca.ts`): Client-side mirror synchronized via WebSocket
|
||||
- **Shaca** (`apps/server/src/share/`): Optimized cache for public/shared notes
|
||||
|
||||
**Key insight**: Never bypass these caches with direct DB queries. Always use `becca.notes[noteId]`, `froca.getNote()`, or equivalent cache methods.
|
||||
|
||||
### Entity Relationship Model
|
||||
Notes use a **multi-parent tree** via branches:
|
||||
- `BNote` - The note content and metadata
|
||||
- `BBranch` - Tree relationships (one note can have multiple parents via cloning)
|
||||
- `BAttribute` - Key-value metadata attached to notes (labels and relations)
|
||||
|
||||
### Entity Change System & Sync
|
||||
Every entity modification (notes, branches, attributes) creates an `EntityChange` record that drives synchronization:
|
||||
|
||||
```typescript
|
||||
// Entity changes are automatically tracked
|
||||
note.title = "New Title";
|
||||
note.save(); // Creates EntityChange record with changeId
|
||||
|
||||
// Sync protocol via WebSocket
|
||||
ws.sendMessage({ type: 'sync-pull-in-progress', ... });
|
||||
```
|
||||
|
||||
**Critical**: This is why you must use Becca/Froca methods instead of direct DB writes - they create the change tracking records needed for sync.
|
||||
|
||||
### Entity Lifecycle & Events
|
||||
The event system (`apps/server/src/services/events.ts`) broadcasts entity lifecycle events:
|
||||
|
||||
```typescript
|
||||
// Subscribe to events in widgets or services
|
||||
eventService.subscribe('noteChanged', ({ noteId }) => {
|
||||
// React to note changes
|
||||
});
|
||||
|
||||
// Common events: noteChanged, branchChanged, attributeChanged, noteDeleted
|
||||
// Widget method: entitiesReloadedEvent({loadResults}) for handling reloads
|
||||
```
|
||||
|
||||
**Becca loader priorities**: Events are emitted in order (notes → branches → attributes) during initial load to ensure referential integrity.
|
||||
|
||||
### TaskContext for Long Operations
|
||||
Use `TaskContext` for operations with progress reporting (imports, exports, bulk operations):
|
||||
|
||||
```typescript
|
||||
const taskContext = new TaskContext("task-id", "import", "Import Notes");
|
||||
taskContext.increaseProgressCount();
|
||||
|
||||
// WebSocket messages: { type: 'taskProgressCount', taskId, taskType, data, progressCount }
|
||||
|
||||
**Pattern**: All long-running operations (delete note trees, export, import) use TaskContext to send WebSocket updates to the frontend.
|
||||
|
||||
### Protected Session Handling
|
||||
Protected notes require an active encryption session:
|
||||
|
||||
```typescript
|
||||
// Always check before accessing protected content
|
||||
if (note.isContentAvailable()) {
|
||||
const content = note.getContent(); // Safe
|
||||
} else {
|
||||
const title = note.getTitleOrProtected(); // Returns "[protected]"
|
||||
}
|
||||
|
||||
// Protected session management
|
||||
protectedSessionService.isProtectedSessionAvailable() // Check session
|
||||
protectedSessionService.startProtectedSession() // After password entry
|
||||
```
|
||||
|
||||
**Session timeout**: Protected sessions expire after inactivity. The encryption key is kept in memory only.
|
||||
|
||||
### Attribute Inheritance Patterns
|
||||
Attributes can be inherited through three mechanisms:
|
||||
|
||||
```typescript
|
||||
// 1. Standard inheritance (#hidePromotedAttributes ~hidePromotedAttributes)
|
||||
note.getInheritableAttributes() // Walks up parent tree
|
||||
|
||||
// 2. Child prefix inheritance (child:label copies to children)
|
||||
parentNote.setLabel("child:icon", "book") // All children inherit this
|
||||
|
||||
// 3. Template relation inheritance (#template=templateNoteId)
|
||||
note.setRelation("template", templateNoteId)
|
||||
note.getInheritedAttributes() // Includes template's inheritable attributes
|
||||
```
|
||||
|
||||
**Cycle prevention**: Inheritance tracking prevents infinite loops when notes reference each other.
|
||||
|
||||
### Widget-Based UI Architecture
|
||||
All UI components extend from widget base classes (`apps/client/src/widgets/`):
|
||||
|
||||
```typescript
|
||||
// Right panel widget (sidebar)
|
||||
class MyWidget extends RightPanelWidget {
|
||||
get position() { return 100; } // Order in panel
|
||||
get parentWidget() { return 'right-pane'; }
|
||||
isEnabled() { return this.note && this.note.hasLabel('myLabel'); }
|
||||
async refreshWithNote(note) { /* Update UI */ }
|
||||
}
|
||||
|
||||
// Note-aware widget (responds to note changes)
|
||||
class MyNoteWidget extends NoteContextAwareWidget {
|
||||
async refreshWithNote(note) { /* Refresh when note changes */ }
|
||||
async entitiesReloadedEvent({loadResults}) { /* Handle entity updates */ }
|
||||
}
|
||||
```
|
||||
|
||||
**Important**: Widgets use jQuery (`this.$widget`) for DOM manipulation. Don't mix React patterns here.
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Running & Testing
|
||||
```bash
|
||||
# From root directory
|
||||
pnpm install # Install dependencies
|
||||
corepack enable # Enable pnpm if not available
|
||||
pnpm server:start # Dev server (http://localhost:8080)
|
||||
pnpm server:start-prod # Production mode server
|
||||
pnpm desktop:start # Desktop app development
|
||||
pnpm server:test spec/etapi/search.spec.ts # Run specific test
|
||||
pnpm test:parallel # Client tests (can run parallel)
|
||||
pnpm test:sequential # Server tests (sequential due to shared DB)
|
||||
pnpm test:all # All tests (parallel + sequential)
|
||||
pnpm coverage # Generate coverage reports
|
||||
pnpm typecheck # Type check all projects
|
||||
```
|
||||
|
||||
### Building
|
||||
```bash
|
||||
pnpm client:build # Build client application
|
||||
pnpm server:build # Build server application
|
||||
pnpm desktop:build # Build desktop application
|
||||
```
|
||||
|
||||
### Test Organization
|
||||
- **Server tests** (`apps/server/spec/`): Must run sequentially (shared database state)
|
||||
- **Client tests** (`apps/client/src/`): Can run in parallel
|
||||
- **E2E tests** (`apps/server-e2e/`): Use Playwright for integration testing
|
||||
- **ETAPI tests** (`apps/server/spec/etapi/`): External API contract tests
|
||||
|
||||
**Pattern**: When adding new API endpoints, add tests in `spec/etapi/` following existing patterns (see `search.spec.ts`).
|
||||
|
||||
### Monorepo Navigation
|
||||
```
|
||||
apps/
|
||||
client/ # Frontend (shared by server & desktop)
|
||||
server/ # Node.js backend with REST API
|
||||
desktop/ # Electron wrapper
|
||||
web-clipper/ # Browser extension for saving web content
|
||||
db-compare/ # Database comparison tool
|
||||
dump-db/ # Database export utility
|
||||
edit-docs/ # Documentation editing tools
|
||||
packages/
|
||||
commons/ # Shared types and utilities
|
||||
ckeditor5/ # Custom rich text editor with Trilium-specific plugins
|
||||
codemirror/ # Code editor integration
|
||||
highlightjs/ # Syntax highlighting
|
||||
share-theme/ # Theme for shared/published notes
|
||||
ckeditor5-admonition/ # Admonition blocks plugin
|
||||
ckeditor5-footnotes/ # Footnotes plugin
|
||||
ckeditor5-math/ # Math equations plugin
|
||||
ckeditor5-mermaid/ # Mermaid diagrams plugin
|
||||
```
|
||||
|
||||
**Filter commands**: Use `pnpm --filter server test` to run commands in specific packages.
|
||||
|
||||
## Critical Code Patterns
|
||||
|
||||
### ETAPI Backwards Compatibility
|
||||
When adding query parameters to ETAPI endpoints (`apps/server/src/etapi/`), maintain backwards compatibility by checking if new params exist before changing response format.
|
||||
|
||||
**Pattern**: ETAPI consumers expect specific response shapes. Always check for breaking changes.
|
||||
|
||||
### Frontend-Backend Communication
|
||||
- **REST API**: `apps/server/src/routes/api/` - Internal endpoints (no auth required when `noAuthentication=true`)
|
||||
- **ETAPI**: `apps/server/src/etapi/` - External API with authentication
|
||||
- **WebSocket**: Real-time sync via `apps/server/src/services/ws.ts`
|
||||
|
||||
**Auth note**: ETAPI uses basic auth with tokens. Internal API endpoints trust the frontend.
|
||||
|
||||
### Database Migrations
|
||||
- Add scripts in `apps/server/src/migrations/YYMMDD_HHMM__description.sql`
|
||||
- Update schema in `apps/server/src/assets/db/schema.sql`
|
||||
- Never bypass Becca cache after migrations
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Never bypass the cache layers** - Always use `becca.notes[noteId]`, `froca.getNote()`, or equivalent cache methods. Direct database queries will cause sync issues between Becca/Froca/Shaca and won't create EntityChange records needed for synchronization.
|
||||
|
||||
2. **Protected notes require session check** - Before accessing `note.title` or `note.getContent()` on protected notes, check `note.isContentAvailable()` or use `note.getTitleOrProtected()` which handles this automatically.
|
||||
|
||||
3. **Widget lifecycle matters** - Override `refreshWithNote()` for note changes, `doRenderBody()` for initial render, `entitiesReloadedEvent()` for entity updates. Widgets use jQuery (`this.$widget`) - don't mix React patterns.
|
||||
|
||||
4. **Tests run differently** - Server tests must run sequentially (shared database state), client tests can run in parallel. Use `pnpm test:sequential` for backend, `pnpm test:parallel` for frontend.
|
||||
|
||||
5. **ETAPI requires authentication** - ETAPI endpoints use basic auth with tokens. Internal API endpoints (`apps/server/src/routes/api/`) trust the frontend when `noAuthentication=true`.
|
||||
|
||||
6. **Search expressions are evaluated in memory** - The search service loads all matching notes, scores them in JavaScript, then sorts. You cannot add SQL-level LIMIT/OFFSET without losing scoring functionality.
|
||||
|
||||
7. **Documentation edits have rules** - `docs/Script API/` is auto-generated (never edit directly). `docs/User Guide/` should be edited via `pnpm edit-docs:edit-docs`, not manually. Only `docs/Developer Guide/` and `docs/Release Notes/` are safe for direct Markdown editing.
|
||||
|
||||
8. **pnpm workspace filtering** - Use `pnpm --filter server <command>` or shorthand `pnpm server:test` defined in root `package.json`. Note the `--filter` syntax, not `-F` or other shortcuts.
|
||||
|
||||
9. **Event subscription cleanup** - When subscribing to events in widgets, unsubscribe in `cleanup()` or `doDestroy()` to prevent memory leaks.
|
||||
|
||||
10. **Attribute inheritance can be complex** - When checking for labels/relations, use `note.getOwnedAttribute()` for direct attributes or `note.getAttribute()` for inherited ones. Don't assume attributes are directly on the note.
|
||||
|
||||
## TypeScript Configuration
|
||||
|
||||
- **Project references**: Monorepo uses TypeScript project references (`tsconfig.json`)
|
||||
- **Path mapping**: Use relative imports, not path aliases
|
||||
- **Build order**: `pnpm typecheck` builds all projects in dependency order
|
||||
- **Build system**: Uses Vite for fast development, ESBuild for production optimization
|
||||
- **Patches**: Custom patches in `patches/` directory for CKEditor and other dependencies
|
||||
|
||||
## Key Files for Context
|
||||
|
||||
- `apps/server/src/becca/entities/bnote.ts` - Note entity methods
|
||||
- `apps/client/src/services/froca.ts` - Frontend cache API
|
||||
- `apps/server/src/services/search/services/search.ts` - Search implementation
|
||||
- `apps/server/src/routes/routes.ts` - API route registration
|
||||
- `apps/client/src/widgets/basic_widget.ts` - Widget base class
|
||||
- `apps/server/src/main.ts` - Server startup entry point
|
||||
- `apps/client/src/desktop.ts` - Client initialization
|
||||
- `apps/server/src/services/backend_script_api.ts` - Scripting API
|
||||
- `apps/server/src/assets/db/schema.sql` - Database schema
|
||||
|
||||
## Note Types and Features
|
||||
|
||||
Trilium supports multiple note types with specialized widgets in `apps/client/src/widgets/type_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
|
||||
|
||||
### Collections
|
||||
Notes can be marked with the `#collection` label to enable collection view modes. Collections support multiple view types:
|
||||
- **List**: Standard list view
|
||||
- **Grid**: Card/grid layout
|
||||
- **Calendar**: Calendar-based view
|
||||
- **Table**: Tabular data view
|
||||
- **GeoMap**: Geographic map view
|
||||
- **Board**: Kanban-style board
|
||||
- **Presentation**: Slideshow presentation mode
|
||||
|
||||
View types are configured via `#viewType` label (e.g., `#viewType=table`). Each view mode stores its configuration in a separate attachment (e.g., `table.json`). Collections are organized separately from regular note type templates in the note creation menu.
|
||||
|
||||
## 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
|
||||
- Remember: scoring happens in-memory, not at database level
|
||||
|
||||
### Custom CKEditor Plugins
|
||||
- Create new package in `packages/` following existing plugin structure
|
||||
- Register in `packages/ckeditor5/src/plugins.ts`
|
||||
- See `ckeditor5-admonition`, `ckeditor5-footnotes`, `ckeditor5-math`, `ckeditor5-mermaid` for examples
|
||||
|
||||
### Database Migrations
|
||||
- Add migration scripts in `apps/server/src/migrations/YYMMDD_HHMM__description.sql`
|
||||
- Update schema in `apps/server/src/assets/db/schema.sql`
|
||||
- Never bypass Becca cache after migrations
|
||||
|
||||
## Security & Features
|
||||
|
||||
### Security Considerations
|
||||
- Per-note encryption with granular protected sessions
|
||||
- CSRF protection for API endpoints
|
||||
- OpenID and TOTP authentication support
|
||||
- Sanitization of user-generated content
|
||||
|
||||
### Scripting System
|
||||
Trilium provides powerful user scripting capabilities:
|
||||
- **Frontend scripts**: Run in browser context with UI access
|
||||
- **Backend scripts**: Run in Node.js context with full API access
|
||||
- Script API documentation in `docs/Script API/`
|
||||
- Backend API available via `api` object in script context
|
||||
|
||||
### Internationalization
|
||||
- Translation files in `apps/client/src/translations/`
|
||||
- Use translation system via `t()` function
|
||||
- Automatic pluralization: Add `_other` suffix to translation keys (e.g., `item` and `item_other` for singular/plural)
|
||||
|
||||
## Testing Conventions
|
||||
|
||||
```typescript
|
||||
// ETAPI test pattern
|
||||
describe("etapi/feature", () => {
|
||||
beforeAll(async () => {
|
||||
config.General.noAuthentication = false;
|
||||
app = await buildApp();
|
||||
token = await login(app);
|
||||
});
|
||||
|
||||
it("should test feature", async () => {
|
||||
const response = await supertest(app)
|
||||
.get("/etapi/notes?search=test")
|
||||
.auth(USER, token, { type: "basic" })
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.results).toBeDefined();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Questions to Verify Understanding
|
||||
|
||||
Before implementing significant changes, confirm:
|
||||
- Is this touching the cache layer? (Becca/Froca/Shaca must stay in sync via EntityChange records)
|
||||
- Does this change API response shape? (Check backwards compatibility for ETAPI)
|
||||
- Are you adding search features? (Understand expression-based architecture and in-memory scoring first)
|
||||
- Is this a new widget? (Know which base class and lifecycle methods to use)
|
||||
- Does this involve protected notes? (Check `isContentAvailable()` before accessing content)
|
||||
- Is this a long-running operation? (Use TaskContext for progress reporting)
|
||||
- Are you working with attributes? (Understand inheritance patterns: direct, child-prefix, template)
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,6 +8,7 @@ out-tsc
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
.pnpm-store
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
@ -18,6 +19,7 @@ node_modules
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
.devcontainer
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
|
||||
19
README.md
19
README.md
@ -146,6 +146,21 @@ Here's the language coverage we have so far:
|
||||
|
||||
### Code
|
||||
|
||||
General (OS / docker / podman, etc.) dependencies:
|
||||
|
||||
Debian
|
||||
```
|
||||
apt update
|
||||
apt install -y build-essential python3 make g++ libsqlite3-dev
|
||||
corepack enable
|
||||
```
|
||||
|
||||
Alpine
|
||||
```
|
||||
apk add --no-cache build-base python3 python3-dev sqlite-dev
|
||||
corepack enable
|
||||
```
|
||||
|
||||
Download the repository, install dependencies using `pnpm` and then run the server (available at http://localhost:8080):
|
||||
```shell
|
||||
git clone https://github.com/TriliumNext/Trilium.git
|
||||
@ -154,6 +169,10 @@ pnpm install
|
||||
pnpm run server:start
|
||||
```
|
||||
|
||||
> If you faced with some problems, try to delete all `node_modules` and `.pnpm-store` folders, not only from the root, from every directory, like `apps/{app_name}/node_modules`and `/packages/{package_name}/node_modules` and then reinstall it by the `pnpm install`.
|
||||
|
||||
Share styles not compiling by default, if you see share page without styles, make `pnpm run server:build` and then run development server.
|
||||
|
||||
### Documentation
|
||||
|
||||
Download the repository, install dependencies using `pnpm` and then run the environment required to edit the documentation:
|
||||
|
||||
@ -29,6 +29,7 @@ import type AppContext from "../components/app_context.js";
|
||||
import NoteDetail from "../widgets/NoteDetail.jsx";
|
||||
import MobileEditorToolbar from "../widgets/type_widgets/text/mobile_editor_toolbar.jsx";
|
||||
import PromotedAttributes from "../widgets/PromotedAttributes.jsx";
|
||||
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
|
||||
|
||||
const MOBILE_CSS = `
|
||||
<style>
|
||||
@ -142,6 +143,7 @@ export default class MobileLayout {
|
||||
.id("detail-container")
|
||||
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-7 col-md-8 col-lg-9")
|
||||
.child(
|
||||
new SplitNoteContainer(() =>
|
||||
new NoteWrapperWidget()
|
||||
.child(
|
||||
new FlexContainer("row")
|
||||
@ -172,6 +174,7 @@ export default class MobileLayout {
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.child(
|
||||
new FlexContainer("column")
|
||||
.contentSized()
|
||||
|
||||
@ -2,26 +2,32 @@ import { t } from "../services/i18n.js";
|
||||
import contextMenu, { type ContextMenuEvent, type MenuItem } from "./context_menu.js";
|
||||
import appContext, { type CommandNames } from "../components/app_context.js";
|
||||
import type { ViewScope } from "../services/link.js";
|
||||
import utils, { isMobile } from "../services/utils.js";
|
||||
import { getClosestNtxId } from "../widgets/widget_utils.js";
|
||||
import type { LeafletMouseEvent } from "leaflet";
|
||||
|
||||
function openContextMenu(notePath: string, e: ContextMenuEvent, viewScope: ViewScope = {}, hoistedNoteId: string | null = null) {
|
||||
contextMenu.show({
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
items: getItems(),
|
||||
selectMenuItemHandler: ({ command }) => handleLinkContextMenuItem(command, notePath, viewScope, hoistedNoteId)
|
||||
items: getItems(e),
|
||||
selectMenuItemHandler: ({ command }) => handleLinkContextMenuItem(command, e, notePath, viewScope, hoistedNoteId)
|
||||
});
|
||||
}
|
||||
|
||||
function getItems(): MenuItem<CommandNames>[] {
|
||||
function getItems(e: ContextMenuEvent | LeafletMouseEvent): MenuItem<CommandNames>[] {
|
||||
const ntxId = getNtxId(e);
|
||||
const isMobileSplitOpen = isMobile() && appContext.tabManager.getNoteContextById(ntxId).getMainContext().getSubContexts().length > 1;
|
||||
|
||||
return [
|
||||
{ title: t("link_context_menu.open_note_in_new_tab"), command: "openNoteInNewTab", uiIcon: "bx bx-link-external" },
|
||||
{ title: t("link_context_menu.open_note_in_new_split"), command: "openNoteInNewSplit", uiIcon: "bx bx-dock-right" },
|
||||
{ title: !isMobileSplitOpen ? t("link_context_menu.open_note_in_new_split") : t("link_context_menu.open_note_in_other_split"), command: "openNoteInNewSplit", uiIcon: "bx bx-dock-right" },
|
||||
{ title: t("link_context_menu.open_note_in_new_window"), command: "openNoteInNewWindow", uiIcon: "bx bx-window-open" },
|
||||
{ title: t("link_context_menu.open_note_in_popup"), command: "openNoteInPopup", uiIcon: "bx bx-edit" }
|
||||
];
|
||||
}
|
||||
|
||||
function handleLinkContextMenuItem(command: string | undefined, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
|
||||
function handleLinkContextMenuItem(command: string | undefined, e: ContextMenuEvent | LeafletMouseEvent, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
|
||||
if (!hoistedNoteId) {
|
||||
hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId ?? null;
|
||||
}
|
||||
@ -29,15 +35,8 @@ function handleLinkContextMenuItem(command: string | undefined, notePath: string
|
||||
if (command === "openNoteInNewTab") {
|
||||
appContext.tabManager.openContextWithNote(notePath, { hoistedNoteId, viewScope });
|
||||
} else if (command === "openNoteInNewSplit") {
|
||||
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
|
||||
|
||||
if (!subContexts) {
|
||||
logError("subContexts is null");
|
||||
return;
|
||||
}
|
||||
|
||||
const { ntxId } = subContexts[subContexts.length - 1];
|
||||
|
||||
const ntxId = getNtxId(e);
|
||||
if (!ntxId) return;
|
||||
appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath, hoistedNoteId, viewScope });
|
||||
} else if (command === "openNoteInNewWindow") {
|
||||
appContext.triggerCommand("openInWindow", { notePath, hoistedNoteId, viewScope });
|
||||
@ -46,6 +45,18 @@ function handleLinkContextMenuItem(command: string | undefined, notePath: string
|
||||
}
|
||||
}
|
||||
|
||||
function getNtxId(e: ContextMenuEvent | LeafletMouseEvent) {
|
||||
if (utils.isDesktop()) {
|
||||
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
|
||||
if (!subContexts) return null;
|
||||
return subContexts[subContexts.length - 1].ntxId;
|
||||
} else if (e.target instanceof HTMLElement) {
|
||||
return getClosestNtxId(e.target);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getItems,
|
||||
handleLinkContextMenuItem,
|
||||
|
||||
@ -1016,11 +1016,9 @@ div[data-notify="container"] {
|
||||
}
|
||||
|
||||
svg.ck-icon {
|
||||
&.ck-icon_inherit-color {
|
||||
* {
|
||||
&.ck-icon_inherit-color path[fill="#333"] {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
&.note-icon {
|
||||
color: var(--main-text-color);
|
||||
@ -2596,3 +2594,24 @@ iframe.print-iframe {
|
||||
/* Workaround: set font weight only if the theme-next is not active */
|
||||
font-weight: var(--root-background, 800);
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
body.mobile {
|
||||
.split-note-container-widget {
|
||||
flex-direction: column !important;
|
||||
|
||||
.note-split {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.note-split.visible + .note-split.visible {
|
||||
border-top: 1px solid var(--main-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
#root-widget.virtual-keyboard-opened .note-split:not(:focus-within) {
|
||||
max-height: 80px;
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -201,8 +201,12 @@
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"relation": "العلاقة",
|
||||
"backlink": "{{count}} رابط راجع",
|
||||
"backlinks": "{{count}} روابط راجعة"
|
||||
"backlink_zero": "",
|
||||
"backlink_one": "{{count}} رابط راجع",
|
||||
"backlink_two": "",
|
||||
"backlink_few": "",
|
||||
"backlink_many": "{{count}} روابط راجعة",
|
||||
"backlink_other": ""
|
||||
},
|
||||
"note_icon": {
|
||||
"category": "الفئة:",
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
{
|
||||
"about": {
|
||||
"title": "Sobre Trilium Notes",
|
||||
"homepage": "Pàgina principal:"
|
||||
"homepage": "Pàgina principal:",
|
||||
"app_version": "Versió de l'aplicació:",
|
||||
"db_version": "Versió de la base de dades:"
|
||||
},
|
||||
"add_link": {
|
||||
"note": "Nota"
|
||||
|
||||
@ -736,9 +736,8 @@
|
||||
"zoom_out_title": "缩小"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} 个反链",
|
||||
"backlinks": "{{count}} 个反链",
|
||||
"relation": "关系"
|
||||
"relation": "关系",
|
||||
"backlink_other": "{{count}} 个反链"
|
||||
},
|
||||
"mobile_detail_menu": {
|
||||
"insert_child_note": "插入子笔记",
|
||||
@ -1898,9 +1897,7 @@
|
||||
"indexing_stopped": "索引已停止",
|
||||
"indexing_in_progress": "索引进行中...",
|
||||
"last_indexed": "最后索引时间",
|
||||
"n_notes_queued_0": "{{ count }} 条笔记已加入索引队列",
|
||||
"note_chat": "笔记聊天",
|
||||
"notes_indexed_0": "{{ count }} 条笔记已索引",
|
||||
"sources": "来源",
|
||||
"start_indexing": "开始索引",
|
||||
"use_advanced_context": "使用高级上下文",
|
||||
|
||||
@ -24,14 +24,6 @@
|
||||
"message": "Uživatelský skript z poznámky s ID \"{{id}}\" a názvem \"{{title}}\" nemohl být spuštěn z důvodu: \n\n{{message}}"
|
||||
}
|
||||
},
|
||||
"ai_llm": {
|
||||
"n_notes_queued_0": "{{ count }} poznámka ve frontě k indexaci",
|
||||
"n_notes_queued_1": "{{ count }} poznámky ve frontě k indexaci",
|
||||
"n_notes_queued_2": "{{ count }} poznámek ve frontě k indexaci",
|
||||
"notes_indexed_0": "{{ count }} poznámka indexována",
|
||||
"notes_indexed_1": "{{ count }} poznámky indexovány",
|
||||
"notes_indexed_2": "{{ count }} poznámek indexováno"
|
||||
},
|
||||
"add_link": {
|
||||
"add_link": "Přidat odkaz",
|
||||
"help_on_links": "Nápověda k odkazům",
|
||||
|
||||
@ -732,9 +732,9 @@
|
||||
"zoom_out_title": "Herauszoomen"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} Rückverlinkung",
|
||||
"backlinks": "{{count}} Rückverlinkungen",
|
||||
"relation": "Beziehung"
|
||||
"relation": "Beziehung",
|
||||
"backlink_one": "{{count}} Rückverlinkung",
|
||||
"backlink_other": "{{count}} Rückverlinkungen"
|
||||
},
|
||||
"mobile_detail_menu": {
|
||||
"insert_child_note": "Untergeordnete Notiz einfügen",
|
||||
@ -1729,10 +1729,6 @@
|
||||
"help_title": "Zeige mehr Informationen zu diesem Fenster"
|
||||
},
|
||||
"ai_llm": {
|
||||
"n_notes_queued": "{{ count }} Notiz zur Indizierung vorgemerkt",
|
||||
"n_notes_queued_plural": "{{ count }} Notizen zur Indizierung vorgemerkt",
|
||||
"notes_indexed": "{{ count }} Notiz indiziert",
|
||||
"notes_indexed_plural": "{{ count }} Notizen indiziert",
|
||||
"not_started": "Nicht gestartet",
|
||||
"title": "KI Einstellungen",
|
||||
"processed_notes": "Verarbeitete Notizen",
|
||||
|
||||
@ -14,11 +14,5 @@
|
||||
"title": "Κρίσιμο σφάλμα",
|
||||
"message": "Συνέβη κάποιο κρίσιμο σφάλμα, το οποίο δεν επιτρέπει στην εφαρμογή χρήστη να ξεκινήσει:\n\n{{message}}\n\nΤο πιθανότερο είναι να προκλήθηκε από κάποιο script που απέτυχε απρόοπτα. Δοκιμάστε να ξεκινήσετε την εφαρμογή σε ασφαλή λειτουργία για να λύσετε το πρόβλημα."
|
||||
}
|
||||
},
|
||||
"ai_llm": {
|
||||
"n_notes_queued": "{{ count }} σημείωση στην ουρά για εύρεση",
|
||||
"n_notes_queued_plural": "{{ count }} σημειώσεις στην ουρά για εύρεση",
|
||||
"notes_indexed": "{{ count }} σημείωση με ευρετήριο",
|
||||
"notes_indexed_plural": "{{ count }} σημειώσεις με ευρετήριο"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1271,11 +1271,7 @@
|
||||
"indexing_stopped": "Indexing stopped",
|
||||
"indexing_in_progress": "Indexing in progress...",
|
||||
"last_indexed": "Last Indexed",
|
||||
"n_notes_queued": "{{ count }} note queued for indexing",
|
||||
"n_notes_queued_plural": "{{ count }} notes queued for indexing",
|
||||
"note_chat": "Note Chat",
|
||||
"notes_indexed": "{{ count }} note indexed",
|
||||
"notes_indexed_plural": "{{ count }} notes indexed",
|
||||
"sources": "Sources",
|
||||
"start_indexing": "Start Indexing",
|
||||
"use_advanced_context": "Use Advanced Context",
|
||||
@ -1883,6 +1879,7 @@
|
||||
"link_context_menu": {
|
||||
"open_note_in_new_tab": "Open note in a new tab",
|
||||
"open_note_in_new_split": "Open note in a new split",
|
||||
"open_note_in_other_split": "Open note in the other split",
|
||||
"open_note_in_new_window": "Open note in a new window",
|
||||
"open_note_in_popup": "Quick edit"
|
||||
},
|
||||
|
||||
@ -735,9 +735,10 @@
|
||||
"zoom_out_title": "Alejar"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} Vínculo de retroceso",
|
||||
"backlinks": "{{count}} vínculos de retroceso",
|
||||
"relation": "relación"
|
||||
"relation": "relación",
|
||||
"backlink_one": "{{count}} Vínculo de retroceso",
|
||||
"backlink_many": "",
|
||||
"backlink_other": "{{count}} vínculos de retroceso"
|
||||
},
|
||||
"mobile_detail_menu": {
|
||||
"insert_child_note": "Insertar subnota",
|
||||
@ -1262,12 +1263,7 @@
|
||||
"indexing_stopped": "Indexado detenido",
|
||||
"indexing_in_progress": "Indexado en progreso...",
|
||||
"last_indexed": "Último indexado",
|
||||
"n_notes_queued_0": "{{ count }} nota agregada a la cola para indexar",
|
||||
"n_notes_queued_1": "{{ count }} notas agregadas a la cola para indexar",
|
||||
"n_notes_queued_2": "",
|
||||
"note_chat": "Chat de nota",
|
||||
"notes_indexed": "{{ count }} nota indexada",
|
||||
"notes_indexed_plural": "{{ count }} notas indexadas",
|
||||
"sources": "Fuentes",
|
||||
"start_indexing": "Comenzar indexado",
|
||||
"use_advanced_context": "Usar contexto avanzado",
|
||||
|
||||
@ -733,9 +733,10 @@
|
||||
"zoom_out_title": "Zoom arrière"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} Lien inverse",
|
||||
"backlinks": "{{count}} Liens inverses",
|
||||
"relation": "relation"
|
||||
"relation": "relation",
|
||||
"backlink_one": "{{count}} Lien inverse",
|
||||
"backlink_many": "",
|
||||
"backlink_other": "{{count}} Liens inverses"
|
||||
},
|
||||
"mobile_detail_menu": {
|
||||
"insert_child_note": "Insérer une note enfant",
|
||||
@ -1766,12 +1767,6 @@
|
||||
"not_started": "Non démarré",
|
||||
"title": "Paramètres IA",
|
||||
"processed_notes": "Notes traitées",
|
||||
"n_notes_queued_0": "{{ count }} note en attente d’indexation",
|
||||
"n_notes_queued_1": "{{ count }} notes en attente d’indexation",
|
||||
"n_notes_queued_2": "",
|
||||
"notes_indexed_0": "{{ count }} note indexée",
|
||||
"notes_indexed_1": "{{ count }} notes indexées",
|
||||
"notes_indexed_2": "",
|
||||
"anthropic_url_description": "URL de base pour l'API Anthropic (par défaut : https ://api.anthropic.com)",
|
||||
"anthropic_model_description": "Modèles Anthropic Claude pour la complétion",
|
||||
"voyage_settings": "Réglages d'IA Voyage",
|
||||
|
||||
@ -324,7 +324,8 @@
|
||||
"copy-link": "Copia collegamento",
|
||||
"paste-as-plain-text": "Incolla come testo semplice",
|
||||
"add-term-to-dictionary": "Aggiungi \"{{term}}\" al dizionario",
|
||||
"search_online": "Cerca \"{{term}}\" con {{searchEngine}}"
|
||||
"search_online": "Cerca \"{{term}}\" con {{searchEngine}}",
|
||||
"search_in_trilium": "Cerca \"{{term}}\" in Trilium"
|
||||
},
|
||||
"editing": {
|
||||
"editor_type": {
|
||||
@ -614,7 +615,8 @@
|
||||
"showSQLConsole": "mostra console SQL",
|
||||
"other": "Altro",
|
||||
"quickSearch": "concentrati sull'input della ricerca rapida",
|
||||
"inPageSearch": "ricerca all'interno della pagina"
|
||||
"inPageSearch": "ricerca all'interno della pagina",
|
||||
"editShortcuts": "Modifica scorciatoie da tastiera"
|
||||
},
|
||||
"i18n": {
|
||||
"saturday": "Sabato",
|
||||
@ -638,12 +640,6 @@
|
||||
"friday": "Venerdì"
|
||||
},
|
||||
"ai_llm": {
|
||||
"n_notes_queued_0": "{{ count }} nota in coda per l'indicizzazione",
|
||||
"n_notes_queued_1": "{{ count }} note in coda per l'indicizzazione",
|
||||
"n_notes_queued_2": "{{ count }} note in coda per l'indicizzazione",
|
||||
"notes_indexed_0": "{{ count }} nota indicizzata",
|
||||
"notes_indexed_1": "{{ count }} note indicizzate",
|
||||
"notes_indexed_2": "{{ count }} note indicizzate",
|
||||
"not_started": "Non iniziato",
|
||||
"title": "Impostazioni AI",
|
||||
"processed_notes": "Note elaborate",
|
||||
@ -1308,9 +1304,10 @@
|
||||
"zoom_out_title": "Rimpicciolisci"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} Backlink",
|
||||
"backlinks": "{{count}} Backlinks",
|
||||
"relation": "relazione"
|
||||
"relation": "relazione",
|
||||
"backlink_one": "{{count}} Backlink",
|
||||
"backlink_many": "",
|
||||
"backlink_other": "{{count}} Backlink"
|
||||
},
|
||||
"mobile_detail_menu": {
|
||||
"insert_child_note": "Inserisci nota secondaria",
|
||||
@ -1346,7 +1343,11 @@
|
||||
"geo-map": "Mappa geografica",
|
||||
"board": "Asse",
|
||||
"presentation": "Presentazione",
|
||||
"include_archived_notes": "Mostra note archiviate"
|
||||
"include_archived_notes": "Mostra note archiviate",
|
||||
"expand_tooltip": "Espande i figli diretti di questa raccolta (a un livello di profondità). Per ulteriori opzioni, premere la freccia a destra.",
|
||||
"expand_first_level": "Espandi figli diretti",
|
||||
"expand_nth_level": "Espandi {{depth}} livelli",
|
||||
"expand_all_levels": "Espandi tutti i livelli"
|
||||
},
|
||||
"edited_notes": {
|
||||
"no_edited_notes_found": "Nessuna nota modificata per questo giorno...",
|
||||
@ -1852,7 +1853,8 @@
|
||||
"refresh-saved-search-results": "Aggiorna i risultati della ricerca salvati",
|
||||
"create-child-note": "Crea nota figlio",
|
||||
"unhoist": "Sganciare",
|
||||
"toggle-sidebar": "Attiva/disattiva la barra laterale"
|
||||
"toggle-sidebar": "Attiva/disattiva la barra laterale",
|
||||
"dropping-not-allowed": "Non è consentito lasciare appunti in questa posizione."
|
||||
},
|
||||
"title_bar_buttons": {
|
||||
"window-on-top": "Mantieni la finestra in primo piano"
|
||||
@ -1936,7 +1938,8 @@
|
||||
"duplicate-launcher": "Duplica il launcher <kbd data-command=\"duplicateSubtree\">"
|
||||
},
|
||||
"editable-text": {
|
||||
"auto-detect-language": "Rilevato automaticamente"
|
||||
"auto-detect-language": "Rilevato automaticamente",
|
||||
"keeps-crashing": "Il componente di modifica continua a bloccarsi. Prova a riavviare Trilium. Se il problema persiste, valuta la possibilità di creare una segnalazione di bug."
|
||||
},
|
||||
"highlighting": {
|
||||
"title": "Blocchi di codice",
|
||||
@ -2101,5 +2104,8 @@
|
||||
"set-color": "Imposta colore nota",
|
||||
"set-custom-color": "Imposta colore personalizzato per le note",
|
||||
"clear-color": "Pulisci colore della nota"
|
||||
},
|
||||
"popup-editor": {
|
||||
"maximize": "Passa all'editor completo"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1507,9 +1507,7 @@
|
||||
"indexing_stopped": "インデックス登録を停止しました",
|
||||
"indexing_in_progress": "インデックス登録中です...",
|
||||
"last_indexed": "最終インデックス作成日時",
|
||||
"n_notes_queued_0": "{{ count }} 件のノートがインデックス作成待ちです",
|
||||
"note_chat": "ノートチャット",
|
||||
"notes_indexed_0": "{{ count }} 件のノートをインデックスしました",
|
||||
"sources": "ソース",
|
||||
"start_indexing": "インデックス作成を開始",
|
||||
"use_advanced_context": "高度なコンテキストを使用",
|
||||
@ -1590,9 +1588,8 @@
|
||||
"this_launcher_doesnt_define_target_note": "このランチャーはターゲットノートを定義していません。"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} バックリンク",
|
||||
"backlinks": "{{count}} バックリンク",
|
||||
"relation": "リレーション"
|
||||
"relation": "リレーション",
|
||||
"backlink_other": "{{count}} 個のバックリンク"
|
||||
},
|
||||
"mobile_detail_menu": {
|
||||
"delete_this_note": "このノートを削除",
|
||||
|
||||
@ -16,10 +16,12 @@
|
||||
},
|
||||
"widget-error": {
|
||||
"title": "Starten widget mislukt",
|
||||
"message-unknown": "Onbekende widget kan niet gestart worden omdat:\n\n{{message}}"
|
||||
"message-unknown": "Onbekende widget kan niet gestart worden omdat:\n\n{{message}}",
|
||||
"message-custom": "Aangepaste widget van notitie met ID \"{{id}}\", getiteld \"{{title}}\" kon niet worden geïnitialiseerd vanwege:\n\n{{message}}"
|
||||
},
|
||||
"bundle-error": {
|
||||
"title": "Custom script laden mislukt"
|
||||
"title": "Custom script laden mislukt",
|
||||
"message": "Script van notitie met ID \"{{id}}\", getiteld \"{{title}}\" kon niet worden uitgevoerd vanwege:\n\n{{message}}"
|
||||
}
|
||||
},
|
||||
"add_link": {
|
||||
@ -29,14 +31,17 @@
|
||||
"search_note": "zoek voor notitie op naam",
|
||||
"link_title_mirrors": "De link titel is hetzelfde als de notitie's huidige titel",
|
||||
"link_title": "Link titel",
|
||||
"button_add_link": "Link toevoegen"
|
||||
"button_add_link": "Link toevoegen",
|
||||
"link_title_arbitrary": "snelkoppelingsnaam kan willekeurig worden aangepast"
|
||||
},
|
||||
"branch_prefix": {
|
||||
"edit_branch_prefix": "Bewerk branch prefix",
|
||||
"save": "Opslaan",
|
||||
"branch_prefix_saved": "Branch prefix is opgeslagen.",
|
||||
"help_on_tree_prefix": "Help bij boomvoorvoegsel",
|
||||
"prefix": "Voorvoegsel: "
|
||||
"prefix": "Voorvoegsel: ",
|
||||
"edit_branch_prefix_multiple": "Bewerk zijtakvoorvoegsel voor {{count}} zijtakken",
|
||||
"branch_prefix_saved_multiple": "Vertakkingsvoorvoegsel opgeslagen voor {{count}} vertakkingen."
|
||||
},
|
||||
"bulk_actions": {
|
||||
"bulk_actions": "Bulk acties",
|
||||
|
||||
@ -750,9 +750,6 @@
|
||||
"indexing_stopped": "Indeksowanie zatrzymane",
|
||||
"indexing_in_progress": "Indeksowanie w trakcie...",
|
||||
"last_indexed": "Ostatnio zindeksowane",
|
||||
"n_notes_queued_0": "{{ count }} notatka zakolejkowana do indeksowania",
|
||||
"n_notes_queued_1": "{{ count }} notatek zakolejkowanych do indeksowania",
|
||||
"n_notes_queued_2": "{{ count }} notatek zakolejkowanych do indeksowania",
|
||||
"note_chat": "Czat notatki",
|
||||
"note_title": "Tytuł notatki",
|
||||
"error": "Błąd",
|
||||
@ -860,9 +857,6 @@
|
||||
"enter_message": "Wpisz swoją wiadomość...",
|
||||
"error_contacting_provider": "Błąd kontaktu z dostawcą AI. Sprawdź ustawienia i połączenie internetowe.",
|
||||
"error_generating_response": "Błąd generowania odpowiedzi AI",
|
||||
"notes_indexed_0": "{{ count }} notatka zaindeksowana",
|
||||
"notes_indexed_1": "{{ count }} notatek zaindeksowanych",
|
||||
"notes_indexed_2": "",
|
||||
"sources": "Źródła",
|
||||
"start_indexing": "Rozpocznij indeksowanie",
|
||||
"use_advanced_context": "Użyj zaawansowanego kontekstu",
|
||||
@ -1237,9 +1231,10 @@
|
||||
"zoom_out_title": "Pomniejsz"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} Backlink",
|
||||
"backlinks": "{{count}} Backlinków",
|
||||
"relation": "relacja"
|
||||
"relation": "relacja",
|
||||
"backlink_one": "{{count}} Backlink",
|
||||
"backlink_few": "",
|
||||
"backlink_many": "{{count}} Backlinków"
|
||||
},
|
||||
"mobile_detail_menu": {
|
||||
"insert_child_note": "Wstaw notatkę podrzędną",
|
||||
|
||||
@ -711,9 +711,10 @@
|
||||
"zoom_out_title": "Reduzir"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} Ligação Reversa",
|
||||
"backlinks": "{{count}} Ligações Reversas",
|
||||
"relation": "relação"
|
||||
"relation": "relação",
|
||||
"backlink_one": "{{count}} Ligação Reversa",
|
||||
"backlink_many": "",
|
||||
"backlink_other": "{{count}} Ligações Reversas"
|
||||
},
|
||||
"mobile_detail_menu": {
|
||||
"insert_child_note": "Inserir nota filha",
|
||||
@ -1234,13 +1235,7 @@
|
||||
"indexing_stopped": "Indexação interrompida",
|
||||
"indexing_in_progress": "Indexação em andamento…",
|
||||
"last_indexed": "Última Indexada",
|
||||
"n_notes_queued_0": "{{ count }} nota enfileirada para indexação",
|
||||
"n_notes_queued_1": "{{ count }} notas enfileiradas para indexação",
|
||||
"n_notes_queued_2": "{{ count }} notas enfileiradas para indexação",
|
||||
"note_chat": "Conversa de Nota",
|
||||
"notes_indexed_0": "{{ count }} nota indexada",
|
||||
"notes_indexed_1": "{{ count }} notas indexadas",
|
||||
"notes_indexed_2": "{{ count }} notas indexadas",
|
||||
"sources": "Origens",
|
||||
"start_indexing": "Iniciar Indexação",
|
||||
"use_advanced_context": "Usar Contexto Avançado",
|
||||
|
||||
@ -75,12 +75,6 @@
|
||||
"note_cloned": "A nota \"{{clonedTitle}}\" foi clonada para \"{{targetTitle}}\""
|
||||
},
|
||||
"ai_llm": {
|
||||
"n_notes_queued_0": "{{ count }} nota enfileirada para indexação",
|
||||
"n_notes_queued_1": "{{ count }} notas enfileiradas para indexação",
|
||||
"n_notes_queued_2": "{{ count }} notas enfileiradas para indexação",
|
||||
"notes_indexed_0": "{{ count }} nota indexada",
|
||||
"notes_indexed_1": "{{ count }} notas indexadas",
|
||||
"notes_indexed_2": "{{ count }} notas indexadas",
|
||||
"temperature": "Temperatura",
|
||||
"retry_queued": "Nota enfileirada para nova tentativa",
|
||||
"queued_notes": "Notas Enfileiradas",
|
||||
@ -976,9 +970,10 @@
|
||||
"reset_pan_zoom_title": "Redefinir pan & zoom para coordenadas e ampliação iniciais"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} Links Reversos",
|
||||
"backlinks": "{{count}} Links Reversos",
|
||||
"relation": "relação"
|
||||
"relation": "relação",
|
||||
"backlink_one": "",
|
||||
"backlink_many": "",
|
||||
"backlink_other": "{{count}} Links Reversos"
|
||||
},
|
||||
"mobile_detail_menu": {
|
||||
"insert_child_note": "Inserir nota filha",
|
||||
|
||||
@ -1888,13 +1888,7 @@
|
||||
"indexing_stopped": "Indexarea s-a oprit",
|
||||
"indexing_in_progress": "Indexare în curs...",
|
||||
"last_indexed": "Ultima indexare",
|
||||
"n_notes_queued_0": "O notiță adăugată în coada de indexare",
|
||||
"n_notes_queued_1": "{{ count }} notițe adăugate în coada de indexare",
|
||||
"n_notes_queued_2": "{{ count }} de notițe adăugate în coada de indexare",
|
||||
"note_chat": "Discuție pe baza notițelor",
|
||||
"notes_indexed_0": "O notiță indexată",
|
||||
"notes_indexed_1": "{{ count }} notițe indexate",
|
||||
"notes_indexed_2": "{{ count }} de notițe indexate",
|
||||
"sources": "Surse",
|
||||
"start_indexing": "Indexează",
|
||||
"use_advanced_context": "Folosește context îmbogățit",
|
||||
|
||||
@ -984,9 +984,10 @@
|
||||
"new-version-available": "Доступно обновление"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} ссылки",
|
||||
"backlinks": "{{count}} ссылок",
|
||||
"relation": "отношение"
|
||||
"relation": "отношение",
|
||||
"backlink_one": "{{count}} ссылки",
|
||||
"backlink_few": "",
|
||||
"backlink_many": "{{count}} ссылок"
|
||||
},
|
||||
"note_icon": {
|
||||
"category": "Категория:",
|
||||
@ -1334,16 +1335,10 @@
|
||||
"error_fetching": "Ошибка получения списка моделей: {{error}}",
|
||||
"index_rebuild_status_error": "Ошибка проверки статуса перестроения индекса",
|
||||
"enhanced_context_description": "Предоставляет ИИ больше контекста из заметки и связанных с ней заметок для более точных ответов",
|
||||
"n_notes_queued_0": "{{ count }} заметка в очереди на индексирование",
|
||||
"n_notes_queued_1": "{{ count }} заметки в очереди на индексирование",
|
||||
"n_notes_queued_2": "{{ count }} заметок в очереди на индексирование",
|
||||
"no_models_found_ollama": "Модели Ollama не найдены. Проверьте, запущена ли Ollama.",
|
||||
"no_models_found_online": "Модели не найдены. Проверьте ваш ключ API и настройки.",
|
||||
"experimental_warning": "Функция LLM в настоящее время является экспериментальной — вы предупреждены.",
|
||||
"ollama_no_url": "Ollama не настроена. Введите корректный URL-адрес.",
|
||||
"notes_indexed_0": "{{ count }} заметка проиндексирована",
|
||||
"notes_indexed_1": "{{ count }} заметки проиндексировано",
|
||||
"notes_indexed_2": "{{ count }} заметок проиндексировано",
|
||||
"show_thinking_description": "Показать цепочку мыслительного процесса ИИ",
|
||||
"api_key_tooltip": "API-ключ для доступа к сервису",
|
||||
"all_notes_queued_for_retry": "Все неудачные заметки поставлены в очередь на повторную попытку",
|
||||
|
||||
@ -425,14 +425,6 @@
|
||||
"print_page_size": "Prilikom izvoza u PDF, menja veličinu stranice. Podržane vrednosti: <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": "Boja"
|
||||
},
|
||||
"ai_llm": {
|
||||
"n_notes_queued_0": "{{ count }} beleška stavljena u red za indeksiranje",
|
||||
"n_notes_queued_1": "{{ count }} beleški stavljeno u red za indeksiranje",
|
||||
"n_notes_queued_2": "{{ count }} beleški stavljeno u red za indeksiranje",
|
||||
"notes_indexed_0": "{{ count }} beleška je indeksirana",
|
||||
"notes_indexed_1": "{{ count }} beleški je indeksirano",
|
||||
"notes_indexed_2": "{{ count }} beleški je indeksirano"
|
||||
},
|
||||
"attribute_editor": {
|
||||
"help_text_body1": "Da biste dodali oznaku, samo unesite npr. <code>#rock</code> ili ako želite da dodate i vrednost, onda npr. <code>#year = 2020</code>",
|
||||
"help_text_body2": "Za relaciju, unesite <code>~author = @</code> što bi trebalo da otvori automatsko dovršavanje gde možete potražiti željenu belešku.",
|
||||
|
||||
@ -96,11 +96,5 @@
|
||||
"are_you_sure_remove_note": "\"{{title}}\" notunu ilişki haritasından kaldırmak istediğinize emin misiniz?. ",
|
||||
"also_delete_note": "Notu da sil",
|
||||
"if_you_dont_check": "Bunu işaretlemezseniz, not yalnızca ilişki haritasından kaldırılacaktır."
|
||||
},
|
||||
"ai_llm": {
|
||||
"n_notes_queued": "{{ count }} not dizinleme için sıraya alındı",
|
||||
"n_notes_queued_plural": "{{ count }} adet not dizinleme için sıraya alındı",
|
||||
"notes_indexed": "{{ count }} not dizinlendi",
|
||||
"notes_indexed_plural": "{{ count }} adet not dizinlendi"
|
||||
}
|
||||
}
|
||||
|
||||
@ -733,9 +733,8 @@
|
||||
"zoom_out_title": "縮小"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} 個反連結",
|
||||
"backlinks": "{{count}} 個反連結",
|
||||
"relation": "關聯"
|
||||
"relation": "關聯",
|
||||
"backlink_one": "{{count}} 個反連結"
|
||||
},
|
||||
"mobile_detail_menu": {
|
||||
"insert_child_note": "插入子筆記",
|
||||
@ -1798,9 +1797,7 @@
|
||||
"indexing_stopped": "已停止索引",
|
||||
"indexing_in_progress": "正在進行索引…",
|
||||
"last_indexed": "最後索引時間",
|
||||
"n_notes_queued_0": "{{ count }} 條筆記已加入索引隊列",
|
||||
"note_chat": "筆記聊天",
|
||||
"notes_indexed_0": "已索引 {{ count }} 條筆記",
|
||||
"sources": "來源",
|
||||
"start_indexing": "開始索引",
|
||||
"use_advanced_context": "使用進階上下文",
|
||||
|
||||
@ -839,9 +839,10 @@
|
||||
"zoom_out_title": "Зменшити масштаб"
|
||||
},
|
||||
"zpetne_odkazy": {
|
||||
"backlink": "{{count}} Зворотне посилання",
|
||||
"backlinks": "{{count}} Зворотні посилання",
|
||||
"relation": "зв'язок"
|
||||
"relation": "зв'язок",
|
||||
"backlink_one": "{{count}} Зворотне посилання",
|
||||
"backlink_few": "{{count}} Зворотні посилання",
|
||||
"backlink_many": "{{count}} Зворотні посилання"
|
||||
},
|
||||
"mobile_detail_menu": {
|
||||
"insert_child_note": "Вставити дочірню нотатку",
|
||||
@ -1350,13 +1351,7 @@
|
||||
"indexing_stopped": "Індексацію зупинено",
|
||||
"indexing_in_progress": "Триває індексація...",
|
||||
"last_indexed": "Остання індексація",
|
||||
"n_notes_queued_0": "{{ count }} нотатка в черзі на індексацію",
|
||||
"n_notes_queued_1": "{{ count }} нотатки в черзі на індексацію",
|
||||
"n_notes_queued_2": "{{ count }} нотаток в черзі на індексацію",
|
||||
"note_chat": "Нотатка Чат",
|
||||
"notes_indexed_0": "{{ count }} нотатка індексовано",
|
||||
"notes_indexed_1": "{{ count }} нотатки індексовано",
|
||||
"notes_indexed_2": "{{ count }} нотаток індексовано",
|
||||
"sources": "Джерела",
|
||||
"start_indexing": "Почати індексацію",
|
||||
"use_advanced_context": "Використовувати розширений контекст",
|
||||
|
||||
@ -19,6 +19,7 @@ import NoteLink from "./react/NoteLink";
|
||||
import RawHtml from "./react/RawHtml";
|
||||
import { ViewTypeOptions } from "./collections/interface";
|
||||
import attributes from "../services/attributes";
|
||||
import LoadResults from "../services/load_results";
|
||||
|
||||
export interface FloatingButtonContext {
|
||||
parentComponent: Component;
|
||||
@ -321,13 +322,7 @@ function Backlinks({ note, isDefaultViewMode }: FloatingButtonContext) {
|
||||
|
||||
useEffect(() => refresh(), [ note ]);
|
||||
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||
loadResults.getAttributeRows().some(attr =>
|
||||
attr.type === "relation" &&
|
||||
attr.name === "internalLink" &&
|
||||
attributes.isAffecting(attr, note))
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
if (needsRefresh(note, loadResults)) refresh();
|
||||
});
|
||||
|
||||
// Determine the max height of the container.
|
||||
@ -353,18 +348,18 @@ function Backlinks({ note, isDefaultViewMode }: FloatingButtonContext) {
|
||||
|
||||
{popupOpen && (
|
||||
<div ref={backlinksContainerRef} className="backlinks-items dropdown-menu" style={{ display: "block" }}>
|
||||
<BacklinksList noteId={note.noteId} />
|
||||
<BacklinksList note={note} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BacklinksList({ noteId }: { noteId: string }) {
|
||||
function BacklinksList({ note }: { note: FNote }) {
|
||||
const [ backlinks, setBacklinks ] = useState<BacklinksResponse>([]);
|
||||
|
||||
useEffect(() => {
|
||||
server.get<BacklinksResponse>(`note-map/${noteId}/backlinks`).then(async (backlinks) => {
|
||||
function refresh() {
|
||||
server.get<BacklinksResponse>(`note-map/${note.noteId}/backlinks`).then(async (backlinks) => {
|
||||
// prefetch all
|
||||
const noteIds = backlinks
|
||||
.filter(bl => "noteId" in bl)
|
||||
@ -372,7 +367,12 @@ function BacklinksList({ noteId }: { noteId: string }) {
|
||||
await froca.getNotes(noteIds);
|
||||
setBacklinks(backlinks);
|
||||
});
|
||||
}, [ noteId ]);
|
||||
}
|
||||
|
||||
useEffect(() => refresh(), [ note ]);
|
||||
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||
if (needsRefresh(note, loadResults)) refresh();
|
||||
});
|
||||
|
||||
return backlinks.map(backlink => (
|
||||
<div>
|
||||
@ -392,3 +392,9 @@ function BacklinksList({ noteId }: { noteId: string }) {
|
||||
</div>
|
||||
));
|
||||
}
|
||||
|
||||
function needsRefresh(note: FNote, loadResults: LoadResults) {
|
||||
return loadResults.getAttributeRows().some(attr =>
|
||||
attr.type === "relation" &&
|
||||
attributes.isAffecting(attr, note));
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ export function openNoteContextMenu(api: Api, event: ContextMenuEvent, note: FNo
|
||||
x: event.pageX,
|
||||
y: event.pageY,
|
||||
items: [
|
||||
...link_context_menu.getItems(),
|
||||
...link_context_menu.getItems(event),
|
||||
{ kind: "separator" },
|
||||
{
|
||||
title: t("board_view.insert-above"),
|
||||
@ -81,7 +81,7 @@ export function openNoteContextMenu(api: Api, event: ContextMenuEvent, note: FNo
|
||||
componentFn: () => NoteColorPicker({note})
|
||||
}
|
||||
],
|
||||
selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, note.noteId),
|
||||
selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, event, note.noteId),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ export function openCalendarContextMenu(e: ContextMenuEvent, note: FNote, parent
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
items: [
|
||||
...link_context_menu.getItems(),
|
||||
...link_context_menu.getItems(e),
|
||||
{ kind: "separator" },
|
||||
getArchiveMenuItem(note),
|
||||
{
|
||||
@ -40,6 +40,6 @@ export function openCalendarContextMenu(e: ContextMenuEvent, note: FNote, parent
|
||||
componentFn: () => NoteColorPicker({note: note})
|
||||
}
|
||||
],
|
||||
selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, note.noteId),
|
||||
selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, e, note.noteId),
|
||||
})
|
||||
}
|
||||
|
||||
@ -117,7 +117,10 @@ export default function CalendarView({ note, noteIds }: ViewModeProps<CalendarVi
|
||||
if (loadResults.getNoteIds().some(noteId => noteIds.includes(noteId)) // note title change.
|
||||
|| loadResults.getAttributeRows().some((a) => noteIds.includes(a.noteId ?? ""))) // subnote change.
|
||||
{
|
||||
// Defer execution after the load results are processed so that the event builder has the updated data to work with.
|
||||
setTimeout(() => {
|
||||
calendarRef.current?.refetchEvents();
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ export default function openContextMenu(noteId: string, e: LeafletMouseEvent, is
|
||||
let items: MenuItem<keyof CommandMappings>[] = [
|
||||
...buildGeoLocationItem(e),
|
||||
{ kind: "separator" },
|
||||
...linkContextMenu.getItems(),
|
||||
...linkContextMenu.getItems(e),
|
||||
];
|
||||
|
||||
if (isEditable) {
|
||||
@ -32,14 +32,14 @@ export default function openContextMenu(noteId: string, e: LeafletMouseEvent, is
|
||||
x: e.originalEvent.pageX,
|
||||
y: e.originalEvent.pageY,
|
||||
items,
|
||||
selectMenuItemHandler: ({ command }, e) => {
|
||||
selectMenuItemHandler: ({ command }) => {
|
||||
if (command === "deleteFromMap") {
|
||||
appContext.triggerCommand(command, { noteId });
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass the events to the link context menu
|
||||
linkContextMenu.handleLinkContextMenuItem(command, noteId);
|
||||
linkContextMenu.handleLinkContextMenuItem(command, e, noteId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ export function showRowContextMenu(parentComponent: Component, e: MouseEvent, ro
|
||||
|
||||
contextMenu.show({
|
||||
items: [
|
||||
...link_context_menu.getItems(),
|
||||
...link_context_menu.getItems(e),
|
||||
{ kind: "separator" },
|
||||
{
|
||||
title: t("table_view.row-insert-above"),
|
||||
@ -227,7 +227,7 @@ export function showRowContextMenu(parentComponent: Component, e: MouseEvent, ro
|
||||
componentFn: () => NoteColorPicker({note: rowData.noteId})
|
||||
}
|
||||
],
|
||||
selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, rowData.noteId),
|
||||
selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, e, rowData.noteId),
|
||||
x: e.pageX,
|
||||
y: e.pageY
|
||||
});
|
||||
|
||||
@ -3,6 +3,8 @@ import appContext, { type CommandData, type CommandListenerData, type EventData,
|
||||
import type BasicWidget from "../basic_widget.js";
|
||||
import Component from "../../components/component.js";
|
||||
import splitService from "../../services/resizer.js";
|
||||
import { isMobile } from "../../services/utils.js";
|
||||
import NoteContext from "../../components/note_context.js";
|
||||
|
||||
interface SplitNoteWidget extends BasicWidget {
|
||||
hasBeenAlreadyShown?: boolean;
|
||||
@ -49,13 +51,14 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
||||
|
||||
this.child(widget);
|
||||
|
||||
if (noteContext.mainNtxId && noteContext.ntxId) {
|
||||
if (noteContext.mainNtxId && noteContext.ntxId && !isMobile()) {
|
||||
splitService.setupNoteSplitResizer([noteContext.mainNtxId,noteContext.ntxId]);
|
||||
}
|
||||
}
|
||||
|
||||
async openNewNoteSplitEvent({ ntxId, notePath, hoistedNoteId, viewScope }: EventData<"openNewNoteSplit">) {
|
||||
const mainNtxId = appContext.tabManager.getActiveMainContext()?.ntxId;
|
||||
const activeContext = appContext.tabManager.getActiveMainContext();
|
||||
const mainNtxId = activeContext?.ntxId;
|
||||
if (!mainNtxId) {
|
||||
console.warn("Missing main note context ID");
|
||||
return;
|
||||
@ -69,22 +72,30 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
||||
|
||||
hoistedNoteId = hoistedNoteId || appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||
|
||||
const noteContext = await appContext.tabManager.openEmptyTab(null, hoistedNoteId, mainNtxId);
|
||||
|
||||
const subContexts = activeContext.getSubContexts();
|
||||
let noteContext: NoteContext | undefined = undefined;
|
||||
if (isMobile() && subContexts.length > 1) {
|
||||
noteContext = subContexts.find(s => s.ntxId !== ntxId);
|
||||
}
|
||||
if (!noteContext) {
|
||||
noteContext = await appContext.tabManager.openEmptyTab(null, hoistedNoteId, mainNtxId);
|
||||
// remove the original position of newly created note context
|
||||
const ntxIds = appContext.tabManager.children.map((c) => c.ntxId).filter((id) => id !== noteContext?.ntxId) as string[];
|
||||
|
||||
// insert the note context after the originating note context
|
||||
if (!noteContext.ntxId) {
|
||||
logError("Failed to create new note context!");
|
||||
return;
|
||||
}
|
||||
|
||||
// remove the original position of newly created note context
|
||||
const ntxIds = appContext.tabManager.children.map((c) => c.ntxId).filter((id) => id !== noteContext.ntxId) as string[];
|
||||
|
||||
// insert the note context after the originating note context
|
||||
ntxIds.splice(ntxIds.indexOf(ntxId) + 1, 0, noteContext.ntxId);
|
||||
|
||||
this.triggerCommand("noteContextReorder", { ntxIdsInOrder: ntxIds });
|
||||
|
||||
// move the note context rendered widget after the originating widget
|
||||
this.$widget.find(`[data-ntx-id="${noteContext.ntxId}"]`).insertAfter(this.$widget.find(`[data-ntx-id="${ntxId}"]`));
|
||||
}
|
||||
|
||||
|
||||
await appContext.tabManager.activateNoteContext(noteContext.ntxId);
|
||||
|
||||
|
||||
@ -34,15 +34,26 @@ export default function MarkdownImportDialog() {
|
||||
}
|
||||
});
|
||||
|
||||
function submit() {
|
||||
setShown(false);
|
||||
if (editorApiRef.current && text) {
|
||||
convertMarkdownToHtml(text, editorApiRef.current);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="markdown-import-dialog" title={t("markdown_import.dialog_title")} size="lg"
|
||||
footer={<Button className="markdown-import-button" text={t("markdown_import.import_button")} onClick={() => setShown(false)} keyboardShortcut="Ctrl+Enter" />}
|
||||
onShown={() => markdownImportTextArea.current?.focus()}
|
||||
onHidden={async () => {
|
||||
if (editorApiRef.current) {
|
||||
await convertMarkdownToHtml(text, editorApiRef.current);
|
||||
footer={
|
||||
<Button
|
||||
className="markdown-import-button"
|
||||
text={t("markdown_import.import_button")}
|
||||
keyboardShortcut="Ctrl+Enter"
|
||||
onClick={submit}
|
||||
/>
|
||||
}
|
||||
onShown={() => markdownImportTextArea.current?.focus()}
|
||||
onHidden={() => {
|
||||
setShown(false);
|
||||
setText("");
|
||||
}}
|
||||
@ -55,7 +66,7 @@ export default function MarkdownImportDialog() {
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" && e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
setShown(false);
|
||||
submit();
|
||||
}
|
||||
}}></textarea>
|
||||
</Modal>
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { useContext } from "preact/hooks";
|
||||
import appContext from "../../components/app_context";
|
||||
import contextMenu from "../../menus/context_menu";
|
||||
import appContext, { CommandMappings } from "../../components/app_context";
|
||||
import contextMenu, { MenuItem } from "../../menus/context_menu";
|
||||
import branches from "../../services/branches";
|
||||
import { t } from "../../services/i18n";
|
||||
import note_create from "../../services/note_create";
|
||||
import tree from "../../services/tree";
|
||||
import ActionButton from "../react/ActionButton";
|
||||
import { ParentComponent } from "../react/react_utils";
|
||||
import BasicWidget from "../basic_widget";
|
||||
|
||||
export default function MobileDetailMenu() {
|
||||
const parentComponent = useContext(ParentComponent);
|
||||
@ -16,17 +17,33 @@ export default function MobileDetailMenu() {
|
||||
icon="bx bx-dots-vertical-rounded"
|
||||
text=""
|
||||
onClick={(e) => {
|
||||
const note = appContext.tabManager.getActiveContextNote();
|
||||
const ntxId = (parentComponent as BasicWidget | null)?.getClosestNtxId();
|
||||
if (!ntxId) return;
|
||||
|
||||
contextMenu.show<"insertChildNote" | "delete" | "showRevisions">({
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
items: [
|
||||
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
|
||||
const subContexts = noteContext.getMainContext().getSubContexts();
|
||||
const isMainContext = noteContext?.isMainContext();
|
||||
const note = noteContext.note;
|
||||
|
||||
const items: (MenuItem<keyof CommandMappings>)[] = [
|
||||
{ title: t("mobile_detail_menu.insert_child_note"), command: "insertChildNote", uiIcon: "bx bx-plus", enabled: note?.type !== "search" },
|
||||
{ title: t("mobile_detail_menu.delete_this_note"), command: "delete", uiIcon: "bx bx-trash", enabled: note?.noteId !== "root" },
|
||||
{ kind: "separator" },
|
||||
{ title: t("mobile_detail_menu.note_revisions"), command: "showRevisions", uiIcon: "bx bx-history" }
|
||||
],
|
||||
{ title: t("mobile_detail_menu.note_revisions"), command: "showRevisions", uiIcon: "bx bx-history" },
|
||||
{ kind: "separator" },
|
||||
subContexts.length < 2 && { title: t("create_pane_button.create_new_split"), command: "openNewNoteSplit", uiIcon: "bx bx-dock-right" },
|
||||
!isMainContext && { title: t("close_pane_button.close_this_pane"), command: "closeThisNoteSplit", uiIcon: "bx bx-x" }
|
||||
].filter(i => !!i) as MenuItem<keyof CommandMappings>[];
|
||||
|
||||
const lastItem = items.at(-1);
|
||||
if (lastItem && "kind" in lastItem && lastItem.kind === "separator") {
|
||||
items.pop();
|
||||
}
|
||||
|
||||
contextMenu.show<keyof CommandMappings>({
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
items,
|
||||
selectMenuItemHandler: async ({ command }) => {
|
||||
if (command === "insertChildNote") {
|
||||
note_create.createNote(appContext.tabManager.getActiveContextNotePath() ?? undefined);
|
||||
@ -46,7 +63,7 @@ export default function MobileDetailMenu() {
|
||||
parentComponent.triggerCommand("setActiveScreen", { screen: "tree" });
|
||||
}
|
||||
} else if (command && parentComponent) {
|
||||
parentComponent.triggerCommand(command);
|
||||
parentComponent.triggerCommand(command, { ntxId });
|
||||
}
|
||||
},
|
||||
forcePositionOnMobile: true
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
import { useContext } from "preact/hooks";
|
||||
import ActionButton from "../react/ActionButton";
|
||||
import { ParentComponent } from "../react/react_utils";
|
||||
import { t } from "../../services/i18n";
|
||||
import { useNoteContext } from "../react/hooks";
|
||||
|
||||
export default function ToggleSidebarButton() {
|
||||
const parentComponent = useContext(ParentComponent);
|
||||
const { noteContext, parentComponent } = useNoteContext();
|
||||
|
||||
return (
|
||||
<ActionButton
|
||||
<div style={{ contain: "none", minWidth: 8 }}>
|
||||
{ noteContext?.isMainContext() && <ActionButton
|
||||
icon="bx bx-sidebar"
|
||||
text={t("note_tree.toggle-sidebar")}
|
||||
onClick={() => parentComponent?.triggerCommand("setActiveScreen", {
|
||||
screen: "tree"
|
||||
})}
|
||||
/>
|
||||
/>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -484,8 +484,7 @@ export function useNoteBlob(note: FNote | null | undefined, componentId?: string
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadResults.hasRevisionForNote(note.noteId) ||
|
||||
loadResults.isNoteContentReloaded(note.noteId, componentId)) {
|
||||
if (loadResults.isNoteContentReloaded(note.noteId, componentId)) {
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
|
||||
@ -26,7 +26,7 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [
|
||||
&& !(await noteContext?.isReadOnly()),
|
||||
toggleCommand: "toggleRibbonTabClassicEditor",
|
||||
content: FormattingToolbar,
|
||||
activate: () => !options.is("editedNotesOpenInRibbon"),
|
||||
activate: ({ note }) => !options.is("editedNotesOpenInRibbon") || !note?.hasOwnedLabel("dateNote"),
|
||||
stayInDom: true
|
||||
},
|
||||
{
|
||||
|
||||
@ -19,3 +19,9 @@ export function onWheelHorizontalScroll(event: WheelEvent) {
|
||||
event.stopImmediatePropagation();
|
||||
(event.currentTarget as HTMLElement).scrollLeft += event.deltaY + event.deltaX;
|
||||
}
|
||||
|
||||
export function getClosestNtxId(element: HTMLElement) {
|
||||
const closestNtxEl = element.closest<HTMLElement>("[data-ntx-id]");
|
||||
if (!closestNtxEl) return null;
|
||||
return closestNtxEl.dataset.ntxId ?? null;
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@
|
||||
"electron-debug": "4.1.0",
|
||||
"electron-window-state": "5.0.3",
|
||||
"escape-html": "1.0.3",
|
||||
"express": "5.1.0",
|
||||
"express": "5.2.0",
|
||||
"express-http-proxy": "2.1.2",
|
||||
"express-openid-connect": "2.19.3",
|
||||
"express-rate-limit": "8.2.1",
|
||||
|
||||
@ -6,23 +6,21 @@
|
||||
which will work even without Trilium being in focus (requires app restart
|
||||
to take effect).</p>
|
||||
<h2>Tree</h2>
|
||||
<p>See the corresponding section: <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/oPVyFC7WL2Lp/_help_DvdZhoQZY9Yd">Keyboard shortcuts</a>
|
||||
<p>See the corresponding section: <a class="reference-link" href="#root/_help_DvdZhoQZY9Yd">Keyboard shortcuts</a>
|
||||
</p>
|
||||
<h2>Note navigation</h2>
|
||||
<ul>
|
||||
<li data-list-item-id="ed8fd9c2ab08ae80ea76c1ae5dc943e90"><kbd>Alt</kbd> + <kbd><span>←</span></kbd>, <kbd>Alt</kbd> + <kbd><span>→</span></kbd> –
|
||||
<li><kbd>Alt</kbd> + <kbd><span>←</span></kbd>, <kbd>Alt</kbd> + <kbd><span>→</span></kbd> –
|
||||
go back / forwards in the history</li>
|
||||
<li data-list-item-id="eff3c5760b3d985e7808f1524982fcb9b"><kbd>Ctrl</kbd> + <kbd>J</kbd> – show <a href="#root/_help_MMiBEQljMQh2">"Jump to" dialog</a>
|
||||
<li><kbd>Ctrl</kbd> + <kbd>J</kbd> – show <a href="#root/_help_MMiBEQljMQh2">"Jump to" dialog</a>
|
||||
</li>
|
||||
<li data-list-item-id="e1fdee08a424868922b0579e78584279c"><kbd>Ctrl</kbd> + <kbd>.</kbd> – scroll to current note (useful when you
|
||||
<li><kbd>Ctrl</kbd> + <kbd>.</kbd> – scroll to current note (useful when you
|
||||
scroll away from your note or your focus is currently in the editor)</li>
|
||||
<li
|
||||
data-list-item-id="e9bbc51f19a729c10997010a915779d07"><kbd><span>Backspace</span></kbd> – jumps to parent note</li>
|
||||
<li data-list-item-id="eaaa0f36b2e561d4a24358552e633d924"><kbd>Alt</kbd> + <kbd>C</kbd> – collapse whole note tree</li>
|
||||
<li data-list-item-id="ee625a141f5298dbd52ecb6e56693e647"><kbd>Alt</kbd> + <kbd>-</kbd> (alt with minus sign) – collapse subtree (if
|
||||
<li><kbd><span>Backspace</span></kbd> – jumps to parent note</li>
|
||||
<li><kbd>Alt</kbd> + <kbd>C</kbd> – collapse whole note tree</li>
|
||||
<li><kbd>Alt</kbd> + <kbd>-</kbd> (alt with minus sign) – collapse subtree (if
|
||||
some subtree takes too much space on tree pane you can collapse it)</li>
|
||||
<li
|
||||
data-list-item-id="ec1bf00840a017ea798880470e419f3d9">you can define a <a href="#root/_help_zEY4DaJG4YT5">label</a> <code>#keyboardShortcut</code> with
|
||||
<li>you can define a <a href="#root/_help_zEY4DaJG4YT5">label</a> <code>#keyboardShortcut</code> with
|
||||
e.g. value <kbd>Ctrl</kbd> + <kbd>I</kbd> . Pressing this keyboard combination
|
||||
will then bring you to the note on which it is defined. Note that Trilium
|
||||
must be reloaded/restarted (<kbd>Ctrl</kbd> + <kbd>R</kbd> ) for changes to
|
||||
@ -31,23 +29,21 @@
|
||||
<p>See demo of some of these features in <a href="#root/_help_MMiBEQljMQh2">note navigation</a>.</p>
|
||||
<h2>Tabs</h2>
|
||||
<ul>
|
||||
<li data-list-item-id="e6a9d460427c0394177000c8a0eecfca4"><kbd>Ctrl</kbd> + <kbd>🖱 Left click</kbd> – (or middle mouse click) on note
|
||||
<li><kbd>Ctrl</kbd> + <kbd>🖱 Left click</kbd> – (or middle mouse click) on note
|
||||
link opens note in a new tab</li>
|
||||
</ul>
|
||||
<p>Only in desktop (electron build):</p>
|
||||
<ul>
|
||||
<li data-list-item-id="e745e6690db1a4bee718faa94b272013e"><kbd>Ctrl</kbd> + <kbd>T</kbd> – opens empty tab</li>
|
||||
<li data-list-item-id="ec34c224c2c0dd74367753a6aff4f9721"><kbd>Ctrl</kbd> + <kbd>W</kbd> – closes active tab</li>
|
||||
<li data-list-item-id="e4b9a2163cccc3ab7dbdcf6fb7f149315"><kbd>Ctrl</kbd> + <kbd>Tab</kbd> – activates next tab</li>
|
||||
<li data-list-item-id="eeeabd750227ec42c30b282b9b8ab5c8d"><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Tab</kbd> – activates previous tab</li>
|
||||
<li><kbd>Ctrl</kbd> + <kbd>T</kbd> – opens empty tab</li>
|
||||
<li><kbd>Ctrl</kbd> + <kbd>W</kbd> – closes active tab</li>
|
||||
<li><kbd>Ctrl</kbd> + <kbd>Tab</kbd> – activates next tab</li>
|
||||
<li><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Tab</kbd> – activates previous tab</li>
|
||||
</ul>
|
||||
<h2>Creating notes</h2>
|
||||
<ul>
|
||||
<li data-list-item-id="e90a7441e67297c921f8bb2145829dd55"><kbd>CTRL</kbd>+<kbd>O</kbd> – creates new note after the current note</li>
|
||||
<li
|
||||
data-list-item-id="e6064f6dd7b09083448cfb52247c94baa"><kbd>CTRL</kbd>+<kbd>P</kbd> – creates new sub-note into current note</li>
|
||||
<li
|
||||
data-list-item-id="e191cb0e7231c4439632d70da65dcf800"><kbd>F2</kbd> – edit <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/BFs8mudNFgCS/IakOLONlIfGI/_help_TBwsyfadTA18">Branch prefix</a> of
|
||||
<li><kbd>CTRL</kbd>+<kbd>O</kbd> – creates new note after the current note</li>
|
||||
<li><kbd>CTRL</kbd>+<kbd>P</kbd> – creates new sub-note into current note</li>
|
||||
<li><kbd>F2</kbd> – edit <a class="reference-link" href="#root/_help_TBwsyfadTA18">Branch prefix</a> of
|
||||
current note clone</li>
|
||||
</ul>
|
||||
<h2>Editing notes</h2>
|
||||
@ -58,32 +54,30 @@
|
||||
class="reference-link" href="#root/_help_QrtTYPmdd1qq">Markdown-like formatting</a>.</p>
|
||||
</aside>
|
||||
<ul>
|
||||
<li data-list-item-id="eea3611b470b2482ccf07e8844e4f66b9"><kbd>Enter</kbd> in tree pane switches from tree pane into note title.
|
||||
<li><kbd>Enter</kbd> in tree pane switches from tree pane into note title.
|
||||
Enter from note title switches focus to text editor. <kbd>Ctrl</kbd>+<kbd>.</kbd> switches
|
||||
back from editor to tree pane.</li>
|
||||
<li data-list-item-id="e5dab831910ac694ccef7bc474b2e67fc"><kbd>Ctrl</kbd>+<kbd>.</kbd> – jump away from the editor to tree pane and
|
||||
<li><kbd>Ctrl</kbd>+<kbd>.</kbd> – jump away from the editor to tree pane and
|
||||
scroll to current note</li>
|
||||
</ul>
|
||||
<h2>Runtime shortcuts</h2>
|
||||
<p>These are hooked in Electron to be similar to native browser keyboard
|
||||
shortcuts.</p>
|
||||
<ul>
|
||||
<li data-list-item-id="e1a17ce297e78109362180536b7eabd68"><kbd>F5</kbd>, <kbd>Ctrl</kbd>+<kbd>R</kbd> – reloads Trilium front-end</li>
|
||||
<li
|
||||
data-list-item-id="e07d1b25dc46ce0b0bd76a8db13373198"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>I</kbd> – show developer tools</li>
|
||||
<li
|
||||
data-list-item-id="e7dc66078ba520c48364952f2ac48aa79"><kbd>Ctrl</kbd>+<kbd>F</kbd> – show search dialog</li>
|
||||
<li data-list-item-id="e63161bd165a51e19fd04dd1223173e68"><kbd>Ctrl</kbd>+<kbd>-</kbd> – zoom out</li>
|
||||
<li data-list-item-id="e961144d0a57b51792830a2182459c8cf"><kbd>Ctrl</kbd>+<kbd>=</kbd> – zoom in</li>
|
||||
<li><kbd>F5</kbd>, <kbd>Ctrl</kbd>+<kbd>R</kbd> – reloads Trilium front-end</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>I</kbd> – show developer tools</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>F</kbd> – show search dialog</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>-</kbd> – zoom out</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>=</kbd> – zoom in</li>
|
||||
</ul>
|
||||
<h2>Other</h2>
|
||||
<ul>
|
||||
<li data-list-item-id="eb007c4ea2f2cd6eefbc8972d538c724c"><kbd>Alt</kbd> + <kbd>O</kbd> – show SQL console (use only if you know what
|
||||
<li><kbd>Alt</kbd> + <kbd>O</kbd> – show SQL console (use only if you know what
|
||||
you're doing)</li>
|
||||
<li data-list-item-id="e9caedb43e6c66fc7c57b4899bd6d5d76"><kbd>Alt</kbd> + <kbd>M</kbd> – distraction-free mode - display only note
|
||||
<li><kbd>Alt</kbd> + <kbd>M</kbd> – distraction-free mode - display only note
|
||||
editor, everything else is hidden</li>
|
||||
<li data-list-item-id="ec55bd0e5ff7da0c488a5378c0bdabed5"><kbd>F11</kbd> – toggle full screen</li>
|
||||
<li data-list-item-id="e8d4ec0d76155371de5d060ad661cb19e"><kbd>Ctrl</kbd> + <kbd>S</kbd> – toggle <a href="#root/_help_eIg8jdvaoNNd">search</a> form
|
||||
<li><kbd>F11</kbd> – toggle full screen</li>
|
||||
<li><kbd>Ctrl</kbd> + <kbd>S</kbd> – toggle <a href="#root/_help_eIg8jdvaoNNd">search</a> form
|
||||
in tree pane</li>
|
||||
<li data-list-item-id="eb938081f527f44b158be7c3ed4af4925"><kbd>Alt</kbd> +<kbd>A</kbd> – show note <a href="#root/_help_zEY4DaJG4YT5">attributes</a> dialog</li>
|
||||
<li><kbd>Alt</kbd> +<kbd>A</kbd> – show note <a href="#root/_help_zEY4DaJG4YT5">attributes</a> dialog</li>
|
||||
</ul>
|
||||
@ -2,52 +2,47 @@
|
||||
with multiple keyboard shortcuts to make editing faster:</p>
|
||||
<h2>Navigation within the tree</h2>
|
||||
<ul>
|
||||
<li data-list-item-id="eede1a5721dab5213153c6bb25bad38c9"><kbd><span>↑</span></kbd> and <kbd><span>↑</span></kbd> to navigate between
|
||||
<li><kbd><span>↑</span></kbd> and <kbd><span>↑</span></kbd> to navigate between
|
||||
notes.</li>
|
||||
<li data-list-item-id="ec4c4ab60720b34a95f73e93223ed3628"><kbd><span>←</span></kbd> to collapse a note with children, or <kbd><span>→</span></kbd> to
|
||||
<li><kbd><span>←</span></kbd> to collapse a note with children, or <kbd><span>→</span></kbd> to
|
||||
expand it.</li>
|
||||
<li data-list-item-id="eb5ad59d78e70611d0233ffbb5ede3b96"><kbd><span>←</span></kbd> on a note with no children to navigate to its
|
||||
<li><kbd><span>←</span></kbd> on a note with no children to navigate to its
|
||||
parent.</li>
|
||||
</ul>
|
||||
<h2>Opening notes</h2>
|
||||
<ul>
|
||||
<li data-list-item-id="e3d92e8c3a1e5b9d6f2efe5067004ef5c"><kbd>Click</kbd> to open the note in the current tab.</li>
|
||||
<li data-list-item-id="e8ae44ff429a0da1d529c627874f54908"><kbd>Ctrl</kbd>+<kbd>Click</kbd> or <kbd>Middle click</kbd> to open the note
|
||||
<li><kbd>Click</kbd> to open the note in the current tab.</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>Click</kbd> or <kbd>Middle click</kbd> to open the note
|
||||
in a new tab.</li>
|
||||
<li data-list-item-id="e183b1a014e2bd563450dac0c8913c6f0"><kbd>Ctrl</kbd>+<kbd>Right click</kbd> to open the note in <a class="reference-link"
|
||||
<li><kbd>Ctrl</kbd>+<kbd>Right click</kbd> to open the note in <a class="reference-link"
|
||||
href="#root/_help_ZjLYv08Rp3qC">Quick edit</a>.</li>
|
||||
</ul>
|
||||
<h2>Clipboard management</h2>
|
||||
<ul>
|
||||
<li data-list-item-id="e28fb424bcc0d06b97ddcbcbc5145c350"><kbd>Ctrl</kbd>+<kbd>C</kbd> to copy one or more notes based on selection
|
||||
(see <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/BFs8mudNFgCS/_help_IakOLONlIfGI">Cloning Notes</a>).</li>
|
||||
<li
|
||||
data-list-item-id="e11c4c9fcdfeb566a58d9319a340ff893"><kbd>Ctrl</kbd>+<kbd>X</kbd> to cut one or more notes (for moving them).</li>
|
||||
<li
|
||||
data-list-item-id="e242519cf16b3414242c4d11f20688b5b"><kbd>Ctrl</kbd>+<kbd>V</kbd> to paste them somewhere (which results in
|
||||
<li><kbd>Ctrl</kbd>+<kbd>C</kbd> to copy one or more notes based on selection
|
||||
(see <a class="reference-link" href="#root/_help_IakOLONlIfGI">Cloning Notes</a>).</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>X</kbd> to cut one or more notes (for moving them).</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>V</kbd> to paste them somewhere (which results in
|
||||
a copy or move based on the shortcut used).</li>
|
||||
</ul>
|
||||
<h2>Moving notes</h2>
|
||||
<ul>
|
||||
<li data-list-item-id="ee2d0e5633e0ab23d0fcde7b968731794"><kbd>Ctrl</kbd> + <kbd><span>↑</span></kbd> , <kbd>Ctrl</kbd> + <kbd><span>↓</span></kbd> -
|
||||
<li><kbd>Ctrl</kbd> + <kbd><span>↑</span></kbd> , <kbd>Ctrl</kbd> + <kbd><span>↓</span></kbd> -
|
||||
move note up/down in the note list.</li>
|
||||
<li data-list-item-id="e14146e7aacfc92aa7daf8215ce4b379c"><kbd>Ctrl</kbd> + <kbd><span>←</span></kbd> - move note up in the note tree.</li>
|
||||
<li
|
||||
data-list-item-id="e21a7f5ab9d62af04b6ab81b0cc4cf8c7"><kbd>Ctrl</kbd>+<kbd><span>→</span></kbd> - move note down in the note
|
||||
<li><kbd>Ctrl</kbd> + <kbd><span>←</span></kbd> - move note up in the note tree.</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd><span>→</span></kbd> - move note down in the note
|
||||
tree.</li>
|
||||
<li data-list-item-id="e99f4b0aef9693ad25ca0ce62c5b67418"><kbd>Del</kbd> - deletes note and optionally its subtree (asked in the
|
||||
<li><kbd>Del</kbd> - deletes note and optionally its subtree (asked in the
|
||||
dialog).</li>
|
||||
</ul>
|
||||
<h2>Multiple selection</h2>
|
||||
<p>See <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/oPVyFC7WL2Lp/_help_yTjUdsOi4CIE">Multiple selection</a> for
|
||||
<p>See <a class="reference-link" href="#root/_help_yTjUdsOi4CIE">Multiple selection</a> for
|
||||
more information about how selection works.</p>
|
||||
<ul>
|
||||
<li data-list-item-id="ebe4907d702229ab599e50f753d23cfb6"><kbd>Alt</kbd>+<kbd>Click</kbd> – add a single note to the current selection.</li>
|
||||
<li
|
||||
data-list-item-id="ecf6bb49f8d993d376382dcab4e4a7ee8"><kbd>Shift</kbd>+<kbd>Click</kbd> – select a range of notes, starting from
|
||||
<li><kbd>Alt</kbd>+<kbd>Click</kbd> – add a single note to the current selection.</li>
|
||||
<li><kbd>Shift</kbd>+<kbd>Click</kbd> – select a range of notes, starting from
|
||||
the current note (the highlighted one) to the one that is being clicked.</li>
|
||||
<li
|
||||
data-list-item-id="eb1398c2cf85b53eafc3b7ab7a0974668"><kbd>Shift</kbd>+<kbd><span>↑</span></kbd>, <kbd>Shift</kbd>+<kbd><span>↓</span></kbd> –
|
||||
<li><kbd>Shift</kbd>+<kbd><span>↑</span></kbd>, <kbd>Shift</kbd>+<kbd><span>↓</span></kbd> –
|
||||
multi-select not above/below.</li>
|
||||
<li data-list-item-id="ebc1274f576f67d08a6d665a7318d80a8"><kbd>Ctrl</kbd>+<kbd>A</kbd> – select all notes in the current level</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>A</kbd> – select all notes in the current level</li>
|
||||
</ul>
|
||||
@ -4,29 +4,28 @@ class="image image-style-align-center">
|
||||
<img style="aspect-ratio:1398/1015;" src="Split View_2_Split View_im.png"
|
||||
width="1398" height="1015">
|
||||
</figure>
|
||||
|
||||
<h2><strong>Interactions</strong></h2>
|
||||
<ul>
|
||||
<li>Press the
|
||||
<li data-list-item-id="eb22263532280510ca0efeb2c2e757629">Press the
|
||||
<img src="Split View_Split View_imag.png">button to the right of a note's title to open a new split to the right
|
||||
of it.
|
||||
<ul>
|
||||
<li>It is possible to have as many splits as desired, simply press again the
|
||||
<li data-list-item-id="eda17492ea2d8da7c4bf2fb3e2f7bfbe9">It is possible to have as many splits as desired, simply press again the
|
||||
button.</li>
|
||||
<li>Only horizontal splits are possible, vertical or drag & dropping is
|
||||
<li data-list-item-id="ea0223c947ea17534d577c9cfef4d5c6e">Only horizontal splits are possible, vertical or drag & dropping is
|
||||
not supported.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>When at least one split is open, press the
|
||||
<li data-list-item-id="e77d99fdc9a0846903de57d1b710fdd56">When at least one split is open, press the
|
||||
<img src="Split View_3_Split View_im.png">button next to it to close it.</li>
|
||||
<li>Use the
|
||||
<li data-list-item-id="ec9d11f5bcfd10795f282e275938b2f4a">Use the
|
||||
<img src="Split View_4_Split View_im.png">or the
|
||||
<img src="Split View_1_Split View_im.png">button to move around the splits.</li>
|
||||
<li>Each <a href="#root/_help_3seOhtN8uLIY">tab</a> has its own split view configuration
|
||||
<li data-list-item-id="e8384a579c3d6ee8df4d7dbf9b07c3436">Each <a href="#root/_help_3seOhtN8uLIY">tab</a> has its own split view configuration
|
||||
(e.g. one tab can have two notes in a split view, whereas the others are
|
||||
one-note views).
|
||||
<ul>
|
||||
<li>The tab will indicate only the title of the main note (the first one in
|
||||
<li data-list-item-id="e298299f6b2f1b9d8b5f26a5f8a0c9092">The tab will indicate only the title of the main note (the first one in
|
||||
the list).</li>
|
||||
</ul>
|
||||
</li>
|
||||
@ -44,3 +43,41 @@ class="image image-style-align-center">
|
||||
the other, by hoisting the old place in the first split and hoisting the
|
||||
new place to the second one. This will allow easy cut and paste without
|
||||
the tree jumping around from switching between notes.</p>
|
||||
<h2>Mobile support</h2>
|
||||
<p>Since v0.100.0, it's possible to have a split view on the mobile view
|
||||
as well, with the following differences from the desktop version of the
|
||||
split:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="efc0bb8eb81ea1617b73188613f2ede5d">On smartphones, the split views are laid out vertically (one on the top
|
||||
and one on the bottom), instead of horizontally as on the desktop.</li>
|
||||
<li
|
||||
data-list-item-id="e7659da22c36db39ae8e3dc5424afba1e">There can be only one split open per tab.</li>
|
||||
<li data-list-item-id="eb848af9837a484a9de9117922c6d7186">It's not possible to resize the two split panes.</li>
|
||||
<li data-list-item-id="e869c240066f602fbc1c0e55259ba62e5">When the keyboard is opened, the active note will be “maximized”, thus
|
||||
allowing for more space even when a split is open. When the keyboard is
|
||||
closed, the splits become equal in size again.</li>
|
||||
</ul>
|
||||
<p>Interaction:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="edbf5c644758db5aca9867c516f97542b">To create a new split, click the three dots button on the right of the
|
||||
note title and select <em>Create new split</em>.
|
||||
<ul>
|
||||
<li data-list-item-id="eed272873b629f70418c3e7074a829369">This option will only be available if there is no split already open in
|
||||
the current tab.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li data-list-item-id="e733863e6058336ebfcf27042f56be312">To close a split, click the three dots button on the right of the note
|
||||
title and select <em>Close this pane</em>.
|
||||
<ul>
|
||||
<li data-list-item-id="e9e6c191873dcd658242c64553343e0c7">Note that this option will only be available on the second note in the
|
||||
split (the one at the bottom on smartphones, the one on the right on tablets).</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li data-list-item-id="e780e5e7736b4d26705102545c5626a6a">When long-pressing a link, a contextual menu will show up with an option
|
||||
to <em>Open note in a new split</em>.
|
||||
<ul>
|
||||
<li data-list-item-id="e264277ee51f6d266a4088e2545f6648d">If there's already a split, the option will replace the existing split
|
||||
instead.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
2
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Calendar.html
generated
vendored
2
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Calendar.html
generated
vendored
@ -193,7 +193,7 @@
|
||||
<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>
|
||||
in the calendar and not for other places such as the note tree. (<em>Deprecated</em>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#iconClass</code>
|
||||
|
||||
10
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text.html
generated
vendored
10
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text.html
generated
vendored
@ -4,7 +4,7 @@
|
||||
<p>Most of the interaction with text notes is done via the built-in toolbars.
|
||||
Depending on preference, there are two different layouts:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="eafcb31c309cb140eedbb41048a0e0db1">The <em>Floating toolbar</em> is hidden by default and only appears when
|
||||
<li>The <em>Floating toolbar</em> is hidden by default and only appears when
|
||||
needed. In this mode there are actually two different toolbars:
|
||||
<br>
|
||||
<img src="1_Text_image.png" width="496"
|
||||
@ -12,7 +12,7 @@
|
||||
<img src="2_Text_image.png" width="812"
|
||||
height="114">
|
||||
</li>
|
||||
<li data-list-item-id="e59aa3103fa4c5de33075e51f8d482164">A toolbar that appears when text is selected. This provides text-level
|
||||
<li>A toolbar that appears when text is selected. This provides text-level
|
||||
formatting such as bold, italic, text colors, inline code, etc.
|
||||
<br><em><img src="Text_image.png" width="1109" height="124"></em>
|
||||
</li>
|
||||
@ -20,8 +20,6 @@
|
||||
<p>Fore more information see <a class="reference-link" href="#root/_help_nRhnJkTT8cPs">Formatting toolbar</a>.</p>
|
||||
<h2>Features and formatting</h2>
|
||||
<p>Here's a list of various features supported by text notes:</p>
|
||||
<figure
|
||||
class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@ -163,14 +161,14 @@ class="table">
|
||||
</li>
|
||||
<li data-list-item-id="e564b978c09fe5adf476b331b1e0640e3"><a class="reference-link" href="#root/_help_KC1HB96bqqHX">Templates</a>
|
||||
</li>
|
||||
<li data-list-item-id="e756306c31d9beffbba3820b6d1b9bc61"><a class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/iPIMuisry3hd/gLt3vA97tMcp/_help_5wZallV2Qo1t">Format Painter</a>
|
||||
<li data-list-item-id="e756306c31d9beffbba3820b6d1b9bc61"><a class="reference-link" href="#root/_help_5wZallV2Qo1t">Format Painter</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
|
||||
<h2>Read-Only vs. Editing Mode</h2>
|
||||
<p>Text notes are usually opened in edit mode. However, they may open in
|
||||
read-only mode if the note is too big or the note is explicitly marked
|
||||
|
||||
@ -12,11 +12,11 @@
|
||||
<p>Apart from using the UI, it is also possible to quickly insert headings
|
||||
using the Markdown-like shortcuts:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="e6083df2814e520e138326ef004812a46"><code>##</code> for Heading 2</li>
|
||||
<li data-list-item-id="e8b8acfd06c340271cc4fe84f65e67375"><code>###</code> for Heading 3</li>
|
||||
<li data-list-item-id="e19e95a52ddbcd54428ed5ab40df165a7"><code>####</code> for Heading 4</li>
|
||||
<li data-list-item-id="e69788e0bd8f4459748b8df997a19fa92"><code>#####</code> for Heading 5</li>
|
||||
<li data-list-item-id="eb7205b764231a314c9e9c1590c12ac1c"><code>######</code> for Heading 6</li>
|
||||
<li><code>##</code> for Heading 2</li>
|
||||
<li><code>###</code> for Heading 3</li>
|
||||
<li><code>####</code> for Heading 4</li>
|
||||
<li><code>#####</code> for Heading 5</li>
|
||||
<li><code>######</code> for Heading 6</li>
|
||||
</ul>
|
||||
<h2>Font size</h2>
|
||||
<figure class="image image-style-align-right">
|
||||
@ -44,17 +44,17 @@
|
||||
<p>This formatting can be easily removed using the <em>Remove formatting</em> item.</p>
|
||||
<p>The following keyboard shortcuts can be used here:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="ecda6994b8be7fe33daeccbeb8bffe20b"><kbd>Ctrl</kbd>+<kbd>B</kbd> for bold</li>
|
||||
<li data-list-item-id="eaa20ab7965945937bd10a273e5fc323f"><kbd>Ctrl</kbd>+<kbd>I</kbd> for italic</li>
|
||||
<li data-list-item-id="e86881b33671d3bd6930d361e4bb76767"><kbd>Ctrl</kbd>+<kbd>U</kbd> for underline</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>B</kbd> for bold</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>I</kbd> for italic</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>U</kbd> for underline</li>
|
||||
</ul>
|
||||
<p>Alternatively, Markdown-like formatting can be used:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="e90b0f040ab78084c41cd8f44045528ec"><strong>Bold</strong>: Type <code>**text**</code> or <code>__text__</code>
|
||||
<li><strong>Bold</strong>: Type <code>**text**</code> or <code>__text__</code>
|
||||
</li>
|
||||
<li data-list-item-id="eebc210ee7f2b92e0885cd411c6fb8b80"><em>Italic</em>: Type <code>*text*</code> or <code>_text_</code>
|
||||
<li><em>Italic</em>: Type <code>*text*</code> or <code>_text_</code>
|
||||
</li>
|
||||
<li data-list-item-id="eb7628d2cb441576b3b3edf6c42ab84b4"><del>Strikethrough</del>: Type <code>~~text~~</code>
|
||||
<li><del>Strikethrough</del>: Type <code>~~text~~</code>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>Superscript, subscript</h2>
|
||||
@ -90,7 +90,7 @@
|
||||
<p>When pasting content that comes with undesired formatting, an alternative
|
||||
to pasting and then removing formatting is pasting as plain text via <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>V</kbd>.</p>
|
||||
<h2>Format painter</h2>
|
||||
<p>The <a class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/iPIMuisry3hd/gLt3vA97tMcp/_help_5wZallV2Qo1t">Format Painter</a> allows
|
||||
<p>The <a class="reference-link" href="#root/_help_5wZallV2Qo1t">Format Painter</a> allows
|
||||
users to copy the formatting of text (such as bold, italic, Strikethrough,
|
||||
etc.) and apply it to other parts of the document. It helps maintain consistent
|
||||
formatting and accelerates the creation of rich content.</p>
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<p>This is a premium feature of the editor we are using (CKEditor) and we
|
||||
benefit from it thanks to an written agreement with the team. See
|
||||
<a
|
||||
class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/iPIMuisry3hd/_help_gLt3vA97tMcp">Premium features</a> for more information.</p>
|
||||
class="reference-link" href="#root/_help_gLt3vA97tMcp">Premium features</a> for more information.</p>
|
||||
</aside>
|
||||
<p>The Format Painter is a feature in text notes that allows users to copy
|
||||
the formatting of text (such as <strong>bold</strong>, <em>italic</em>, <del>Strikethrough</del>,
|
||||
@ -14,20 +14,24 @@
|
||||
formatting and accelerates the creation of rich content.</p>
|
||||
<h2>Usage Instructions</h2>
|
||||
<p>Click the text that you want to copy the formatting from and use the paint
|
||||
formatting toolbar button (<span><img class="image_resized" style="aspect-ratio:150/150;width:2.7%;" src="Format Painter_746436a2e1.svg" alt="Format painter" width="150" height="150">) </span>to
|
||||
copy the style. Then select the target text with your mouse to apply the
|
||||
formatting.</p>
|
||||
formatting toolbar button (
|
||||
<img class="image_resized" style="aspect-ratio:150/150;width:2.7%;"
|
||||
src="Format Painter_746436a2e1.svg"
|
||||
alt="Format painter" width="150" height="150">) to copy the style. Then select the target text with your mouse to apply
|
||||
the formatting.</p>
|
||||
<ul>
|
||||
<li data-list-item-id="e9a728e8f49fb3ecf1202002dfafccabd"><strong>To copy the formatting</strong>: Place the cursor inside a text
|
||||
<li><strong>To copy the formatting</strong>: Place the cursor inside a text
|
||||
with some formatting and click the paint formatting toolbar button. Notice
|
||||
that the mouse cursor changes to the <span><img class="image_resized" style="aspect-ratio:30/20;width:3.64%;" src="Format Painter_e144e96df9.svg" alt="Format painter text cursor" width="30" height="20"></span>.</li>
|
||||
<li
|
||||
data-list-item-id="e745c039a3502d4e979d6dcde7876461b"><strong>To paint with the copied formatting</strong>: Click any word in
|
||||
that the mouse cursor changes to the
|
||||
<img class="image_resized" style="aspect-ratio:30/20;width:3.64%;"
|
||||
src="Format Painter_e144e96df9.svg"
|
||||
alt="Format painter text cursor" width="30" height="20">.</li>
|
||||
<li><strong>To paint with the copied formatting</strong>: Click any word in
|
||||
the document and the new formatting will be applied. Alternatively, instead
|
||||
of clicking a single word, you can select a text fragment (like an entire
|
||||
paragraph). Notice that the cursor will go back to the default one after
|
||||
the formatting is applied.</li>
|
||||
<li data-list-item-id="e49e547cfd4e4cbb45712bace9a6e0979"><strong>To keep painting using the same formatting</strong>: Open the
|
||||
<li><strong>To keep painting using the same formatting</strong>: Open the
|
||||
toolbar dropdown and enable the continuous painting mode. Once copied,
|
||||
the same formatting can be applied multiple times in different places until
|
||||
the paint formatting button is clicked (the cursor will then revert to
|
||||
@ -35,11 +39,10 @@
|
||||
</ul>
|
||||
<h2>Limitations</h2>
|
||||
<ol>
|
||||
<li data-list-item-id="ea3e6a7c317b51341c7a83cee5387ac1e">Painting with block-level formatting (like headings or image styles) is
|
||||
not supported yet. This is because, in <a class="reference-link" href="#root/pOsGYCXsbNQG/tC7s2alapj8V/1YeN2MzFUluU/_help_MI26XDLSAlCD">CKEditor</a>,
|
||||
<li>Painting with block-level formatting (like headings or image styles) is
|
||||
not supported yet. This is because, in <a class="reference-link" href="#root/_help_MI26XDLSAlCD">CKEditor</a>,
|
||||
they are considered a part of the content rather than text formatting.</li>
|
||||
<li
|
||||
data-list-item-id="edbd74adb8916daaae6024d8b0ae80e32">When applying formatting to words, spaces or other Western punctuation
|
||||
<li>When applying formatting to words, spaces or other Western punctuation
|
||||
are used as word boundaries, which prevents proper handling of languages
|
||||
that do not use space-based word segmentation.</li>
|
||||
</ol>
|
||||
@ -3,7 +3,9 @@
|
||||
"back-in-note-history": "Navega a la nota previa a l'historial",
|
||||
"forward-in-note-history": "Navega a la següent nota a l'historial",
|
||||
"dialogs": "Diàlegs",
|
||||
"other": "Altres"
|
||||
"other": "Altres",
|
||||
"open-jump-to-note-dialog": "Obre \"Salta a la nota\"",
|
||||
"open-command-palette": "Obre el panell de comandes"
|
||||
},
|
||||
"login": {
|
||||
"title": "Inicia sessió",
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
"open-command-palette": "Open het opdrachtvenster",
|
||||
"scroll-to-active-note": "Scroll naar actieve notitie in de notitieboom",
|
||||
"quick-search": "Snelle zoekbalk activeren",
|
||||
"search-in-subtree": "Zoek naar notities in de subboom van de actieve notitie",
|
||||
"search-in-subtree": "Zoek naar notities in de subtak van de actieve notitie",
|
||||
"expand-subtree": "Subboom van huidige notitie uitbreiden",
|
||||
"collapse-tree": "Vouwt de volledige notitieboom samen",
|
||||
"collapse-subtree": "Vouwt de subboom van de huidige notitie samen",
|
||||
|
||||
@ -40,6 +40,13 @@ interface Subroot {
|
||||
|
||||
type GetNoteFunction = (id: string) => SNote | BNote | null;
|
||||
|
||||
function addContentAccessQuery(note: SNote | BNote, secondEl?:boolean) {
|
||||
if (!(note instanceof BNote) && note.contentAccessor && note.contentAccessor?.type === "query") {
|
||||
return secondEl ? `&cat=${note.contentAccessor.getToken()}` : `?cat=${note.contentAccessor.getToken()}`;
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
function getSharedSubTreeRoot(note: SNote | BNote | undefined): Subroot {
|
||||
if (!note || note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) {
|
||||
// share root itself is not shared
|
||||
@ -111,7 +118,7 @@ export function renderNoteContent(note: SNote) {
|
||||
cssToLoad.push(`assets/scripts.css`);
|
||||
}
|
||||
for (const cssRelation of note.getRelations("shareCss")) {
|
||||
cssToLoad.push(`api/notes/${cssRelation.value}/download`);
|
||||
cssToLoad.push(`api/notes/${cssRelation.value}/download${addContentAccessQuery(note)}`);
|
||||
}
|
||||
|
||||
// Determine JS to load.
|
||||
@ -119,11 +126,11 @@ export function renderNoteContent(note: SNote) {
|
||||
"assets/scripts.js"
|
||||
];
|
||||
for (const jsRelation of note.getRelations("shareJs")) {
|
||||
jsToLoad.push(`api/notes/${jsRelation.value}/download`);
|
||||
jsToLoad.push(`api/notes/${jsRelation.value}/download${addContentAccessQuery(note)}`);
|
||||
}
|
||||
|
||||
const customLogoId = note.getRelation("shareLogo")?.value;
|
||||
const logoUrl = customLogoId ? `api/images/${customLogoId}/image.png` : `../${assetUrlFragment}/images/icon-color.svg`;
|
||||
const logoUrl = customLogoId ? `api/images/${customLogoId}/image.png${addContentAccessQuery(note)}` : `../${assetUrlFragment}/images/icon-color.svg`;
|
||||
|
||||
return renderNoteContentInternal(note, {
|
||||
subRoot,
|
||||
@ -133,7 +140,7 @@ export function renderNoteContent(note: SNote) {
|
||||
logoUrl,
|
||||
ancestors,
|
||||
isStatic: false,
|
||||
faviconUrl: note.hasRelation("shareFavicon") ? `api/notes/${note.getRelationValue("shareFavicon")}/download` : `../favicon.ico`
|
||||
faviconUrl: note.hasRelation("shareFavicon") ? `api/notes/${note.getRelationValue("shareFavicon")}/download${addContentAccessQuery(note)}` : `../favicon.ico`
|
||||
});
|
||||
}
|
||||
|
||||
@ -158,6 +165,7 @@ function renderNoteContentInternal(note: SNote | BNote, renderArgs: RenderArgs)
|
||||
isEmpty,
|
||||
assetPath: shareAdjustedAssetPath,
|
||||
assetUrlFragment,
|
||||
addContentAccessQuery: (second: boolean | undefined) => addContentAccessQuery(note, second),
|
||||
showLoginInShareTheme,
|
||||
t,
|
||||
isDev,
|
||||
@ -325,7 +333,7 @@ function renderText(result: Result, note: SNote | BNote) {
|
||||
}
|
||||
|
||||
if (href?.startsWith("#")) {
|
||||
handleAttachmentLink(linkEl, href, getNote, getAttachment);
|
||||
handleAttachmentLink(linkEl, href, getNote, getAttachment, note);
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,7 +357,7 @@ function renderText(result: Result, note: SNote | BNote) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleAttachmentLink(linkEl: HTMLElement, href: string, getNote: GetNoteFunction, getAttachment: (id: string) => BAttachment | SAttachment | null) {
|
||||
function handleAttachmentLink(linkEl: HTMLElement, href: string, getNote: GetNoteFunction, getAttachment: (id: string) => BAttachment | SAttachment | null, note: SNote | BNote) {
|
||||
const linkRegExp = /attachmentId=([a-zA-Z0-9_]+)/g;
|
||||
let attachmentMatch;
|
||||
if ((attachmentMatch = linkRegExp.exec(href))) {
|
||||
@ -357,7 +365,7 @@ function handleAttachmentLink(linkEl: HTMLElement, href: string, getNote: GetNot
|
||||
const attachment = getAttachment(attachmentId);
|
||||
|
||||
if (attachment) {
|
||||
linkEl.setAttribute("href", `api/attachments/${attachmentId}/download`);
|
||||
linkEl.setAttribute("href", `api/attachments/${attachmentId}/download${addContentAccessQuery(note)}`);
|
||||
linkEl.classList.add(`attachment-link`);
|
||||
linkEl.classList.add(`role-${attachment.role}`);
|
||||
linkEl.childNodes.length = 0;
|
||||
@ -430,7 +438,7 @@ function renderMermaid(result: Result, note: SNote | BNote) {
|
||||
}
|
||||
|
||||
result.content = `
|
||||
<img src="api/images/${note.noteId}/${note.encodedTitle}?${note.utcDateModified}">
|
||||
<img src="api/images/${note.noteId}/${note.encodedTitle}?${note.utcDateModified}${addContentAccessQuery(note, true)}">
|
||||
<hr>
|
||||
<details>
|
||||
<summary>Chart source</summary>
|
||||
@ -439,14 +447,14 @@ function renderMermaid(result: Result, note: SNote | BNote) {
|
||||
}
|
||||
|
||||
function renderImage(result: Result, note: SNote | BNote) {
|
||||
result.content = `<img src="api/images/${note.noteId}/${note.encodedTitle}?${note.utcDateModified}">`;
|
||||
result.content = `<img src="api/images/${note.noteId}/${note.encodedTitle}?${note.utcDateModified}${addContentAccessQuery(note, true)}">`;
|
||||
}
|
||||
|
||||
function renderFile(note: SNote | BNote, result: Result) {
|
||||
if (note.mime === "application/pdf") {
|
||||
result.content = `<iframe class="pdf-view" src="api/notes/${note.noteId}/view"></iframe>`;
|
||||
result.content = `<iframe class="pdf-view" src="api/notes/${note.noteId}/view${addContentAccessQuery(note)}"></iframe>`;
|
||||
} else {
|
||||
result.content = `<button type="button" onclick="location.href='api/notes/${note.noteId}/download'">Download file</button>`;
|
||||
result.content = `<button type="button" onclick="location.href='api/notes/${note.noteId}/download${addContentAccessQuery(note)}'">Download file</button>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -60,6 +60,20 @@ function checkNoteAccess(noteId: string, req: Request, res: Response) {
|
||||
const header = req.header("Authorization");
|
||||
|
||||
if (!header?.startsWith("Basic ")) {
|
||||
if (req.path.startsWith("/share/api") && note.contentAccessor) {
|
||||
let contentAccessToken = ""
|
||||
if (note.contentAccessor.type === "cookie") contentAccessToken += req.cookies["trilium.cat"] || ""
|
||||
else if (note.contentAccessor.type === "query") contentAccessToken += req.query['cat'] || ""
|
||||
|
||||
if (contentAccessToken){
|
||||
if (note.contentAccessor.isTokenValid(contentAccessToken)){
|
||||
return note
|
||||
}
|
||||
res.status(401).send("Access is expired. Return back and update the page.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -124,9 +138,14 @@ function register(router: Router) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (note.isLabelTruthy("shareExclude")) {
|
||||
res.status(404);
|
||||
render404(res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkNoteAccess(note.noteId, req, res)) {
|
||||
requestCredentials(res);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -138,6 +157,10 @@ function register(router: Router) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (note.contentAccessor && note.contentAccessor.type === "cookie") {
|
||||
res.cookie('trilium.cat', note.contentAccessor.getToken(), { maxAge: note.contentAccessor.getTokenExpiration() * 1000, httpOnly: true })
|
||||
}
|
||||
|
||||
res.send(renderNoteContent(note));
|
||||
}
|
||||
|
||||
@ -163,6 +186,9 @@ function register(router: Router) {
|
||||
const { shareId } = req.params;
|
||||
|
||||
const note = shaca.aliasToNote[shareId] || shaca.notes[shareId];
|
||||
if (note){
|
||||
note.initContentAccessor()
|
||||
}
|
||||
|
||||
renderNote(note, req, res);
|
||||
});
|
||||
|
||||
81
apps/server/src/share/shaca/entities/content_accessor.ts
Normal file
81
apps/server/src/share/shaca/entities/content_accessor.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import crypto from "crypto";
|
||||
import SNote from "./snote";
|
||||
import utils from "../../../services/utils";
|
||||
|
||||
const DefaultAccessTimeoutSec = 10 * 60; // 10 minutes
|
||||
|
||||
export class ContentAccessor {
|
||||
note: SNote;
|
||||
token: string;
|
||||
timestamp: number;
|
||||
type: string;
|
||||
timeout: number;
|
||||
key: Buffer;
|
||||
|
||||
constructor(note: SNote) {
|
||||
this.note = note;
|
||||
this.key = crypto.randomBytes(32);
|
||||
this.token = "";
|
||||
this.timestamp = 0;
|
||||
this.timeout = Number(this.note.getAttributeValue("label", "shareAccessTokenTimeout") || DefaultAccessTimeoutSec)
|
||||
|
||||
switch (this.note.getAttributeValue("label", "shareContentAccess")) {
|
||||
case "basic": this.type = "basic"; break
|
||||
case "query": this.type = "query"; break
|
||||
default: this.type = "cookie"; break
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
__encrypt(text: string) {
|
||||
const iv = crypto.randomBytes(16);
|
||||
const cipher = crypto.createCipheriv('aes-256-cbc', this.key, iv);
|
||||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
return iv.toString('hex') + encrypted;
|
||||
}
|
||||
|
||||
__decrypt(encryptedText: string) {
|
||||
try {
|
||||
const iv = Buffer.from(encryptedText.slice(0, 32), 'hex');
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, iv);
|
||||
let decrypted = decipher.update(encryptedText.slice(32), 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
return decrypted;
|
||||
} catch {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
__compare(originalText: string, encryptedText: string) {
|
||||
return originalText === this.__decrypt(encryptedText)
|
||||
}
|
||||
|
||||
update() {
|
||||
if (new Date().getTime() < this.timestamp + this.getTimeout() * 1000) return
|
||||
this.token = utils.randomString(36);
|
||||
this.key = crypto.randomBytes(32);
|
||||
this.timestamp = new Date().getTime();
|
||||
}
|
||||
|
||||
isTokenValid(encToken: string) {
|
||||
return this.__compare(this.token, encToken) && new Date().getTime() < this.timestamp + this.getTimeout() * 1000;
|
||||
}
|
||||
|
||||
getToken() {
|
||||
return this.__encrypt(this.token);
|
||||
}
|
||||
|
||||
getTokenExpiration() {
|
||||
return (this.timestamp + (this.timeout * 1000) - new Date().getTime()) /1000;
|
||||
}
|
||||
|
||||
getTimeout() {
|
||||
return this.timeout;
|
||||
}
|
||||
|
||||
getContentAccessType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
}
|
||||
@ -10,6 +10,7 @@ import type SAttribute from "./sattribute.js";
|
||||
import type SBranch from "./sbranch.js";
|
||||
import type { SNoteRow } from "./rows.js";
|
||||
import { NOTE_TYPE_ICONS } from "../../../becca/entities/bnote.js";
|
||||
import { ContentAccessor } from "./content_accessor.js";
|
||||
|
||||
const LABEL = "label";
|
||||
const RELATION = "relation";
|
||||
@ -33,6 +34,7 @@ class SNote extends AbstractShacaEntity {
|
||||
private __inheritableAttributeCache: SAttribute[] | null;
|
||||
targetRelations: SAttribute[];
|
||||
attachments: SAttachment[];
|
||||
contentAccessor: ContentAccessor | undefined;
|
||||
|
||||
constructor([noteId, title, type, mime, blobId, utcDateModified, isProtected]: SNoteRow) {
|
||||
super();
|
||||
@ -59,6 +61,15 @@ class SNote extends AbstractShacaEntity {
|
||||
this.shaca.notes[this.noteId] = this;
|
||||
}
|
||||
|
||||
initContentAccessor(){
|
||||
if (!this.contentAccessor && this.getCredentials().length > 0) {
|
||||
this.contentAccessor = new ContentAccessor(this);
|
||||
}
|
||||
if (this.contentAccessor) {
|
||||
this.contentAccessor.update()
|
||||
}
|
||||
}
|
||||
|
||||
getParentBranches() {
|
||||
return this.parentBranches;
|
||||
}
|
||||
@ -72,7 +83,7 @@ class SNote extends AbstractShacaEntity {
|
||||
}
|
||||
|
||||
getVisibleChildBranches() {
|
||||
return this.getChildBranches().filter((branch) => !branch.isHidden && !branch.getNote().isLabelTruthy("shareHiddenFromTree"));
|
||||
return this.getChildBranches().filter((branch) => !branch.isHidden && !branch.getNote().isLabelTruthy("shareHiddenFromTree") && !branch.getNote().isLabelTruthy("shareExclude"));
|
||||
}
|
||||
|
||||
getParentNotes() {
|
||||
@ -80,7 +91,7 @@ class SNote extends AbstractShacaEntity {
|
||||
}
|
||||
|
||||
getChildNotes() {
|
||||
return this.children;
|
||||
return this.children.filter((note) => !note.isLabelTruthy("shareExclude"));
|
||||
}
|
||||
|
||||
getVisibleChildNotes() {
|
||||
|
||||
@ -1 +1,8 @@
|
||||
{}
|
||||
{
|
||||
"get-started": {
|
||||
"title": "Comença",
|
||||
"desktop_title": "Descarrega l'aplicació d'escriptori (v{{version}})",
|
||||
"architecture": "Arquitectura:",
|
||||
"older_releases": "Veure versions anteriors"
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,13 @@
|
||||
"revisions_content": "Notities worden periodiek op de achtergrond opgeslagen, en versies kunnen worden gebruikt om wijzigingen te bekijken of om per ongeluk gemaakte aanpassingen ongedaan te maken. Revisies kunnen ook handmatig worden aangemaakt.",
|
||||
"sync_title": "Synchronisatie",
|
||||
"sync_content": "Gebruik een zelfgehoste of cloudinstantie om je notities eenvoudig te synchroniseren tussen meerdere apparaten, en om er via een PWA toegang toe te krijgen op je mobiele telefoon.",
|
||||
"protected_notes_title": "Beveiligde notities"
|
||||
"protected_notes_title": "Beveiligde notities",
|
||||
"protected_notes_content": "Bescherm gevoelige persoonlijke informatie door notities te versleutelen en te beschermen achter een met wachtwoord beveiligde sessie.",
|
||||
"jump_to_title": "Snel zoeken en commando's",
|
||||
"jump_to_content": "Spring snel naar notities of gebruikersinterfacecommando's binnen de hiërarchie door te zoeken naar de titel, met benaderingslogica om tikfouten en kleine verschillen te omzeilen.",
|
||||
"search_title": "Krachtige zoekfunctie",
|
||||
"search_content": "Of zoek naar tekst binnen notities en verfijn de zoekopdracht door in de bovenliggende notitie te filteren of zoekdiepte aan te geven.",
|
||||
"web_clipper_title": "Web clipper",
|
||||
"web_clipper_content": "Bewaar webpagina's (of schermafbeeldingen) en plaats deze direct in Trillium door de web clipper browser extensie te gebruiken."
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# Documentation
|
||||
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/Xtlv3JlumknM/Documentation_image.png" width="205" height="162">
|
||||
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/KJUp1g3csedB/Documentation_image.png" width="205" height="162">
|
||||
|
||||
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>.
|
||||
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.
|
||||
|
||||
13
docs/README-ca.md
vendored
13
docs/README-ca.md
vendored
@ -20,13 +20,14 @@ releases)](https://img.shields.io/github/downloads/triliumnext/trilium/total)\
|
||||
[](https://hosted.weblate.org/engage/trilium/)
|
||||
|
||||
[English](./README.md) | [Chinese (Simplified)](./docs/README-ZH_CN.md) |
|
||||
[Chinese (Traditional)](./docs/README-ZH_TW.md) | [Russian](./docs/README-ru.md)
|
||||
| [Japanese](./docs/README-ja.md) | [Italian](./docs/README-it.md) |
|
||||
[Spanish](./docs/README-es.md)
|
||||
[Anglès](./README.md) | [Xinès (simplificado)](./docs/README-ZH_CN.md) | [Xinès
|
||||
(tradicional)](./docs/README-ZH_TW.md) | [Rus](./docs/README-ru.md) |
|
||||
[Japonès](./docs/README-ja.md) | [Italià](./docs/README-it.md) |
|
||||
[Castellà](./docs/README-es.md)
|
||||
|
||||
Trilium Notes is a free and open-source, cross-platform hierarchical note taking
|
||||
application with focus on building large personal knowledge bases.
|
||||
Trillium Notes és una aplicació gratuïta i de codi obert, multiplataforma, per
|
||||
prendre notes jeràrquiques, amb l'objectiu de crear grans bases de coneixement
|
||||
personal.
|
||||
|
||||
See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for
|
||||
quick overview:
|
||||
|
||||
38
docs/README-nl.md
vendored
38
docs/README-nl.md
vendored
@ -54,12 +54,12 @@ Onze documentatie is beschikbaar in meerdere formaten:
|
||||
- **GitHub**: Navigeer door de [User Guide](./docs/User%20Guide/User%20Guide/)
|
||||
in deze repository
|
||||
|
||||
### Quick Links
|
||||
### Snelkoppelingen
|
||||
- [Getting Started Gids](https://docs.triliumnotes.org/)
|
||||
- [Vertaal
|
||||
Instructies](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation.md)
|
||||
- [Docker
|
||||
Setup](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation/1.%20Installing%20the%20server/Using%20Docker.md)
|
||||
Installatie](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation/1.%20Installing%20the%20server/Using%20Docker.md)
|
||||
- [TriliumNext
|
||||
Upgraden](./docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Upgrading%20TriliumNext.md)
|
||||
- [Basis Concepten en
|
||||
@ -67,7 +67,7 @@ Onze documentatie is beschikbaar in meerdere formaten:
|
||||
- [Patronen van Personal Knowledge
|
||||
Base](https://triliumnext.github.io/Docs/Wiki/patterns-of-personal-knowledge)
|
||||
|
||||
## 🎁 Features
|
||||
## 🎁 Kenmerken
|
||||
|
||||
* Notes kunnen in een diepe boomstructuur geplaatst worden. Een enkele note kan
|
||||
op meerdere plekken in de boom geplaatst worden. (zie
|
||||
@ -78,22 +78,22 @@ Onze documentatie is beschikbaar in meerdere formaten:
|
||||
* Ondersteuning voor bewerken van [notes met source
|
||||
code](https://triliumnext.github.io/Docs/Wiki/code-notes), inclusief syntax
|
||||
highlighting
|
||||
* Fast and easy [navigation between
|
||||
notes](https://triliumnext.github.io/Docs/Wiki/note-navigation), full text
|
||||
search and [note
|
||||
hoisting](https://triliumnext.github.io/Docs/Wiki/note-hoisting)
|
||||
* Seamless [note
|
||||
versioning](https://triliumnext.github.io/Docs/Wiki/note-revisions)
|
||||
* Note [attributes](https://triliumnext.github.io/Docs/Wiki/attributes) can be
|
||||
used for note organization, querying and advanced
|
||||
[scripting](https://triliumnext.github.io/Docs/Wiki/scripts)
|
||||
* UI available in English, German, Spanish, French, Romanian, and Chinese
|
||||
(simplified and traditional)
|
||||
* Direct [OpenID and TOTP
|
||||
integration](./docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Server%20Installation/Multi-Factor%20Authentication.md)
|
||||
for more secure login
|
||||
* [Synchronization](https://triliumnext.github.io/Docs/Wiki/synchronization)
|
||||
with self-hosted sync server
|
||||
* Snelle en gemakkelijke [navigatie tussen
|
||||
notities](https://triliumnext.github.io/Docs/Wiki/note-navigation),
|
||||
uitgebreide text zoekopdrachten en [notities
|
||||
promoveren](https://triliumnext.github.io/Docs/Wiki/note-hoisting)
|
||||
* Naadloze [notitie
|
||||
versiegeschiedenis](https://triliumnext.github.io/Docs/Wiki/note-revisions)
|
||||
* Notitie[-attributen](https://triliumnext.github.io/Docs/Wiki/attributes)
|
||||
kunnen worden ingezet voor notitie-organisatie, queries en geavanceerd
|
||||
[scripten](https://triliumnext.github.io/Docs/Wiki/scripts)
|
||||
* Gebruikersinterfacevariabele in het Engels, Duits, Spaans, Frans, Roemeens en
|
||||
Chinees (versimpeld en traditioneel)
|
||||
* Directe [OpenID en TOTP
|
||||
integratie](./docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Server%20Installation/Multi-Factor%20Authentication.md)
|
||||
voor beter beveiligde aanmelding
|
||||
* [Synchronisatie](https://triliumnext.github.io/Docs/Wiki/synchronization) met
|
||||
zelfgehoste synchronisatieserver
|
||||
* there's a [3rd party service for hosting synchronisation
|
||||
server](https://trilium.cc/paid-hosting)
|
||||
* [Sharing](https://triliumnext.github.io/Docs/Wiki/sharing) (publishing) notes
|
||||
|
||||
42
docs/User Guide/!!!meta.json
vendored
42
docs/User Guide/!!!meta.json
vendored
@ -6192,31 +6192,38 @@
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "CoFPLs3dRlXc",
|
||||
"value": "5wZallV2Qo1t",
|
||||
"isInheritable": false,
|
||||
"position": 180
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "A9Oc6YKKc65v",
|
||||
"value": "CoFPLs3dRlXc",
|
||||
"isInheritable": false,
|
||||
"position": 190
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "QrtTYPmdd1qq",
|
||||
"value": "A9Oc6YKKc65v",
|
||||
"isInheritable": false,
|
||||
"position": 200
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "MI26XDLSAlCD",
|
||||
"value": "QrtTYPmdd1qq",
|
||||
"isInheritable": false,
|
||||
"position": 210
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "MI26XDLSAlCD",
|
||||
"isInheritable": false,
|
||||
"position": 220
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"name": "shareAlias",
|
||||
@ -6244,13 +6251,6 @@
|
||||
"value": "",
|
||||
"isInheritable": false,
|
||||
"position": 40
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "5wZallV2Qo1t",
|
||||
"isInheritable": false,
|
||||
"position": 220
|
||||
}
|
||||
],
|
||||
"format": "markdown",
|
||||
@ -6904,10 +6904,17 @@
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "Oau6X9rCuegd",
|
||||
"value": "5wZallV2Qo1t",
|
||||
"isInheritable": false,
|
||||
"position": 40
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "Oau6X9rCuegd",
|
||||
"isInheritable": false,
|
||||
"position": 50
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"name": "iconClass",
|
||||
@ -6921,13 +6928,6 @@
|
||||
"value": "general-formatting",
|
||||
"isInheritable": false,
|
||||
"position": 60
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "5wZallV2Qo1t",
|
||||
"isInheritable": false,
|
||||
"position": 70
|
||||
}
|
||||
],
|
||||
"format": "markdown",
|
||||
@ -8460,14 +8460,14 @@
|
||||
"name": "internalLink",
|
||||
"value": "gLt3vA97tMcp",
|
||||
"isInheritable": false,
|
||||
"position": 30
|
||||
"position": 10
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "MI26XDLSAlCD",
|
||||
"isInheritable": false,
|
||||
"position": 40
|
||||
"position": 20
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
|
||||
@ -131,7 +131,7 @@ To do so, create a shared text note and apply the `shareIndex` label. When viewe
|
||||
|
||||
## Attribute reference
|
||||
|
||||
<table class="ck-table-resized"><colgroup><col style="width:18.38%;"><col style="width:81.62%;"></colgroup><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="Sharing/Serving%20directly%20the%20content%20o.md">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 data-list-item-id="e6baa9f60bf59d085fd31aa2cce07a0e7">Setting the <code>X-Robots-Tag: noindex</code> HTTP header.</li><li data-list-item-id="ec0d067db136ef9794e4f1033405880b7">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><tr><td><code>#shareHtmlLocation</code></td><td>defines where custom HTML injected via <code>~shareHtml</code> relation should be placed. Applied to the HTML snippet note itself. Format: <code>location:position</code> where location is <code>head</code>, <code>body</code>, or <code>content</code> and position is <code>start</code> or <code>end</code>. Defaults to <code>content:end</code>.</td></tr></tbody></table>
|
||||
<table class="ck-table-resized"><colgroup><col style="width:18.38%;"><col style="width:81.62%;"></colgroup><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>#shareTemplateNoPrevNext</code></td><td>hide bottom page navigation prev and next page.</td></tr><tr><td><code>#shareTemplateNoLeftPanel</code></td><td>hide left panel fully.</td></tr><tr><td><code>#shareExclude</code></td><td>this note will be excluded from share, not accessible via direct URL (implemented to hide scripts from share)</td></tr><tr><td><code>#shareContentAccess</code></td><td>method for attachments authorization in case when note protected with login and password (#shareCredentials). Could be cookie (the cookie will be provided when page loads) / query (every url will be updated with token) / basic (only basic header authorization)). By default for browser used cookie.</td></tr><tr><td><code>#shareAccessTokenTimeout</code></td><td>token expiration timeout in seconds, by default 10 minutes. While token not expired user could download attachment, after that he will get message `Access is expired. Return back and update the page.`</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="Sharing/Serving%20directly%20the%20content%20o.md">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 data-list-item-id="e6baa9f60bf59d085fd31aa2cce07a0e7">Setting the <code>X-Robots-Tag: noindex</code> HTTP header.</li><li data-list-item-id="ec0d067db136ef9794e4f1033405880b7">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><tr><td><code>#shareHtmlLocation</code></td><td>defines where custom HTML injected via <code>~shareHtml</code> relation should be placed. Applied to the HTML snippet note itself. Format: <code>location:position</code> where location is <code>head</code>, <code>body</code>, or <code>content</code> and position is <code>start</code> or <code>end</code>. Defaults to <code>content:end</code>.</td></tr></tbody></table>
|
||||
|
||||
### Customizing logo
|
||||
|
||||
|
||||
@ -22,3 +22,21 @@ It is possible for each of the splits to have their own <a class="reference-lin
|
||||
When a new split is created, it will share the same note hoisting as the previous one. An easy solution to this is to simply hoist the notes after the split is created.
|
||||
|
||||
This is generally quite useful for reorganizing notes from one place to the other, by hoisting the old place in the first split and hoisting the new place to the second one. This will allow easy cut and paste without the tree jumping around from switching between notes.
|
||||
|
||||
## Mobile support
|
||||
|
||||
Since v0.100.0, it's possible to have a split view on the mobile view as well, with the following differences from the desktop version of the split:
|
||||
|
||||
* On smartphones, the split views are laid out vertically (one on the top and one on the bottom), instead of horizontally as on the desktop.
|
||||
* There can be only one split open per tab.
|
||||
* It's not possible to resize the two split panes.
|
||||
* When the keyboard is opened, the active note will be “maximized”, thus allowing for more space even when a split is open. When the keyboard is closed, the splits become equal in size again.
|
||||
|
||||
Interaction:
|
||||
|
||||
* To create a new split, click the three dots button on the right of the note title and select _Create new split_.
|
||||
* This option will only be available if there is no split already open in the current tab.
|
||||
* To close a split, click the three dots button on the right of the note title and select _Close this pane_.
|
||||
* Note that this option will only be available on the second note in the split (the one at the bottom on smartphones, the one on the right on tablets).
|
||||
* When long-pressing a link, a contextual menu will show up with an option to _Open note in a new split_.
|
||||
* If there's already a split, the option will replace the existing split instead.
|
||||
@ -63,7 +63,7 @@ For each note of the calendar, the following attributes can be used:
|
||||
| `#startTime` | The time the event starts at. If this value is missing, then the event is considered a full-day event. The format is `HH:MM` (hours in 24-hour format and minutes). |
|
||||
| `#endTime` | Similar to `startTime`, it mentions the time at which the event ends (in relation with `endDate` if present, or `startDate`). |
|
||||
| `#color` | Displays the event with a specified color (named such as `red`, `gray` or hex such as `#FF0000`). This will also change the color of the note in other places such as the note tree. |
|
||||
| `#calendar:color` | Similar to `#color`, but applies the color only for the event in the calendar and not for other places such as the note tree. (*Deprecated*) |
|
||||
| `#calendar:color` | Similar to `#color`, but applies the color only for the event in the calendar and not for other places such as the note tree. (_Deprecated_) |
|
||||
| `#iconClass` | If present, the icon of the note will be displayed to the left of the event title. |
|
||||
| `#calendar:title` | 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 `#` or `~` symbol). See _Use-cases_ for more information. |
|
||||
| `#calendar:displayedAttributes` | Allows displaying the value of one or more attributes in the calendar like this: <br> <br> <br> <br>`#weight="70" #Mood="Good" #calendar:displayedAttributes="weight,Mood"` <br> <br>It can also be used with relations, case in which it will display the title of the target note: <br> <br>`~assignee=@My assignee #calendar:displayedAttributes="assignee"` |
|
||||
|
||||
@ -62,6 +62,16 @@ export default class MathEditing extends Plugin {
|
||||
allowAttributes: [ 'equation', 'type', 'display', 'fontSize', 'fontColor', 'fontBackgroundColor' ]
|
||||
} );
|
||||
|
||||
// Prevent <mathtex-inline> from being inserted inside <codeBlock>
|
||||
schema.addChildCheck( ( context, childDefinition ) => {
|
||||
if ( childDefinition && childDefinition.name === 'mathtex-inline' ) {
|
||||
// If the context is inside a codeBlock, disallow it
|
||||
if ( context.endsWith( 'codeBlock' ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
schema.register( 'mathtex-display', {
|
||||
inheritAllFrom: '$blockObject',
|
||||
allowAttributes: [ 'equation', 'type', 'display', 'fontSize', 'fontColor' ]
|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
let openGraphImage = subRoot.note.getLabelValue("shareOpenGraphImage");
|
||||
// Relation takes priority and requires some altering
|
||||
if (subRoot.note.hasRelation("shareOpenGraphImage")) {
|
||||
openGraphImage = `api/images/${subRoot.note.getRelation("shareOpenGraphImage").value}/image.png`;
|
||||
openGraphImage = `api/images/${subRoot.note.getRelation("shareOpenGraphImage").value}/image.png${addContentAccessQuery()}`;
|
||||
}
|
||||
%>
|
||||
<title><%= pageTitle %></title>
|
||||
@ -109,6 +109,7 @@ content = content.replaceAll(headingRe, (...match) => {
|
||||
<button aria-label="Show Mobile Menu" id="show-menu-button"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M4 6h16v2H4zm0 5h16v2H4zm0 5h16v2H4z"></path></svg></button>
|
||||
</div>
|
||||
<div id="split-pane">
|
||||
<% if (!note.isLabelTruthy("shareTemplateNoLeftPanel")) { %>
|
||||
<div id="left-pane">
|
||||
<div id="navigation">
|
||||
<div id="site-header">
|
||||
@ -143,6 +144,8 @@ content = content.replaceAll(headingRe, (...match) => {
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div id="right-pane">
|
||||
<div id="main">
|
||||
<div id="content" class="type-<%= note.type %><% if (note.type === "text") { %> ck-content<% } %><% if (isEmpty) { %> no-content<% } %>">
|
||||
@ -152,7 +155,9 @@ content = content.replaceAll(headingRe, (...match) => {
|
||||
<p>This note has no content.</p>
|
||||
<% } else { %>
|
||||
<%
|
||||
content = content.replace(/<img /g, `<img alt="${t("share_theme.image_alt")}" loading="lazy" `);
|
||||
content = content
|
||||
.replace(/<img /g, `<img alt="${t("share_theme.image_alt")}" loading="lazy" `)
|
||||
.replace(/src="(api\/[^"]+)"/g, (m, url) => `src="${url}${addContentAccessQuery(url.includes('?'))}"`);
|
||||
%>
|
||||
<%- content %>
|
||||
<% } %>
|
||||
@ -189,7 +194,7 @@ content = content.replaceAll(headingRe, (...match) => {
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (hasTree) { %>
|
||||
<% if (hasTree && !note.isLabelTruthy("shareTemplateNoPrevNext")) { %>
|
||||
<%- include("prev_next", { note: note, subRoot: subRoot }) %>
|
||||
<% } %>
|
||||
</footer>
|
||||
|
||||
89
pnpm-lock.yaml
generated
89
pnpm-lock.yaml
generated
@ -661,17 +661,17 @@ importers:
|
||||
specifier: 1.0.3
|
||||
version: 1.0.3
|
||||
express:
|
||||
specifier: 5.1.0
|
||||
version: 5.1.0
|
||||
specifier: 5.2.0
|
||||
version: 5.2.0
|
||||
express-http-proxy:
|
||||
specifier: 2.1.2
|
||||
version: 2.1.2
|
||||
express-openid-connect:
|
||||
specifier: 2.19.3
|
||||
version: 2.19.3(express@5.1.0)
|
||||
version: 2.19.3(express@5.2.0)
|
||||
express-rate-limit:
|
||||
specifier: 8.2.1
|
||||
version: 8.2.1(express@5.1.0)
|
||||
version: 8.2.1(express@5.2.0)
|
||||
express-session:
|
||||
specifier: 1.18.2
|
||||
version: 1.18.2
|
||||
@ -6214,8 +6214,8 @@ packages:
|
||||
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
|
||||
body-parser@2.2.0:
|
||||
resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==}
|
||||
body-parser@2.2.1:
|
||||
resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
bonjour-service@1.3.0:
|
||||
@ -8001,8 +8001,8 @@ packages:
|
||||
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
|
||||
express@5.1.0:
|
||||
resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==}
|
||||
express@5.2.0:
|
||||
resolution: {integrity: sha512-XdpJDLxfztVY59X0zPI6sibRiGcxhTPXRD3IhJmjKf2jwMvkRGV1j7loB8U+heeamoU3XvihAaGRTR4aXXUN3A==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
exsolve@1.0.5:
|
||||
@ -8743,6 +8743,10 @@ packages:
|
||||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
http-errors@2.0.1:
|
||||
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
http-parser-js@0.5.10:
|
||||
resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==}
|
||||
|
||||
@ -8824,6 +8828,10 @@ packages:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
iconv-lite@0.7.0:
|
||||
resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
icss-utils@5.1.0:
|
||||
resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
|
||||
engines: {node: ^10 || ^12 || >= 14}
|
||||
@ -11822,9 +11830,9 @@ packages:
|
||||
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
raw-body@3.0.0:
|
||||
resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==}
|
||||
engines: {node: '>= 0.8'}
|
||||
raw-body@3.0.2:
|
||||
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
raw-loader@0.5.1:
|
||||
resolution: {integrity: sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q==}
|
||||
@ -14996,8 +15004,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-core': 47.2.0
|
||||
'@ckeditor/ckeditor5-upload': 47.2.0
|
||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-ai@47.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)':
|
||||
dependencies:
|
||||
@ -15144,8 +15150,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-core': 47.2.0
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-code-block@47.2.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)':
|
||||
dependencies:
|
||||
@ -15210,6 +15214,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
'@ckeditor/ckeditor5-watchdog': 47.2.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-dev-build-tools@43.1.0(@swc/helpers@0.5.17)(tslib@2.8.1)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
@ -15336,8 +15342,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-classic@47.2.0':
|
||||
dependencies:
|
||||
@ -15347,8 +15351,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-decoupled@47.2.0':
|
||||
dependencies:
|
||||
@ -15367,8 +15369,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-multi-root@47.2.0':
|
||||
dependencies:
|
||||
@ -15391,8 +15391,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-table': 47.2.0
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-emoji@47.2.0':
|
||||
dependencies:
|
||||
@ -15475,8 +15473,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-font@47.2.0':
|
||||
dependencies:
|
||||
@ -15551,8 +15547,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
'@ckeditor/ckeditor5-widget': 47.2.0
|
||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-html-embed@47.2.0':
|
||||
dependencies:
|
||||
@ -15879,8 +15873,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.2.0
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-restricted-editing@47.2.0':
|
||||
dependencies:
|
||||
@ -15967,8 +15959,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.2.0
|
||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-special-characters@47.2.0':
|
||||
dependencies:
|
||||
@ -21164,16 +21154,16 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
body-parser@2.2.0:
|
||||
body-parser@2.2.1:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
content-type: 1.0.5
|
||||
debug: 4.4.3(supports-color@8.1.1)
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.6.3
|
||||
iconv-lite: 0.7.0
|
||||
on-finished: 2.4.1
|
||||
qs: 6.14.0
|
||||
raw-body: 3.0.0
|
||||
raw-body: 3.0.2
|
||||
type-is: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@ -23541,13 +23531,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
express-openid-connect@2.19.3(express@5.1.0):
|
||||
express-openid-connect@2.19.3(express@5.2.0):
|
||||
dependencies:
|
||||
base64url: 3.0.1
|
||||
clone: 2.1.2
|
||||
cookie: 0.7.2
|
||||
debug: 4.4.3(supports-color@8.1.1)
|
||||
express: 5.1.0
|
||||
express: 5.2.0
|
||||
futoin-hkdf: 1.5.3
|
||||
http-errors: 1.8.1
|
||||
joi: 17.13.3
|
||||
@ -23559,9 +23549,9 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
express-rate-limit@8.2.1(express@5.1.0):
|
||||
express-rate-limit@8.2.1(express@5.2.0):
|
||||
dependencies:
|
||||
express: 5.1.0
|
||||
express: 5.2.0
|
||||
ip-address: 10.0.1
|
||||
|
||||
express-session@1.18.2:
|
||||
@ -23613,15 +23603,16 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
express@5.1.0:
|
||||
express@5.2.0:
|
||||
dependencies:
|
||||
accepts: 2.0.0
|
||||
body-parser: 2.2.0
|
||||
body-parser: 2.2.1
|
||||
content-disposition: 1.0.0
|
||||
content-type: 1.0.5
|
||||
cookie: 0.7.2
|
||||
cookie-signature: 1.2.2
|
||||
debug: 4.4.3(supports-color@8.1.1)
|
||||
depd: 2.0.0
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
@ -24567,6 +24558,14 @@ snapshots:
|
||||
statuses: 2.0.1
|
||||
toidentifier: 1.0.1
|
||||
|
||||
http-errors@2.0.1:
|
||||
dependencies:
|
||||
depd: 2.0.0
|
||||
inherits: 2.0.4
|
||||
setprototypeof: 1.2.0
|
||||
statuses: 2.0.2
|
||||
toidentifier: 1.0.1
|
||||
|
||||
http-parser-js@0.5.10: {}
|
||||
|
||||
http-proxy-agent@4.0.1:
|
||||
@ -24682,6 +24681,10 @@ snapshots:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
iconv-lite@0.7.0:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
icss-utils@5.1.0(postcss@8.5.6):
|
||||
dependencies:
|
||||
postcss: 8.5.6
|
||||
@ -28032,11 +28035,11 @@ snapshots:
|
||||
iconv-lite: 0.4.24
|
||||
unpipe: 1.0.0
|
||||
|
||||
raw-body@3.0.0:
|
||||
raw-body@3.0.2:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.6.3
|
||||
http-errors: 2.0.1
|
||||
iconv-lite: 0.7.0
|
||||
unpipe: 1.0.0
|
||||
|
||||
raw-loader@0.5.1: {}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user