Moved all custom tags/groups into customizable attributes, added working example with default attribute values, added detailed explanations in README.

Update !!!meta.json, Timeline Widget Import.zip, and 18 more files...
This commit is contained in:
Quentin Le Caignec 2021-09-06 15:03:44 +02:00
parent 94490b83fa
commit c8a3807a9f
21 changed files with 1715 additions and 10 deletions

690
!!!meta.json Normal file
View File

@ -0,0 +1,690 @@
{
"formatVersion": 1,
"appVersion": "0.47.7",
"files": [
{
"isClone": false,
"noteId": "2sZ7j1WAYIEc",
"notePath": [
"2sZ7j1WAYIEc"
],
"title": "Timeline Widget",
"notePosition": 100,
"prefix": null,
"isExpanded": 0,
"type": "render",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "hideChildrenOverview",
"value": "",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "renderNote",
"value": "Pj8YRsJi8dTy",
"isInheritable": false,
"position": 20
},
{
"type": "label",
"name": "iconClass",
"value": "bx bxs-calendar",
"isInheritable": false,
"position": 30
},
{
"type": "label",
"name": "label:timeline_start",
"value": "promoted,single,text",
"isInheritable": false,
"position": 40
},
{
"type": "label",
"name": "label:timeline_end",
"value": "promoted,single,text",
"isInheritable": false,
"position": 50
},
{
"type": "label",
"name": "label:timeline_present",
"value": "promoted,single,text",
"isInheritable": false,
"position": 60
},
{
"type": "label",
"name": "timeline_start",
"value": "2020-01-01",
"isInheritable": false,
"position": 70
},
{
"type": "label",
"name": "timeline_end",
"value": "2050-01-01",
"isInheritable": false,
"position": 80
},
{
"type": "label",
"name": "timeline_present",
"value": "2021-02-02:02:00",
"isInheritable": false,
"position": 90
},
{
"type": "label",
"name": "label:event_label_start",
"value": "single,text",
"isInheritable": false,
"position": 100
},
{
"type": "label",
"name": "label:event_label_end",
"value": "single,text",
"isInheritable": false,
"position": 110
},
{
"type": "label",
"name": "event_label_start",
"value": "event_start",
"isInheritable": false,
"position": 120
},
{
"type": "label",
"name": "event_label_end",
"value": "event_end",
"isInheritable": false,
"position": 130
},
{
"type": "label",
"name": "label:person_label_start",
"value": "single,text",
"isInheritable": false,
"position": 140
},
{
"type": "label",
"name": "label:person_label_end",
"value": "single,text",
"isInheritable": false,
"position": 150
},
{
"type": "label",
"name": "person_label_end",
"value": "person_end",
"isInheritable": false,
"position": 160
},
{
"type": "label",
"name": "person_label_start",
"value": "person_start",
"isInheritable": false,
"position": 170
},
{
"type": "label",
"name": "event_type_list",
"value": "first,1,First,white;second,2,Second,royalblue,false;third,3,Third,orange;forcedInto,4,Forced Into,blueviolet,false,null,third;background,8,Background,null,false,background,second",
"isInheritable": false,
"position": 180
},
{
"type": "label",
"name": "label:event_type_list",
"value": "multi,text",
"isInheritable": false,
"position": 190
},
{
"type": "label",
"name": "event_label_type",
"value": "event_type",
"isInheritable": false,
"position": 200
},
{
"type": "label",
"name": "label:event_label_type",
"value": "single,text",
"isInheritable": false,
"position": 210
},
{
"type": "label",
"name": "label:event_type_default",
"value": "single,text",
"isInheritable": false,
"position": 220
},
{
"type": "label",
"name": "event_type_default",
"value": "first",
"isInheritable": false,
"position": 230
}
],
"dirFileName": "Timeline Widget",
"children": [
{
"isClone": false,
"noteId": "Pj8YRsJi8dTy",
"notePath": [
"2sZ7j1WAYIEc",
"Pj8YRsJi8dTy"
],
"title": "render.html",
"notePosition": 5,
"prefix": null,
"isExpanded": 0,
"type": "code",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "archived",
"value": "",
"isInheritable": false,
"position": 10
}
],
"dataFileName": "render.html",
"dirFileName": "1_render.html",
"children": [
{
"isClone": false,
"noteId": "HPQBuorDTNkS",
"notePath": [
"2sZ7j1WAYIEc",
"Pj8YRsJi8dTy",
"HPQBuorDTNkS"
],
"title": "script.js",
"notePosition": 153,
"prefix": null,
"isExpanded": 0,
"type": "code",
"mime": "application/javascript;env=frontend",
"attributes": [],
"dataFileName": "script.js",
"dirFileName": "1_script.js",
"children": [
{
"isClone": false,
"noteId": "9v0I9nV5BLwz",
"notePath": [
"2sZ7j1WAYIEc",
"Pj8YRsJi8dTy",
"HPQBuorDTNkS",
"9v0I9nV5BLwz"
],
"title": "moment.min.js",
"notePosition": 123,
"prefix": null,
"isExpanded": 0,
"type": "file",
"mime": "application/javascript",
"attributes": [
{
"type": "label",
"name": "originalFileName",
"value": "moment.min.js",
"isInheritable": false,
"position": 1000
}
],
"dataFileName": "moment.min.js"
},
{
"isClone": false,
"noteId": "BLHMY7p9amHk",
"notePath": [
"2sZ7j1WAYIEc",
"Pj8YRsJi8dTy",
"HPQBuorDTNkS",
"BLHMY7p9amHk"
],
"title": "vis.min.js",
"notePosition": 133,
"prefix": null,
"isExpanded": 0,
"type": "file",
"mime": "application/javascript",
"attributes": [
{
"type": "label",
"name": "originalFileName",
"value": "vis.min.js",
"isInheritable": false,
"position": 1000
}
],
"dataFileName": "vis.min.js"
},
{
"isClone": false,
"noteId": "gcPseIMlkPRS",
"notePath": [
"2sZ7j1WAYIEc",
"Pj8YRsJi8dTy",
"HPQBuorDTNkS",
"gcPseIMlkPRS"
],
"title": "vis-timeline-graph2d.min.js",
"notePosition": 143,
"prefix": null,
"isExpanded": 0,
"type": "file",
"mime": "application/javascript",
"attributes": [
{
"type": "label",
"name": "originalFileName",
"value": "vis-timeline-graph2d.min.js",
"isInheritable": false,
"position": 1000
}
],
"dataFileName": "vis-timeline-graph2d.min.js"
},
{
"isClone": false,
"noteId": "m78T7NY9ObE1",
"notePath": [
"2sZ7j1WAYIEc",
"Pj8YRsJi8dTy",
"HPQBuorDTNkS",
"m78T7NY9ObE1"
],
"title": "vis.min.css",
"notePosition": 153,
"prefix": null,
"isExpanded": 0,
"type": "file",
"mime": "text/css",
"attributes": [
{
"type": "label",
"name": "originalFileName",
"value": "vis.min.css",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "appCss",
"value": "",
"isInheritable": false,
"position": 20
}
],
"dataFileName": "vis.min.css"
},
{
"isClone": false,
"noteId": "JhvzMCV72opp",
"notePath": [
"2sZ7j1WAYIEc",
"Pj8YRsJi8dTy",
"HPQBuorDTNkS",
"JhvzMCV72opp"
],
"title": "vis-timeline-graph2d.min.css",
"notePosition": 163,
"prefix": null,
"isExpanded": 0,
"type": "file",
"mime": "text/css",
"attributes": [
{
"type": "label",
"name": "originalFileName",
"value": "vis-timeline-graph2d.min.css",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "appCss",
"value": "",
"isInheritable": false,
"position": 20
}
],
"dataFileName": "vis-timeline-graph2d.min.css"
}
]
}
]
},
{
"isClone": false,
"noteId": "d7gJEGqNGL9p",
"notePath": [
"2sZ7j1WAYIEc",
"d7gJEGqNGL9p"
],
"title": "button.js",
"notePosition": 15,
"prefix": null,
"isExpanded": 0,
"type": "code",
"mime": "application/javascript;env=frontend",
"attributes": [
{
"type": "relation",
"name": "targetNote",
"value": "2sZ7j1WAYIEc",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "run",
"value": "frontendStartup",
"isInheritable": false,
"position": 20
},
{
"type": "label",
"name": "archived",
"value": "",
"isInheritable": false,
"position": 30
}
],
"dataFileName": "button.js"
},
{
"isClone": false,
"noteId": "JlnkqhfQ8JR4",
"notePath": [
"2sZ7j1WAYIEc",
"JlnkqhfQ8JR4"
],
"title": "EXAMPLES",
"notePosition": 25,
"prefix": null,
"isExpanded": 0,
"type": "text",
"mime": "text/html",
"attributes": [],
"format": "html",
"dirFileName": "EXAMPLES",
"children": [
{
"isClone": false,
"noteId": "cDZAPX7ORCRa",
"notePath": [
"2sZ7j1WAYIEc",
"JlnkqhfQ8JR4",
"cDZAPX7ORCRa"
],
"title": "This event is in First",
"notePosition": 0,
"prefix": null,
"isExpanded": 0,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "event_start",
"value": "2025-02-05",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "event_type",
"value": "first",
"isInheritable": false,
"position": 20
}
],
"format": "html",
"dataFileName": "This event is in First.html"
},
{
"isClone": false,
"noteId": "skEcjQY0NgNy",
"notePath": [
"2sZ7j1WAYIEc",
"JlnkqhfQ8JR4",
"skEcjQY0NgNy"
],
"title": "This event is in Second and this group is not visible by default",
"notePosition": 1,
"prefix": null,
"isExpanded": 0,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "event_start",
"value": "2030-02-05",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "event_type",
"value": "second",
"isInheritable": false,
"position": 20
}
],
"format": "html",
"dataFileName": "This event is in Second and th.html"
},
{
"isClone": false,
"noteId": "f5jq5D5cb4NX",
"notePath": [
"2sZ7j1WAYIEc",
"JlnkqhfQ8JR4",
"f5jq5D5cb4NX"
],
"title": "This event is in Third and has an end date",
"notePosition": 2,
"prefix": null,
"isExpanded": 0,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "event_start",
"value": "2035-02-05",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "event_type",
"value": "third",
"isInheritable": false,
"position": 20
},
{
"type": "label",
"name": "event_end",
"value": "2048-01-01",
"isInheritable": false,
"position": 30
}
],
"format": "html",
"dataFileName": "This event is in Third and has.html"
},
{
"isClone": false,
"noteId": "bxl4R5CTPvvn",
"notePath": [
"2sZ7j1WAYIEc",
"JlnkqhfQ8JR4",
"bxl4R5CTPvvn"
],
"title": "This event is in a seperate group then inserted into \"Third\"",
"notePosition": 3,
"prefix": null,
"isExpanded": 0,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "event_start",
"value": "2040-02-05",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "event_type",
"value": "forcedInto",
"isInheritable": false,
"position": 20
}
],
"format": "html",
"dataFileName": "This event is in a seperate gr.html"
},
{
"isClone": false,
"noteId": "JTjoGTswR2GA",
"notePath": [
"2sZ7j1WAYIEc",
"JlnkqhfQ8JR4",
"JTjoGTswR2GA"
],
"title": "This event is in Third",
"notePosition": 3,
"prefix": null,
"isExpanded": 0,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "event_start",
"value": "2023-02-05",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "event_type",
"value": "third",
"isInheritable": false,
"position": 20
}
],
"format": "html",
"dataFileName": "This event is in Third.html"
},
{
"isClone": false,
"noteId": "K7Fjghj31lUs",
"notePath": [
"2sZ7j1WAYIEc",
"JlnkqhfQ8JR4",
"K7Fjghj31lUs"
],
"title": "This is a background event then inserted into \"Second\"",
"notePosition": 4,
"prefix": null,
"isExpanded": 0,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "event_start",
"value": "2000-01-01",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "event_type",
"value": "background",
"isInheritable": false,
"position": 20
},
{
"type": "label",
"name": "event_end",
"value": "2050-01-01",
"isInheritable": false,
"position": 30
}
],
"format": "html",
"dataFileName": "This is a background event the.html"
},
{
"isClone": false,
"noteId": "aVUiO3d3lfmo",
"notePath": [
"2sZ7j1WAYIEc",
"JlnkqhfQ8JR4",
"aVUiO3d3lfmo"
],
"title": "This is a person note generating birth/death events",
"notePosition": 14,
"prefix": null,
"isExpanded": 0,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "person_start",
"value": "2018-02-02",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "person_end",
"value": "2046-08-02",
"isInheritable": false,
"position": 20
}
],
"format": "html",
"dataFileName": "This is a person note generati.html"
}
]
}
]
},
{
"noImport": true,
"dataFileName": "navigation.html"
},
{
"noImport": true,
"dataFileName": "index.html"
},
{
"noImport": true,
"dataFileName": "style.css"
}
]
}

View File

@ -4,9 +4,9 @@ Interactive timeline in html+js widget for [Trilium Notes](https://github.com/za
## What is this?
Fetches notes with specific labels to be displayed as events in various ways (single point, range, background box) grouped in multiple categories and with varying colors, uses root note's promoted attributes to easily set the timeline values (present marker, start/end values for the initial loaded view).
Fetches notes with specific labels to be displayed as events in various ways (box, single point, range, background) grouped in multiple categories and with varying colors, uses root note's promoted attributes to easily set the timeline values (present marker, start/end values for the initial loaded view), defined which labels should be fetched and defined the types/groups in which to sort events.
Used for my own personal purposes (prepping and tracking a Pathfinder 2e tabletop campaign) and will require tweaking to be used for other labels and note structures.
Used for my own personal purposes (prepping and tracking a Pathfinder 2e tabletop campaign) and might require some tweaking for other purposes, but the customizable attributes should be enough.
## Install
@ -18,14 +18,63 @@ The Timeline Widget is then imported but nothing is displayed yet, as indicated
## Usage
The widget fetches notes with a "date_start" label attribute (and treats them as events of various types), and "date_birth"/"date_death" label attributes (and treats them as date of birth/death "agent" events).
A note can also contain a "date_type" label attribute that sorts it into different categories. The existing ones (the ones I need, you will need to change them in "script.js" if you want your own) are "session", "astrological", "player", "omen", "agent", "local", "global" and "era". All of those have their own toggle button to show/hide the events under that category.
The "era" type is special and will be displayed as a block in the background of the global events category (used for large ranges of years).
The setup will require a bit of effort in defining the required attributes, but the widget contains a working example that can be more easily tweaked.
The widget has 3 label promoted attributes (of type Text and not Date to be able to enter hours/minutes/seconds) that can be configured, they are all in the format "YYYY-MM-DD:hh:mm:ss" but you can enter simply "YYYY", or "YYYY-MM-DD:hh" and not the entire date.
They are as follows :
The events (notes fetched by the widget with the proper labels) showing up on the timeline can be hovered and clicked.
- "timeline_start" and "timeline_end" are the range of dates where the view STARTS, to be able to determine where (and how "zoomed-in") you want the timeline to be when loading the widget. You may still scroll and drag to the rest of the full timeline, this only affects the visible part of the timeline in the view when loading.
#### Understanding the attributes
The widget reads the values of certain attributes ("event_label_start", "event_label_end", "person_label_start", "person_label_end" and "event_label_type") are used to define what labels (or "tags") you want to use for your own setup. The values you input into these attributes will be read by the widget as the labels it should look for.
All date values are in the format "YYYY-MM-DD:hh:mm:ss" but you can enter simply "YYYY", or "YYYY-MM-DD:hh" and not the entire date.
#### Date attributes
The labels defined by the attributes "event_label_start", "event_label_end", "person_label_start" and "person_label_end" are date attributes containing a date value in the text format described previously.
- By default, "event_label_start" is set as "date_start", which means all notes in trilium with a label attribute named "date_start" will be fetched, and the date value in the attribute will be displayed on the timeline.
- By default, "event_label_end" is set as "date_end", which means any note that has already been fetched through "date_start" and also contains the label attribute "date_end" with a date value will be displayed as a range (with valid start and end date values) on the timeline. A note that only contains the defined "event_label_end" label and not the "event_label_start" label will not be fetched (an event note requires a starting date to be seen as valid).
A special case is the "person" type notes. By default, "person_label_start" is set as "date_birth", and "person_label_end" is set as "date_death". Any note which contains *either* of those labels will be fetched (UNLIKE the normal event notes which require a starting date), any starting dates will appear as a "Note Title (Birth)" date on the timeline, and any ending dates will appear as a "Note Title (Death)" date on the timeline. If you need this special case but want to change the title of the generated dates (for something other then Birth and Death), you will have to change the values in *script.js* lines 45 and 56.
#### Group attributes
The label defined by the attribute "event_label_type" is a tagging attribute containing a string/text used to mark your event/person notes and sort them into corresponding groups.
- By default, "event_label_type" is set as "date_type", which means all notes that contain the label attribute "date_type" will be sorted into the group corresponding to the value (if they already contain the label defined by "event_label_start" or by one of the two person labels so they can be detected by the widget).
For example, if you have defined a group with the id "birthdays", and the attribute "event_label_type" is defined as "date_group", then any valid note containing the label "date_group" and with the value "birthdays" will be sorted into the corresponding group on the timeline. If a note has no group, or if the group name isn't valid/does not have a corresponding group defined, it will simply be put into an empty default group.
The groups are the hardest to setup. Declaring your groups is done through the attribute "event_type_list" in a text value. The widget reads this text value to generate the groups and their settings.
The "event_type_list" value follows this format, in () is the type of data it needs to be :
> id(string), order(int), title(string), color(string), visible(bool), display(string), forceGroup(int)
Each group is defined with these values, each value separated by a **, (comma)** and each group separated by a **; (semicolon)**.
- The "id" is a string (such as "birthdays", "first" or "historical"), it is also the value that needs to match the values of the "event_label_type" labels in order to sort events into groups.
- The "order" is an integer value that defines the order in which the groups appear on the timeline, going from top (0) to bottom (99+).
- The "title" is the text that will appear as the title of the group on the timeline and on the UI button.
- The "color" needs to be a valid CSS color value ([CSS color values](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value)) and will determine the color of all boxes in that group on the timeline.
- The "visible" determines if the group is visible by default or not. If the group is not visible by default (and is not forcedInto another group), it can be displayed by clicking the corresponding UI button.
- The "display" determines what kind of date box the events in that group will be displayed as. It has 3 valid values, "box" (the default), "point" (minimal point instead of a box) and "background" (appears in the background of the group, useful for eras/epochs or large stretches of time).
- The "forcedInto" is a special option which allows for notes to be defined into their own group (with their own id, color, display type, etc..) but then merged into another group on timeline display. For example, if you have a group "special days" appearing in blue and another group "birthdays" appearing in red, but you want birthdays to still appear in the wider "special days" group to avoid splitting your timeline into too many groups, you can set the "birthdays" group to have "special days" as the forcedInto value.
By default, "event_type_list" is set as :
> first,1,First,white;second,2,Second,royalblue,false;third,3,Third,orange;forcedInto,4,Forced Into,blueviolet,false,null,third;background,8,Background,null,false,background,second
This reads as :
- Group of id "first", order 1, title "First", color white ;
- Group of id "second", order 2, title "Second", color royalblue, not visible by default ;
- Group of id "third", order 3, title "Third", color orange ;
- Group of id "forcedInto", order 4, title "Forced Into", color blueviolet, not visible by default, default display type, events are inserted into the other group of id "third" ;
- Group of id "background", order 8, title "Background", no color, not visible by default, background display type, events are inserted into the other group of id "second" ;
This can take a bit to figure out and setup, but you can also keep it very simple with a few basic groups. The widget contains a working example with groups and notes setup, as well as explanations in the notes.
### Timeline attributes
The widget has 3 label promoted attributes (of date values in the text format described previously), "timeline_start", "timeline_end" and "timeline_present".
- "timeline_start" and "timeline_end" are the range of dates where the view *starts*, to be able to determine where (and how "zoomed-in") you want the timeline to be when loading the widget. You may still scroll and drag to the rest of the full timeline, this only affects the visible part of the timeline in the view when it first loads.
- "timeline_present" optionally shows a red vertical line indicating where the "present" is if you need a timeline where the present isn't actually the present (in my case, to represent the present within the setting of a tabletop campaign, which I change session to session). Be aware this line doesn't stay still, so if you stay on the widget for some time you will notice it moves ahead in real time, but of course resets every time you load/render the widget.
The events showing up on the timeline can be hovered and clicked.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,174 @@
/** Version 1.0 **/
// Initialize libraries and context
var vis = require('vis.min.js');
var moment = require('moment.min.js');
var container = document.getElementById("timeline");
var menu = document.getElementById("menu");
// Get widget attribute values and fetch notes from defined tags
const events = await api.runOnServer(() => {
const parentNote = api.startNote.getParentNotes()[0];
// fetch what labels to look for from widget attributes
// event notes (start/end)
const event_note_label_start = parentNote.getLabelValue('event_label_start').toString();
const event_note_label_end = parentNote.getLabelValue('event_label_end').toString();
// agent notes (birth/death)
const person_note_label_start = parentNote.getLabelValue('person_label_start').toString();
const person_note_label_end = parentNote.getLabelValue('person_label_end').toString();
// date types (categories)
const event_label_type = parentNote.getLabelValue('event_label_type').toString();
// fetch notes with those labels
const event_notes = api.getNotesWithLabel(event_note_label_start);
const person_notes_birth = api.getNotesWithLabel(person_note_label_start);
const person_notes_death = api.getNotesWithLabel(person_note_label_end);
const events = [];
for (const note of event_notes) {
const id = note.noteId;
const content = note.title;
const start = note.getLabelValue(event_note_label_start);
const end = note.getLabelValue(event_note_label_end);
const group = note.getLabelValue(event_label_type) ? note.getLabelValue(event_label_type) : (
parentNote.getLabelValue('event_type_default') ? parentNote.getLabelValue('event_type_default') : 'default');
if (content && start) {
events.push({ id, content, start, end, group });
}
}
// make a 'birth' event for each person note with start date
for (const note of person_notes_birth) {
const id = note.noteId;
const start = note.getLabelValue(person_note_label_start);
const group = note.getLabelValue(event_label_type) ? note.getLabelValue(event_label_type) : (
parentNote.getLabelValue('event_type_default') ? parentNote.getLabelValue('event_type_default') : 'default');
if (start) {
const content = note.title + ' (Birth)';
events.push({ id, content, start, group });
}
}
// make a 'death' event for each person note with end date
for (const note of person_notes_death) {
const id = note.noteId;
const start = note.getLabelValue(person_note_label_end);
const group = note.getLabelValue(event_label_type) ? note.getLabelValue(event_label_type) : (
parentNote.getLabelValue('event_type_default') ? parentNote.getLabelValue('event_type_default') : 'default');
if (start) {
const content = note.title + ' (Death)';
events.push({ id, content, start, group });
}
}
return events;
});
// Get event types, make groups
const types = await api.runOnServer(() => {
const parentNote = api.startNote.getParentNotes()[0];
const event_type_list = parentNote.getLabelValue('event_type_list').toString().split(';');
const types = [];
var i = 1;
for (const type of event_type_list) {
// type format each type seperated by ;
// id(string), order(int), label(string), color(string), visible(bool), type(string), forceGroup(int)
types.push({
id: type.split(',')[0].toString(),
order: type.split(',')[1] ? (parseInt(type.split(',')[1],10)) : i,
content: type.split(',')[2] ? (type.split(',')[2].toString()) : type.split(',')[0].toString(),
color: type.split(',')[3] ? (type.split(',')[3].toString()) : null,
visible: type.split(',')[4] ? (type.split(',')[4] === 'true') : true,
type: type.split(',')[5] ? type.split(',')[5].toString() : null,
forceGroup: type.split(',')[6] ? (type.split(',')[6].toString()) : null,
});
i++;
}
return types
});
// merge the default group with attribute-added groups
var groups = new vis.DataSet([
{id:'default', order: 100, content:'', color:'white', visible:true, type:null, forceGroup:null},
...types,
]);
// Create a DataSet for all event items
var items = new vis.DataSet();
for (var i = 0; i < events.length; i++) {
// get date values and note link
var note_id = events[i].id;
var note_link = await api.createNoteLink(note_id);
var event_content = events[i].content;
note_link[0].firstChild.innerText = event_content;
var event_start = vis.moment(events[i].start);
var event_end = events[i].end ? vis.moment(events[i].end) : null;
// set event group
var event_group = events[i].group
? (groups.get({filter: function (item) { return (item.id === events[i].group) }})[0]
? groups.get({filter: function (item) { return (item.id === events[i].group) }})[0]
: groups.get({filter: function (item) { return (item.id === 'default') }})[0])
: groups.get({filter: function (item) { return (item.id === 'default') }})[0];
// fill event data
items.add({
id: i,
content: note_link[0].firstChild,
start: event_start,
end: event_end,
group: event_group.forceGroup ? event_group.forceGroup : event_group.id,
type: (event_group.type=='box' || event_group.type=='point' || event_group.type=='range' || event_group.type=='background') ? event_group.type : null,
className: event_group.id,
style: `background-color:${event_group.color};border-color:${event_group.color};`,
});
}
console.log(items);
// Timeline options
const timeline_options = await api.runOnServer(() => {
const parentNote = api.startNote.getParentNotes()[0];
const timeline_start = parentNote.getLabelValue('timeline_start');
const timeline_end = parentNote.getLabelValue('timeline_end');
const timeline_present = parentNote.getLabelValue('timeline_present');
return {timeline_start, timeline_end, timeline_present};
});
var options = {
clickToUse: false,
showCurrentTime: true,
height: '95%',
start: timeline_options.timeline_start,
end: timeline_options.timeline_end,
};
// Set timeline wrapper height
container.parentNode.style.height = '100%';
// Fix timeline display
container.parentNode.parentNode.parentNode.parentNode.style.display = 'initial';
// Create Timeline
var timeline = new vis.Timeline(container, items, groups, options);
// Add present time marker based on "timeline_present" label
timeline.setCurrentTime(timeline_options.timeline_present);
// Create UI menu buttons from added groups (that are not forced into another group)
menu.innerHTML = '';
const toggle_groups = groups.get({filter:function(item){return(item.forceGroup===null && item.id!=='default')}});
for (const group of toggle_groups) {
var button = document.createElement('input');
button.type = 'button';
button.id = `toggle-${group.id}`;
button.value = group.content;
button.className = (group.visible===true) ? 'toggled' : '';
menu.appendChild(button);
}
// Bind toggle buttons
for (const group of toggle_groups) {
document.getElementById(`toggle-${group.id}`).onclick = function(){
toggleGroupVisibility(group.id);
}
}
// Function to toggle group visibility
function toggleGroupVisibility(group_id){
var visibility = groups.get({filter:function(item){return(item.id==group_id)}})[0].visible;
groups.update({id: group_id, visible: !visibility});
console.log(document.getElementById(`toggle-${group_id}`));
document.getElementById(`toggle-${group_id}`).className = (!visibility===true) ? 'toggled' : '';
timeline.setGroups(groups);
timeline.redraw();
}

View File

@ -0,0 +1,20 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
</head>
<body class="ck-content">
<h1>This event is in First</h1>
<p>This event has the “event_start” label attribute which is defined in the
widget as the tag marking events to be fetched.</p>
<p>This event has the “event_type” label of “first”, sorting it into the
corresponding group with id “first” defined in “event_type_list” attribute
on the widget.</p>
</body>
</html>

View File

@ -0,0 +1,23 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
</head>
<body class="ck-content">
<h1>This event is in Second and this group is not visible by default</h1>
<p>This event has the “event_start” label attribute which is defined in the
widget as the tag marking events to be fetched.</p>
<p>the “Second” group is by default not visible as defined in the “event_type_list”
attribute on the widget, showing it requires clicking the UI button appearing
at the top of the timeline.</p>
<p>This event has the “event_type” label of value “first”, sorting it into
the corresponding group with id “first” defined in “event_type_list” attribute
on the widget.</p>
</body>
</html>

View File

@ -0,0 +1,23 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
</head>
<body class="ck-content">
<h1>This event is in Third and has an end date</h1>
<p>This event has the “event_start” label attribute which is defined in the
widget as the tag marking events to be fetched.</p>
<p>This event has the “event_end” label attribute which, combined with “event_start”,
makes this event a “range” event showing in a limited box between the start
and end dates.</p>
<p>This event has the “event_type” label of value “first”, sorting it into
the corresponding group with id “first” defined in “event_type_list” attribute
on the widget.</p>
</body>
</html>

View File

@ -0,0 +1,20 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
</head>
<body class="ck-content">
<h1>This event is in Third</h1>
<p>This event has the “event_start” label attribute which is defined in the
widget as the tag marking events to be fetched.</p>
<p>This event has the “event_type” label of value “first”, sorting it into
the corresponding group with id “first” defined in “event_type_list” attribute
on the widget.</p>
</body>
</html>

View File

@ -0,0 +1,24 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
</head>
<body class="ck-content">
<h1>This event is in a seperate group then inserted into &quot;Third&quot;</h1>
<p>This event has the “event_start” label attribute which is defined in the
widget as the tag marking events to be fetched.</p>
<p>This event has the “event_type” label of value “forcedInto”, sorting it
into the corresponding group with id “forcedInto” defined in “event_type_list”
attribute on the widget, but then the event is forced into the group “third”
as defined. The group “forcedInto” never appears on the timeline or as
a button, it is a way of sorting events separately (with a different type
tag and potentially different colors or other settings) within the same
visual group.</p>
</body>
</html>

View File

@ -0,0 +1,27 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
</head>
<body class="ck-content">
<h1>This is a background event then inserted into &quot;Second&quot;</h1>
<p>This event has the “event_start” label attribute which is defined in the
widget as the tag marking events to be fetched.</p>
<p>This event has the “event_end” label attribute which, combined with “event_start”,
makes this event a “range” event showing in a limited box between the start
and end dates.</p>
<p>This event has the “event_type” label of value “background”, sorting it
into the corresponding group with id “background” defined in “event_type_list”
attribute on the widget, but then the event is forced into the group “second”
as defined. The group “background” never appears on the timeline or as
a button, it is a way of sorting specific events as “background” events
(showing in the background of the timeline, commonly used for eras/epochs
or large stretches of time) within an existing non-background group.</p>
</body>
</html>

View File

@ -0,0 +1,26 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
</head>
<body class="ck-content">
<h1>This is a person note generating birth/death events</h1>
<p>This is a special type of note being fetched by the tags defined in the
“person_label_start” and “person_label_end” attributes on the widget, in
this example the tags are “person_start” and “person_end”.</p>
<p>This note does not have an “event_type” attribute, but there is a default
group defined on the widget with the attribute “event_type_default”, in
this case the value being “first”. The value set in “event_type_default”
must match one of the groups defined in the “event_type_list” attribute
on the widget.</p>
<p>The start and end dates of this person will generate a (Birth) and (Death)
event on the timeline rather than show as a range. the (birth) and (death)
are inserted after the note's title.</p>
</body>
</html>

View File

@ -0,0 +1,6 @@
// Add UI Button shortcut
api.addButtonToToolbar({
title: 'Timeline',
icon: 'bx bxs-calendar',
action: async () => api.activateNote(await api.startNote.getRelationValue('targetNote'))
});

124
Timeline Widget/render.html Normal file
View File

@ -0,0 +1,124 @@
<div id="timeline">
<div id="menu"></div>
</div>
<style>
/* CUSTOM CSS */
.session {
background-color : white;
border-color : white;
}
.astrological {
background-color: royalblue;
border-color : royalblue;
}
.player {
background-color : orange;
border-color: orange;
}
.omen {
background-color: blueviolet;
border-color : blueviolet;
}
.agent {
background-color: forestgreen;
border-color: forestgreen;
}
.local {
background-color : orangered;
border-color : orangered;
}
.global {
background-color : brown;
border-color : brown;
}
.era {
/*background-color : brown;*/
/*border-color : brown;*/
}
/* GENERAL CSS*/
body, html {
font-family: sans-serif;
}
/** Fix window scrolling bug**/
.note-detail.type-render, .note-detail-render {
height: 100%;
}
/* Wrapper */
#timeline {
height: 100%;
}
/* button menu */
#menu {
z-index: 5;
display: flex;
justify-content: center;
font-size: small;
}
#menu > input {
border-color: gray;
border-radius: 5px;
border-width: 1px;
background-color: #4c4949 !important;
margin: 0 2px;
}
#menu > input.toggled {
border-style: inset;
background-color: #272626 !important;
}
/* add visible dotted line to ranges */
.vis-item.vis-range {
border-style: dashed !important;
border-color: black !important;
border-width: 2px;
}
/* change text and label color */
.vis-text, .vis-label {
color: lightgray !important;
}
/* change link color */
.vis-item-content > a {
color: #1a1a1a;
font-size: small;
}
/* change current time marker color */
.vis-current-time {
background-color: red;
}
/* alternating column backgrounds */
.vis-time-axis .vis-grid.vis-odd {
background: #424242;
}
/* gray background in weekends, white text color */
.vis-time-axis .vis-grid.vis-saturday,
.vis-time-axis .vis-grid.vis-sunday {
background: gray;
}
.vis-time-axis .vis-text.vis-saturday,
.vis-time-axis .vis-text.vis-sunday {
color: dimgray;
}
/* interior grid color */
.vis-grid.vis-minor {
border-color: dimgray !important;
}
.vis-foreground .vis-group {
border-color: dimgray !important;
}
/* reduce padding */
.vis-item .vis-item-content {
padding: 2px;
}
</style>

11
index.html Normal file
View File

@ -0,0 +1,11 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<frameset cols="25%,75%">
<frame name="navigation" src="navigation.html">
<frame name="detail" src="Timeline%20Widget/render.html">
</frameset>
</html>

64
navigation.html Normal file
View File

@ -0,0 +1,64 @@
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<ul>
<li>Timeline Widget
<ul>
<li><a href="Timeline%20Widget/render.html" target="detail">render.html</a>
<ul>
<li><a href="Timeline%20Widget/1_render.html/script.js" target="detail">script.js</a>
<ul>
<li><a href="Timeline%20Widget/1_render.html/1_script.js/moment.min.js" target="detail">moment.min.js</a>
</li>
<li><a href="Timeline%20Widget/1_render.html/1_script.js/vis.min.js" target="detail">vis.min.js</a>
</li>
<li><a href="Timeline%20Widget/1_render.html/1_script.js/vis-timeline-graph2d.min.js"
target="detail">vis-timeline-graph2d.min.js</a>
</li>
<li><a href="Timeline%20Widget/1_render.html/1_script.js/vis.min.css" target="detail">vis.min.css</a>
</li>
<li><a href="Timeline%20Widget/1_render.html/1_script.js/vis-timeline-graph2d.min.css"
target="detail">vis-timeline-graph2d.min.css</a>
</li>
</ul>
</li>
</ul>
</li>
<li><a href="Timeline%20Widget/button.js" target="detail">button.js</a>
</li>
<li>EXAMPLES
<ul>
<li><a href="Timeline%20Widget/EXAMPLES/This%20event%20is%20in%20First.html"
target="detail">This event is in First</a>
</li>
<li><a href="Timeline%20Widget/EXAMPLES/This%20event%20is%20in%20Second%20and%20th.html"
target="detail">This event is in Second and this group is not visible by default</a>
</li>
<li><a href="Timeline%20Widget/EXAMPLES/This%20event%20is%20in%20Third%20and%20has.html"
target="detail">This event is in Third and has an end date</a>
</li>
<li><a href="Timeline%20Widget/EXAMPLES/This%20event%20is%20in%20a%20seperate%20gr.html"
target="detail">This event is in a seperate group then inserted into &quot;Third&quot;</a>
</li>
<li><a href="Timeline%20Widget/EXAMPLES/This%20event%20is%20in%20Third.html"
target="detail">This event is in Third</a>
</li>
<li><a href="Timeline%20Widget/EXAMPLES/This%20is%20a%20background%20event%20the.html"
target="detail">This is a background event then inserted into &quot;Second&quot;</a>
</li>
<li><a href="Timeline%20Widget/EXAMPLES/This%20is%20a%20person%20note%20generati.html"
target="detail">This is a person note generating birth/death events</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</body>
</html>

343
style.css Normal file
View File

@ -0,0 +1,343 @@
/* !!!!!! TRILIUM CUSTOM CHANGES !!!!!! */
.printed-content .ck-widget__selection-handle, .printed-content .ck-widget__type-around { /* gets rid of triangles: https://github.com/zadam/trilium/issues/1129 */
display: none;
}
/*
* CKEditor 5 (v27.1.0) content styles.
* Generated on Wed, 21 Apr 2021 07:00:21 GMT.
* For more information, check out https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/content-styles.html
*/
:root {
--ck-color-mention-background: hsla(341, 100%, 30%, 0.1);
--ck-color-mention-text: hsl(341, 100%, 30%);
--ck-highlight-marker-blue: hsl(201, 97%, 72%);
--ck-highlight-marker-green: hsl(120, 93%, 68%);
--ck-highlight-marker-pink: hsl(345, 96%, 73%);
--ck-highlight-marker-yellow: hsl(60, 97%, 73%);
--ck-highlight-pen-green: hsl(112, 100%, 27%);
--ck-highlight-pen-red: hsl(0, 85%, 49%);
--ck-image-style-spacing: 1.5em;
--ck-todo-list-checkmark-size: 16px;
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-yellow {
background-color: var(--ck-highlight-marker-yellow);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-green {
background-color: var(--ck-highlight-marker-green);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-pink {
background-color: var(--ck-highlight-marker-pink);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-blue {
background-color: var(--ck-highlight-marker-blue);
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-red {
color: var(--ck-highlight-pen-red);
background-color: transparent;
}
/* ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-green {
color: var(--ck-highlight-pen-green);
background-color: transparent;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-tiny {
font-size: .7em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-small {
font-size: .85em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-big {
font-size: 1.4em;
}
/* ckeditor5-font/theme/fontsize.css */
.ck-content .text-huge {
font-size: 1.8em;
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-side {
float: right;
margin-left: var(--ck-image-style-spacing);
max-width: 50%;
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left {
float: left;
margin-right: var(--ck-image-style-spacing);
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-center {
margin-left: auto;
margin-right: auto;
}
/* ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-right {
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* ckeditor5-code-block/theme/codeblock.css */
.ck-content pre {
padding: 1em;
color: hsl(0, 0%, 20.8%);
background: hsla(0, 0%, 78%, 0.3);
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
text-align: left;
direction: ltr;
tab-size: 4;
white-space: pre-wrap;
font-style: normal;
min-width: 200px;
}
/* ckeditor5-code-block/theme/codeblock.css */
.ck-content pre code {
background: unset;
padding: 0;
border-radius: 0;
}
/* ckeditor5-html-embed/theme/htmlembed.css */
.ck-content .raw-html-embed {
margin: 1em auto;
min-width: 15em;
font-style: normal;
}
/* ckeditor5-horizontal-line/theme/horizontalline.css */
.ck-content hr {
margin: 15px 0;
height: 4px;
background: hsl(0, 0%, 87%);
border: 0;
}
/* ckeditor5-image/theme/imagecaption.css */
.ck-content .image > figcaption {
display: table-caption;
caption-side: bottom;
word-break: break-word;
color: hsl(0, 0%, 20%);
background-color: hsl(0, 0%, 97%);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized {
max-width: 100%;
display: block;
box-sizing: border-box;
}
/* ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized img {
width: 100%;
}
/* ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized > figcaption {
display: block;
}
/* ckeditor5-image/theme/image.css */
.ck-content .image {
display: table;
clear: both;
text-align: center;
margin: 1em auto;
}
/* ckeditor5-image/theme/image.css */
.ck-content .image img {
display: block;
margin: 0 auto;
max-width: 100%;
min-width: 50px;
}
/* ckeditor5-language/theme/language.css */
.ck-content span[lang] {
font-style: italic;
}
/* ckeditor5-basic-styles/theme/code.css */
.ck-content code {
background-color: hsla(0, 0%, 78%, 0.3);
padding: .15em;
border-radius: 2px;
}
/* ckeditor5-block-quote/theme/blockquote.css */
.ck-content blockquote {
overflow: hidden;
padding-right: 1.5em;
padding-left: 1.5em;
margin-left: 0;
margin-right: 0;
font-style: italic;
border-left: solid 5px hsl(0, 0%, 80%);
}
/* ckeditor5-block-quote/theme/blockquote.css */
.ck-content[dir="rtl"] blockquote {
border-left: 0;
border-right: solid 5px hsl(0, 0%, 80%);
}
/* ckeditor5-table/theme/table.css */
.ck-content .table {
margin: 1em auto;
display: table;
}
/* ckeditor5-table/theme/table.css */
.ck-content .table table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
height: 100%;
border: 1px double hsl(0, 0%, 70%);
}
/* ckeditor5-table/theme/table.css */
.ck-content .table table td,
.ck-content .table table th {
min-width: 2em;
padding: .4em;
border: 1px solid hsl(0, 0%, 75%);
}
/* ckeditor5-table/theme/table.css */
.ck-content .table table th {
font-weight: bold;
background: hsla(0, 0%, 0%, 5%);
}
/* ckeditor5-table/theme/table.css */
.ck-content[dir="rtl"] .table th {
text-align: right;
}
/* ckeditor5-table/theme/table.css */
.ck-content[dir="ltr"] .table th {
text-align: left;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
position: relative;
clear: both;
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
content: '';
position: absolute;
border-bottom: 2px dashed hsl(0, 0%, 77%);
width: 100%;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break__label {
position: relative;
z-index: 1;
padding: .3em .6em;
display: block;
text-transform: uppercase;
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
font-size: 0.75em;
font-weight: bold;
color: hsl(0, 0%, 20%);
background: hsl(0, 0%, 100%);
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* ckeditor5-media-embed/theme/mediaembed.css */
.ck-content .media {
clear: both;
margin: 1em 0;
display: block;
min-width: 15em;
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list {
list-style: none;
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list li {
margin-bottom: 5px;
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list li .todo-list {
margin-top: 5px;
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label > input {
-webkit-appearance: none;
display: inline-block;
position: relative;
width: var(--ck-todo-list-checkmark-size);
height: var(--ck-todo-list-checkmark-size);
vertical-align: middle;
border: 0;
left: -25px;
margin-right: -15px;
right: 0;
margin-left: 0;
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label > input::before {
display: block;
position: absolute;
box-sizing: border-box;
content: '';
width: 100%;
height: 100%;
border: 1px solid hsl(0, 0%, 20%);
border-radius: 2px;
transition: 250ms ease-in-out box-shadow, 250ms ease-in-out background, 250ms ease-in-out border;
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label > input::after {
display: block;
position: absolute;
box-sizing: content-box;
pointer-events: none;
content: '';
left: calc( var(--ck-todo-list-checkmark-size) / 3 );
top: calc( var(--ck-todo-list-checkmark-size) / 5.3 );
width: calc( var(--ck-todo-list-checkmark-size) / 5.3 );
height: calc( var(--ck-todo-list-checkmark-size) / 2.6 );
border-style: solid;
border-color: transparent;
border-width: 0 calc( var(--ck-todo-list-checkmark-size) / 8 ) calc( var(--ck-todo-list-checkmark-size) / 8 ) 0;
transform: rotate(45deg);
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label > input[checked]::before {
background: hsl(126, 64%, 41%);
border-color: hsl(126, 64%, 41%);
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label > input[checked]::after {
border-color: hsl(0, 0%, 100%);
}
/* ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label .todo-list__label__description {
vertical-align: middle;
}
/* ckeditor5-mention/theme/mention.css */
.ck-content .mention {
background: var(--ck-color-mention-background);
color: var(--ck-color-mention-text);
}
@media print {
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
padding: 0;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
display: none;
}
}