mirror of
https://github.com/zadam/trilium.git
synced 2025-10-30 01:36:24 +01:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a717ee00fb | ||
|
|
f5e27278ab | ||
|
|
20c24e26cc | ||
|
|
3bafc396fc | ||
|
|
3fa3e912a4 | ||
|
|
44219e7ccc | ||
|
|
5b67854cbe | ||
|
|
c6d912dcb7 | ||
|
|
da53c1eaa8 | ||
|
|
73bf2dcb02 | ||
|
|
719f10981e | ||
|
|
deb67d6275 | ||
|
|
e4039ea5e1 | ||
|
|
78a50be663 | ||
|
|
3d3ad3b99b | ||
|
|
0d9cdcac85 | ||
|
|
350331e2ef | ||
|
|
e8a9e49e9e | ||
|
|
fb55cdaea6 | ||
|
|
b9b2cc8364 | ||
|
|
8dfdd090f5 | ||
|
|
fe7705524a | ||
|
|
2b1b7774f8 | ||
|
|
2d58019d6e | ||
|
|
fe31f08c0d | ||
|
|
ad7a55d305 | ||
|
|
4ce4ac9584 | ||
|
|
88bd65c679 | ||
|
|
9eab3026bb | ||
|
|
7abaedbf31 | ||
|
|
402718d293 | ||
|
|
990a84c202 | ||
|
|
d8e181a828 | ||
|
|
adb8caa8a2 | ||
|
|
9a13edd490 |
3
.idea/dataSources.xml
generated
3
.idea/dataSources.xml
generated
@@ -6,9 +6,6 @@
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/../trilium-data/document.db</jdbc-url>
|
||||
<driver-properties>
|
||||
<property name="enable_load_extension" value="true" />
|
||||
</driver-properties>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
@@ -57,7 +57,6 @@
|
||||
<index id="24" parent="6" name="sqlite_autoindex_api_tokens_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>apiTokenId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="25" parent="6">
|
||||
@@ -131,21 +130,17 @@
|
||||
<index id="38" parent="7" name="sqlite_autoindex_attributes_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>attributeId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="39" parent="7" name="IDX_attributes_noteId_index">
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="40" parent="7" name="IDX_attributes_name_value">
|
||||
<ColNames>name
|
||||
value</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="41" parent="7" name="IDX_attributes_value_index">
|
||||
<ColNames>value</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="42" parent="7">
|
||||
<ColNames>attributeId</ColNames>
|
||||
@@ -212,17 +207,14 @@ value</ColNames>
|
||||
<index id="54" parent="8" name="sqlite_autoindex_branches_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>branchId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="55" parent="8" name="IDX_branches_noteId_parentNoteId">
|
||||
<ColNames>noteId
|
||||
parentNoteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="56" parent="8" name="IDX_branches_parentNoteId">
|
||||
<ColNames>parentNoteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="57" parent="8">
|
||||
<ColNames>branchId</ColNames>
|
||||
@@ -253,7 +245,6 @@ parentNoteId</ColNames>
|
||||
<index id="62" parent="9" name="sqlite_autoindex_note_contents_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="63" parent="9">
|
||||
@@ -284,7 +275,6 @@ parentNoteId</ColNames>
|
||||
<index id="68" parent="10" name="sqlite_autoindex_note_revision_contents_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="69" parent="10">
|
||||
@@ -369,28 +359,22 @@ parentNoteId</ColNames>
|
||||
<index id="84" parent="11" name="sqlite_autoindex_note_revisions_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="85" parent="11" name="IDX_note_revisions_noteId">
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="86" parent="11" name="IDX_note_revisions_utcDateLastEdited">
|
||||
<ColNames>utcDateLastEdited</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="87" parent="11" name="IDX_note_revisions_utcDateCreated">
|
||||
<ColNames>utcDateCreated</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="88" parent="11" name="IDX_note_revisions_dateLastEdited">
|
||||
<ColNames>dateLastEdited</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="89" parent="11" name="IDX_note_revisions_dateCreated">
|
||||
<ColNames>dateCreated</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="90" parent="11">
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
@@ -477,36 +461,28 @@ parentNoteId</ColNames>
|
||||
<index id="105" parent="12" name="sqlite_autoindex_notes_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="106" parent="12" name="IDX_notes_title">
|
||||
<ColNames>title</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="107" parent="12" name="IDX_notes_type">
|
||||
<ColNames>type</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="108" parent="12" name="IDX_notes_isDeleted">
|
||||
<ColNames>isDeleted</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="109" parent="12" name="IDX_notes_dateCreated">
|
||||
<ColNames>dateCreated</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="110" parent="12" name="IDX_notes_dateModified">
|
||||
<ColNames>dateModified</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="111" parent="12" name="IDX_notes_utcDateCreated">
|
||||
<ColNames>utcDateCreated</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="112" parent="12" name="IDX_notes_utcDateModified">
|
||||
<ColNames>utcDateModified</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="113" parent="12">
|
||||
<ColNames>noteId</ColNames>
|
||||
@@ -547,7 +523,6 @@ parentNoteId</ColNames>
|
||||
<index id="120" parent="13" name="sqlite_autoindex_options_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>name</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="121" parent="13">
|
||||
@@ -583,7 +558,6 @@ parentNoteId</ColNames>
|
||||
<index id="127" parent="14" name="sqlite_autoindex_recent_notes_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="128" parent="14">
|
||||
@@ -604,12 +578,10 @@ parentNoteId</ColNames>
|
||||
<index id="131" parent="15" name="sqlite_autoindex_source_ids_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>sourceId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="132" parent="15" name="IDX_source_ids_utcDateCreated">
|
||||
<ColNames>utcDateCreated</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="133" parent="15">
|
||||
<ColNames>sourceId</ColNames>
|
||||
@@ -663,22 +635,26 @@ parentNoteId</ColNames>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="145" parent="18" name="utcSyncDate">
|
||||
<column id="145" parent="18" name="isSynced">
|
||||
<Position>5</Position>
|
||||
<DataType>INTEGER|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="146" parent="18" name="utcSyncDate">
|
||||
<Position>6</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="146" parent="18" name="IDX_sync_entityName_entityId">
|
||||
<index id="147" parent="18" name="IDX_sync_entityName_entityId">
|
||||
<ColNames>entityName
|
||||
entityId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="147" parent="18" name="IDX_sync_utcSyncDate">
|
||||
<index id="148" parent="18" name="IDX_sync_utcSyncDate">
|
||||
<ColNames>utcSyncDate</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="148" parent="18">
|
||||
<key id="149" parent="18">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
|
||||
22
db/migrations/0158__add_isSynced_to_sync.sql
Normal file
22
db/migrations/0158__add_isSynced_to_sync.sql
Normal file
@@ -0,0 +1,22 @@
|
||||
CREATE TABLE IF NOT EXISTS "sync_mig" (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`entityName` TEXT NOT NULL,
|
||||
`entityId` TEXT NOT NULL,
|
||||
`sourceId` TEXT NOT NULL,
|
||||
`isSynced` INTEGER default 0 not null,
|
||||
`utcSyncDate` TEXT NOT NULL);
|
||||
|
||||
INSERT INTO sync_mig (id, entityName, entityId, sourceId, isSynced, utcSyncDate)
|
||||
SELECT id, entityName, entityId, sourceId, 1, utcSyncDate FROM sync;
|
||||
|
||||
DROP TABLE sync;
|
||||
|
||||
ALTER TABLE sync_mig RENAME TO sync;
|
||||
|
||||
CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` (
|
||||
`entityName`,
|
||||
`entityId`
|
||||
);
|
||||
CREATE INDEX `IDX_sync_utcSyncDate` ON `sync` (
|
||||
`utcSyncDate`
|
||||
);
|
||||
@@ -1,16 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS "sync" (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`entityName` TEXT NOT NULL,
|
||||
`entityId` TEXT NOT NULL,
|
||||
`sourceId` TEXT NOT NULL,
|
||||
`utcSyncDate` TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS "source_ids" (
|
||||
`sourceId` TEXT NOT NULL,
|
||||
`utcDateCreated` TEXT NOT NULL,
|
||||
PRIMARY KEY(`sourceId`)
|
||||
);
|
||||
CREATE INDEX IDX_source_ids_utcDateCreated
|
||||
on source_ids (utcDateCreated);
|
||||
CREATE TABLE IF NOT EXISTS "api_tokens"
|
||||
(
|
||||
apiTokenId TEXT PRIMARY KEY NOT NULL,
|
||||
@@ -27,13 +19,6 @@ CREATE TABLE IF NOT EXISTS "options"
|
||||
utcDateCreated TEXT not null,
|
||||
utcDateModified TEXT NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` (
|
||||
`entityName`,
|
||||
`entityId`
|
||||
);
|
||||
CREATE INDEX `IDX_sync_utcSyncDate` ON `sync` (
|
||||
`utcSyncDate`
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "note_contents" (
|
||||
`noteId` TEXT NOT NULL,
|
||||
`content` TEXT NULL DEFAULT NULL,
|
||||
@@ -72,6 +57,8 @@ CREATE INDEX `IDX_note_revisions_utcDateCreated` ON `note_revisions` (`utcDateCr
|
||||
CREATE INDEX `IDX_note_revisions_utcDateLastEdited` ON `note_revisions` (`utcDateLastEdited`);
|
||||
CREATE INDEX `IDX_note_revisions_dateCreated` ON `note_revisions` (`dateCreated`);
|
||||
CREATE INDEX `IDX_note_revisions_dateLastEdited` ON `note_revisions` (`dateLastEdited`);
|
||||
CREATE INDEX IDX_source_ids_utcDateCreated
|
||||
on source_ids (utcDateCreated);
|
||||
CREATE TABLE IF NOT EXISTS "notes" (
|
||||
`noteId` TEXT NOT NULL,
|
||||
`title` TEXT NOT NULL DEFAULT "note",
|
||||
@@ -130,3 +117,17 @@ CREATE INDEX IDX_attributes_noteId_index
|
||||
on attributes (noteId);
|
||||
CREATE INDEX IDX_attributes_value_index
|
||||
on attributes (value);
|
||||
CREATE TABLE IF NOT EXISTS "sync" (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`entityName` TEXT NOT NULL,
|
||||
`entityId` TEXT NOT NULL,
|
||||
`sourceId` TEXT NOT NULL,
|
||||
`isSynced` INTEGER default 0 not null,
|
||||
`utcSyncDate` TEXT NOT NULL);
|
||||
CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` (
|
||||
`entityName`,
|
||||
`entityId`
|
||||
);
|
||||
CREATE INDEX `IDX_sync_utcSyncDate` ON `sync` (
|
||||
`utcSyncDate`
|
||||
);
|
||||
|
||||
@@ -26,9 +26,9 @@ app.on('ready', async () => {
|
||||
|
||||
await sqlInit.dbConnection;
|
||||
|
||||
// if schema doesn't exist -> setup process
|
||||
// if schema exists, then we need to wait until the migration process is finished
|
||||
if (await sqlInit.schemaExists()) {
|
||||
// if db is not initialized -> setup process
|
||||
// if db is initialized, then we need to wait until the migration process is finished
|
||||
if (await sqlInit.isDbInitialized()) {
|
||||
await sqlInit.dbReady;
|
||||
|
||||
await windowService.createMainWindow();
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "trilium",
|
||||
"version": "0.40.1",
|
||||
"version": "0.40.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "trilium",
|
||||
"productName": "Trilium Notes",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.40.2",
|
||||
"version": "0.40.6",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
|
||||
@@ -11,7 +11,9 @@ const ENTITY_NAME_TO_ENTITY = {
|
||||
"attributes": Attribute,
|
||||
"branches": Branch,
|
||||
"notes": Note,
|
||||
"note_contents": Note,
|
||||
"note_revisions": NoteRevision,
|
||||
"note_revision_contents": NoteRevision,
|
||||
"recent_notes": RecentNote,
|
||||
"options": Option,
|
||||
"api_tokens": ApiToken,
|
||||
|
||||
@@ -811,8 +811,10 @@ class Note extends Entity {
|
||||
FROM attributes
|
||||
WHERE noteId = ? AND
|
||||
isDeleted = 0 AND
|
||||
type = 'relation' AND
|
||||
name IN ('internalLink', 'imageLink', 'relationMapLink', 'includeNoteLink')`, [this.noteId]);
|
||||
((type = 'relation' AND
|
||||
name IN ('internalLink', 'imageLink', 'relationMapLink', 'includeNoteLink'))
|
||||
OR
|
||||
(type = 'label' AND name = 'externalLink'))`, [this.noteId]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -161,7 +161,13 @@ async function printActiveNote() {
|
||||
importCSS: false,
|
||||
loadCSS: [
|
||||
"libraries/codemirror/codemirror.css",
|
||||
"libraries/ckeditor/ckeditor-content.css"
|
||||
"libraries/ckeditor/ckeditor-content.css",
|
||||
"libraries/ckeditor/ckeditor-content.css",
|
||||
"libraries/bootstrap/css/bootstrap.min.css",
|
||||
"stylesheets/print.css",
|
||||
"stylesheets/relation_map.css",
|
||||
"stylesheets/themes.css",
|
||||
"stylesheets/detail.css"
|
||||
],
|
||||
debug: true
|
||||
});
|
||||
|
||||
@@ -10,8 +10,6 @@ const $buildRevision = $("#build-revision");
|
||||
const $dataDirectory = $("#data-directory");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
const appInfo = await server.get('app-info');
|
||||
|
||||
$appVersion.text(appInfo.appVersion);
|
||||
@@ -22,7 +20,5 @@ export async function showDialog() {
|
||||
$buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision);
|
||||
$dataDirectory.text(appInfo.dataDirectory);
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
@@ -11,13 +11,9 @@ const $linkTitle = $("#link-title");
|
||||
const $addLinkTitleFormGroup = $("#add-link-title-form-group");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
$addLinkTitleFormGroup.toggle(!hasSelection());
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
$autoComplete.val('').trigger('focus');
|
||||
$linkTitle.val('');
|
||||
|
||||
@@ -287,8 +287,6 @@ function initKoPlugins() {
|
||||
}
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
await libraryLoader.requireLibrary(libraryLoader.KNOCKOUT);
|
||||
|
||||
// lazily apply bindings on first use
|
||||
@@ -300,11 +298,9 @@ export async function showDialog() {
|
||||
ko.applyBindings(attributesModel, $dialog[0]);
|
||||
}
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
await attributesModel.loadAttributes();
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
|
||||
$dialog.on('focus', '.attribute-name', function (e) {
|
||||
|
||||
@@ -6,11 +6,7 @@ const $backendLogTextArea = $("#backend-log-textarea");
|
||||
const $refreshBackendLog = $("#refresh-backend-log-button");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@@ -13,10 +13,6 @@ const $noteTitle = $('#branch-prefix-note-title');
|
||||
let branchId;
|
||||
|
||||
export async function showDialog(node) {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
branchId = node.data.branchId;
|
||||
const branch = treeCache.getBranch(branchId);
|
||||
|
||||
@@ -30,7 +26,7 @@ export async function showDialog(node) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
$treePrefixInput.val(branch.prefix);
|
||||
|
||||
|
||||
@@ -22,11 +22,7 @@ export async function showDialog(noteIds) {
|
||||
}
|
||||
}
|
||||
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
$noteAutoComplete.val('').trigger('focus');
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@ let taskId = '';
|
||||
let branchId = null;
|
||||
|
||||
export async function showDialog(node, defaultType) {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
// each opening of the dialog resets the taskId so we don't associate it with previous exports anymore
|
||||
taskId = '';
|
||||
$exportButton.removeAttr("disabled");
|
||||
@@ -38,9 +36,7 @@ export async function showDialog(node, defaultType) {
|
||||
|
||||
$("#opml-v2").prop("checked", true); // setting default
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
branchId = node.data.branchId;
|
||||
|
||||
|
||||
@@ -3,9 +3,5 @@ import utils from "../services/utils.js";
|
||||
const $dialog = $("#help-dialog");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
@@ -16,8 +16,6 @@ const $explodeArchivesCheckbox = $("#explode-archives-checkbox");
|
||||
let parentNoteId = null;
|
||||
|
||||
export async function showDialog(node) {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
$fileUploadInput.val('').trigger('change'); // to trigger Import button disabling listener below
|
||||
|
||||
$safeImportCheckbox.prop("checked", true);
|
||||
@@ -26,13 +24,11 @@ export async function showDialog(node) {
|
||||
$codeImportedAsCodeCheckbox.prop("checked", true);
|
||||
$explodeArchivesCheckbox.prop("checked", true);
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
parentNoteId = node.data.noteId;
|
||||
|
||||
$noteTitle.text(await treeUtils.getNoteTitle(parentNoteId));
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
|
||||
$form.on('submit', () => {
|
||||
|
||||
@@ -10,13 +10,9 @@ let callback = null;
|
||||
export async function showDialog(cb) {
|
||||
callback = cb;
|
||||
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$autoComplete.val('');
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
noteAutocompleteService.initNoteAutocomplete($autoComplete, { hideGoToSelectedNoteButton: true });
|
||||
noteAutocompleteService.showRecentNotes($autoComplete);
|
||||
|
||||
@@ -10,13 +10,9 @@ let $originallyFocused; // element focused before the dialog was opened so we ca
|
||||
export function info(message) {
|
||||
$originallyFocused = $(':focus');
|
||||
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$infoContent.text(message);
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
return new Promise((res, rej) => { resolve = res; });
|
||||
}
|
||||
|
||||
@@ -8,13 +8,9 @@ const $autoComplete = $("#jump-to-note-autocomplete");
|
||||
const $showInFullTextButton = $("#show-in-full-text-button");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$autoComplete.val('');
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
noteAutocompleteService.initNoteAutocomplete($autoComplete, { hideGoToSelectedNoteButton: true })
|
||||
.on('autocomplete:selected', function(event, suggestion, dataset) {
|
||||
|
||||
@@ -16,10 +16,6 @@ function getOptions() {
|
||||
}
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
// set default settings
|
||||
$maxNotesInput.val(20);
|
||||
|
||||
@@ -27,7 +23,7 @@ export async function showDialog() {
|
||||
|
||||
$linkMapContainer.empty();
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
|
||||
$dialog.on('shown.bs.modal', () => {
|
||||
|
||||
@@ -37,9 +37,7 @@ export async function importMarkdownInline() {
|
||||
convertMarkdownToHtml(text);
|
||||
}
|
||||
else {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import noteAutocompleteService from "../services/note_autocomplete.js";
|
||||
import utils from "../services/utils.js";
|
||||
import cloningService from "../services/cloning.js";
|
||||
import treeUtils from "../services/tree_utils.js";
|
||||
import toastService from "../services/toast.js";
|
||||
import treeCache from "../services/tree_cache.js";
|
||||
import treeChangesService from "../services/branches.js";
|
||||
@@ -18,11 +16,7 @@ let movedNodes;
|
||||
export async function showDialog(nodes) {
|
||||
movedNodes = nodes;
|
||||
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
$noteAutoComplete.val('').trigger('focus');
|
||||
|
||||
|
||||
@@ -10,11 +10,7 @@ const $mime = $("#note-info-mime");
|
||||
const $okButton = $("#note-info-ok-button");
|
||||
|
||||
export function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
const activeNote = noteDetailService.getActiveTabNote();
|
||||
|
||||
|
||||
@@ -29,11 +29,7 @@ export async function showCurrentNoteRevisions() {
|
||||
}
|
||||
|
||||
export async function showNoteRevisionsDialog(noteId, noteRevisionId) {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
await loadNoteRevisions(noteId, noteRevisionId);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,7 @@ const $dialog = $("#note-source-dialog");
|
||||
const $noteSource = $("#note-source");
|
||||
|
||||
export function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
const noteText = noteDetailService.getActiveTabNote().content;
|
||||
|
||||
|
||||
@@ -6,13 +6,9 @@ import utils from "../services/utils.js";
|
||||
const $dialog = $("#options-dialog");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
const options = await server.get('options');
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
(await Promise.all([
|
||||
import('./options/advanced.js'),
|
||||
|
||||
@@ -12,10 +12,6 @@ let resolve;
|
||||
let shownCb;
|
||||
|
||||
export function ask({ message, defaultValue, shown }) {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
shownCb = shown;
|
||||
|
||||
$question = $("<label>")
|
||||
@@ -34,7 +30,7 @@ export function ask({ message, defaultValue, shown }) {
|
||||
.append($question)
|
||||
.append($answer));
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
return new Promise((res, rej) => { resolve = res; });
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import protectedSessionService from "../services/protected_session.js";
|
||||
import utils from "../services/utils.js";
|
||||
|
||||
const $dialog = $("#protected-session-password-dialog");
|
||||
const $passwordForm = $dialog.find(".protected-session-password-form");
|
||||
const $passwordInput = $dialog.find(".protected-session-password");
|
||||
|
||||
export function show() {
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
$passwordInput.trigger('focus');
|
||||
}
|
||||
|
||||
@@ -8,11 +8,7 @@ const $dialog = $("#recent-changes-dialog");
|
||||
const $content = $("#recent-changes-content");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
const result = await server.get('recent-changes');
|
||||
|
||||
|
||||
@@ -14,13 +14,9 @@ let codeEditor;
|
||||
$dialog.on("shown.bs.modal", e => initEditor());
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
await showTableSchemas();
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
|
||||
async function initEditor() {
|
||||
|
||||
@@ -7,9 +7,26 @@ import contextMenuWidget from "./services/context_menu.js";
|
||||
import treeChangesService from "./services/branches.js";
|
||||
import utils from "./services/utils.js";
|
||||
import treeUtils from "./services/tree_utils.js";
|
||||
import linkService from "./services/link.js";
|
||||
import noteContentRenderer from "./services/note_content_renderer.js";
|
||||
|
||||
window.glob.isDesktop = utils.isDesktop;
|
||||
window.glob.isMobile = utils.isMobile;
|
||||
window.glob.showAddLinkDialog = () => import('./dialogs/add_link.js').then(d => d.showDialog());
|
||||
window.glob.showIncludeNoteDialog = cb => import('./dialogs/include_note.js').then(d => d.showDialog(cb));
|
||||
window.glob.loadIncludedNote = async (noteId, el) => {
|
||||
const note = await treeCache.getNote(noteId);
|
||||
|
||||
if (note) {
|
||||
$(el).empty().append($("<h3>").append(await linkService.createNoteLink(note.noteId, {
|
||||
showTooltip: false
|
||||
})));
|
||||
|
||||
const {renderedContent} = await noteContentRenderer.getRenderedContent(note);
|
||||
|
||||
$(el).append(renderedContent);
|
||||
}
|
||||
};
|
||||
|
||||
const $leftPane = $("#left-pane");
|
||||
const $tree = $("#tree");
|
||||
|
||||
@@ -7,31 +7,20 @@ import protectedSessionHolder from "./protected_session_holder.js";
|
||||
async function getRenderedContent(note) {
|
||||
const type = getRenderingType(note);
|
||||
|
||||
let rendered;
|
||||
let $rendered;
|
||||
|
||||
if (type === 'text') {
|
||||
const fullNote = await server.get('notes/' + note.noteId);
|
||||
|
||||
const $content = $("<div>").html(fullNote.content);
|
||||
|
||||
if (utils.isHtmlEmpty(fullNote.content)) {
|
||||
rendered = "";
|
||||
}
|
||||
else {
|
||||
rendered = $content;
|
||||
}
|
||||
$rendered = $("<div>").html(fullNote.content);
|
||||
}
|
||||
else if (type === 'code') {
|
||||
const fullNote = await server.get('notes/' + note.noteId);
|
||||
|
||||
if (fullNote.content.trim() === "") {
|
||||
rendered = "";
|
||||
}
|
||||
|
||||
rendered = $("<pre>").text(fullNote.content);
|
||||
$rendered = $("<pre>").text(fullNote.content);
|
||||
}
|
||||
else if (type === 'image') {
|
||||
rendered = $("<img>").attr("src", `api/images/${note.noteId}/${note.title}`);
|
||||
$rendered = $("<img>").attr("src", `api/images/${note.noteId}/${note.title}`);
|
||||
}
|
||||
else if (type === 'file') {
|
||||
function getFileUrl() {
|
||||
@@ -56,33 +45,35 @@ async function getRenderedContent(note) {
|
||||
// open doesn't work for protected notes since it works through browser which isn't in protected session
|
||||
$openButton.toggle(!note.isProtected);
|
||||
|
||||
rendered = $('<div>')
|
||||
$rendered = $('<div>')
|
||||
.append($downloadButton)
|
||||
.append(' ')
|
||||
.append($openButton);
|
||||
}
|
||||
else if (type === 'render') {
|
||||
const $el = $('<div>');
|
||||
$rendered = $('<div>');
|
||||
|
||||
await renderService.render(note, $el, this.ctx);
|
||||
|
||||
rendered = $el;
|
||||
await renderService.render(note, $rendered, this.ctx);
|
||||
}
|
||||
else if (type === 'protected-session') {
|
||||
const $button = $(`<button class="btn btn-sm"><span class="bx bx-log-in"></span> Enter protected session</button>`)
|
||||
.on('click', protectedSessionService.enterProtectedSession);
|
||||
|
||||
rendered = $("<div>")
|
||||
$rendered = $("<div>")
|
||||
.append("<div>This note is protected and to access it you need to enter password.</div>")
|
||||
.append("<br/>")
|
||||
.append($button);
|
||||
}
|
||||
else {
|
||||
rendered = "<em>Content of this note cannot be displayed in the book format</em>";
|
||||
$rendered = $("<em>Content of this note cannot be displayed in the book format</em>");
|
||||
}
|
||||
|
||||
if (note.cssClass) {
|
||||
$rendered.addClass(note.cssClass);
|
||||
}
|
||||
|
||||
return {
|
||||
renderedContent: rendered,
|
||||
renderedContent: $rendered,
|
||||
type
|
||||
};
|
||||
}
|
||||
|
||||
@@ -148,8 +148,8 @@ class NoteDetailBook {
|
||||
const label = `${childCount} child${childCount > 1 ? 'ren' : ''}`;
|
||||
|
||||
$card.append($('<div class="note-book-children">')
|
||||
.append($(`<a class="note-book-open-children-button" href="javascript:">+ Show ${label}</a>`))
|
||||
.append($(`<a class="note-book-hide-children-button" href="javascript:">- Hide ${label}</a>`).hide())
|
||||
.append($(`<a class="note-book-open-children-button no-print" href="javascript:">+ Show ${label}</a>`))
|
||||
.append($(`<a class="note-book-hide-children-button no-print" href="javascript:">- Hide ${label}</a>`).hide())
|
||||
.append($('<div class="note-book-children-content">'))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ class NoteDetailCode {
|
||||
// CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check)
|
||||
// we provide fallback
|
||||
this.codeEditor.setValue(this.ctx.note.content || "");
|
||||
this.codeEditor.clearHistory();
|
||||
|
||||
const info = CodeMirror.findModeByMIME(this.ctx.note.mime);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import treeService from './tree.js';
|
||||
import treeCache from "./tree_cache.js";
|
||||
import server from './server.js';
|
||||
import toastService from "./toast.js";
|
||||
import utils from "./utils.js";
|
||||
|
||||
const $searchInput = $("input[name='search-text']");
|
||||
const $resetSearchButton = $("#reset-search-button");
|
||||
@@ -28,6 +29,8 @@ const helpText = `
|
||||
</p>`;
|
||||
|
||||
function showSearch() {
|
||||
utils.saveFocusedElement();
|
||||
|
||||
$searchBox.slideDown();
|
||||
|
||||
$searchBox.tooltip({
|
||||
@@ -49,6 +52,8 @@ function hideSearch() {
|
||||
|
||||
$searchResults.hide();
|
||||
$searchBox.slideUp();
|
||||
|
||||
utils.focusSavedElement();
|
||||
}
|
||||
|
||||
function toggleSearch() {
|
||||
|
||||
@@ -635,7 +635,7 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) {
|
||||
extraOptions.saveSelection = false;
|
||||
}
|
||||
|
||||
if (extraOptions.saveSelection) {
|
||||
if (extraOptions.saveSelection && utils.isCKEditorInitialized()) {
|
||||
[extraOptions.title, extraOptions.content] = parseSelectedHtml(window.cutToNote.getSelectedHtml());
|
||||
}
|
||||
|
||||
@@ -648,7 +648,7 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) {
|
||||
type: extraOptions.type
|
||||
});
|
||||
|
||||
if (extraOptions.saveSelection) {
|
||||
if (extraOptions.saveSelection && utils.isCKEditorInitialized()) {
|
||||
// we remove the selection only after it was saved to server to make sure we don't lose anything
|
||||
window.cutToNote.removeSelection();
|
||||
}
|
||||
@@ -870,7 +870,7 @@ window.glob.cutIntoNote = () => createNoteInto(true);
|
||||
|
||||
keyboardActionService.setGlobalActionHandler('CutIntoNote', () => createNoteInto(true));
|
||||
|
||||
keyboardActionService.setGlobalActionHandler('CreateNoteInto', createNoteInto);
|
||||
keyboardActionService.setGlobalActionHandler('CreateNoteInto', () => createNoteInto(true));
|
||||
|
||||
keyboardActionService.setGlobalActionHandler('ScrollToActiveNote', scrollToActiveNote);
|
||||
|
||||
|
||||
@@ -62,6 +62,11 @@ class TreeContextMenu {
|
||||
!isHoisted || !isNotRoot ? null : { title: 'Unhoist note <kbd data-kb-action="ToggleNoteHoisting"></kbd>', cmd: "unhoist", uiIcon: "arrow-up" },
|
||||
{ title: 'Edit branch prefix <kbd data-kb-action="EditBranchPrefix"></kbd>', cmd: "editBranchPrefix", uiIcon: "empty",
|
||||
enabled: isNotRoot && parentNotSearch && noSelectedNotes},
|
||||
{ title: "Advanced", uiIcon: "empty", enabled: true, items: [
|
||||
{ title: 'Collapse subtree <kbd data-kb-action="CollapseSubtree"></kbd>', cmd: "collapseSubtree", uiIcon: "align-justify", enabled: noSelectedNotes },
|
||||
{ title: "Force note sync", cmd: "forceNoteSync", uiIcon: "recycle", enabled: noSelectedNotes },
|
||||
{ title: 'Sort alphabetically <kbd data-kb-action="SortChildNotes"></kbd>', cmd: "sortAlphabetically", uiIcon: "empty", enabled: noSelectedNotes && notSearch }
|
||||
] },
|
||||
{ title: "----" },
|
||||
{ title: "Protect subtree", cmd: "protectSubtree", uiIcon: "check-shield", enabled: noSelectedNotes },
|
||||
{ title: "Unprotect subtree", cmd: "unprotectSubtree", uiIcon: "shield", enabled: noSelectedNotes },
|
||||
@@ -84,12 +89,7 @@ class TreeContextMenu {
|
||||
{ title: "Export", cmd: "export", uiIcon: "empty",
|
||||
enabled: notSearch && noSelectedNotes },
|
||||
{ title: "Import into note", cmd: "importIntoNote", uiIcon: "empty",
|
||||
enabled: notSearch && noSelectedNotes },
|
||||
{ title: "Advanced", uiIcon: "empty", enabled: true, items: [
|
||||
{ title: 'Collapse subtree <kbd data-kb-action="CollapseSubtree"></kbd>', cmd: "collapseSubtree", uiIcon: "align-justify", enabled: noSelectedNotes },
|
||||
{ title: "Force note sync", cmd: "forceNoteSync", uiIcon: "recycle", enabled: noSelectedNotes },
|
||||
{ title: 'Sort alphabetically <kbd data-kb-action="SortChildNotes"></kbd>', cmd: "sortAlphabetically", uiIcon: "empty", enabled: noSelectedNotes && notSearch }
|
||||
] },
|
||||
enabled: notSearch && noSelectedNotes }
|
||||
].filter(row => row !== null);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,9 +209,50 @@ function getMimeTypeClass(mime) {
|
||||
function closeActiveDialog() {
|
||||
if (glob.activeDialog) {
|
||||
glob.activeDialog.modal('hide');
|
||||
glob.activeDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
let $lastFocusedElement = null;
|
||||
|
||||
function saveFocusedElement() {
|
||||
$lastFocusedElement = $(":focus");
|
||||
}
|
||||
|
||||
function focusSavedElement() {
|
||||
if (!$lastFocusedElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($lastFocusedElement.hasClass("ck")) {
|
||||
// must handle CKEditor separately because of this bug: https://github.com/ckeditor/ckeditor5/issues/607
|
||||
|
||||
import("./note_detail.js").then(noteDetail => {
|
||||
noteDetail.default.getActiveEditor().editing.view.focus();
|
||||
});
|
||||
} else {
|
||||
$lastFocusedElement.focus();
|
||||
}
|
||||
|
||||
$lastFocusedElement = null;
|
||||
}
|
||||
|
||||
function openDialog($dialog) {
|
||||
closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
saveFocusedElement();
|
||||
|
||||
$dialog.modal();
|
||||
|
||||
$dialog.on('hidden.bs.modal', () => {
|
||||
if (!glob.activeDialog || glob.activeDialog === $dialog) {
|
||||
focusSavedElement();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isHtmlEmpty(html) {
|
||||
html = html.toLowerCase();
|
||||
|
||||
@@ -248,6 +289,10 @@ function copySelectionToClipboard() {
|
||||
}
|
||||
}
|
||||
|
||||
function isCKEditorInitialized() {
|
||||
return !!(window && window.cutToNote);
|
||||
}
|
||||
|
||||
export default {
|
||||
reloadApp,
|
||||
parseDate,
|
||||
@@ -277,9 +322,13 @@ export default {
|
||||
getNoteTypeClass,
|
||||
getMimeTypeClass,
|
||||
closeActiveDialog,
|
||||
openDialog,
|
||||
saveFocusedElement,
|
||||
focusSavedElement,
|
||||
isHtmlEmpty,
|
||||
clearBrowserCache,
|
||||
getUrlForDownload,
|
||||
normalizeShortcut,
|
||||
copySelectionToClipboard
|
||||
copySelectionToClipboard,
|
||||
isCKEditorInitialized
|
||||
};
|
||||
@@ -71,7 +71,19 @@ class AttributesWidget extends StandardWidget {
|
||||
async renderAttributes(attributes, $container) {
|
||||
for (const attribute of attributes) {
|
||||
if (attribute.type === 'label') {
|
||||
$container.append(utils.formatLabel(attribute) + " ");
|
||||
if (attribute.name === 'externalLink') {
|
||||
$container.append('@' + attribute.name + "=");
|
||||
$container.append(
|
||||
$('<a>')
|
||||
.text(attribute.value)
|
||||
.attr('href', attribute.value)
|
||||
.addClass('external')
|
||||
);
|
||||
$container.append(" ");
|
||||
}
|
||||
else {
|
||||
$container.append(utils.formatLabel(attribute) + " ");
|
||||
}
|
||||
} else if (attribute.type === 'relation') {
|
||||
if (attribute.value) {
|
||||
$container.append('@' + attribute.name + "=");
|
||||
|
||||
@@ -43,6 +43,7 @@ body {
|
||||
min-height: 0;
|
||||
padding-left: 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#search-box {
|
||||
|
||||
69
src/public/stylesheets/detail.css
Normal file
69
src/public/stylesheets/detail.css
Normal file
@@ -0,0 +1,69 @@
|
||||
.note-detail-book {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.note-detail-book-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.note-book-card {
|
||||
border-radius: 10px;
|
||||
background-color: var(--accented-background-color);
|
||||
padding: 15px;
|
||||
padding-bottom: 5px;
|
||||
margin: 5px;
|
||||
margin-left: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.note-book-card .note-book-card {
|
||||
border: 1px solid var(--main-border-color);
|
||||
}
|
||||
|
||||
.note-book-content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.note-book-card.type-image .note-book-content, .note-book-card.type-file .note-book-content, .note-book-card.type-protected-session .note-book-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.note-book-card.type-image .note-book-content img, .note-book-card.type-text .note-book-content img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.note-book-title {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.note-book-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.note-book-auto-message {
|
||||
background-color: var(--accented-background-color);
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.note-detail-image {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.note-detail-image-view {
|
||||
max-width: 100%;
|
||||
}
|
||||
11
src/public/stylesheets/print.css
Normal file
11
src/public/stylesheets/print.css
Normal file
@@ -0,0 +1,11 @@
|
||||
@media print
|
||||
{
|
||||
.no-print, .no-print *
|
||||
{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.relation-map-wrapper {
|
||||
height: 100vh !important;
|
||||
}
|
||||
}
|
||||
@@ -518,14 +518,6 @@ button.icon-button {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.note-detail-image {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.note-detail-image-view {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
pre:not(.CodeMirror-line) {
|
||||
color: var(--main-text-color) !important;
|
||||
white-space: pre-wrap;
|
||||
@@ -857,68 +849,6 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.note-detail-book {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.note-detail-book-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.note-book-card {
|
||||
border-radius: 10px;
|
||||
background-color: var(--accented-background-color);
|
||||
padding: 15px;
|
||||
padding-bottom: 5px;
|
||||
margin: 5px;
|
||||
margin-left: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.note-book-card .note-book-card {
|
||||
border: 1px solid var(--main-border-color);
|
||||
}
|
||||
|
||||
.note-book-content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.note-book-card.type-image .note-book-content, .note-book-card.type-file .note-book-content, .note-book-card.type-protected-session .note-book-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.note-book-card.type-image .note-book-content img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.note-book-title {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.note-book-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.note-book-auto-message {
|
||||
background-color: var(--accented-background-color);
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#toast-container {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
const app_info = require('../../services/app_info');
|
||||
const appInfo = require('../../services/app_info');
|
||||
|
||||
async function getAppInfo() {
|
||||
return app_info;
|
||||
return appInfo;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
const attributeService = require("../../services/attributes");
|
||||
const noteService = require('../../services/notes');
|
||||
const dateNoteService = require('../../services/date_notes');
|
||||
const dateUtils = require('../../services/date_utils');
|
||||
@@ -23,16 +24,26 @@ async function findClippingNote(todayNote, pageUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getClipperInboxNote() {
|
||||
let clipperInbox = await attributeService.getNoteWithLabel('clipperInbox');
|
||||
|
||||
if (!clipperInbox) {
|
||||
clipperInbox = await dateNoteService.getDateNote(dateUtils.localNowDate());
|
||||
}
|
||||
|
||||
return clipperInbox;
|
||||
}
|
||||
|
||||
async function addClipping(req) {
|
||||
const {title, content, pageUrl, images} = req.body;
|
||||
|
||||
const todayNote = await dateNoteService.getDateNote(dateUtils.localNowDate());
|
||||
const clipperInbox = await getClipperInboxNote();
|
||||
|
||||
let clippingNote = await findClippingNote(todayNote, pageUrl);
|
||||
let clippingNote = await findClippingNote(clipperInbox, pageUrl);
|
||||
|
||||
if (!clippingNote) {
|
||||
clippingNote = (await noteService.createNewNote({
|
||||
parentNoteId: todayNote.noteId,
|
||||
parentNoteId: clipperInbox.noteId,
|
||||
title: title,
|
||||
content: '',
|
||||
type: 'text'
|
||||
@@ -54,10 +65,10 @@ async function addClipping(req) {
|
||||
async function createNote(req) {
|
||||
const {title, content, pageUrl, images, clipType} = req.body;
|
||||
|
||||
const todayNote = await dateNoteService.getDateNote(dateUtils.localNowDate());
|
||||
const clipperInbox = await getClipperInboxNote();
|
||||
|
||||
const {note} = await noteService.createNewNote({
|
||||
parentNoteId: todayNote.noteId,
|
||||
parentNoteId: clipperInbox.noteId,
|
||||
title,
|
||||
content,
|
||||
type: 'text'
|
||||
|
||||
@@ -49,7 +49,7 @@ async function loginSync(req) {
|
||||
|
||||
return {
|
||||
sourceId: sourceIdService.getCurrentSourceId(),
|
||||
maxSyncId: await sql.getValue("SELECT MAX(id) FROM sync")
|
||||
maxSyncId: await sql.getValue("SELECT MAX(id) FROM sync WHERE isSynced = 1")
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ async function getStats() {
|
||||
async function checkSync() {
|
||||
return {
|
||||
entityHashes: await contentHashService.getEntityHashes(),
|
||||
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync')
|
||||
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync WHERE isSynced = 1')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -116,11 +116,11 @@ async function forceNoteSync(req) {
|
||||
async function getChanged(req) {
|
||||
const lastSyncId = parseInt(req.query.lastSyncId);
|
||||
|
||||
const syncs = await sql.getRows("SELECT * FROM sync WHERE id > ? LIMIT 1000", [lastSyncId]);
|
||||
const syncs = await sql.getRows("SELECT * FROM sync WHERE isSynced = 1 AND id > ? LIMIT 1000", [lastSyncId]);
|
||||
|
||||
return {
|
||||
syncs: await syncService.getSyncRecords(syncs),
|
||||
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync')
|
||||
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync WHERE isSynced = 1')
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ const build = require('./build');
|
||||
const packageJson = require('../../package');
|
||||
const {TRILIUM_DATA_DIR} = require('./data_dir');
|
||||
|
||||
const APP_DB_VERSION = 157;
|
||||
const APP_DB_VERSION = 158;
|
||||
const SYNC_VERSION = 14;
|
||||
const CLIPPER_PROTOCOL_VERSION = "1.0";
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = { buildDate:"2020-02-01T10:17:51+01:00", buildRevision: "0f25c8a95f381d99b66735b9c0af3e319edb72ed" };
|
||||
module.exports = { buildDate:"2020-03-15T11:21:43+01:00", buildRevision: "f5e27278ab2a38484019ee2510781099b60ec2b6" };
|
||||
|
||||
@@ -51,7 +51,7 @@ class ConsistencyChecks {
|
||||
childToParents[childNoteId].push(parentNoteId);
|
||||
}
|
||||
|
||||
function checkTreeCycle(noteId, path) {
|
||||
const checkTreeCycle = (noteId, path) => {
|
||||
if (noteId === 'root') {
|
||||
return;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ class ConsistencyChecks {
|
||||
checkTreeCycle(parentNoteId, newPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const noteIds = Object.keys(childToParents);
|
||||
|
||||
@@ -323,14 +323,25 @@ class ConsistencyChecks {
|
||||
WHERE isErased = 1
|
||||
AND content IS NOT NULL`,
|
||||
async ({noteId}) => {
|
||||
if (this.autoFix) {
|
||||
await sql.execute(`UPDATE note_contents SET content = NULL WHERE noteId = ?`, [noteId]);
|
||||
|
||||
logFix(`Note ${noteId} content has been set to null since the note is erased`);
|
||||
}
|
||||
else {
|
||||
logError(`Note ${noteId} content is not null even though the note is erased`);
|
||||
}
|
||||
// we always fix this issue because there does not seem to be a good way to prevent it.
|
||||
// Scenario in which this can happen:
|
||||
// 1. user on instance A deletes the note (sync for notes is created, but not for note_contents) and is later erased
|
||||
// 2. instance B gets synced from instance A, note is updated because of sync row for notes,
|
||||
// but note_contents is not because erasion does not create sync rows
|
||||
// 3. therefore note.isErased = true, but note_contents.content remains not updated and not erased.
|
||||
//
|
||||
// Considered solutions:
|
||||
// - don't sync erased notes - this might prevent syncing also of the isDeleted flag and note would continue
|
||||
// to exist on the other instance
|
||||
// - create sync rows for erased event - this would be a problem for undeletion since erasion might happen
|
||||
// on one instance after undelete and thus would win even though there's no user action behind it
|
||||
//
|
||||
// So instead we just fix such cases afterwards here.
|
||||
|
||||
await sql.execute(`UPDATE note_contents SET content = NULL WHERE noteId = ?`, [noteId]);
|
||||
|
||||
logFix(`Note ${noteId} content has been set to null since the note is erased`);
|
||||
});
|
||||
|
||||
await this.findAndFixIssues(`
|
||||
@@ -535,7 +546,7 @@ class ConsistencyChecks {
|
||||
${entityName}
|
||||
LEFT JOIN sync ON sync.entityName = '${entityName}' AND entityId = ${key}
|
||||
WHERE
|
||||
sync.id IS NULL AND ` + (entityName === 'options' ? 'isSynced = 1' : '1'),
|
||||
sync.id IS NULL AND ` + (entityName === 'options' ? 'options.isSynced = 1' : '1'),
|
||||
async ({entityId}) => {
|
||||
if (this.autoFix) {
|
||||
await syncTableService.addEntitySync(entityName, entityId);
|
||||
@@ -547,23 +558,23 @@ class ConsistencyChecks {
|
||||
});
|
||||
|
||||
await this.findAndFixIssues(`
|
||||
SELECT
|
||||
id, entityId
|
||||
FROM
|
||||
sync
|
||||
LEFT JOIN ${entityName} ON entityId = ${key}
|
||||
WHERE
|
||||
sync.entityName = '${entityName}'
|
||||
AND ${key} IS NULL`,
|
||||
async ({id, entityId}) => {
|
||||
if (this.autoFix) {
|
||||
await sql.execute("DELETE FROM sync WHERE entityName = ? AND entityId = ?", [entityName, entityId]);
|
||||
SELECT
|
||||
id, entityId
|
||||
FROM
|
||||
sync
|
||||
LEFT JOIN ${entityName} ON entityId = ${key}
|
||||
WHERE
|
||||
sync.entityName = '${entityName}'
|
||||
AND ${key} IS NULL`,
|
||||
async ({id, entityId}) => {
|
||||
if (this.autoFix) {
|
||||
await sql.execute("DELETE FROM sync WHERE entityName = ? AND entityId = ?", [entityName, entityId]);
|
||||
|
||||
logFix(`Deleted extra sync record id=${id}, entityName=${entityName}, entityId=${entityId}`);
|
||||
} else {
|
||||
logError(`Unrecognized sync record id=${id}, entityName=${entityName}, entityId=${entityId}`);
|
||||
}
|
||||
});
|
||||
logFix(`Deleted extra sync record id=${id}, entityName=${entityName}, entityId=${entityId}`);
|
||||
} else {
|
||||
logError(`Unrecognized sync record id=${id}, entityName=${entityName}, entityId=${entityId}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async findSyncRowsIssues() {
|
||||
|
||||
@@ -235,8 +235,6 @@ async function importEnex(taskContext, file, parentNote) {
|
||||
|
||||
taskContext.increaseProgressCount();
|
||||
|
||||
let noteContent = await noteEntity.getContent();
|
||||
|
||||
for (const resource of resources) {
|
||||
const hash = utils.md5(resource.content);
|
||||
|
||||
@@ -268,7 +266,7 @@ async function importEnex(taskContext, file, parentNote) {
|
||||
|
||||
const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`;
|
||||
|
||||
noteContent = noteContent.replace(mediaRegex, resourceLink);
|
||||
content = content.replace(mediaRegex, resourceLink);
|
||||
};
|
||||
|
||||
if (["image/jpeg", "image/png", "image/gif", "image/webp"].includes(resource.mime)) {
|
||||
@@ -281,12 +279,12 @@ async function importEnex(taskContext, file, parentNote) {
|
||||
|
||||
const imageLink = `<img src="${url}">`;
|
||||
|
||||
noteContent = noteContent.replace(mediaRegex, imageLink);
|
||||
content = content.replace(mediaRegex, imageLink);
|
||||
|
||||
if (!noteContent.includes(imageLink)) {
|
||||
if (!content.includes(imageLink)) {
|
||||
// if there wasn't any match for the reference, we'll add the image anyway
|
||||
// otherwise image would be removed since no note would include it
|
||||
noteContent += imageLink;
|
||||
content += imageLink;
|
||||
}
|
||||
} catch (e) {
|
||||
log.error("error when saving image from ENEX file: " + e);
|
||||
@@ -298,7 +296,7 @@ async function importEnex(taskContext, file, parentNote) {
|
||||
}
|
||||
|
||||
// save updated content with links to files/images
|
||||
await noteEntity.setContent(noteContent);
|
||||
await noteEntity.setContent(content);
|
||||
|
||||
await noteService.scanForLinks(noteEntity.noteId);
|
||||
|
||||
|
||||
@@ -152,6 +152,11 @@ async function importTar(taskContext, fileBuffer, importRootNote) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attr.type === 'label' && attr.name === 'externalLink') {
|
||||
// also created automatically
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attr.type === 'relation') {
|
||||
attr.value = getNewNoteId(attr.value);
|
||||
}
|
||||
|
||||
@@ -242,6 +242,20 @@ function findInternalLinks(content, foundLinks) {
|
||||
return content.replace(/href="[^"]*#root/g, 'href="#root');
|
||||
}
|
||||
|
||||
function findExternalLinks(content, foundLinks) {
|
||||
const re = /href="([a-zA-Z]+:\/\/[^"]*)"/g;
|
||||
let match;
|
||||
|
||||
while (match = re.exec(content)) {
|
||||
foundLinks.push({
|
||||
name: 'externalLink',
|
||||
value: match[1]
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
function findIncludeNoteLinks(content, foundLinks) {
|
||||
const re = /<section class="include-note" data-note-id="([a-zA-Z0-9]+)">/g;
|
||||
let match;
|
||||
@@ -281,6 +295,7 @@ async function saveLinks(note, content) {
|
||||
if (note.type === 'text') {
|
||||
content = findImageLinks(content, foundLinks);
|
||||
content = findInternalLinks(content, foundLinks);
|
||||
content = findExternalLinks(content, foundLinks);
|
||||
content = findIncludeNoteLinks(content, foundLinks);
|
||||
}
|
||||
else if (note.type === 'relation-map') {
|
||||
@@ -293,9 +308,11 @@ async function saveLinks(note, content) {
|
||||
const existingLinks = await note.getLinks();
|
||||
|
||||
for (const foundLink of foundLinks) {
|
||||
const targetNote = await repository.getNote(foundLink.value);
|
||||
if (!targetNote || targetNote.isDeleted) {
|
||||
continue;
|
||||
if (foundLink.name !== 'externalLink') {
|
||||
const targetNote = await repository.getNote(foundLink.value);
|
||||
if (!targetNote || targetNote.isDeleted) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const existingLink = existingLinks.find(existingLink =>
|
||||
@@ -305,7 +322,7 @@ async function saveLinks(note, content) {
|
||||
if (!existingLink) {
|
||||
await new Attribute({
|
||||
noteId: note.noteId,
|
||||
type: 'relation',
|
||||
type: foundLink.name === 'externalLink' ? 'label' : 'relation',
|
||||
name: foundLink.name,
|
||||
value: foundLink.value,
|
||||
}).save();
|
||||
@@ -582,6 +599,7 @@ async function eraseDeletedNotes() {
|
||||
UPDATE notes
|
||||
SET title = '[deleted]',
|
||||
contentLength = 0,
|
||||
isProtected = 0,
|
||||
isErased = 1
|
||||
WHERE noteId IN (???)`, noteIdsToErase);
|
||||
|
||||
|
||||
@@ -209,6 +209,8 @@ async function transactional(func) {
|
||||
|
||||
transactionActive = false;
|
||||
resolve();
|
||||
|
||||
setTimeout(() => require('./ws').sendPingToAllClients(), 50);
|
||||
}
|
||||
catch (e) {
|
||||
if (transactionActive) {
|
||||
|
||||
@@ -176,7 +176,7 @@ async function pushSync(syncContext) {
|
||||
let lastSyncedPush = await getLastSyncedPush();
|
||||
|
||||
while (true) {
|
||||
const syncs = await sql.getRows('SELECT * FROM sync WHERE id > ? LIMIT 1000', [lastSyncedPush]);
|
||||
const syncs = await sql.getRows('SELECT * FROM sync WHERE isSynced = 1 AND id > ? LIMIT 1000', [lastSyncedPush]);
|
||||
|
||||
if (syncs.length === 0) {
|
||||
log.info("Nothing to push");
|
||||
@@ -228,14 +228,15 @@ async function syncFinished(syncContext) {
|
||||
|
||||
async function checkContentHash(syncContext) {
|
||||
const resp = await syncRequest(syncContext, 'GET', '/api/sync/check');
|
||||
const lastSyncedPullId = await getLastSyncedPull();
|
||||
|
||||
if (await getLastSyncedPull() < resp.maxSyncId) {
|
||||
log.info("There are some outstanding pulls, skipping content check.");
|
||||
if (lastSyncedPullId < resp.maxSyncId) {
|
||||
log.info(`There are some outstanding pulls (${lastSyncedPullId} vs. ${resp.maxSyncId}), skipping content check.`);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const notPushedSyncs = await sql.getValue("SELECT EXISTS(SELECT 1 FROM sync WHERE id > ?)", [await getLastSyncedPush()]);
|
||||
const notPushedSyncs = await sql.getValue("SELECT EXISTS(SELECT 1 FROM sync WHERE isSynced = 1 AND id > ?)", [await getLastSyncedPush()]);
|
||||
|
||||
if (notPushedSyncs) {
|
||||
log.info(`There's ${notPushedSyncs} outstanding pushes, skipping content check.`);
|
||||
@@ -352,7 +353,7 @@ async function updatePushStats() {
|
||||
if (await syncOptions.isSyncSetup()) {
|
||||
const lastSyncedPush = await optionService.getOption('lastSyncedPush');
|
||||
|
||||
stats.outstandingPushes = await sql.getValue("SELECT COUNT(1) FROM sync WHERE id > ?", [lastSyncedPush]);
|
||||
stats.outstandingPushes = await sql.getValue("SELECT COUNT(1) FROM sync WHERE isSynced = 1 AND id > ?", [lastSyncedPush]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ async function insertEntitySync(entityName, entityId, sourceId) {
|
||||
entityName: entityName,
|
||||
entityId: entityId,
|
||||
utcSyncDate: dateUtils.utcNowDateTime(),
|
||||
sourceId: sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId()
|
||||
sourceId: sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId(),
|
||||
isSynced: 1
|
||||
};
|
||||
|
||||
sync.id = await sql.replace("sync", sync);
|
||||
@@ -23,8 +24,6 @@ async function addEntitySync(entityName, entityId, sourceId) {
|
||||
const sync = await insertEntitySync(entityName, entityId, sourceId);
|
||||
|
||||
syncs.push(sync);
|
||||
|
||||
setTimeout(() => require('./ws').sendPingToAllClients(), 50);
|
||||
}
|
||||
|
||||
async function addEntitySyncsForSector(entityName, entityPrimaryKey, sector) {
|
||||
|
||||
@@ -250,6 +250,7 @@
|
||||
|
||||
<link href="stylesheets/themes.css" rel="stylesheet">
|
||||
<link href="stylesheets/style.css" rel="stylesheet">
|
||||
<link href="stylesheets/detail.css" rel="stylesheet">
|
||||
<link href="stylesheets/desktop.css" rel="stylesheet">
|
||||
|
||||
<script src="javascripts/desktop.js" crossorigin type="module"></script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="note-detail-image note-detail-component">
|
||||
<div style="display: flex; justify-content: space-evenly; margin: 10px;">
|
||||
<div class="no-print" style="display: flex; justify-content: space-evenly; margin: 10px;">
|
||||
<button class="image-download btn btn-sm btn-primary" type="button">Download</button>
|
||||
|
||||
<button class="image-copy-to-clipboard btn btn-sm btn-primary" type="button">Copy to clipboard</button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="note-detail-relation-map note-detail-component">
|
||||
<button class="relation-map-create-child-note btn btn-sm floating-button" type="button"
|
||||
<button class="relation-map-create-child-note btn btn-sm floating-button no-print" type="button"
|
||||
title="Create new child note and add it into this relation map">
|
||||
<span class="bx bx-folder-plus"></span>
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
</button>
|
||||
|
||||
<button type="button"
|
||||
class="relation-map-reset-pan-zoom btn icon-button floating-button bx bx-crop"
|
||||
class="relation-map-reset-pan-zoom btn icon-button floating-button bx bx-crop no-print"
|
||||
title="Reset pan & zoom to initial coordinates and magnification"
|
||||
style="right: 70px;"></button>
|
||||
|
||||
<div class="btn-group floating-button" style="right: 10px;">
|
||||
<div class="btn-group floating-button no-print" style="right: 10px;">
|
||||
<button type="button"
|
||||
class="relation-map-zoom-in btn icon-button bx bx-zoom-in"
|
||||
title="Zoom In"></button>
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
<% include details/relation_map.ejs %>
|
||||
|
||||
<% include details/protected_session_password.ejs %>
|
||||
|
||||
<% include details/book.ejs %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,6 +118,7 @@
|
||||
|
||||
<link href="stylesheets/themes.css" rel="stylesheet">
|
||||
<link href="stylesheets/style.css" rel="stylesheet">
|
||||
<link href="stylesheets/detail.css" rel="stylesheet">
|
||||
<link href="stylesheets/mobile.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="libraries/boxicons/css/boxicons.min.css">
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
<label><input type="radio" name="setup-type" value="new-document" data-bind="checked: setupType">
|
||||
I'm a new user and I want to create new Trilium document for my notes</label>
|
||||
</div>
|
||||
<div class="radio" data-bind="if: instanceType == 'server'" style="margin-bottom: 15px;">
|
||||
<div class="radio" style="margin-bottom: 15px;">
|
||||
<label><input type="radio" name="setup-type" value="sync-from-desktop" data-bind="checked: setupType">
|
||||
I have desktop instance already and I want to setup sync with it</label>
|
||||
</div>
|
||||
<div class="radio" data-bind="if: instanceType == 'desktop'" style="margin-bottom: 15px;">
|
||||
<div class="radio" style="margin-bottom: 15px;">
|
||||
<label><input type="radio" name="setup-type" value="sync-from-server" data-bind="checked: setupType">
|
||||
I have server instance up and I want to setup sync with it</label>
|
||||
I have server instance already and I want to setup sync with it</label>
|
||||
</div>
|
||||
|
||||
<button type="button" data-bind="disable: !setupTypeSelected(), click: selectSetupType" class="btn btn-primary">Next</button>
|
||||
|
||||
Reference in New Issue
Block a user