Initial commit after history reset
This commit is contained in:
commit
34207f15b9
50 changed files with 635409 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.obsidian/plugins/
|
||||||
|
.obsidian/workspace.json
|
||||||
|
.copilot-index/
|
3
.obsidian/app.json
vendored
Normal file
3
.obsidian/app.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"alwaysUpdateLinks": true
|
||||||
|
}
|
1
.obsidian/appearance.json
vendored
Normal file
1
.obsidian/appearance.json
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
9
.obsidian/community-plugins.json
vendored
Normal file
9
.obsidian/community-plugins.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[
|
||||||
|
"templater-obsidian",
|
||||||
|
"obsidian-git",
|
||||||
|
"obsidian-linter",
|
||||||
|
"editing-toolbar",
|
||||||
|
"obsidian-auto-link-title",
|
||||||
|
"editor-width-slider",
|
||||||
|
"emoji-shortcodes"
|
||||||
|
]
|
30
.obsidian/core-plugins.json
vendored
Normal file
30
.obsidian/core-plugins.json
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"file-explorer": true,
|
||||||
|
"global-search": true,
|
||||||
|
"switcher": false,
|
||||||
|
"graph": true,
|
||||||
|
"backlink": true,
|
||||||
|
"canvas": false,
|
||||||
|
"outgoing-link": true,
|
||||||
|
"tag-pane": false,
|
||||||
|
"properties": false,
|
||||||
|
"page-preview": true,
|
||||||
|
"daily-notes": false,
|
||||||
|
"templates": false,
|
||||||
|
"note-composer": true,
|
||||||
|
"command-palette": true,
|
||||||
|
"slash-command": false,
|
||||||
|
"editor-status": true,
|
||||||
|
"bookmarks": false,
|
||||||
|
"markdown-importer": false,
|
||||||
|
"zk-prefixer": false,
|
||||||
|
"random-note": false,
|
||||||
|
"outline": true,
|
||||||
|
"word-count": true,
|
||||||
|
"slides": false,
|
||||||
|
"audio-recorder": false,
|
||||||
|
"workspaces": false,
|
||||||
|
"file-recovery": false,
|
||||||
|
"publish": false,
|
||||||
|
"sync": false
|
||||||
|
}
|
22
.obsidian/graph.json
vendored
Normal file
22
.obsidian/graph.json
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"collapse-filter": true,
|
||||||
|
"search": "",
|
||||||
|
"showTags": false,
|
||||||
|
"showAttachments": false,
|
||||||
|
"hideUnresolved": false,
|
||||||
|
"showOrphans": true,
|
||||||
|
"collapse-color-groups": true,
|
||||||
|
"colorGroups": [],
|
||||||
|
"collapse-display": true,
|
||||||
|
"showArrow": false,
|
||||||
|
"textFadeMultiplier": 0,
|
||||||
|
"nodeSizeMultiplier": 1,
|
||||||
|
"lineSizeMultiplier": 1,
|
||||||
|
"collapse-forces": true,
|
||||||
|
"centerStrength": 0.518713248970312,
|
||||||
|
"repelStrength": 10,
|
||||||
|
"linkStrength": 1,
|
||||||
|
"linkDistance": 250,
|
||||||
|
"scale": 1,
|
||||||
|
"close": false
|
||||||
|
}
|
12
.obsidian/plugins/editing-toolbar/main.js
vendored
Normal file
12
.obsidian/plugins/editing-toolbar/main.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
.obsidian/plugins/editing-toolbar/manifest.json
vendored
Normal file
11
.obsidian/plugins/editing-toolbar/manifest.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"id": "editing-toolbar",
|
||||||
|
"name": "Editing Toolbar",
|
||||||
|
"version": "2.4.16",
|
||||||
|
"minAppVersion": "0.14.0",
|
||||||
|
"description": "The Obsidian Editing Toolbar is modified from cmenu, which provides more powerful customization settings and has many built-in editing commands to be a MS Word-like toolbar editing experience.",
|
||||||
|
"author": "Cuman",
|
||||||
|
"authorUrl": "https://github.com/cumany/obsidian-editing-toolbar",
|
||||||
|
"fundingUrl": "https://github.com/cumany#thank-you-very-much-for-your-support",
|
||||||
|
"isDesktopOnly": false
|
||||||
|
}
|
971
.obsidian/plugins/editing-toolbar/styles.css
vendored
Normal file
971
.obsidian/plugins/editing-toolbar/styles.css
vendored
Normal file
|
@ -0,0 +1,971 @@
|
||||||
|
#cMenuToolbarModalBar {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
padding: 3px;
|
||||||
|
display: grid;
|
||||||
|
user-select: none;
|
||||||
|
border-radius: var(--radius-m);
|
||||||
|
position: absolute;
|
||||||
|
transition: 100ms cubic-bezier(0.92, -0.53, 0.65, 1.21);
|
||||||
|
-webkit-transition: 100ms cubic-bezier(0.92, -0.53, 0.65, 1.21);
|
||||||
|
min-width: fit-content;
|
||||||
|
justify-content: space-around;
|
||||||
|
z-index: var(--layer-modal);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarFlex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarFlex :is(.cMenuToolbarCommandItem, button[class^=cMenuToolbarCommandsubItem]) {
|
||||||
|
min-width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar .cMenuToolbarCommandItem {
|
||||||
|
margin: 2px;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
cursor: default;
|
||||||
|
padding: 5px 6px;
|
||||||
|
box-shadow: none;
|
||||||
|
margin-left: 3px;
|
||||||
|
margin-right: 3px;
|
||||||
|
position: relative;
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
font-size: initial !important;
|
||||||
|
background-color: var(--background-primary-alt);
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button[class^=cMenuToolbarCommandsubItem]>.subitem button:hover,
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button[class^=cMenuToolbarCommand]:hover,
|
||||||
|
#cMenuToolbarSecond:hover {
|
||||||
|
background-color: var(--background-modifier-hover) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #cMenuToolbarModalBar button.cMenuToolbarCommandItem svg {
|
||||||
|
width: 1.3em;
|
||||||
|
height: 1.3em;
|
||||||
|
} */
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------
|
||||||
|
cMenuToolbar SETTINGS BUTTONS
|
||||||
|
----------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.modal.mod-settings button:not(.mod-cta):not(.mod-warning).cMenuToolbarSettingsButton.cMenuToolbarSettingsButtonAdd,
|
||||||
|
button:not(.mod-cta):not(.mod-warning).cMenuToolbarSettingsButton.cMenuToolbarSettingsButtonAdd {
|
||||||
|
background-color: var(--interactive-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.mod-settings button:not(.mod-cta):not(.mod-warning).cMenuToolbarSettingsButton.cMenuToolbarSettingsButtonDelete,
|
||||||
|
button:not(.mod-cta):not(.mod-warning).cMenuToolbarSettingsButton.cMenuToolbarSettingsButtonDelete {
|
||||||
|
background-color: #989cab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.mod-settings button:not(.mod-cta):not(.mod-warning).cMenuToolbarSettingsButton.cMenuToolbarSettingsButtonRefresh,
|
||||||
|
button:not(.mod-cta):not(.mod-warning).cMenuToolbarSettingsButton.cMenuToolbarSettingsButtonRefresh {
|
||||||
|
background-color: var(--text-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.cMenuToolbarSettingsButton {
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: var(--radius-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------
|
||||||
|
cMenuToolbar SETTING ITEMS
|
||||||
|
----------------------------------------------------------------*/
|
||||||
|
.setting-item.cMenuToolbarCommandItem:first-child {
|
||||||
|
padding-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarCommandItem {
|
||||||
|
cursor: grab;
|
||||||
|
padding: 18px 0 18px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .sortable-fallback {
|
||||||
|
cursor: grabbing;
|
||||||
|
box-shadow: 0px 3px 32px rgb(31 38 135 / 15%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .sortable-grab {
|
||||||
|
cursor: grabbing !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .sortable-ghost {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .sortable-chosen {
|
||||||
|
cursor: grabbing;
|
||||||
|
padding: 18px 0 18px 18px;
|
||||||
|
background-color: var(--color-base-10, --background-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .sortable-drag {
|
||||||
|
cursor: grabbing;
|
||||||
|
box-shadow: 0px 3px 32px rgb(31 38 135 / 15%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer {
|
||||||
|
border-top: 1px solid var(--background-modifier-border);
|
||||||
|
border-bottom: 1px solid var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------
|
||||||
|
cMenuToolbar CLASS CHANGES
|
||||||
|
----------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarDefaultAesthetic {
|
||||||
|
border: 1px solid var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarDefaultAesthetic:not(.top) :is(.cMenuToolbarCommandItem, button[class^=cMenuToolbarCommandsubItem]) {
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarDefaultAesthetic:not(.top) button[class^=cMenuToolbarCommandsubItem] {
|
||||||
|
margin: auto;
|
||||||
|
padding: 6px;
|
||||||
|
box-shadow: none;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarDefaultAesthetic {
|
||||||
|
background-color: var(--color-base-10, --background-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarGlassAesthetic,
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarGlassAesthetic~#cMenuToolbarPopoverBar {
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
-webkit-backdrop-filter: blur(6px);
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarGlassAesthetic:not(.top) button[class^=cMenuToolbarCommandsubItem] {
|
||||||
|
margin: auto;
|
||||||
|
padding: 0px;
|
||||||
|
box-shadow: none;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
font-size: 10px;
|
||||||
|
margin-right: 0px;
|
||||||
|
position: relative;
|
||||||
|
background-color: transparent;
|
||||||
|
place-items: center;
|
||||||
|
clear: both;
|
||||||
|
max-width: 28px;
|
||||||
|
max-height: 23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------
|
||||||
|
cMenuToolbar ICONS
|
||||||
|
----------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.cMenuToolbarIconPick {
|
||||||
|
line-height: normal;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarIconPick svg {
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------
|
||||||
|
cMenuToolbar STATUS BAR MENU
|
||||||
|
----------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu {
|
||||||
|
text-align: center;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu .menu-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu .menu-item.settings-item {
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1;
|
||||||
|
border-radius: var(--radius-m);
|
||||||
|
height: auto;
|
||||||
|
padding: 8px 5px 0px 5px;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: fit-content;
|
||||||
|
color: var(--text-faint);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu .menu-item.settings-item .menu-item-title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu .menu-item:hover,
|
||||||
|
.cMenuToolbar-statusbar-menu .menu-item .selected:hover,
|
||||||
|
.cMenuToolbar-statusbar-menu .menu-item.selected:not(.is-disabled):not(.is-label) {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu .menu-item-title {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu .slider {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu .menu-item.buttonitem {
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu .menu-item.buttonitem button.cMenuToolbarSettingsButton {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu .menu-item-icon svg path {
|
||||||
|
fill: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu .menu-item-icon svg {
|
||||||
|
stroke: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu>.menu-item:is([data-section="ButtonAdd"]) {
|
||||||
|
display: inline-flex;
|
||||||
|
padding: 0px 0px 5px 5px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu>.menu-item {
|
||||||
|
display: inline-flex;
|
||||||
|
padding: 0px 0px 5px 5px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-menu>.menu-item:is([data-section="ButtonAdd"]) .menu-item-icon {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------
|
||||||
|
cMenuToolbar STATUS BAR BUTTONS
|
||||||
|
----------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-button {
|
||||||
|
cursor: default;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbar-statusbar-button svg {
|
||||||
|
display: flex;
|
||||||
|
width: 1.3em;
|
||||||
|
height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------
|
||||||
|
cMenuToolbar SUPPORT
|
||||||
|
---------------------------------------------`-------------------*/
|
||||||
|
|
||||||
|
.cDonationSection {
|
||||||
|
width: 60%;
|
||||||
|
height: 50vh;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar {
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
border: none;
|
||||||
|
backdrop-filter: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarGlassAesthetic .cMenuToolbarCommandItem {
|
||||||
|
|
||||||
|
background-color: #ffffff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarGlassAesthetic [class^=cMenuToolbarCommandsubItem] {
|
||||||
|
|
||||||
|
background-color: #ffffff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar .cMenuToolbarCommandItem {
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* #cMenuToolbarModalBar.cMenuToolbarTinyAesthetic .cMenuToolbarCommandItem svg{
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
div.modal-container.cMenuToolbar-Modal:not(.changename) .modal-bg {
|
||||||
|
background-color: transparent !important;
|
||||||
|
backdrop-filter: none !important;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-container.cMenuToolbar-Modal:not(.changename) .modal {
|
||||||
|
padding: 10px 30px;
|
||||||
|
min-width: 130px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 2em;
|
||||||
|
right: 0.5em;
|
||||||
|
background-color: rgb(var(--mono-rgb-0), 0.8);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-container.cMenuToolbar-Modal .modal-title {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-container.cMenuToolbar-Modal .modal input[type='range'] {
|
||||||
|
width: 90%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
body.theme-dark .modal-container.cMenuToolbar-Modal .modal input[type='range'] {
|
||||||
|
background-color: var(--background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*tiny 样式*/
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarTinyAesthetic {
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
border: 1px solid var(--background-modifier-border-hover);
|
||||||
|
backdrop-filter: none;
|
||||||
|
background-color: var(--background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.cMenuToolbarTinyAesthetic .cMenuToolbarCommandItem {
|
||||||
|
margin: auto;
|
||||||
|
padding: 0px;
|
||||||
|
box-shadow: none;
|
||||||
|
margin-left: 0px;
|
||||||
|
margin-right: 0px;
|
||||||
|
position: relative;
|
||||||
|
background-color: transparent;
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar .cMenuToolbarCommandItem {
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
padding: 0px;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-right: 0px;
|
||||||
|
position: relative;
|
||||||
|
background-color: transparent;
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar).cMenuToolbarTinyAesthetic:not(.top) button[class^=cMenuToolbarCommandsubItem] {
|
||||||
|
margin: auto;
|
||||||
|
padding: 0px;
|
||||||
|
box-shadow: none;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
font-size: 10px;
|
||||||
|
margin-right: 0px;
|
||||||
|
position: relative;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
place-items: center;
|
||||||
|
clear: both;
|
||||||
|
max-width: 28px;
|
||||||
|
max-height: 18px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
button[class^=cMenuToolbarCommandsubItem]::after {
|
||||||
|
|
||||||
|
content: url("data:image/svg+xml,%3Csvg width='4' height='4' version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024' enable-background='new 0 0 1024 1024' xml:space='preserve'%3E%3Cpath fill='%23666' d='M13.24 80.11 l461.75 560.8 q14.56 16.02 36.41 16.02 q21.85 0 36.42 -16.02 l463.2 -560.8 q10.2 -10.19 12.38 -24.75 q2.18 -14.57 -3.64 -27.68 q-5.82 -13.11 -18.21 -20.39 q-12.39 -7.29 -26.95 -7.29 l-924.95 0 q-20.4 0 -34.23 13.11 q-13.84 13.11 -15.29 32.77 q-1.46 19.66 13.11 34.23 Z'/%3E%3C/svg%3E");
|
||||||
|
|
||||||
|
margin-left: 1px;
|
||||||
|
margin-top: 6px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button[class^=cMenuToolbarCommandsubItem]>.subitem {
|
||||||
|
background-color: var(--color-base-10, --background-primary);
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
border: 1px solid var(--background-modifier-border);
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
z-index: var(--layer-menu);
|
||||||
|
user-select: none;
|
||||||
|
transform: translateY(90%) translateX(0%);
|
||||||
|
-webkit-transform: translateY(90%) translateX(0%);
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button[class^=cMenuToolbarCommandsubItem]>.subitem svg {
|
||||||
|
max-width: 1.3em;
|
||||||
|
max-height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button[class^=cMenuToolbarCommandsubItem]>.subitem button.menu-item {
|
||||||
|
background-color: transparent;
|
||||||
|
line-height: 2em;
|
||||||
|
display: inline-flex;
|
||||||
|
box-shadow: none;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button[class^=cMenuToolbarCommandsubItem]>.subitem .menu-item {
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: 2px;
|
||||||
|
padding: 0px 4px 0px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button[class^=cMenuToolbarCommandsubItem]>.subitem {
|
||||||
|
|
||||||
|
visibility: hidden;
|
||||||
|
transition: all 0.3s linear;
|
||||||
|
-webkit-transition: all 0.3s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button.cMenuToolbarCommandsubItem-font-color .triangle-icon {
|
||||||
|
position: absolute;
|
||||||
|
margin-left: 18px;
|
||||||
|
bottom: 8%;
|
||||||
|
background-size: 4px 4px;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg width='4' height='4' version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024' enable-background='new 0 0 1024 1024' xml:space='preserve'%3E%3Cpath fill='%23666' d='M13.24 80.11 l461.75 560.8 q14.56 16.02 36.41 16.02 q21.85 0 36.42 -16.02 l463.2 -560.8 q10.2 -10.19 12.38 -24.75 q2.18 -14.57 -3.64 -27.68 q-5.82 -13.11 -18.21 -20.39 q-12.39 -7.29 -26.95 -7.29 l-924.95 0 q-20.4 0 -34.23 13.11 q-13.84 13.11 -15.29 32.77 q-1.46 19.66 13.11 34.23 Z'/%3E%3C/svg%3E");
|
||||||
|
|
||||||
|
width: 16px;
|
||||||
|
height: 20px;
|
||||||
|
background-position: center;
|
||||||
|
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
min-width: unset;
|
||||||
|
border-left: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar):not(.top) button.cMenuToolbarCommandsubItem-font-color .triangle-icon {
|
||||||
|
margin-left: 16px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar):not(.top) button.cMenuToolbarCommandsubItem-font-color .x-color-picker-wrapper {
|
||||||
|
top: auto;
|
||||||
|
bottom: calc(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar):not(.top) button[class^=cMenuToolbarCommandsubItem]:not(.cMenuToolbarSecond)>.subitem {
|
||||||
|
bottom: calc(100% + 2.8em);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button.cMenuToolbarCommandsubItem-font-color .subitem {
|
||||||
|
visibility: hidden;
|
||||||
|
transition: all 0.3s linear;
|
||||||
|
-webkit-transition: all 0.3s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button.cMenuToolbarCommandsubItem-font-color .subitem:hover {
|
||||||
|
visibility: visible;
|
||||||
|
transition: all 0.3s linear;
|
||||||
|
-webkit-transition: all 0.3s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button[class^=cMenuToolbarCommandsubItem]:hover>.subitem {
|
||||||
|
|
||||||
|
visibility: visible;
|
||||||
|
transition: all 0.3s linear;
|
||||||
|
-webkit-transition: all 0.3s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* :is(#cMenuToolbarModalBar,#cMenuToolbarPopoverBar) button[class^=cMenuToolbarCommandsubItem] >.subitem:hover {
|
||||||
|
visibility:visible;
|
||||||
|
transition: all 0.3s linear;
|
||||||
|
-webkit-transition: all 0.3s linear;
|
||||||
|
} */
|
||||||
|
|
||||||
|
.cMenuToolbarCommandsubItem-font-color button {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbarCommandItem .setting-item-info {
|
||||||
|
|
||||||
|
flex: 30%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbarSettingsTabsContainer_sub {
|
||||||
|
border-left: 1px solid var(--background-modifier-border);
|
||||||
|
flex-flow: column;
|
||||||
|
min-height: 45px;
|
||||||
|
display: flex;
|
||||||
|
padding: 0;
|
||||||
|
margin-left: 10px;
|
||||||
|
flex: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbarSettingsTabsContainer_sub:empty {
|
||||||
|
border: 2px dashed rgba(var(--interactive-accent-rgb), 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbarSettingsTabsContainer_sub:empty::before {
|
||||||
|
content: "✖️Drag it here";
|
||||||
|
margin: auto;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbarSettingsTabsContainer_sub .cMenuToolbarCommandItem {
|
||||||
|
flex: auto;
|
||||||
|
margin-left: 2em;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbarSettingsTabsContainer_sub .setting-item-control {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbarCommandSubItem>.setting-item-info {
|
||||||
|
flex: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarCommandSubItem>.setting-item-control {
|
||||||
|
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.mod-settings button:not(.mod-cta):not(.mod-warning).cMenuToolbarSettingsButton.cMenuToolbarSettingsButtonaddsub {
|
||||||
|
background-color: var(--background-secondary-alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.setting-item button.cMenuToolbarSettingsIcon {
|
||||||
|
display: block;
|
||||||
|
transform: translateX(-30%);
|
||||||
|
-webkit-transform: translateX(-30%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-item button.cMenuToolbarSettingsIcon:empty::before {
|
||||||
|
content: "❗";
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-item button.cMenuToolbarSettingsIcon svg {
|
||||||
|
max-width: 1.5em;
|
||||||
|
max-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.mod-settings button:not(.mod-cta):not(.mod-warning).cMenuToolbarSettingsIcon:hover {
|
||||||
|
background-color: var(--interactive-accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.mod-settings button:not(.mod-cta):not(.mod-warning).cMenuToolbarSettingsIcon {
|
||||||
|
background-color: transparent;
|
||||||
|
box-shadow: 0 1px 1px 0px var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 781px) {
|
||||||
|
.cMenuToolbar-Modal .wideInputPromptInputEl {
|
||||||
|
width: 40rem;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 20rem;
|
||||||
|
background-color: rgb(var(--mono-rgb-0), 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarcustomIcon svg {
|
||||||
|
max-width: 1.3em;
|
||||||
|
max-height: 1.3em;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsButton svg {
|
||||||
|
max-width: 1.3em;
|
||||||
|
max-height: 1.3em;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmdr-page-header {
|
||||||
|
min-width: 1em;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.x-color-picker-wrapper {
|
||||||
|
right: 0px;
|
||||||
|
top: 1.8em;
|
||||||
|
min-width: 1px;
|
||||||
|
padding: 10px;
|
||||||
|
box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14);
|
||||||
|
position: absolute;
|
||||||
|
width: fit-content;
|
||||||
|
font-weight: 400;
|
||||||
|
font-family: Source Sans Pro, sans-serif;
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
background-color: var(--color-base-10, --background-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-source-view.mod-cm6 .x-color-picker-wrapper table.x-color-picker-table#x-color-picker-table {
|
||||||
|
width: unset;
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 6px;
|
||||||
|
margin: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-color-picker-wrapper table.x-color-picker-table#x-backgroundcolor-picker-table {
|
||||||
|
width: unset;
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 6px;
|
||||||
|
margin: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-color-picker-wrapper .x-color-picker-table th {
|
||||||
|
border: 0;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: normal;
|
||||||
|
background: transparent !important;
|
||||||
|
color: #718096;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-color-picker-wrapper #x-color-picker-table td {
|
||||||
|
font-size: 1px;
|
||||||
|
padding: 9px;
|
||||||
|
cursor: default;
|
||||||
|
border: solid 1px var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-color-picker-wrapper #x-backgroundcolor-picker-table td {
|
||||||
|
font-size: 1px;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 9px;
|
||||||
|
cursor: default;
|
||||||
|
border: solid 1px var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-color-picker-wrapper .x-color-picker-table tr td:hover {
|
||||||
|
filter: brightness(1.2);
|
||||||
|
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .x-color-picker-wrapper tbody>tr:hover {
|
||||||
|
background-color: transparent !important;
|
||||||
|
} */
|
||||||
|
|
||||||
|
/**top**/
|
||||||
|
#cMenuToolbarModalBar.top {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
position: relative;
|
||||||
|
height: 38px;
|
||||||
|
align-items: center;
|
||||||
|
transition: all 0.2s linear;
|
||||||
|
-webkit-transition: all 0.2s linear;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.top.autohide {
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.5s linear;
|
||||||
|
-webkit-transition: all 0.5s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.top.autohide:hover {
|
||||||
|
opacity: 1;
|
||||||
|
transition: all 1s linear;
|
||||||
|
-webkit-transition: all 1s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar.top :is(.cMenuToolbarCommandItem, button[class^=cMenuToolbarCommandsubItem]):not(.cMenuToolbar-Divider-Line) {
|
||||||
|
font-size: 10px;
|
||||||
|
margin-right: 0px;
|
||||||
|
clear: both;
|
||||||
|
opacity: 1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: default;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
transition: all 0.2s linear;
|
||||||
|
-webkit-transition: all 0.2s linear;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #cMenuToolbarModalBar.top button.cMenuToolbarCommandItem:hover {
|
||||||
|
background-color: var(--interactive-hover);
|
||||||
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#cMenuToolbarPopoverBar {
|
||||||
|
|
||||||
|
padding: 0 10px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
width: fit-content;
|
||||||
|
z-index: var(--layer-popover);
|
||||||
|
background-color: var(--color-base-10, --background-primary);
|
||||||
|
background-clip: padding-box;
|
||||||
|
border-radius: var(--radius-m);
|
||||||
|
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: 25px;
|
||||||
|
transition: all 0.1s linear;
|
||||||
|
-webkit-transition: all 0.1s linear;
|
||||||
|
margin-top: 32px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarPopoverBar :is(.cMenuToolbarCommandItem, button[class^=cMenuToolbarCommandsubItem]) {
|
||||||
|
|
||||||
|
height: 26px;
|
||||||
|
margin-left: 4px;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-right: 4px;
|
||||||
|
clear: both;
|
||||||
|
opacity: 1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: default;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
transition: all 0.2s linear;
|
||||||
|
-webkit-transition: all 0.2s linear;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#cMenuToolbarModalBar .more-menu {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: none;
|
||||||
|
margin-left: 4px;
|
||||||
|
border-left: 1px inset var(--background-modifier-form-field);
|
||||||
|
height: 24px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Divider-Line**/
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbar-Divider-Line {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
line-height: 0px;
|
||||||
|
border-left: 200px solid rgba(var(--interactive-accent-rgb), 0.05);
|
||||||
|
border-right: 200px solid rgba(var(--interactive-accent-rgb), 0.05);
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(var(--interactive-accent-rgb), 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbar-Divider-Line .setting-item-control button:not(:last-child) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbar-Divider-Line .setting-item-info {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbar-Divider-Line .setting-item-control {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbar-Divider-Line .setting-item-info .setting-item-name {
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbar-Divider-Line .setting-item-control button:last-child {
|
||||||
|
padding: 0;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbar-Divider-Line .setting-item-control button:last-child svg {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cMenuToolbarSettingsTabsContainer .cMenuToolbar-Divider-Line .setting-item-control button:last-child::before {
|
||||||
|
content: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M511.674077 66.707284c-246.52265 0-446.347744 199.835328-446.347744 446.347744s199.825095 446.356954 446.347744 446.356954c246.50423 0 446.348768-199.844537 446.348768-446.356954S758.177284 66.707284 511.674077 66.707284zM744.967424 667.159826c21.8701 21.8701 21.8701 57.310264 0 79.199807-21.8701 21.851681-57.30924 21.851681-79.198783-0.019443L511.674077 592.264045 357.56007 746.359632c-21.8701 21.8701-57.30924 21.851681-79.17934-0.019443s-21.8701-57.290821 0-79.160921L432.493713 513.065262 278.379707 358.950232c-21.8701-21.86089-21.8701-57.328683 0-79.18855 21.8701-21.87931 57.30924-21.87931 79.17934 0l154.114007 154.104797 154.095587-154.104797c21.889543-21.87931 57.32766-21.87931 79.198783-0.010233 21.8701 21.8701 21.8701 57.348126 0 79.207993L590.89128 513.065262 744.967424 667.159826z' fill='%23666666'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button.cMenuToolbar-Divider-Line
|
||||||
|
{
|
||||||
|
min-width: unset;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: inline-flex;
|
||||||
|
width: 0.6px;
|
||||||
|
background-color: var(--background-modifier-border);
|
||||||
|
height: 22px;
|
||||||
|
opacity: 0.8;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button[class^=cMenuToolbarCommandsubItem]>.subitem button.menu-item.cMenuToolbar-Divider-Line
|
||||||
|
{ min-width: unset;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: inline-flex;
|
||||||
|
width: 0.6px;
|
||||||
|
background-color: var(--background-modifier-border);
|
||||||
|
height: 22px;
|
||||||
|
opacity: 0.8;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark :is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button.cMenuToolbar-Divider-Line {
|
||||||
|
|
||||||
|
background-color: #4f4f5188;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(#cMenuToolbarModalBar, #cMenuToolbarPopoverBar) button.cMenuToolbar-Divider-Line svg {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-tabs.mod-stacked .workspace-tab-header:not(.is-active)+.workspace-leaf #cMenuToolbarModalBar {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(.cm-line, p) span[style^="background:rgba"] {
|
||||||
|
color: var(--text-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(.cm-line, p) span[style^="background:#"] {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-item.toolbar-cta:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -10%;
|
||||||
|
width: 104%;
|
||||||
|
left: -2%;
|
||||||
|
height: 120%;
|
||||||
|
outline: 2px solid var(--text-accent);
|
||||||
|
border-radius: 1em;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-item.toolbar-cta {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-pickr .pcr-last-color,
|
||||||
|
.pickr .pcr-button{
|
||||||
|
background-color: var(--pcr-color);
|
||||||
|
}
|
||||||
|
.toolbar-pickr .pcr-interaction :not(:is(input.pcr-save,.pcr-result)){
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
.toolbar-pickr .pcr-swatches {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
.toolbar-pickr {
|
||||||
|
display:flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div[data-type="thino_view"] .memo-editor-wrapper:has(#cMenuToolbarModalBar)
|
||||||
|
{
|
||||||
|
padding-top:0;
|
||||||
|
padding-left:0;
|
||||||
|
padding-right:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[data-type="thino_view"] .memo-editor-wrapper:has(#cMenuToolbarModalBar) .common-tools-wrapper
|
||||||
|
{
|
||||||
|
padding-left:16px;
|
||||||
|
padding-right:16px;
|
||||||
|
}
|
||||||
|
div[data-type="thino_view"] .memo-editor-wrapper #cMenuToolbarModalBar ~ .cm-editor{
|
||||||
|
padding-top:38px;
|
||||||
|
padding-bottom:0px;
|
||||||
|
padding-left:16px;
|
||||||
|
padding-right:16px;
|
||||||
|
}
|
||||||
|
div[data-type=thino_view] .common-editor-wrapper .common-editor-inputer:has(#cMenuToolbarModalBar)
|
||||||
|
{
|
||||||
|
min-height:118px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[data-type=thino_view] #cMenuToolbarModalBar.top
|
||||||
|
{
|
||||||
|
position:absolute!important;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
body.auto-hide-header .workspace-tab-header-container:hover + .workspace-tab-container .workspace-leaf .workspace-leaf-content>.view-header {
|
||||||
|
margin-top: 0;
|
||||||
|
transition: all 0.1s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.auto-hide-header .workspace-tab-header-container + .workspace-tab-container .workspace-leaf .workspace-leaf-content>.view-header:hover {
|
||||||
|
margin-top: 0;
|
||||||
|
transition: all 0.6s linear;
|
||||||
|
}
|
||||||
|
body.auto-hide-header .workspace-tab-header-container + .workspace-tab-container .workspace-leaf .workspace-leaf-content>.view-header {
|
||||||
|
margin-top: -40px;
|
||||||
|
transition: all 0.6s linear;
|
||||||
|
}
|
1091
.obsidian/plugins/github-copilot/copilot-1.0.11/agent.cjs
vendored
Normal file
1091
.obsidian/plugins/github-copilot/copilot-1.0.11/agent.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
100256
.obsidian/plugins/github-copilot/copilot-1.0.11/resources/cl100k_base.tiktoken
vendored
Normal file
100256
.obsidian/plugins/github-copilot/copilot-1.0.11/resources/cl100k_base.tiktoken
vendored
Normal file
File diff suppressed because it is too large
Load diff
100256
.obsidian/plugins/github-copilot/copilot-1.0.11/resources/cl100k_base.tiktoken.noindex
vendored
Normal file
100256
.obsidian/plugins/github-copilot/copilot-1.0.11/resources/cl100k_base.tiktoken.noindex
vendored
Normal file
File diff suppressed because it is too large
Load diff
199998
.obsidian/plugins/github-copilot/copilot-1.0.11/resources/o200k_base.tiktoken
vendored
Normal file
199998
.obsidian/plugins/github-copilot/copilot-1.0.11/resources/o200k_base.tiktoken
vendored
Normal file
File diff suppressed because it is too large
Load diff
199998
.obsidian/plugins/github-copilot/copilot-1.0.11/resources/o200k_base.tiktoken.noindex
vendored
Normal file
199998
.obsidian/plugins/github-copilot/copilot-1.0.11/resources/o200k_base.tiktoken.noindex
vendored
Normal file
File diff suppressed because it is too large
Load diff
22
.obsidian/plugins/github-copilot/data.json
vendored
Normal file
22
.obsidian/plugins/github-copilot/data.json
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"nodePath": "/usr/bin/node",
|
||||||
|
"enabled": true,
|
||||||
|
"hotkeys": {
|
||||||
|
"accept": "Tab",
|
||||||
|
"cancel": "Escape",
|
||||||
|
"request": "Cmd-Shift-/",
|
||||||
|
"partial": "Cmd-Shift-.",
|
||||||
|
"next": "Cmd-Shift-ArrowDown",
|
||||||
|
"disable": "Cmd-Shift-ArrowRight"
|
||||||
|
},
|
||||||
|
"suggestionDelay": 500,
|
||||||
|
"debug": false,
|
||||||
|
"onlyOnHotkey": false,
|
||||||
|
"onlyInCodeBlock": false,
|
||||||
|
"exclude": [],
|
||||||
|
"deviceSpecificSettings": [
|
||||||
|
"nodePath"
|
||||||
|
],
|
||||||
|
"useDeviceSpecificSettings": false,
|
||||||
|
"proxy": ""
|
||||||
|
}
|
28626
.obsidian/plugins/github-copilot/main.js
vendored
Normal file
28626
.obsidian/plugins/github-copilot/main.js
vendored
Normal file
File diff suppressed because one or more lines are too long
10
.obsidian/plugins/github-copilot/manifest.json
vendored
Normal file
10
.obsidian/plugins/github-copilot/manifest.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"id": "github-copilot",
|
||||||
|
"name": "Github Copilot",
|
||||||
|
"version": "1.0.11",
|
||||||
|
"minAppVersion": "1.5.12",
|
||||||
|
"description": "Implement suggestions from Github Copilot directly in your editor.",
|
||||||
|
"author": "Vasseur Pierre-Adrien",
|
||||||
|
"authorUrl": "https://github.com/Pierrad",
|
||||||
|
"isDesktopOnly": true
|
||||||
|
}
|
107
.obsidian/plugins/github-copilot/styles.css
vendored
Normal file
107
.obsidian/plugins/github-copilot/styles.css
vendored
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
.copilot-status-bar-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
& svg {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.copilot-status-bar-loading-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-inline-suggestion {
|
||||||
|
opacity: 0.4;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-inline-suggestion-box {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
background: var(--background-primary);
|
||||||
|
border: 1px solid var(--background-modifier-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 0 0 1px var(--background-modifier-border);
|
||||||
|
padding: 0.3rem 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--text-primary);
|
||||||
|
min-width: 50px;
|
||||||
|
max-width: 70px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-modal-auth-box {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-settings-note {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
padding-top: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-settings-hotkeys-container {
|
||||||
|
padding: 0.75em 0;
|
||||||
|
border-top: 1px solid var(--background-modifier-border);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-settings-hotkeys-container .setting-item {
|
||||||
|
border-top: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-settings-save-button {
|
||||||
|
width: fit-content;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-settings-item-control {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-settings-suggestion-container {
|
||||||
|
top: 120%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-settings-suggestion-item:hover {
|
||||||
|
background: var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-settings-suggestion-item-active {
|
||||||
|
background: var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-settings-exclude-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-settings-exclude-item-remove {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
771
.obsidian/plugins/obsidian-auto-link-title/main.js
vendored
Normal file
771
.obsidian/plugins/obsidian-auto-link-title/main.js
vendored
Normal file
|
@ -0,0 +1,771 @@
|
||||||
|
/*
|
||||||
|
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
|
||||||
|
if you want to view the source visit the plugins github repository
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var obsidian = require('obsidian');
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Copyright (c) Microsoft Corporation.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
***************************************************************************** */
|
||||||
|
|
||||||
|
function __awaiter(thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
||||||
|
var e = new Error(message);
|
||||||
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_SETTINGS = {
|
||||||
|
regex: /^(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})$/i,
|
||||||
|
lineRegex: /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi,
|
||||||
|
linkRegex: /^\[([^\[\]]*)\]\((https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})\)$/i,
|
||||||
|
linkLineRegex: /\[([^\[\]]*)\]\((https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})\)/gi,
|
||||||
|
imageRegex: /\.(gif|jpe?g|tiff?|png|webp|bmp|tga|psd|ai)$/i,
|
||||||
|
enhanceDefaultPaste: true,
|
||||||
|
shouldPreserveSelectionAsTitle: false,
|
||||||
|
enhanceDropEvents: true,
|
||||||
|
websiteBlacklist: "",
|
||||||
|
maximumTitleLength: 0,
|
||||||
|
useNewScraper: false,
|
||||||
|
linkPreviewApiKey: "",
|
||||||
|
useBetterPasteId: false,
|
||||||
|
};
|
||||||
|
class AutoLinkTitleSettingTab extends obsidian.PluginSettingTab {
|
||||||
|
constructor(app, plugin) {
|
||||||
|
super(app, plugin);
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
display() {
|
||||||
|
let { containerEl } = this;
|
||||||
|
containerEl.empty();
|
||||||
|
new obsidian.Setting(containerEl)
|
||||||
|
.setName("Enhance Default Paste")
|
||||||
|
.setDesc("Fetch the link title when pasting a link in the editor with the default paste command")
|
||||||
|
.addToggle((val) => val
|
||||||
|
.setValue(this.plugin.settings.enhanceDefaultPaste)
|
||||||
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
console.log(value);
|
||||||
|
this.plugin.settings.enhanceDefaultPaste = value;
|
||||||
|
yield this.plugin.saveSettings();
|
||||||
|
})));
|
||||||
|
new obsidian.Setting(containerEl)
|
||||||
|
.setName("Enhance Drop Events")
|
||||||
|
.setDesc("Fetch the link title when drag and dropping a link from another program")
|
||||||
|
.addToggle((val) => val
|
||||||
|
.setValue(this.plugin.settings.enhanceDropEvents)
|
||||||
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
console.log(value);
|
||||||
|
this.plugin.settings.enhanceDropEvents = value;
|
||||||
|
yield this.plugin.saveSettings();
|
||||||
|
})));
|
||||||
|
new obsidian.Setting(containerEl)
|
||||||
|
.setName("Maximum title length")
|
||||||
|
.setDesc("Set the maximum length of the title. Set to 0 to disable.")
|
||||||
|
.addText((val) => val
|
||||||
|
.setValue(this.plugin.settings.maximumTitleLength.toString(10))
|
||||||
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const titleLength = Number(value);
|
||||||
|
this.plugin.settings.maximumTitleLength =
|
||||||
|
isNaN(titleLength) || titleLength < 0 ? 0 : titleLength;
|
||||||
|
yield this.plugin.saveSettings();
|
||||||
|
})));
|
||||||
|
new obsidian.Setting(containerEl)
|
||||||
|
.setName("Preserve selection as title")
|
||||||
|
.setDesc("Whether to prefer selected text as title over fetched title when pasting")
|
||||||
|
.addToggle((val) => val
|
||||||
|
.setValue(this.plugin.settings.shouldPreserveSelectionAsTitle)
|
||||||
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
console.log(value);
|
||||||
|
this.plugin.settings.shouldPreserveSelectionAsTitle = value;
|
||||||
|
yield this.plugin.saveSettings();
|
||||||
|
})));
|
||||||
|
new obsidian.Setting(containerEl)
|
||||||
|
.setName("Website Blacklist")
|
||||||
|
.setDesc("List of strings (comma separated) that disable autocompleting website titles. Can be URLs or arbitrary text.")
|
||||||
|
.addTextArea((val) => val
|
||||||
|
.setValue(this.plugin.settings.websiteBlacklist)
|
||||||
|
.setPlaceholder("localhost, tiktok.com")
|
||||||
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
this.plugin.settings.websiteBlacklist = value;
|
||||||
|
yield this.plugin.saveSettings();
|
||||||
|
})));
|
||||||
|
new obsidian.Setting(containerEl)
|
||||||
|
.setName("Use New Scraper")
|
||||||
|
.setDesc("Use experimental new scraper, seems to work well on desktop but not mobile.")
|
||||||
|
.addToggle((val) => val
|
||||||
|
.setValue(this.plugin.settings.useNewScraper)
|
||||||
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
console.log(value);
|
||||||
|
this.plugin.settings.useNewScraper = value;
|
||||||
|
yield this.plugin.saveSettings();
|
||||||
|
})));
|
||||||
|
new obsidian.Setting(containerEl)
|
||||||
|
.setName("Use Better Fetching Placeholder")
|
||||||
|
.setDesc("Use a more readable placeholder when fetching the title of a link.")
|
||||||
|
.addToggle((val) => val
|
||||||
|
.setValue(this.plugin.settings.useBetterPasteId)
|
||||||
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
console.log(value);
|
||||||
|
this.plugin.settings.useBetterPasteId = value;
|
||||||
|
yield this.plugin.saveSettings();
|
||||||
|
})));
|
||||||
|
new obsidian.Setting(containerEl)
|
||||||
|
.setName("LinkPreview API Key")
|
||||||
|
.setDesc("API key for the LinkPreview.net service. Get one at https://my.linkpreview.net/access_keys")
|
||||||
|
.addText((text) => text
|
||||||
|
.setValue(this.plugin.settings.linkPreviewApiKey || "")
|
||||||
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const trimmedValue = value.trim();
|
||||||
|
if (trimmedValue.length > 0 && trimmedValue.length !== 32) {
|
||||||
|
new obsidian.Notice("LinkPreview API key must be 32 characters long");
|
||||||
|
this.plugin.settings.linkPreviewApiKey = "";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.plugin.settings.linkPreviewApiKey = trimmedValue;
|
||||||
|
}
|
||||||
|
yield this.plugin.saveSettings();
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CheckIf {
|
||||||
|
static isMarkdownLinkAlready(editor) {
|
||||||
|
let cursor = editor.getCursor();
|
||||||
|
// Check if the characters before the url are ]( to indicate a markdown link
|
||||||
|
var titleEnd = editor.getRange({ ch: cursor.ch - 2, line: cursor.line }, { ch: cursor.ch, line: cursor.line });
|
||||||
|
return titleEnd == "](";
|
||||||
|
}
|
||||||
|
static isAfterQuote(editor) {
|
||||||
|
let cursor = editor.getCursor();
|
||||||
|
// Check if the characters before the url are " or ' to indicate we want the url directly
|
||||||
|
// This is common in elements like <a href="linkhere"></a>
|
||||||
|
var beforeChar = editor.getRange({ ch: cursor.ch - 1, line: cursor.line }, { ch: cursor.ch, line: cursor.line });
|
||||||
|
return beforeChar == "\"" || beforeChar == "'";
|
||||||
|
}
|
||||||
|
static isUrl(text) {
|
||||||
|
let urlRegex = new RegExp(DEFAULT_SETTINGS.regex);
|
||||||
|
return urlRegex.test(text);
|
||||||
|
}
|
||||||
|
static isImage(text) {
|
||||||
|
let imageRegex = new RegExp(DEFAULT_SETTINGS.imageRegex);
|
||||||
|
return imageRegex.test(text);
|
||||||
|
}
|
||||||
|
static isLinkedUrl(text) {
|
||||||
|
let urlRegex = new RegExp(DEFAULT_SETTINGS.linkRegex);
|
||||||
|
return urlRegex.test(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditorExtensions {
|
||||||
|
static getSelectedText(editor) {
|
||||||
|
if (!editor.somethingSelected()) {
|
||||||
|
let wordBoundaries = this.getWordBoundaries(editor);
|
||||||
|
editor.setSelection(wordBoundaries.start, wordBoundaries.end);
|
||||||
|
}
|
||||||
|
return editor.getSelection();
|
||||||
|
}
|
||||||
|
static cursorWithinBoundaries(cursor, match) {
|
||||||
|
let startIndex = match.index;
|
||||||
|
let endIndex = match.index + match[0].length;
|
||||||
|
return startIndex <= cursor.ch && cursor.ch <= endIndex;
|
||||||
|
}
|
||||||
|
static getWordBoundaries(editor) {
|
||||||
|
let cursor = editor.getCursor();
|
||||||
|
// If its a normal URL token this is not a markdown link
|
||||||
|
// In this case we can simply overwrite the link boundaries as-is
|
||||||
|
let lineText = editor.getLine(cursor.line);
|
||||||
|
// First check if we're in a link
|
||||||
|
let linksInLine = lineText.matchAll(DEFAULT_SETTINGS.linkLineRegex);
|
||||||
|
for (let match of linksInLine) {
|
||||||
|
if (this.cursorWithinBoundaries(cursor, match)) {
|
||||||
|
return {
|
||||||
|
start: { line: cursor.line, ch: match.index },
|
||||||
|
end: { line: cursor.line, ch: match.index + match[0].length },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If not, check if we're in just a standard ol' URL.
|
||||||
|
let urlsInLine = lineText.matchAll(DEFAULT_SETTINGS.lineRegex);
|
||||||
|
for (let match of urlsInLine) {
|
||||||
|
if (this.cursorWithinBoundaries(cursor, match)) {
|
||||||
|
return {
|
||||||
|
start: { line: cursor.line, ch: match.index },
|
||||||
|
end: { line: cursor.line, ch: match.index + match[0].length },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
start: cursor,
|
||||||
|
end: cursor,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
static getEditorPositionFromIndex(content, index) {
|
||||||
|
let substr = content.substr(0, index);
|
||||||
|
let l = 0;
|
||||||
|
let offset = -1;
|
||||||
|
let r = -1;
|
||||||
|
for (; (r = substr.indexOf("\n", r + 1)) !== -1; l++, offset = r)
|
||||||
|
;
|
||||||
|
offset += 1;
|
||||||
|
let ch = content.substr(offset, index - offset).length;
|
||||||
|
return { line: l, ch: ch };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function blank$1(text) {
|
||||||
|
return text === undefined || text === null || text === '';
|
||||||
|
}
|
||||||
|
function notBlank$1(text) {
|
||||||
|
return !blank$1(text);
|
||||||
|
}
|
||||||
|
function scrape(url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
try {
|
||||||
|
const response = yield obsidian.requestUrl(url);
|
||||||
|
if (!response.headers['content-type'].includes('text/html'))
|
||||||
|
return getUrlFinalSegment$1(url);
|
||||||
|
const html = response.text;
|
||||||
|
const doc = new DOMParser().parseFromString(html, 'text/html');
|
||||||
|
const title = doc.querySelector('title');
|
||||||
|
if (blank$1(title === null || title === void 0 ? void 0 : title.innerText)) {
|
||||||
|
// If site is javascript based and has a no-title attribute when unloaded, use it.
|
||||||
|
var noTitle = title === null || title === void 0 ? void 0 : title.getAttr('no-title');
|
||||||
|
if (notBlank$1(noTitle)) {
|
||||||
|
return noTitle;
|
||||||
|
}
|
||||||
|
// Otherwise if the site has no title/requires javascript simply return Title Unknown
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return title.innerText;
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
console.error(ex);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getUrlFinalSegment$1(url) {
|
||||||
|
try {
|
||||||
|
const segments = new URL(url).pathname.split('/');
|
||||||
|
const last = segments.pop() || segments.pop(); // Handle potential trailing slash
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
catch (_) {
|
||||||
|
return 'File';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getPageTitle$1(url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
if (!(url.startsWith('http') || url.startsWith('https'))) {
|
||||||
|
url = 'https://' + url;
|
||||||
|
}
|
||||||
|
return scrape(url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const electronPkg = require("electron");
|
||||||
|
function blank(text) {
|
||||||
|
return text === undefined || text === null || text === "";
|
||||||
|
}
|
||||||
|
function notBlank(text) {
|
||||||
|
return !blank(text);
|
||||||
|
}
|
||||||
|
// async wrapper to load a url and settle on load finish or fail
|
||||||
|
function load(window, url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
window.webContents.on("did-finish-load", (event) => resolve(event));
|
||||||
|
window.webContents.on("did-fail-load", (event) => reject(event));
|
||||||
|
window.loadURL(url);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function electronGetPageTitle(url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const { remote } = electronPkg;
|
||||||
|
const { BrowserWindow } = remote;
|
||||||
|
try {
|
||||||
|
const window = new BrowserWindow({
|
||||||
|
width: 1000,
|
||||||
|
height: 600,
|
||||||
|
webPreferences: {
|
||||||
|
webSecurity: false,
|
||||||
|
nodeIntegration: true,
|
||||||
|
images: false,
|
||||||
|
},
|
||||||
|
show: false,
|
||||||
|
});
|
||||||
|
window.webContents.setAudioMuted(true);
|
||||||
|
window.webContents.on("will-navigate", (event, newUrl) => {
|
||||||
|
event.preventDefault();
|
||||||
|
window.loadURL(newUrl);
|
||||||
|
});
|
||||||
|
yield load(window, url);
|
||||||
|
try {
|
||||||
|
const title = window.webContents.getTitle();
|
||||||
|
window.destroy();
|
||||||
|
if (notBlank(title)) {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
window.destroy();
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
console.error(ex);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function nonElectronGetPageTitle(url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
try {
|
||||||
|
const html = yield obsidian.request({ url });
|
||||||
|
const doc = new DOMParser().parseFromString(html, "text/html");
|
||||||
|
const title = doc.querySelectorAll("title")[0];
|
||||||
|
if (title == null || blank(title === null || title === void 0 ? void 0 : title.innerText)) {
|
||||||
|
// If site is javascript based and has a no-title attribute when unloaded, use it.
|
||||||
|
var noTitle = title === null || title === void 0 ? void 0 : title.getAttr("no-title");
|
||||||
|
if (notBlank(noTitle)) {
|
||||||
|
return noTitle;
|
||||||
|
}
|
||||||
|
// Otherwise if the site has no title/requires javascript simply return Title Unknown
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return title.innerText;
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
console.error(ex);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getUrlFinalSegment(url) {
|
||||||
|
try {
|
||||||
|
const segments = new URL(url).pathname.split('/');
|
||||||
|
const last = segments.pop() || segments.pop(); // Handle potential trailing slash
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
catch (_) {
|
||||||
|
return "File";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function tryGetFileType(url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
try {
|
||||||
|
const response = yield fetch(url, { method: "HEAD" });
|
||||||
|
// Ensure site returns an ok status code before scraping
|
||||||
|
if (!response.ok) {
|
||||||
|
return "Site Unreachable";
|
||||||
|
}
|
||||||
|
// Ensure site is an actual HTML page and not a pdf or 3 gigabyte video file.
|
||||||
|
let contentType = response.headers.get("content-type");
|
||||||
|
if (!contentType.includes("text/html")) {
|
||||||
|
return getUrlFinalSegment(url);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getPageTitle(url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
// If we're on Desktop use the Electron scraper
|
||||||
|
if (!(url.startsWith("http") || url.startsWith("https"))) {
|
||||||
|
url = "https://" + url;
|
||||||
|
}
|
||||||
|
// Try to do a HEAD request to see if the site is reachable and if it's an HTML page
|
||||||
|
// If we error out due to CORS, we'll just try to scrape the page anyway.
|
||||||
|
let fileType = yield tryGetFileType(url);
|
||||||
|
if (fileType) {
|
||||||
|
return fileType;
|
||||||
|
}
|
||||||
|
if (electronPkg != null) {
|
||||||
|
return electronGetPageTitle(url);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nonElectronGetPageTitle(url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class AutoLinkTitle extends obsidian.Plugin {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
this.shortTitle = (title) => {
|
||||||
|
if (this.settings.maximumTitleLength === 0) {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
if (title.length < this.settings.maximumTitleLength + 3) {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
const shortenedTitle = `${title.slice(0, this.settings.maximumTitleLength)}...`;
|
||||||
|
return shortenedTitle;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
onload() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
console.log("loading obsidian-auto-link-title");
|
||||||
|
yield this.loadSettings();
|
||||||
|
this.blacklist = this.settings.websiteBlacklist
|
||||||
|
.split(",")
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter((s) => s.length > 0);
|
||||||
|
// Listen to paste event
|
||||||
|
this.pasteFunction = this.pasteUrlWithTitle.bind(this);
|
||||||
|
// Listen to drop event
|
||||||
|
this.dropFunction = this.dropUrlWithTitle.bind(this);
|
||||||
|
this.addCommand({
|
||||||
|
id: "auto-link-title-paste",
|
||||||
|
name: "Paste URL and auto fetch title",
|
||||||
|
editorCallback: (editor) => this.manualPasteUrlWithTitle(editor),
|
||||||
|
hotkeys: [],
|
||||||
|
});
|
||||||
|
this.addCommand({
|
||||||
|
id: "auto-link-title-normal-paste",
|
||||||
|
name: "Normal paste (no fetching behavior)",
|
||||||
|
editorCallback: (editor) => this.normalPaste(editor),
|
||||||
|
hotkeys: [
|
||||||
|
{
|
||||||
|
modifiers: ["Mod", "Shift"],
|
||||||
|
key: "v",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
this.registerEvent(this.app.workspace.on("editor-paste", this.pasteFunction));
|
||||||
|
this.registerEvent(this.app.workspace.on("editor-drop", this.dropFunction));
|
||||||
|
this.addCommand({
|
||||||
|
id: "enhance-url-with-title",
|
||||||
|
name: "Enhance existing URL with link and title",
|
||||||
|
editorCallback: (editor) => this.addTitleToLink(editor),
|
||||||
|
hotkeys: [
|
||||||
|
{
|
||||||
|
modifiers: ["Mod", "Shift"],
|
||||||
|
key: "e",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
this.addSettingTab(new AutoLinkTitleSettingTab(this.app, this));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
addTitleToLink(editor) {
|
||||||
|
// Only attempt fetch if online
|
||||||
|
if (!navigator.onLine)
|
||||||
|
return;
|
||||||
|
let selectedText = (EditorExtensions.getSelectedText(editor) || "").trim();
|
||||||
|
// If the cursor is on a raw html link, convert to a markdown link and fetch title
|
||||||
|
if (CheckIf.isUrl(selectedText)) {
|
||||||
|
this.convertUrlToTitledLink(editor, selectedText);
|
||||||
|
}
|
||||||
|
// If the cursor is on the URL part of a markdown link, fetch title and replace existing link title
|
||||||
|
else if (CheckIf.isLinkedUrl(selectedText)) {
|
||||||
|
const link = this.getUrlFromLink(selectedText);
|
||||||
|
this.convertUrlToTitledLink(editor, link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
normalPaste(editor) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let clipboardText = yield navigator.clipboard.readText();
|
||||||
|
if (clipboardText === null || clipboardText === "")
|
||||||
|
return;
|
||||||
|
editor.replaceSelection(clipboardText);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Simulate standard paste but using editor.replaceSelection with clipboard text since we can't seem to dispatch a paste event.
|
||||||
|
manualPasteUrlWithTitle(editor) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const clipboardText = yield navigator.clipboard.readText();
|
||||||
|
// Only attempt fetch if online
|
||||||
|
if (!navigator.onLine) {
|
||||||
|
editor.replaceSelection(clipboardText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (clipboardText == null || clipboardText == "")
|
||||||
|
return;
|
||||||
|
// If its not a URL, we return false to allow the default paste handler to take care of it.
|
||||||
|
// Similarly, image urls don't have a meaningful <title> attribute so downloading it
|
||||||
|
// to fetch the title is a waste of bandwidth.
|
||||||
|
if (!CheckIf.isUrl(clipboardText) || CheckIf.isImage(clipboardText)) {
|
||||||
|
editor.replaceSelection(clipboardText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If it looks like we're pasting the url into a markdown link already, don't fetch title
|
||||||
|
// as the user has already probably put a meaningful title, also it would lead to the title
|
||||||
|
// being inside the link.
|
||||||
|
if (CheckIf.isMarkdownLinkAlready(editor) || CheckIf.isAfterQuote(editor)) {
|
||||||
|
editor.replaceSelection(clipboardText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If url is pasted over selected text and setting is enabled, no need to fetch title,
|
||||||
|
// just insert a link
|
||||||
|
let selectedText = (EditorExtensions.getSelectedText(editor) || "").trim();
|
||||||
|
if (selectedText && this.settings.shouldPreserveSelectionAsTitle) {
|
||||||
|
editor.replaceSelection(`[${selectedText}](${clipboardText})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// At this point we're just pasting a link in a normal fashion, fetch its title.
|
||||||
|
this.convertUrlToTitledLink(editor, clipboardText);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pasteUrlWithTitle(clipboard, editor) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
if (!this.settings.enhanceDefaultPaste) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (clipboard.defaultPrevented)
|
||||||
|
return;
|
||||||
|
// Only attempt fetch if online
|
||||||
|
if (!navigator.onLine)
|
||||||
|
return;
|
||||||
|
let clipboardText = clipboard.clipboardData.getData("text/plain");
|
||||||
|
if (clipboardText === null || clipboardText === "")
|
||||||
|
return;
|
||||||
|
// If its not a URL, we return false to allow the default paste handler to take care of it.
|
||||||
|
// Similarly, image urls don't have a meaningful <title> attribute so downloading it
|
||||||
|
// to fetch the title is a waste of bandwidth.
|
||||||
|
if (!CheckIf.isUrl(clipboardText) || CheckIf.isImage(clipboardText)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We've decided to handle the paste, stop propagation to the default handler.
|
||||||
|
clipboard.stopPropagation();
|
||||||
|
clipboard.preventDefault();
|
||||||
|
// If it looks like we're pasting the url into a markdown link already, don't fetch title
|
||||||
|
// as the user has already probably put a meaningful title, also it would lead to the title
|
||||||
|
// being inside the link.
|
||||||
|
if (CheckIf.isMarkdownLinkAlready(editor) || CheckIf.isAfterQuote(editor)) {
|
||||||
|
editor.replaceSelection(clipboardText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If url is pasted over selected text and setting is enabled, no need to fetch title,
|
||||||
|
// just insert a link
|
||||||
|
let selectedText = (EditorExtensions.getSelectedText(editor) || "").trim();
|
||||||
|
if (selectedText && this.settings.shouldPreserveSelectionAsTitle) {
|
||||||
|
editor.replaceSelection(`[${selectedText}](${clipboardText})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// At this point we're just pasting a link in a normal fashion, fetch its title.
|
||||||
|
this.convertUrlToTitledLink(editor, clipboardText);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
dropUrlWithTitle(dropEvent, editor) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
if (!this.settings.enhanceDropEvents) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dropEvent.defaultPrevented)
|
||||||
|
return;
|
||||||
|
// Only attempt fetch if online
|
||||||
|
if (!navigator.onLine)
|
||||||
|
return;
|
||||||
|
let dropText = dropEvent.dataTransfer.getData("text/plain");
|
||||||
|
if (dropText === null || dropText === "")
|
||||||
|
return;
|
||||||
|
// If its not a URL, we return false to allow the default paste handler to take care of it.
|
||||||
|
// Similarly, image urls don't have a meaningful <title> attribute so downloading it
|
||||||
|
// to fetch the title is a waste of bandwidth.
|
||||||
|
if (!CheckIf.isUrl(dropText) || CheckIf.isImage(dropText)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We've decided to handle the paste, stop propagation to the default handler.
|
||||||
|
dropEvent.stopPropagation();
|
||||||
|
dropEvent.preventDefault();
|
||||||
|
// If it looks like we're pasting the url into a markdown link already, don't fetch title
|
||||||
|
// as the user has already probably put a meaningful title, also it would lead to the title
|
||||||
|
// being inside the link.
|
||||||
|
if (CheckIf.isMarkdownLinkAlready(editor) || CheckIf.isAfterQuote(editor)) {
|
||||||
|
editor.replaceSelection(dropText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If url is pasted over selected text and setting is enabled, no need to fetch title,
|
||||||
|
// just insert a link
|
||||||
|
let selectedText = (EditorExtensions.getSelectedText(editor) || "").trim();
|
||||||
|
if (selectedText && this.settings.shouldPreserveSelectionAsTitle) {
|
||||||
|
editor.replaceSelection(`[${selectedText}](${dropText})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// At this point we're just pasting a link in a normal fashion, fetch its title.
|
||||||
|
this.convertUrlToTitledLink(editor, dropText);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
isBlacklisted(url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
yield this.loadSettings();
|
||||||
|
this.blacklist = this.settings.websiteBlacklist
|
||||||
|
.split(/,|\n/)
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter((s) => s.length > 0);
|
||||||
|
return this.blacklist.some((site) => url.includes(site));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
convertUrlToTitledLink(editor, url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
if (yield this.isBlacklisted(url)) {
|
||||||
|
let domain = new URL(url).hostname;
|
||||||
|
editor.replaceSelection(`[${domain}](${url})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Generate a unique id for find/replace operations for the title.
|
||||||
|
const pasteId = this.getPasteId();
|
||||||
|
// Instantly paste so you don't wonder if paste is broken
|
||||||
|
editor.replaceSelection(`[${pasteId}](${url})`);
|
||||||
|
// Fetch title from site, replace Fetching Title with actual title
|
||||||
|
const title = yield this.fetchUrlTitle(url);
|
||||||
|
const escapedTitle = this.escapeMarkdown(title);
|
||||||
|
const shortenedTitle = this.shortTitle(escapedTitle);
|
||||||
|
const text = editor.getValue();
|
||||||
|
const start = text.indexOf(pasteId);
|
||||||
|
if (start < 0) {
|
||||||
|
console.log(`Unable to find text "${pasteId}" in current editor, bailing out; link ${url}`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const end = start + pasteId.length;
|
||||||
|
const startPos = EditorExtensions.getEditorPositionFromIndex(text, start);
|
||||||
|
const endPos = EditorExtensions.getEditorPositionFromIndex(text, end);
|
||||||
|
editor.replaceRange(shortenedTitle, startPos, endPos);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
escapeMarkdown(text) {
|
||||||
|
var unescaped = text.replace(/\\(\*|_|`|~|\\|\[|\])/g, "$1"); // unescape any "backslashed" character
|
||||||
|
var escaped = unescaped.replace(/(\*|_|`|<|>|~|\\|\[|\])/g, "\\$1"); // escape *, _, `, ~, \, [, ], <, and >
|
||||||
|
var escaped = unescaped.replace(/(\*|_|`|\||<|>|~|\\|\[|\])/g, "\\$1"); // escape *, _, `, ~, \, |, [, ], <, and >
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
fetchUrlTitleViaLinkPreview(url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
if (this.settings.linkPreviewApiKey.length !== 32) {
|
||||||
|
console.error("LinkPreview API key is not 32 characters long, please check your settings");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const apiEndpoint = `https://api.linkpreview.net/?q=${encodeURIComponent(url)}`;
|
||||||
|
const response = yield fetch(apiEndpoint, {
|
||||||
|
headers: {
|
||||||
|
"X-Linkpreview-Api-Key": this.settings.linkPreviewApiKey,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const data = yield response.json();
|
||||||
|
return data.title;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fetchUrlTitle(url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
try {
|
||||||
|
let title = "";
|
||||||
|
title = yield this.fetchUrlTitleViaLinkPreview(url);
|
||||||
|
console.log(`Title via Link Preview: ${title}`);
|
||||||
|
if (title === "") {
|
||||||
|
console.log("Title via Link Preview failed, falling back to scraper");
|
||||||
|
if (this.settings.useNewScraper) {
|
||||||
|
console.log("Using new scraper");
|
||||||
|
title = yield getPageTitle$1(url);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Using old scraper");
|
||||||
|
title = yield getPageTitle(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(`Title: ${title}`);
|
||||||
|
title =
|
||||||
|
title.replace(/(\r\n|\n|\r)/gm, "").trim() ||
|
||||||
|
"Title Unavailable | Site Unreachable";
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return "Error fetching title";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getUrlFromLink(link) {
|
||||||
|
let urlRegex = new RegExp(DEFAULT_SETTINGS.linkRegex);
|
||||||
|
return urlRegex.exec(link)[2];
|
||||||
|
}
|
||||||
|
getPasteId() {
|
||||||
|
var base = "Fetching Title";
|
||||||
|
if (this.settings.useBetterPasteId) {
|
||||||
|
return this.getBetterPasteId(base);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return `${base}#${this.createBlockHash()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getBetterPasteId(base) {
|
||||||
|
// After every character, add 0, 1 or 2 invisible characters
|
||||||
|
// so that to the user it looks just like the base string.
|
||||||
|
// The number of combinations is 3^14 = 4782969
|
||||||
|
let result = "";
|
||||||
|
var invisibleCharacter = "\u200B";
|
||||||
|
var maxInvisibleCharacters = 2;
|
||||||
|
for (var i = 0; i < base.length; i++) {
|
||||||
|
var count = Math.floor(Math.random() * (maxInvisibleCharacters + 1));
|
||||||
|
result += base.charAt(i) + invisibleCharacter.repeat(count);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// Custom hashid by @shabegom
|
||||||
|
createBlockHash() {
|
||||||
|
let result = "";
|
||||||
|
var characters = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
var charactersLength = characters.length;
|
||||||
|
for (var i = 0; i < 4; i++) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
onunload() {
|
||||||
|
console.log("unloading obsidian-auto-link-title");
|
||||||
|
}
|
||||||
|
loadSettings() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, yield this.loadData());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
saveSettings() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
yield this.saveData(this.settings);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AutoLinkTitle;
|
||||||
|
|
||||||
|
|
||||||
|
/* nosourcemap */
|
10
.obsidian/plugins/obsidian-auto-link-title/manifest.json
vendored
Normal file
10
.obsidian/plugins/obsidian-auto-link-title/manifest.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"id": "obsidian-auto-link-title",
|
||||||
|
"name": "Auto Link Title",
|
||||||
|
"version": "1.5.5",
|
||||||
|
"minAppVersion": "0.12.17",
|
||||||
|
"description": "This plugin automatically fetches the titles of links from the web",
|
||||||
|
"author": "Matt Furden",
|
||||||
|
"authorUrl": "https://github.com/zolrath",
|
||||||
|
"isDesktopOnly": false
|
||||||
|
}
|
1
.obsidian/plugins/obsidian-auto-link-title/styles.css
vendored
Normal file
1
.obsidian/plugins/obsidian-auto-link-title/styles.css
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/* no styles */
|
59
.obsidian/plugins/obsidian-git/data.json
vendored
Normal file
59
.obsidian/plugins/obsidian-git/data.json
vendored
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
{
|
||||||
|
"commitMessage": "vault backup: {{date}}",
|
||||||
|
"commitDateFormat": "YYYY-MM-DD HH:mm:ss",
|
||||||
|
"autoSaveInterval": 5,
|
||||||
|
"autoPushInterval": 10,
|
||||||
|
"autoPullInterval": 0,
|
||||||
|
"autoPullOnBoot": true,
|
||||||
|
"disablePush": false,
|
||||||
|
"pullBeforePush": true,
|
||||||
|
"disablePopups": false,
|
||||||
|
"disablePopupsForNoChanges": false,
|
||||||
|
"listChangedFilesInMessageBody": false,
|
||||||
|
"showStatusBar": true,
|
||||||
|
"updateSubmodules": false,
|
||||||
|
"syncMethod": "merge",
|
||||||
|
"customMessageOnAutoBackup": false,
|
||||||
|
"autoBackupAfterFileChange": false,
|
||||||
|
"treeStructure": false,
|
||||||
|
"refreshSourceControl": true,
|
||||||
|
"basePath": "",
|
||||||
|
"differentIntervalCommitAndPush": true,
|
||||||
|
"changedFilesInStatusBar": false,
|
||||||
|
"showedMobileNotice": true,
|
||||||
|
"refreshSourceControlTimer": 7000,
|
||||||
|
"showBranchStatusBar": true,
|
||||||
|
"setLastSaveToLastCommit": false,
|
||||||
|
"submoduleRecurseCheckout": false,
|
||||||
|
"gitDir": "",
|
||||||
|
"showFileMenu": true,
|
||||||
|
"authorInHistoryView": "initials",
|
||||||
|
"dateInHistoryView": true,
|
||||||
|
"diffStyle": "split",
|
||||||
|
"lineAuthor": {
|
||||||
|
"show": false,
|
||||||
|
"followMovement": "inactive",
|
||||||
|
"authorDisplay": "initials",
|
||||||
|
"showCommitHash": false,
|
||||||
|
"dateTimeFormatOptions": "date",
|
||||||
|
"dateTimeFormatCustomString": "YYYY-MM-DD HH:mm",
|
||||||
|
"dateTimeTimezone": "viewer-local",
|
||||||
|
"coloringMaxAge": "1y",
|
||||||
|
"colorNew": {
|
||||||
|
"r": 255,
|
||||||
|
"g": 150,
|
||||||
|
"b": 150
|
||||||
|
},
|
||||||
|
"colorOld": {
|
||||||
|
"r": 120,
|
||||||
|
"g": 160,
|
||||||
|
"b": 255
|
||||||
|
},
|
||||||
|
"textColorCss": "var(--text-muted)",
|
||||||
|
"ignoreWhitespace": false,
|
||||||
|
"gutterSpacingFallbackLength": 5,
|
||||||
|
"lastShownAuthorDisplay": "initials",
|
||||||
|
"lastShownDateTimeFormatOptions": "date"
|
||||||
|
},
|
||||||
|
"autoCommitMessage": "vault backup: {{date}}"
|
||||||
|
}
|
414
.obsidian/plugins/obsidian-git/main.js
vendored
Normal file
414
.obsidian/plugins/obsidian-git/main.js
vendored
Normal file
File diff suppressed because one or more lines are too long
10
.obsidian/plugins/obsidian-git/manifest.json
vendored
Normal file
10
.obsidian/plugins/obsidian-git/manifest.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"author": "Vinzent",
|
||||||
|
"authorUrl": "https://github.com/Vinzent03",
|
||||||
|
"id": "obsidian-git",
|
||||||
|
"name": "Git",
|
||||||
|
"description": "Integrate Git version control with automatic backup and other advanced features.",
|
||||||
|
"isDesktopOnly": false,
|
||||||
|
"fundingUrl": "https://ko-fi.com/vinzent",
|
||||||
|
"version": "2.31.1"
|
||||||
|
}
|
23
.obsidian/plugins/obsidian-git/obsidian_askpass.sh
vendored
Executable file
23
.obsidian/plugins/obsidian-git/obsidian_askpass.sh
vendored
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PROMPT="$1"
|
||||||
|
TEMP_FILE="$OBSIDIAN_GIT_CREDENTIALS_INPUT"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -f "$TEMP_FILE" "$TEMP_FILE.response"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
echo "$PROMPT" > "$TEMP_FILE"
|
||||||
|
|
||||||
|
while [ ! -e "$TEMP_FILE.response" ]; do
|
||||||
|
if [ ! -e "$TEMP_FILE" ]; then
|
||||||
|
echo "Trigger file got removed: Abort" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
RESPONSE=$(cat "$TEMP_FILE.response")
|
||||||
|
|
||||||
|
echo "$RESPONSE"
|
576
.obsidian/plugins/obsidian-git/styles.css
vendored
Normal file
576
.obsidian/plugins/obsidian-git/styles.css
vendored
Normal file
|
@ -0,0 +1,576 @@
|
||||||
|
@keyframes loading {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="git-view"] .button-border {
|
||||||
|
border: 2px solid var(--interactive-accent);
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="git-view"] .view-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="git-history-view"] .view-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading > svg {
|
||||||
|
animation: 2s linear infinite loading;
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.obsidian-git-center {
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.obsidian-git-textarea {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.obsidian-git-disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.obsidian-git-center-button {
|
||||||
|
display: block;
|
||||||
|
margin: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip.mod-left {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip.mod-right {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
.git-tools {
|
||||||
|
display: flex;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
.git-tools .type {
|
||||||
|
padding-left: var(--size-2-1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.git-tools .type[data-type="M"] {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
.git-tools .type[data-type="D"] {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.git-tools .buttons {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.git-tools .buttons > * {
|
||||||
|
padding: 0 0;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-active .git-tools .buttons > * {
|
||||||
|
color: var(--nav-item-color-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
.git-author {
|
||||||
|
color: var(--text-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.git-date {
|
||||||
|
color: var(--text-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.git-ref {
|
||||||
|
color: var(--text-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-d-none {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-wrapper {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-header {
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
border-bottom: 1px solid var(--interactive-accent);
|
||||||
|
font-family: var(--font-monospace);
|
||||||
|
height: 35px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-header,
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-stats {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-stats {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-lines-added {
|
||||||
|
border: 1px solid #b4e2b4;
|
||||||
|
border-radius: 5px 0 0 5px;
|
||||||
|
color: #399839;
|
||||||
|
padding: 2px;
|
||||||
|
text-align: right;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-lines-deleted {
|
||||||
|
border: 1px solid #e9aeae;
|
||||||
|
border-radius: 0 5px 5px 0;
|
||||||
|
color: #c33;
|
||||||
|
margin-left: 1px;
|
||||||
|
padding: 2px;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-name-wrapper {
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
font-size: 15px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-name {
|
||||||
|
overflow-x: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-wrapper {
|
||||||
|
border: 1px solid var(--background-modifier-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-collapse {
|
||||||
|
-webkit-box-pack: end;
|
||||||
|
-ms-flex-pack: end;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--background-modifier-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: none;
|
||||||
|
font-size: 12px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-collapse.d2h-selected {
|
||||||
|
background-color: #c8e1ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-collapse-input {
|
||||||
|
margin: 0 4px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-diff-table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-family: Menlo, Consolas, monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-files-diff {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-diff {
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-side-diff {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: -8px;
|
||||||
|
margin-right: -4px;
|
||||||
|
overflow-x: scroll;
|
||||||
|
overflow-y: hidden;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line {
|
||||||
|
padding: 0 8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line,
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line {
|
||||||
|
display: inline-block;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line {
|
||||||
|
padding: 0 4.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line-ctn {
|
||||||
|
word-wrap: normal;
|
||||||
|
background: none;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0;
|
||||||
|
-webkit-user-select: text;
|
||||||
|
-moz-user-select: text;
|
||||||
|
-ms-user-select: text;
|
||||||
|
user-select: text;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: pre;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-code-line del,
|
||||||
|
.theme-light
|
||||||
|
.workspace-leaf-content[data-type="diff-view"]
|
||||||
|
.d2h-code-side-line
|
||||||
|
del {
|
||||||
|
background-color: #ffb6ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-code-line del,
|
||||||
|
.theme-dark
|
||||||
|
.workspace-leaf-content[data-type="diff-view"]
|
||||||
|
.d2h-code-side-line
|
||||||
|
del {
|
||||||
|
background-color: #8d232881;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line del,
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line ins,
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line del,
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-line ins {
|
||||||
|
border-radius: 0.2em;
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: -1px;
|
||||||
|
text-decoration: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-code-line ins,
|
||||||
|
.theme-light
|
||||||
|
.workspace-leaf-content[data-type="diff-view"]
|
||||||
|
.d2h-code-side-line
|
||||||
|
ins {
|
||||||
|
background-color: #97f295;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-code-line ins,
|
||||||
|
.theme-dark
|
||||||
|
.workspace-leaf-content[data-type="diff-view"]
|
||||||
|
.d2h-code-side-line
|
||||||
|
ins {
|
||||||
|
background-color: #1d921996;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line-prefix {
|
||||||
|
word-wrap: normal;
|
||||||
|
background: none;
|
||||||
|
display: inline;
|
||||||
|
padding: 0;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .line-num1 {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .line-num1,
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .line-num2 {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
width: 3.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .line-num2 {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber {
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
border: solid var(--background-modifier-border);
|
||||||
|
border-width: 0 1px;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: var(--text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
text-align: right;
|
||||||
|
width: 7.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber:after {
|
||||||
|
content: "\200b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber {
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
border: solid var(--background-modifier-border);
|
||||||
|
border-width: 0 1px;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: var(--text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
position: absolute;
|
||||||
|
text-align: right;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
width: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-diff-tbody tr {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber:after {
|
||||||
|
content: "\200b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-emptyplaceholder,
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-emptyplaceholder {
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
border-color: var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-line-prefix,
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber,
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber,
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-emptyplaceholder {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-linenumber,
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-code-side-linenumber {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-del {
|
||||||
|
background-color: #fee8e9;
|
||||||
|
border-color: #e9aeae;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .workspace-leaf-content[data-type="diff-view"] .d2h-ins {
|
||||||
|
background-color: #dfd;
|
||||||
|
border-color: #b4e2b4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-del {
|
||||||
|
background-color: #521b1d83;
|
||||||
|
border-color: #691d1d73;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .workspace-leaf-content[data-type="diff-view"] .d2h-ins {
|
||||||
|
background-color: rgba(30, 71, 30, 0.5);
|
||||||
|
border-color: #13501381;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-info {
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
border-color: var(--background-modifier-border);
|
||||||
|
color: var(--text-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light
|
||||||
|
.workspace-leaf-content[data-type="diff-view"]
|
||||||
|
.d2h-file-diff
|
||||||
|
.d2h-del.d2h-change {
|
||||||
|
background-color: #fdf2d0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark
|
||||||
|
.workspace-leaf-content[data-type="diff-view"]
|
||||||
|
.d2h-file-diff
|
||||||
|
.d2h-del.d2h-change {
|
||||||
|
background-color: #55492480;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light
|
||||||
|
.workspace-leaf-content[data-type="diff-view"]
|
||||||
|
.d2h-file-diff
|
||||||
|
.d2h-ins.d2h-change {
|
||||||
|
background-color: #ded;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark
|
||||||
|
.workspace-leaf-content[data-type="diff-view"]
|
||||||
|
.d2h-file-diff
|
||||||
|
.d2h-ins.d2h-change {
|
||||||
|
background-color: rgba(37, 78, 37, 0.418);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list-wrapper {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list-wrapper a {
|
||||||
|
color: #3572b0;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"]
|
||||||
|
.d2h-file-list-wrapper
|
||||||
|
a:visited {
|
||||||
|
color: #3572b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list-header {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list-title {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list-line {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list {
|
||||||
|
display: block;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list > li {
|
||||||
|
border-bottom: 1px solid var(--background-modifier-border);
|
||||||
|
margin: 0;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-list > li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-file-switch {
|
||||||
|
cursor: pointer;
|
||||||
|
display: none;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-icon {
|
||||||
|
fill: currentColor;
|
||||||
|
margin-right: 10px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-deleted {
|
||||||
|
color: #c33;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-added {
|
||||||
|
color: #399839;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-changed {
|
||||||
|
color: #d0b44c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-moved {
|
||||||
|
color: #3572b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-tag {
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-left: 5px;
|
||||||
|
padding: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-deleted-tag {
|
||||||
|
border: 2px solid #c33;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-added-tag {
|
||||||
|
border: 1px solid #399839;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-changed-tag {
|
||||||
|
border: 1px solid #d0b44c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-leaf-content[data-type="diff-view"] .d2h-moved-tag {
|
||||||
|
border: 1px solid #3572b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================== Line Authoring Information ====================== */
|
||||||
|
|
||||||
|
.cm-gutterElement.obs-git-blame-gutter {
|
||||||
|
/* Add background color to spacing inbetween and around the gutter for better aesthetics */
|
||||||
|
border-width: 0px 2px 0.2px 2px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--background-secondary);
|
||||||
|
background-color: var(--background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-gutterElement.obs-git-blame-gutter > div,
|
||||||
|
.line-author-settings-preview {
|
||||||
|
/* delegate text color to settings */
|
||||||
|
color: var(--obs-git-gutter-text);
|
||||||
|
font-family: monospace;
|
||||||
|
height: 100%; /* ensure, that age-based background color occupies entire parent */
|
||||||
|
text-align: right;
|
||||||
|
padding: 0px 6px 0px 6px;
|
||||||
|
white-space: pre; /* Keep spaces and do not collapse them. */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
/* hide git blame gutter not to superpose text */
|
||||||
|
.cm-gutterElement.obs-git-blame-gutter {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.git-unified-diff-view,
|
||||||
|
.git-split-diff-view .cm-deletedLine .cm-changedText {
|
||||||
|
background-color: #ee443330;
|
||||||
|
}
|
||||||
|
|
||||||
|
.git-unified-diff-view,
|
||||||
|
.git-split-diff-view .cm-insertedLine .cm-changedText {
|
||||||
|
background-color: #22bb2230;
|
||||||
|
}
|
292
.obsidian/plugins/obsidian-linter/data.json
vendored
Normal file
292
.obsidian/plugins/obsidian-linter/data.json
vendored
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
{
|
||||||
|
"ruleConfigs": {
|
||||||
|
"add-blank-line-after-yaml": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"dedupe-yaml-array-values": {
|
||||||
|
"enabled": false,
|
||||||
|
"dedupe-alias-key": true,
|
||||||
|
"dedupe-tag-key": true,
|
||||||
|
"dedupe-array-keys": true,
|
||||||
|
"ignore-keys": ""
|
||||||
|
},
|
||||||
|
"escape-yaml-special-characters": {
|
||||||
|
"enabled": false,
|
||||||
|
"try-to-escape-single-line-arrays": false
|
||||||
|
},
|
||||||
|
"force-yaml-escape": {
|
||||||
|
"enabled": false,
|
||||||
|
"force-yaml-escape-keys": ""
|
||||||
|
},
|
||||||
|
"format-tags-in-yaml": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"format-yaml-array": {
|
||||||
|
"enabled": false,
|
||||||
|
"alias-key": true,
|
||||||
|
"tag-key": true,
|
||||||
|
"default-array-style": "single-line",
|
||||||
|
"default-array-keys": true,
|
||||||
|
"force-single-line-array-style": "",
|
||||||
|
"force-multi-line-array-style": ""
|
||||||
|
},
|
||||||
|
"insert-yaml-attributes": {
|
||||||
|
"enabled": false,
|
||||||
|
"text-to-insert": "aliases: \ntags: "
|
||||||
|
},
|
||||||
|
"move-tags-to-yaml": {
|
||||||
|
"enabled": false,
|
||||||
|
"how-to-handle-existing-tags": "Nothing",
|
||||||
|
"tags-to-ignore": ""
|
||||||
|
},
|
||||||
|
"remove-yaml-keys": {
|
||||||
|
"enabled": false,
|
||||||
|
"yaml-keys-to-remove": ""
|
||||||
|
},
|
||||||
|
"sort-yaml-array-values": {
|
||||||
|
"enabled": false,
|
||||||
|
"sort-alias-key": true,
|
||||||
|
"sort-tag-key": true,
|
||||||
|
"sort-array-keys": true,
|
||||||
|
"ignore-keys": "",
|
||||||
|
"sort-order": "Ascending Alphabetical"
|
||||||
|
},
|
||||||
|
"yaml-key-sort": {
|
||||||
|
"enabled": false,
|
||||||
|
"yaml-key-priority-sort-order": "",
|
||||||
|
"priority-keys-at-start-of-yaml": true,
|
||||||
|
"yaml-sort-order-for-other-keys": "None"
|
||||||
|
},
|
||||||
|
"yaml-timestamp": {
|
||||||
|
"enabled": false,
|
||||||
|
"date-created": true,
|
||||||
|
"date-created-key": "date created",
|
||||||
|
"date-created-source-of-truth": "file system",
|
||||||
|
"date-modified": true,
|
||||||
|
"date-modified-key": "date modified",
|
||||||
|
"date-modified-source-of-truth": "file system",
|
||||||
|
"format": "dddd, MMMM Do YYYY, h:mm:ss a",
|
||||||
|
"convert-to-utc": false,
|
||||||
|
"update-on-file-contents-updated": "never"
|
||||||
|
},
|
||||||
|
"yaml-title": {
|
||||||
|
"enabled": false,
|
||||||
|
"title-key": "title",
|
||||||
|
"mode": "first-h1-or-filename-if-h1-missing"
|
||||||
|
},
|
||||||
|
"yaml-title-alias": {
|
||||||
|
"enabled": false,
|
||||||
|
"preserve-existing-alias-section-style": true,
|
||||||
|
"keep-alias-that-matches-the-filename": false,
|
||||||
|
"use-yaml-key-to-keep-track-of-old-filename-or-heading": true,
|
||||||
|
"alias-helper-key": "linter-yaml-title-alias"
|
||||||
|
},
|
||||||
|
"capitalize-headings": {
|
||||||
|
"enabled": false,
|
||||||
|
"style": "Title Case",
|
||||||
|
"ignore-case-words": true,
|
||||||
|
"ignore-words": "macOS, iOS, iPhone, iPad, JavaScript, TypeScript, AppleScript, I",
|
||||||
|
"lowercase-words": "a, an, the, aboard, about, abt., above, abreast, absent, across, after, against, along, aloft, alongside, amid, amidst, mid, midst, among, amongst, anti, apropos, around, round, as, aslant, astride, at, atop, ontop, bar, barring, before, B4, behind, below, beneath, neath, beside, besides, between, 'tween, beyond, but, by, chez, circa, c., ca., come, concerning, contra, counting, cum, despite, spite, down, during, effective, ere, except, excepting, excluding, failing, following, for, from, in, including, inside, into, less, like, minus, modulo, mod, near, nearer, nearest, next, notwithstanding, of, o', off, offshore, on, onto, opposite, out, outside, over, o'er, pace, past, pending, per, plus, post, pre, pro, qua, re, regarding, respecting, sans, save, saving, short, since, sub, than, through, thru, throughout, thruout, till, times, to, t', touching, toward, towards, under, underneath, unlike, until, unto, up, upon, versus, vs., v., via, vice, vis-à-vis, wanting, with, w/, w., c̄, within, w/i, without, 'thout, w/o, abroad, adrift, aft, afterward, afterwards, ahead, apart, ashore, aside, away, back, backward, backwards, beforehand, downhill, downstage, downstairs, downstream, downward, downwards, downwind, east, eastward, eastwards, forth, forward, forwards, heavenward, heavenwards, hence, henceforth, here, hereby, herein, hereof, hereto, herewith, home, homeward, homewards, indoors, inward, inwards, leftward, leftwards, north, northeast, northward, northwards, northwest, now, onward, onwards, outdoors, outward, outwards, overboard, overhead, overland, overseas, rightward, rightwards, seaward, seawards, skywards, skyward, south, southeast, southwards, southward, southwest, then, thence, thenceforth, there, thereby, therein, thereof, thereto, therewith, together, underfoot, underground, uphill, upstage, upstairs, upstream, upward, upwards, upwind, west, westward, westwards, when, whence, where, whereby, wherein, whereto, wherewith, although, because, considering, given, granted, if, lest, once, provided, providing, seeing, so, supposing, though, unless, whenever, whereas, wherever, while, whilst, ago, according to, as regards, counter to, instead of, owing to, pertaining to, at the behest of, at the expense of, at the hands of, at risk of, at the risk of, at variance with, by dint of, by means of, by virtue of, by way of, for the sake of, for sake of, for lack of, for want of, from want of, in accordance with, in addition to, in case of, in charge of, in compliance with, in conformity with, in contact with, in exchange for, in favor of, in front of, in lieu of, in light of, in the light of, in line with, in place of, in point of, in quest of, in relation to, in regard to, with regard to, in respect to, with respect to, in return for, in search of, in step with, in touch with, in terms of, in the name of, in view of, on account of, on behalf of, on grounds of, on the grounds of, on the part of, on top of, with a view to, with the exception of, à la, a la, as soon as, as well as, close to, due to, far from, in case, other than, prior to, pursuant to, regardless of, subsequent to, as long as, as much as, as far as, by the time, in as much as, inasmuch, in order to, in order that, even, provide that, if only, whether, whose, whoever, why, how, or not, whatever, what, both, and, or, not only, but also, either, neither, nor, just, rather, no sooner, such, that, yet, is, it"
|
||||||
|
},
|
||||||
|
"file-name-heading": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"header-increment": {
|
||||||
|
"enabled": false,
|
||||||
|
"start-at-h2": false
|
||||||
|
},
|
||||||
|
"headings-start-line": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"remove-trailing-punctuation-in-heading": {
|
||||||
|
"enabled": false,
|
||||||
|
"punctuation-to-remove": ".,;:!。,;:!"
|
||||||
|
},
|
||||||
|
"footnote-after-punctuation": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"move-footnotes-to-the-bottom": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"re-index-footnotes": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"auto-correct-common-misspellings": {
|
||||||
|
"enabled": false,
|
||||||
|
"ignore-words": "",
|
||||||
|
"skip-words-with-multiple-capitals": false,
|
||||||
|
"extra-auto-correct-files": []
|
||||||
|
},
|
||||||
|
"blockquote-style": {
|
||||||
|
"enabled": false,
|
||||||
|
"style": "space"
|
||||||
|
},
|
||||||
|
"convert-bullet-list-markers": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"default-language-for-code-fences": {
|
||||||
|
"enabled": false,
|
||||||
|
"default-language": ""
|
||||||
|
},
|
||||||
|
"emphasis-style": {
|
||||||
|
"enabled": false,
|
||||||
|
"style": "consistent"
|
||||||
|
},
|
||||||
|
"no-bare-urls": {
|
||||||
|
"enabled": false,
|
||||||
|
"no-bare-uris": false
|
||||||
|
},
|
||||||
|
"ordered-list-style": {
|
||||||
|
"enabled": false,
|
||||||
|
"number-style": "ascending",
|
||||||
|
"list-end-style": "."
|
||||||
|
},
|
||||||
|
"proper-ellipsis": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"quote-style": {
|
||||||
|
"enabled": false,
|
||||||
|
"single-quote-enabled": true,
|
||||||
|
"single-quote-style": "''",
|
||||||
|
"double-quote-enabled": true,
|
||||||
|
"double-quote-style": "\"\""
|
||||||
|
},
|
||||||
|
"remove-consecutive-list-markers": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"remove-empty-list-markers": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"remove-hyphenated-line-breaks": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"remove-multiple-spaces": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"strong-style": {
|
||||||
|
"enabled": false,
|
||||||
|
"style": "consistent"
|
||||||
|
},
|
||||||
|
"two-spaces-between-lines-with-content": {
|
||||||
|
"enabled": false,
|
||||||
|
"line-break-indicator": " "
|
||||||
|
},
|
||||||
|
"unordered-list-style": {
|
||||||
|
"enabled": false,
|
||||||
|
"list-style": "consistent"
|
||||||
|
},
|
||||||
|
"compact-yaml": {
|
||||||
|
"enabled": false,
|
||||||
|
"inner-new-lines": false
|
||||||
|
},
|
||||||
|
"consecutive-blank-lines": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"convert-spaces-to-tabs": {
|
||||||
|
"enabled": false,
|
||||||
|
"tabsize": 4
|
||||||
|
},
|
||||||
|
"empty-line-around-blockquotes": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"empty-line-around-code-fences": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"empty-line-around-horizontal-rules": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"empty-line-around-math-blocks": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"empty-line-around-tables": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"heading-blank-lines": {
|
||||||
|
"enabled": false,
|
||||||
|
"bottom": true,
|
||||||
|
"empty-line-after-yaml": true
|
||||||
|
},
|
||||||
|
"line-break-at-document-end": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"move-math-block-indicators-to-their-own-line": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"paragraph-blank-lines": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"remove-empty-lines-between-list-markers-and-checklists": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"remove-link-spacing": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"remove-space-around-characters": {
|
||||||
|
"enabled": false,
|
||||||
|
"include-fullwidth-forms": true,
|
||||||
|
"include-cjk-symbols-and-punctuation": true,
|
||||||
|
"include-dashes": true,
|
||||||
|
"other-symbols": ""
|
||||||
|
},
|
||||||
|
"remove-space-before-or-after-characters": {
|
||||||
|
"enabled": false,
|
||||||
|
"characters-to-remove-space-before": ",!?;:).’”]",
|
||||||
|
"characters-to-remove-space-after": "¿¡‘“(["
|
||||||
|
},
|
||||||
|
"space-after-list-markers": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"space-between-chinese-japanese-or-korean-and-english-or-numbers": {
|
||||||
|
"enabled": false,
|
||||||
|
"english-symbols-punctuation-before": "-+;:'\"°%$)]",
|
||||||
|
"english-symbols-punctuation-after": "-+'\"([¥$"
|
||||||
|
},
|
||||||
|
"trailing-spaces": {
|
||||||
|
"enabled": false,
|
||||||
|
"twp-space-line-break": false
|
||||||
|
},
|
||||||
|
"add-blockquote-indentation-on-paste": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"prevent-double-checklist-indicator-on-paste": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"prevent-double-list-item-indicator-on-paste": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"proper-ellipsis-on-paste": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"remove-hyphens-on-paste": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"remove-leading-or-trailing-whitespace-on-paste": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"remove-leftover-footnotes-from-quote-on-paste": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"remove-multiple-blank-lines-on-paste": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lintOnSave": false,
|
||||||
|
"recordLintOnSaveLogs": false,
|
||||||
|
"displayChanged": true,
|
||||||
|
"lintOnFileChange": false,
|
||||||
|
"displayLintOnFileChangeNotice": false,
|
||||||
|
"settingsConvertedToConfigKeyValues": true,
|
||||||
|
"foldersToIgnore": [],
|
||||||
|
"filesToIgnore": [],
|
||||||
|
"linterLocale": "system-default",
|
||||||
|
"logLevel": "ERROR",
|
||||||
|
"lintCommands": [],
|
||||||
|
"customRegexes": [],
|
||||||
|
"commonStyles": {
|
||||||
|
"aliasArrayStyle": "single-line",
|
||||||
|
"tagArrayStyle": "single-line",
|
||||||
|
"minimumNumberOfDollarSignsToBeAMathBlock": 2,
|
||||||
|
"escapeCharacter": "\"",
|
||||||
|
"removeUnnecessaryEscapeCharsForMultiLineArrays": false
|
||||||
|
}
|
||||||
|
}
|
325
.obsidian/plugins/obsidian-linter/main.js
vendored
Normal file
325
.obsidian/plugins/obsidian-linter/main.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
.obsidian/plugins/obsidian-linter/manifest.json
vendored
Normal file
11
.obsidian/plugins/obsidian-linter/manifest.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"id": "obsidian-linter",
|
||||||
|
"name": "Linter",
|
||||||
|
"version": "1.28.0",
|
||||||
|
"minAppVersion": "1.5.7",
|
||||||
|
"description": "Formats and styles your notes. It can be used to format YAML tags, aliases, arrays, and metadata; footnotes; headings; spacing; math blocks; regular markdown contents like list, italics, and bold styles; and more with the use of custom rule options as well.",
|
||||||
|
"author": "Victor Tao",
|
||||||
|
"authorUrl": "https://github.com/platers",
|
||||||
|
"helpUrl": "https://platers.github.io/obsidian-linter/",
|
||||||
|
"isDesktopOnly": false
|
||||||
|
}
|
1
.obsidian/plugins/obsidian-linter/styles.css
vendored
Normal file
1
.obsidian/plugins/obsidian-linter/styles.css
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.linter-navigation-item{align-items:center;background-color:var(--background-primary-secondary-alt);border:1px solid var(--background-modifier-border);border-radius:100px;border-radius:8px 8px 2px 2px;cursor:pointer;display:flex;flex-direction:row;font-size:16px;font-weight:700;gap:4px;height:32px;overflow:hidden;padding:4px 6px;transition:color .25s ease-in-out,padding .25s ease-in-out,background-color .35s cubic-bezier(.45,.25,.83,.67),max-width .35s cubic-bezier(.57,.04,.58,1);white-space:nowrap}@media screen and (max-width:1325px){.linter-navigation-item.linter-desktop{max-width:32px}}@media screen and (max-width:800px){.linter-navigation-item.linter-mobile{max-width:32px}}.linter-navigation-item-icon,.linter-warning{padding-top:5px}.linter-navigation-item:hover{border-color:var(--interactive-accent-hover);border-bottom:0}.linter-navigation-item-selected{background-color:var(--interactive-accent)!important;border:1px solid var(--background-modifier-border);border-bottom:0;border-radius:8px 8px 2px 2px;color:var(--text-on-accent);max-width:100%!important;padding:4px 9px!important;transition:color .25s ease-in-out,padding .25s ease-in-out,background-color .35s cubic-bezier(.45,.25,.83,.67),max-width .45s cubic-bezier(.57,.04,.58,1) .2s}.linter{transition:transform .4s 0s}.linter-setting-title{align-items:baseline;display:flex;gap:30px;justify-content:space-between}.linter-setting-title.linter-mobile{justify-content:space-around}.linter-setting-title h1{font-weight:900;margin-bottom:12px;margin-top:6px}.linter-setting-header{margin-bottom:24px;overflow-x:auto;overflow-y:hidden}.linter-setting-header .linter-setting-tab-group{align-items:flex-end;display:flex;flex-wrap:wrap;width:100%}.linter-setting-tab-group{border-bottom:2px solid var(--background-modifier-border);margin-top:6px;padding-left:2px;padding-right:2px}.linter-setting-header .linter-tab-settings{border-left:2px solid transparent;border-right:2px solid transparent;cursor:pointer;font-weight:600;padding:6px 12px;white-space:nowrap}.linter-setting-header .linter-tab-settings:first-child{margin-left:6px}.linter-setting-header .linter-tab-settings.linter-tab-settings-active{border:2px solid var(--background-modifier-border);border-bottom-color:var(--background-primary);border-radius:2px;transform:translateY(2px)}.linter-navigation-item:not(.linter-navigation-item-selected)>span:nth-child(2),.linter-visually-hidden{border:0;clip:rect(0 0 0 0);clip-path:rect(0 0 0 0);height:auto;margin:0;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}textarea.full-width{margin-bottom:.8em;margin-top:.8em;min-height:10em;width:100%}.full-width-textbox-input-wrapper{position:relative}.settings-copy-button{margin:0 0 0 auto;padding:4px;position:absolute;right:.8em;top:.8em}.settings-copy-button svg.linter-clipboard path{fill:var(--text-faint)}.settings-copy-button svg.linter-success path{fill:var(--interactive-success)}.settings-copy-button:active,.settings-copy-button:hover{cursor:pointer}.settings-copy-button:active svg path,.settings-copy-button:hover svg path{fill:var(--text-accent-hover);transition:all .3s ease}.settings-copy-button:focus{outline:0}.linter-custom-regex-replacement-container div:last-child{border:none}.linter-custom-regex-replacement{border:none;border-bottom:var(--hr-thickness) solid;border-color:var(--hr-color);margin-bottom:15px}.linter-custom-regex-replacement-row2{flex-wrap:wrap}.linter-custom-regex-replacement-normal-input{width:40%}.linter-custom-regex-replacement-flags{width:15%}.linter-custom-regex-replacement-label{flex-direction:row-reverse}.linter-custom-regex-replacement-label-input{width:50%}.linter-files-to-ignore-container div:last-child{border:none}.linter-files-to-ignore{border:none;border-bottom:var(--hr-thickness) solid;border-color:var(--hr-color);margin-bottom:15px}.linter-files-to-ignore-normal-input{width:40%}.linter-files-to-ignore-flags{width:15%}.linter-no-border{border:none}.linter-border-bottom{border-bottom:1px solid var(--background-modifier-border);border-top:0;margin-bottom:.75em}.linter-no-padding-top{padding-top:0}.custom-row-description{margin-top:0}.modal-warn,.search-zero-state{font-weight:700}.modal-heading,.search-zero-state{text-align:center}
|
37
.obsidian/plugins/templater-obsidian/data.json
vendored
Normal file
37
.obsidian/plugins/templater-obsidian/data.json
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"command_timeout": 5,
|
||||||
|
"templates_folder": "templates",
|
||||||
|
"templates_pairs": [
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"trigger_on_file_creation": true,
|
||||||
|
"auto_jump_to_cursor": false,
|
||||||
|
"enable_system_commands": false,
|
||||||
|
"shell_path": "",
|
||||||
|
"user_scripts_folder": "",
|
||||||
|
"enable_folder_templates": true,
|
||||||
|
"folder_templates": [
|
||||||
|
{
|
||||||
|
"folder": "templates",
|
||||||
|
"template": "templates/primary.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"enable_file_templates": false,
|
||||||
|
"file_templates": [
|
||||||
|
{
|
||||||
|
"regex": ".*",
|
||||||
|
"template": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"syntax_highlighting": true,
|
||||||
|
"syntax_highlighting_mobile": false,
|
||||||
|
"enabled_templates_hotkeys": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"startup_templates": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
11
.obsidian/plugins/templater-obsidian/manifest.json
vendored
Normal file
11
.obsidian/plugins/templater-obsidian/manifest.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"id": "templater-obsidian",
|
||||||
|
"name": "Templater",
|
||||||
|
"version": "2.9.1",
|
||||||
|
"description": "Create and use templates",
|
||||||
|
"minAppVersion": "1.5.0",
|
||||||
|
"author": "SilentVoid",
|
||||||
|
"authorUrl": "https://github.com/SilentVoid13",
|
||||||
|
"helpUrl": "https://silentvoid13.github.io/Templater/",
|
||||||
|
"isDesktopOnly": false
|
||||||
|
}
|
220
.obsidian/plugins/templater-obsidian/styles.css
vendored
Normal file
220
.obsidian/plugins/templater-obsidian/styles.css
vendored
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
.templater_search {
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater_div {
|
||||||
|
border-top: 1px solid var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater_div > .setting-item {
|
||||||
|
border-top: none !important;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater_div > .setting-item > .setting-item-control {
|
||||||
|
justify-content: space-around;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater_div
|
||||||
|
> .setting-item
|
||||||
|
> .setting-item-control
|
||||||
|
> .setting-editor-extra-setting-button {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater_donating {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater_title {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater_template {
|
||||||
|
align-self: center;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater_cmd {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater_div2 > .setting-item {
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater-prompt-div {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater-prompt-form {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater-prompt-input {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.templater-button-div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.templater-prompt-input {
|
||||||
|
height: 10rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.templater-prompt-input:focus {
|
||||||
|
border-color: var(--interactive-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .templater-command-bg {
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
background-color: var(--background-primary-alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command {
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-family: var(--font-monospace);
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .templater-inline .cm-templater-command {
|
||||||
|
background-color: var(--background-primary-alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-templater-opening-tag {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-templater-closing-tag {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-templater-interpolation-tag {
|
||||||
|
color: var(--code-property, #008bff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-templater-execution-tag {
|
||||||
|
color: var(--code-function, #c0d700);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-keyword {
|
||||||
|
color: var(--code-keyword, #00a7aa);
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-atom {
|
||||||
|
color: var(--code-normal, #f39b35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-value,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-number,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-type {
|
||||||
|
color: var(--code-value, #a06fca);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-def,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-type.cm-def {
|
||||||
|
color: var(--code-normal, var(--text-normal));
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-property,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-property.cm-def,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-attribute {
|
||||||
|
color: var(--code-function, #98e342);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-variable,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-variable-2,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-variable-3,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-meta {
|
||||||
|
color: var(--code-property, #d4d4d4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-callee,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-operator,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-qualifier,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-builtin {
|
||||||
|
color: var(--code-operator, #fc4384);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-tag {
|
||||||
|
color: var(--code-tag, #fc4384);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-comment,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-comment.cm-tag,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-comment.cm-attribute {
|
||||||
|
color: var(--code-comment, #696d70);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-string,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-string-2 {
|
||||||
|
color: var(--code-string, #e6db74);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-header,
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-hr {
|
||||||
|
color: var(--code-keyword, #da7dae);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-link {
|
||||||
|
color: var(--code-normal, #696d70);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-obsidian .cm-templater-command.cm-error {
|
||||||
|
border-bottom: 1px solid #c42412;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-hints {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
overflow: hidden;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 2px;
|
||||||
|
|
||||||
|
-webkit-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
|
||||||
|
-moz-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
|
||||||
|
box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid silver;
|
||||||
|
|
||||||
|
background: white;
|
||||||
|
font-size: 90%;
|
||||||
|
font-family: monospace;
|
||||||
|
|
||||||
|
max-height: 20em;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-hint {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
white-space: pre;
|
||||||
|
color: black;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.CodeMirror-hint-active {
|
||||||
|
background: #08f;
|
||||||
|
color: white;
|
||||||
|
}
|
115
Docker Images/bluesky-pds.md
Normal file
115
Docker Images/bluesky-pds.md
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
---
|
||||||
|
title: bluesky-pds
|
||||||
|
draft: false
|
||||||
|
date: 2025-01-18
|
||||||
|
---
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| ---------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||||
|
| Official bluesky-pds project | [GitHub - bluesky-social/pds](https://github.com/bluesky-social/pds) |
|
||||||
|
| Image on Forgejo | [Forgejo](https://code.modernleft.org/gravityfargo/-/packages/container/bluesky-pds/latest) |
|
||||||
|
| Image on Dockerhub | [Dockerhub](https://hub.docker.com/r/gravityfargo/bluesky-pds) |
|
||||||
|
| Image Source | [Forgejo](https://code.modernleft.org/gravityfargo/bluesky-pds-docker) |
|
||||||
|
| Issue Tracker | [GitHub - gravityfargo/bluesky-pds-docker](https://github.com/gravityfargo/bluesky-pds-docker) |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker pull code.modernleft.org/gravityfargo/bluesky-pds:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
A self-contained Docker image for the [Bluesky PDS (Personal Data Server) ](https://github.com/bluesky-social/pds) for use with Traefik. This image is pinned to v0.4.74.
|
||||||
|
|
||||||
|
It is required to run the instance behind a proxy (like [Traefik](https://doc.traefik.io/traefik/)) to generate SSL certificates. This will not work otherwise. The standard pds install includes caddy to handle this. A wildcard DNS assignment along with a wildcard SSL certificate is required. I use Cloudflare for this, see the screenshot below.
|
||||||
|
|
||||||
|
This is not intended for production, and I am not responsible for any data loss or security issues. This is a personal project, and I am not affiliated with Bluesky.
|
||||||
|
|
||||||
|
> [!warning] Data Warning
|
||||||
|
> Before changing images, upgrading, or any other modification always backup your data!
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
I haven't verified these are the minimum requirements, but they are what I found to be necessary during development. I probably had some dependencies installed already.
|
||||||
|
|
||||||
|
#### Manjaro/Arch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo pacman -S jq
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Debian/Ubuntu
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install make xxd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
Generate secrets and add them to `.env` file.
|
||||||
|
See [example.env](https://code.modernleft.org/gravityfargo/bluesky-pds-docker/src/branch/main/example.env) as an example.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate secret environment variables
|
||||||
|
echo PDS_ADMIN_PASSWORD: $(openssl rand --hex 16)
|
||||||
|
|
||||||
|
echo PDS_JWT_SECRET: $(openssl rand --hex 16)
|
||||||
|
|
||||||
|
echo PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: $(openssl ecparam --name secp256k1 --genkey --noout --outform DER | tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Compose Setup
|
||||||
|
|
||||||
|
Full list of additional Environment Variables provided by bluesky upstream can be found in the
|
||||||
|
[packages/pds/src/config/env.ts](https://github.com/bluesky-social/atproto/blob/main/packages/pds/src/config/env.ts)
|
||||||
|
|
||||||
|
If you'd like to learn more about my docker setup, head over to [[getting started|Docker - Getting Started]]
|
||||||
|
|
||||||
|
By default, the image uses 1000:1000 as the UID:GID for the user. This can be changed by setting the `PUID` and `PGID` environment variables.
|
||||||
|
The compose element `hostname` must be the same value as `PDS_HOSTNAME`.
|
||||||
|
|
||||||
|
![[traefik]]
|
||||||
|
|
||||||
|
Optionally, you can use the [[middleware|BlueskyHeaders]] middleware to set headers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
I do not run this, but it should be possible.
|
||||||
|
|
||||||
|
![[standalone]]
|
||||||
|
|
||||||
|
### Running Commands
|
||||||
|
|
||||||
|
Nothing has changed in this department, other than not needing `sudo`. The commands are the same as the upstream project. Such as
|
||||||
|
|
||||||
|
- [Creating an account using pdsadmin](https://github.com/bluesky-social/pds?tab=readme-ov-file#creating-an-account-using-pdsadmin)
|
||||||
|
- [Creating an account using an invite code](https://github.com/bluesky-social/pds?tab=readme-ov-file#creating-an-account-using-an-invite-code)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it bluesky-pds bash
|
||||||
|
pdsadmin account create
|
||||||
|
pdsadmin create-invite-code
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!danger] Update Warning
|
||||||
|
> Do not under any circumstances update the image using `pdsadmin update`. I have not tested this, and it may break the image. Submit an
|
||||||
|
> [issue to my repository](https://github.com/gravityfargo/bluesky-pds-docker) requesting an update,
|
||||||
|
> and I will update this image and pin the pds to the new version.
|
||||||
|
|
||||||
|
### Cloudflare DNS
|
||||||
|
|
||||||
|
After various testing, I have figured out two things.
|
||||||
|
|
||||||
|
1. In order for email verification to work, you cannot have the DNS Proxy enabled or `AAAA` records active.
|
||||||
|
2. After you have verified emails, and do not intend to use the email feature, you can re-enable the DNS Proxy and `AAAA` records.
|
||||||
|
|
||||||
|
![[cloudflare.png]]
|
||||||
|
|
||||||
|
## SMTP
|
||||||
|
|
||||||
|
I use protonmail for my SMTP server. You can use any SMTP server you like, but I figured I'd share this information.
|
||||||
|
These are the environment variables I use.
|
||||||
|
|
||||||
|
```
|
||||||
|
PDS_EMAIL_SMTP_URL: smtp://user@example.com:TOKEN@smtp.protonmail.ch:587/
|
||||||
|
PDS_EMAIL_FROM_ADDRESS: user@example.com
|
||||||
|
```
|
BIN
Docker Images/bluesky-pds/cloudflare.png
Normal file
BIN
Docker Images/bluesky-pds/cloudflare.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
21
Docker Images/bluesky-pds/middleware.md
Normal file
21
Docker Images/bluesky-pds/middleware.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#### Middleware
|
||||||
|
|
||||||
|
I think file configs are cleaner than having a billion labels. This is not required, but it's nice to have.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# middleware.yaml
|
||||||
|
http:
|
||||||
|
middlewares:
|
||||||
|
BlueskyHeaders:
|
||||||
|
headers:
|
||||||
|
accessControlAllowMethods:
|
||||||
|
- GET
|
||||||
|
- OPTIONS
|
||||||
|
- PUT
|
||||||
|
- POST
|
||||||
|
- DELETE
|
||||||
|
accessControlAllowHeaders: "*"
|
||||||
|
accessControlAllowOriginList: "*"
|
||||||
|
addVaryHeader: true
|
||||||
|
stsSeconds: 63072000
|
||||||
|
```
|
24
Docker Images/bluesky-pds/standalone.md
Normal file
24
Docker Images/bluesky-pds/standalone.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
|
||||||
|
```yaml title="docker-compose.yml"
|
||||||
|
# Standalone, you'll need to add a proxy in front of this with SSL.
|
||||||
|
services:
|
||||||
|
bluesky-pds:
|
||||||
|
container_name: bluesky-pds
|
||||||
|
hostname: example.com
|
||||||
|
extra_hosts:
|
||||||
|
- "example.com:0.0.0.0" # domain:external_ip
|
||||||
|
image: code.modernleft.org/gravityfargo/bluesky-pds:latest
|
||||||
|
environment:
|
||||||
|
# Define variables here or in a .env file
|
||||||
|
PDS_JWT_SECRET: ...
|
||||||
|
PDS_ADMIN_PASSWORD: ...
|
||||||
|
PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: ...
|
||||||
|
PDS_HOSTNAME: example.com
|
||||||
|
PDS_EMAIL_SMTP_URL: ""
|
||||||
|
PDS_EMAIL_FROM_ADDRESS: ""
|
||||||
|
PUID: 1000
|
||||||
|
PGID: 1001
|
||||||
|
volumes:
|
||||||
|
- ./bluesky-pds:/pds
|
||||||
|
```
|
41
Docker Images/bluesky-pds/traefik.md
Normal file
41
Docker Images/bluesky-pds/traefik.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
title: bluesky-pds
|
||||||
|
draft: false
|
||||||
|
date: 2025-01-18
|
||||||
|
---
|
||||||
|
|
||||||
|
```yaml title="docker-compose.yml"
|
||||||
|
# Traefik Proxy
|
||||||
|
services:
|
||||||
|
bluesky-pds:
|
||||||
|
container_name: bluesky-pds
|
||||||
|
image: code.modernleft.org/gravityfargo/bluesky-pds:latest
|
||||||
|
hostname: example.com
|
||||||
|
extra_hosts:
|
||||||
|
- "example.com:0.0.0.0" # domain:external_ip
|
||||||
|
networks:
|
||||||
|
- proxy
|
||||||
|
environment:
|
||||||
|
# Define variables here or in a .env file
|
||||||
|
PDS_JWT_SECRET: ...
|
||||||
|
PDS_ADMIN_PASSWORD: ...
|
||||||
|
PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: ...
|
||||||
|
PDS_HOSTNAME: example.com
|
||||||
|
PDS_EMAIL_SMTP_URL: smtps://resend:<your api key here>@smtp.resend.com:465/
|
||||||
|
PDS_EMAIL_FROM_ADDRESS: admin@example.com
|
||||||
|
PUID: 1000
|
||||||
|
PGID: 1001
|
||||||
|
volumes:
|
||||||
|
- ./bluesky-pds:/pds
|
||||||
|
labels:
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.bluesky-pds-insecure.entrypoints: http
|
||||||
|
traefik.http.routers.bluesky-pds-insecure.rule: HostRegexp(`^.+\.example\.com$`) || Host(`example.com`)
|
||||||
|
# traefik.http.routers.bluesky-pds-insecure.middlewares: BlueskyHeaders@file
|
||||||
|
traefik.http.routers.bluesky-pds-secure.entrypoints: https
|
||||||
|
traefik.http.routers.bluesky-pds-secure.rule: HostRegexp(`^.+\.example\.com$`) || Host(`example.com`)
|
||||||
|
traefik.http.routers.bluesky-pds-secure.tls: "true"
|
||||||
|
traefik.http.services.bluesky-pds.loadbalancer.server.scheme: http
|
||||||
|
traefik.http.services.bluesky-pds.loadbalancer.server.port: 3000
|
||||||
|
# traefik.http.routers.bluesky-pds-secure.middlewares: BlueskyHeaders@file
|
||||||
|
```
|
113
Docker Images/quartz.md
Normal file
113
Docker Images/quartz.md
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
---
|
||||||
|
title: quartz
|
||||||
|
draft: false
|
||||||
|
date: 2025-01-16
|
||||||
|
---
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| ----------------------- | ---------------------------------------------------------------------------------------------- |
|
||||||
|
| Image on Forgejo | [Forgejo](https://code.modernleft.org/gravityfargo/-/packages/container/quartz-docker/v4.4.0) |
|
||||||
|
| Official quartz project | [GitHub - jackyzha0/quartz](https://github.com/jackyzha0/quartz) |
|
||||||
|
| Image Source | [Forgejo - gravityfargo/quartz-docker](https://code.modernleft.org/gravityfargo/quartz-docker) |
|
||||||
|
| Issue Tracker | |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker pull code.modernleft.org/gravityfargo/quartz-docker:v4.4.0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This project runs Quartz v4.4.0, a fast, batteries-included static site generator, inside a Docker container. It transforms Markdown content into a fully functional website.
|
||||||
|
|
||||||
|
While Quartz provides [Docker support](https://quartz.jzhao.xyz/features/Docker-Support), it is not as self contained as this project.
|
||||||
|
|
||||||
|
There is no support for SSL or any other features that are not directly related to building the site. I run everything behind traefik, so I don't need it.
|
||||||
|
|
||||||
|
## 🐳 Docker Compose Setup
|
||||||
|
|
||||||
|
Create a data directory
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /srv/quartz
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml title="docker-compose.yml"
|
||||||
|
services:
|
||||||
|
quartz-wiki:
|
||||||
|
container_name: quartz-wiki
|
||||||
|
image: code.modernleft.org/gravityfargo/quartz-docker:dev
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
environment:
|
||||||
|
USER_ID: 1000
|
||||||
|
GROUP_ID: 1001
|
||||||
|
SERVER_NAME: "docs.modernleft.org"
|
||||||
|
ENABLE_CRON: "true"
|
||||||
|
BUILD_SCHEDULE: "*/30 * * * *"
|
||||||
|
CONTENT_REPO: "https://code.modernleft.org/gravityfargo/modernleft-docs.git"
|
||||||
|
volumes:
|
||||||
|
- /srv/quartz:/quartz
|
||||||
|
```
|
||||||
|
|
||||||
|
**First Run**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
docker stop quartz-wiki
|
||||||
|
```
|
||||||
|
|
||||||
|
On first run, the container will download the necessary dependencies, build the site, and download the content repository.
|
||||||
|
|
||||||
|
After that, you can configure whatever you would like in `/srv/quartz/src` using the stock options found in the quartz [documentation](https://quartz.jzhao.xyz/configuration) Bare minimum should be `baseUrl` and `pageTitle`.
|
||||||
|
|
||||||
|
🥳 Done! 🎉
|
||||||
|
|
||||||
|
## Manually Building the Site
|
||||||
|
|
||||||
|
If you don't use the cron job, and don't feel like restarting the container, you can build the site manually.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it quartz-wiki bash
|
||||||
|
#
|
||||||
|
cd /quartz/content/ && git restore . && git pull
|
||||||
|
cd /quartz/src/ && npx quartz build
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Configuration
|
||||||
|
|
||||||
|
**cron**
|
||||||
|
|
||||||
|
This is the an equivalent command to the cron job.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /quartz/content
|
||||||
|
git restore .
|
||||||
|
git pull
|
||||||
|
cd /quartz/src &
|
||||||
|
npx quartz build
|
||||||
|
echo 'Content Updated.'
|
||||||
|
```
|
||||||
|
|
||||||
|
Setting `ENABLE_CRON` to `true` and defining `BUILD_SCHEDULE` performs these actions.
|
||||||
|
If you need help configuring [Crontab.guru - The cron schedule expression generator](https://crontab.guru/) is a useful tool for that
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
ENABLE_CRON: "true"
|
||||||
|
BUILD_SCHEDULE: "*/1 * * * *" # Runs every minute
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
| Variable | Description | Default Value |
|
||||||
|
| -------------------- | ---------------------------------------------------- | ---------------------------------------------------- |
|
||||||
|
| `USER_ID`/`GROUP_ID` | UID/GID that` /quartz` will be chown'd to on startup | `1000`:`1000` |
|
||||||
|
| `NGINX_PORT` | Port for the Nginx server | `80` |
|
||||||
|
| `SERVER_NAME` | NGINX server name | `quartz.zhao.xyz` |
|
||||||
|
| `ENABLE_CRON` | Enables scheduled builds (`true` or `false`) | `false` |
|
||||||
|
| `BUILD_SCHEDULE` | Cron expression for scheduling site builds | `"*/10 * * * *"` every 10 min |
|
||||||
|
| `CONTENT_REPO` | URL of the content repository | `https://code.modernleft.org/gravityfargo/empty.git` |
|
||||||
|
|
||||||
|
> [!NOTE] > `CONTENT_REPO` Must use https, not git.
|
33
Docker Images/quartz/Developing with Quartz.md
Normal file
33
Docker Images/quartz/Developing with Quartz.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
title: Developing with Quartz
|
||||||
|
draft: false
|
||||||
|
date: 2025-01-17
|
||||||
|
---
|
||||||
|
|
||||||
|
This container is also good for quickly developing components for Quartz. It is a good way to test things out without having to install anything on your local machine, and a simple `make run` -> `ctrl-c` -> `up arrow` -> `enter` is all you need to see your changes.
|
||||||
|
|
||||||
|
I keep all of my projects in `~/Repositories`, so I would do this:
|
||||||
|
|
||||||
|
Link my primary development folder to the container repo's source folder:
|
||||||
|
```bash
|
||||||
|
cd ~/Repositories
|
||||||
|
git clone git@git.modernleft.org:gravityfargo/quartz-docker.git
|
||||||
|
git clone git@github.com:jackyzha0/quartz.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Then mount the quartz directory to the development docker-compose file:
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ~/Repositories/quartz:/quartz/src
|
||||||
|
```
|
||||||
|
Then, you can run the container with the following command:
|
||||||
|
```bash
|
||||||
|
cd ~/Repositories/quartz-docker
|
||||||
|
make run
|
||||||
|
```
|
||||||
|
|
||||||
|
If 200+ files are claiming to be modified, you may need to disable filemode checking:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git config core.fileMode false
|
||||||
|
```
|
17
Tutorials/Docker/Proxy Network.md
Normal file
17
Tutorials/Docker/Proxy Network.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
title: Proxy Network
|
||||||
|
draft: true
|
||||||
|
date: 2025-01-18
|
||||||
|
---
|
||||||
|
|
||||||
|
```json title="/etc/docker/daemon.json"
|
||||||
|
{
|
||||||
|
"ipv6": true,
|
||||||
|
"fixed-cidr-v6": "2001:db8:1::/64"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl reboot
|
||||||
|
docker network create --subnet=192.168.1.0/24 --ipv6 --attachable proxy
|
||||||
|
```
|
5
Tutorials/Docker/getting started.md
Normal file
5
Tutorials/Docker/getting started.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Getting Started
|
||||||
|
draft: true
|
||||||
|
date: 2025-01-18
|
||||||
|
---
|
23
Tutorials/Traefik/CloudFlare.md
Normal file
23
Tutorials/Traefik/CloudFlare.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
title: CloudFlare
|
||||||
|
draft: true
|
||||||
|
date: 2025-01-18
|
||||||
|
---
|
||||||
|
|
||||||
|
Disable:
|
||||||
|
- Always Use HTTPS
|
||||||
|
- Automatic HTTPS Rewrites
|
||||||
|
Enable:
|
||||||
|
- gRPC
|
||||||
|
- Pseudo IPv4 (headers)
|
||||||
|
|
||||||
|
|
||||||
|
If you have A and AAAA then the plugin `github.com/Paxxs/traefik-get-real-ip`
|
||||||
|
|
||||||
|
192.168.1.20 - - [19/Jan/2025:01:01:32 +0000] "GET /static/contentIndex.json HTTP/1.1" 304 0 "[https://docs.modernleft.org/"](https://docs.modernleft.org/"); "Mozilla/5.0 (X11; Linux x86_64; rv:134.0) Gecko/20100101 Firefox/134.0" "2603:7080:f400:ed43:939f:266:9bef:e628, 172.71.255.28"
|
||||||
|
|
||||||
|
192.168.1.20 - proxy
|
||||||
|
172.71.255.28 - cloudflare ipv4
|
||||||
|
2603:7080:f400:ed43:939f:266:9bef:e628 - the actual user's ip
|
||||||
|
|
||||||
|
"Pseudo IPv4" is needed for this
|
21
index.md
Normal file
21
index.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
---
|
||||||
|
title: Home
|
||||||
|
draft: false
|
||||||
|
date: 2025-01-16
|
||||||
|
---
|
||||||
|
|
||||||
|
Welcome to the ModernLeft Documentation!
|
||||||
|
|
||||||
|
This is a consolidated wiki for most of my projects.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Docker Containers
|
||||||
|
|
||||||
|
### [[quartz]]
|
||||||
|
|
||||||
|
Simply [quartz](https://quartz.jzhao.xyz/), in a docker container. This wiki is running on it.
|
||||||
|
|
||||||
|
### [[bluesky-pds]]
|
||||||
|
|
||||||
|
A self-contained Docker image for the [Bluesky PDS (Personal Data Server)](https://github.com/bluesky-social/pds) for use with Traefik.
|
6
templates/primary.md
Normal file
6
templates/primary.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: ModernLeft - CHANGEME
|
||||||
|
draft: false
|
||||||
|
date: <% tp.file.creation_date("YYYY-MM-DD") %>
|
||||||
|
---
|
||||||
|
|
96
utilities/fail2ban-manager.md
Normal file
96
utilities/fail2ban-manager.md
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
---
|
||||||
|
title: fail2ban-manager
|
||||||
|
draft: true
|
||||||
|
date: 2025-01-16
|
||||||
|
---
|
||||||
|
|
||||||
|
An extensible CLI tool for managing [fail2ban](https://github.com/fail2ban/fail2ban).
|
||||||
|
|
||||||
|
- [Introduction](#introduction)
|
||||||
|
- [Working with Jails](#working-with-jails)
|
||||||
|
- [Plugins](#plugins)
|
||||||
|
- [Cloudflare](#cloudflare)
|
||||||
|
- [Plugin: Hetzner](#plugin-hetzner)
|
||||||
|
- [Plugin: Telegram](#plugin-telegram)
|
||||||
|
- [Credits](#credits)
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Source Code hosted on my [Forgejo instance](https://forgejo.gravityfargo.dev/gravityfargo/fail2ban-manager). Registration is enabled for the public with GitHub via Authentik.
|
||||||
|
|
||||||
|
Any issues and feature requests can be submitted here on GitHub.
|
||||||
|
|
||||||
|
This tool is designed to make managing fail2ban easier without the need to edit configuration files. It is designed to be extensible via plugins, and easy to use.
|
||||||
|
|
||||||
|
## Working with Jails
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
|
||||||
|
### Cloudflare
|
||||||
|
|
||||||
|
> Ban an IP address using IP Rules in [Cloudflare WAF](https://developers.cloudflare.com/waf/tools/ip-access-rules/create/). Jails are configured create rules in a specific DNS zone. Bans are cached in a database for fail2ban's check action to use.
|
||||||
|
|
||||||
|
| Notes | |
|
||||||
|
| -------------------- | ----------------------------------------------------------------- |
|
||||||
|
| Typer generated docs | [Link](man/cloudflare.md) |
|
||||||
|
| Provided via | [Officicial SDK](https://github.com/cloudflare/cloudflare-python) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
https://github.com/Paxxs/traefik-get-real-ip
|
||||||
|
|
||||||
|
| Variable | Purpose |
|
||||||
|
| ---------------------- | ----------------------------------------- |
|
||||||
|
| `CLOUDFLARE_EMAIL` | Email address for Cloudflare account |
|
||||||
|
| `CLOUDFLARE_API_TOKEN` | Token with permissions to manage IP Rules |
|
||||||
|
|
||||||
|
1. Enable the Cloudflare plugin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
f2bm plugin enable cloudflare
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Create an [API Token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/)
|
||||||
|
2. Get the [Zone ID](https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/) from Cloudflare and create a zone in the database
|
||||||
|
|
||||||
|
```bash
|
||||||
|
f2bm cloudflare create-zone 11111111111111111111111111111111 example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plugin: Hetzner
|
||||||
|
|
||||||
|
> Whitelist ip addresses in the [Hetzner](https://www.hetzner.com/) cloud firewall, and bulk whitelist Cloudflare IP addresses.
|
||||||
|
|
||||||
|
| Notes | |
|
||||||
|
| -------------------- | --------------------------------------------------------------- |
|
||||||
|
| Typer generated docs | [Link](man/hetzner.md) |
|
||||||
|
| Provided via | [Officicial SDK](https://github.com/hetznercloud/hcloud-python) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
1. Enable the Hetzner plugin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
f2bm plugin enable hetzner
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create an [API Token](https://docs.hetzner.com/cloud/api/getting-started/generating-api-token/)
|
||||||
|
|
||||||
|
### Plugin: Telegram
|
||||||
|
|
||||||
|
> Send a message to a Telegram chat. Custom messages per-jail are supported as well as seperate groups for each jail.
|
||||||
|
|
||||||
|
| Notes | |
|
||||||
|
| -------------------- | ----------------------------------------------------------------- |
|
||||||
|
| Typer generated docs | [Link](man/telegram.md) |
|
||||||
|
| Provided via | [Rest API](https://forgejo.gravityfargo.dev/gravityfargo/envoyer) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- [Typer](https://github.com/fastapi/typer), build great CLIs. Easy to code. Based on Python type hints.
|
||||||
|
|
||||||
|
- [Rich](https://github.com/Textualize/rich) is a Python library for rich text and beautiful formatting in the terminal.
|
||||||
|
|
||||||
|
- [envoyer](https://forgejo.gravityfargo.dev/gravityfargo/envoyer) Stupid simple notifications library. (My Project)
|
274
utilities/fail2ban-manager/base.md
Normal file
274
utilities/fail2ban-manager/base.md
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
---
|
||||||
|
title: fail2ban-manager plugin
|
||||||
|
draft: true
|
||||||
|
date: 2025-01-16
|
||||||
|
---
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager [OPTIONS] COMMAND [ARGS]...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--install-completion`: Install completion for the current shell.
|
||||||
|
- `--show-completion`: Show completion for the current shell, to copy it or customize the installation.
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
**Commands**:
|
||||||
|
|
||||||
|
- `jail`: Configure Fail2ban jails.
|
||||||
|
- `config`: Configure fail2ban-manager's configuration.
|
||||||
|
- `plugin`: Configure fail2ban-manager's plugins.
|
||||||
|
|
||||||
|
## `fail2ban-manager jail`
|
||||||
|
|
||||||
|
Configure Fail2ban jails.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager jail [OPTIONS] COMMAND [ARGS]...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
**Commands**:
|
||||||
|
|
||||||
|
- `ls`: List all defined jails.
|
||||||
|
- `enable`: Enable a jail.
|
||||||
|
- `disable`: Disable a jail.
|
||||||
|
- `info`: Display information about a jail.
|
||||||
|
- `edit`: Edit the properties of a jail.
|
||||||
|
|
||||||
|
### `fail2ban-manager jail ls`
|
||||||
|
|
||||||
|
List all defined jails.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager jail ls [OPTIONS]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
### `fail2ban-manager jail enable`
|
||||||
|
|
||||||
|
Enable a jail.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager jail enable [OPTIONS] NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `NAME`: [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
### `fail2ban-manager jail disable`
|
||||||
|
|
||||||
|
Disable a jail.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager jail disable [OPTIONS] NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `NAME`: [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
### `fail2ban-manager jail info`
|
||||||
|
|
||||||
|
Display information about a jail.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager jail info [OPTIONS] NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `NAME`: [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
### `fail2ban-manager jail edit`
|
||||||
|
|
||||||
|
Edit the properties of a jail.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager jail edit [OPTIONS] NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `NAME`: Name of the jail. [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--backend TEXT`: The backend used to get file modifications.
|
||||||
|
|
||||||
|
Options: .
|
||||||
|
|
||||||
|
- `--bantime-increment`: Enable searching previously banned IPs to increment the ban time.
|
||||||
|
- `--bantime-rndtime INTEGER`: Add random time (in seconds) to the calculated ban time.
|
||||||
|
- `--bantime-maxtime INTEGER`: Maximum ban time (in seconds) that can be reached.
|
||||||
|
- `--bantime-factor FLOAT`: Factor used for exponential growth of ban time.
|
||||||
|
- `--bantime-formula TEXT`: Custom formula for calculating the next ban time.
|
||||||
|
- `--bantime-multipliers TEXT`: Custom multipliers for calculating the next ban time (e.g., 1 5 30 60).
|
||||||
|
- `--bantime-overalljails`: Search for banned IPs across all jails (default: false).
|
||||||
|
- `--ignoreself`: Ignore local/own IP addresses (default: true).
|
||||||
|
- `--ignoreip TEXT`: List of IPs, CIDR masks, or DNS hosts to ignore.
|
||||||
|
|
||||||
|
Example: "127.0.0.1/8 ::1"
|
||||||
|
|
||||||
|
- `--ignorecommand TEXT`: Command to dynamically determine if an IP should be ignored.
|
||||||
|
- `--bantime TEXT`: Duration for which a host is banned (e.g., 10m, 1h).
|
||||||
|
- `--findtime TEXT`: Time window for counting failed attempts before a ban (e.g., 10m).
|
||||||
|
- `--maxretry INTEGER`: Number of failed attempts allowed before a ban is triggered.
|
||||||
|
- `--maxmatches INTEGER`: Maximum number of stored matches for actions (defaults to maxretry).
|
||||||
|
- `--protocol TEXT`: Protocol to be banned (default: tcp).
|
||||||
|
- `--port TEXT`: Ports to ban (e.g., 0:65535 to ban all ports).
|
||||||
|
- `--chain TEXT`: Specify the chain where jumps will be added for ban actions.
|
||||||
|
- `--usedns TEXT`: Behavior for DNS lookups (yes, warn, no, raw).
|
||||||
|
- `--logencoding TEXT`: Encoding of the log files (e.g., utf-8, ascii, auto).
|
||||||
|
- `--action TEXT`: Default action for banning.
|
||||||
|
|
||||||
|
EX: action\_, action_mw, action_mwl, action_xarf, action_cf_mwl, action_abuseipdb
|
||||||
|
|
||||||
|
multiple values can be specified.
|
||||||
|
`--action action_ --action action_mw`
|
||||||
|
|
||||||
|
- `--mta TEXT`: Mail Transfer Agent (e.g., sendmail).
|
||||||
|
- `--sender TEXT`: Sender email address for notifications.
|
||||||
|
- `--destemail TEXT`: Destination email address for notifications.
|
||||||
|
- `--fail2ban-agent TEXT`: User-agent format for Fail2Ban.
|
||||||
|
- `--delete TEXT`: Delete an option from the jail.
|
||||||
|
|
||||||
|
Example: `--delete bantime`
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager config`
|
||||||
|
|
||||||
|
Configure fail2ban-manager's configuration.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager config [OPTIONS] COMMAND [ARGS]...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
**Commands**:
|
||||||
|
|
||||||
|
- `reset`: Copy default configurations from `*.conf`...
|
||||||
|
|
||||||
|
### `fail2ban-manager config reset`
|
||||||
|
|
||||||
|
Copy default configurations from `*.conf` to `*.local` and json files.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager config reset [OPTIONS]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager plugin`
|
||||||
|
|
||||||
|
Configure fail2ban-manager's plugins.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager plugin [OPTIONS] COMMAND [ARGS]...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
**Commands**:
|
||||||
|
|
||||||
|
- `ls`: List installed plugins.
|
||||||
|
- `enable`: Enable a plugin.
|
||||||
|
- `disable`: Disable a plugin.
|
||||||
|
|
||||||
|
### `fail2ban-manager plugin ls`
|
||||||
|
|
||||||
|
List installed plugins.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager plugin ls [OPTIONS]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
### `fail2ban-manager plugin enable`
|
||||||
|
|
||||||
|
Enable a plugin.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager plugin enable [OPTIONS] PLUGIN_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `PLUGIN_NAME`: [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
### `fail2ban-manager plugin disable`
|
||||||
|
|
||||||
|
Disable a plugin.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager plugin disable [OPTIONS] PLUGIN_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `PLUGIN_NAME`: [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
113
utilities/fail2ban-manager/plugins/cloudflare.md
Normal file
113
utilities/fail2ban-manager/plugins/cloudflare.md
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
---
|
||||||
|
title: fail2ban-manager plugin - cloudflare
|
||||||
|
draft: true
|
||||||
|
date: 2025-01-16
|
||||||
|
---
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager cloudflare [OPTIONS] COMMAND [ARGS]...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
**Commands**:
|
||||||
|
|
||||||
|
- `zones`: List configured zones
|
||||||
|
- `zone-info`: Show information about a zone.
|
||||||
|
- `create-zone`: Create a new zone in the database.
|
||||||
|
- `check-ip`: Check if an IP is banned based on rules...
|
||||||
|
- `ban`: Check if an IP is banned based on rules...
|
||||||
|
|
||||||
|
## `fail2ban-manager cloudflare zones`
|
||||||
|
|
||||||
|
List configured zones
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager cloudflare zones [OPTIONS]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager cloudflare zone-info`
|
||||||
|
|
||||||
|
Show information about a zone.
|
||||||
|
|
||||||
|
This command will pull the rules from Cloudflare and update the database as well as display the rules in a table.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager cloudflare zone-info [OPTIONS] ZONE_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `ZONE_ID`: [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager cloudflare create-zone`
|
||||||
|
|
||||||
|
Create a new zone in the database.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager cloudflare create-zone [OPTIONS] ZONE_ID NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `ZONE_ID`: [required]
|
||||||
|
- `NAME`: [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager cloudflare check-ip`
|
||||||
|
|
||||||
|
Check if an IP is banned based on rules cached in the database.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager cloudflare check-ip [OPTIONS] IP
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `IP`: IP Address to check [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager cloudflare ban`
|
||||||
|
|
||||||
|
Check if an IP is banned based on rules cached in the database.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager cloudflare ban [OPTIONS] ZONE_ID IP
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `ZONE_ID`: [required]
|
||||||
|
- `IP`: IP Address to ban [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
176
utilities/fail2ban-manager/plugins/hetzner.md
Normal file
176
utilities/fail2ban-manager/plugins/hetzner.md
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
---
|
||||||
|
title: fail2ban-manager plugin - hetzner
|
||||||
|
draft: true
|
||||||
|
date: 2025-01-16
|
||||||
|
---
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager hetzner [OPTIONS] COMMAND [ARGS]...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
**Commands**:
|
||||||
|
|
||||||
|
- `info`: Check the status of the Hetzner plugin.
|
||||||
|
- `update`: Update the configuration.
|
||||||
|
- `reset`: Reset the configuration to defaults.
|
||||||
|
- `token`: Set Hetzner API token.
|
||||||
|
- `firewall-info`: Pull the firewalls from the server.
|
||||||
|
- `whitelist`: Whitelist an IP address.
|
||||||
|
- `whitelist-cloudflare`: Whitelist Cloudflare IP addresses.
|
||||||
|
- `delete-rule`: Delete a firewall rule.
|
||||||
|
|
||||||
|
## `fail2ban-manager hetzner info`
|
||||||
|
|
||||||
|
Check the status of the Hetzner plugin.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager hetzner info [OPTIONS]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager hetzner update`
|
||||||
|
|
||||||
|
Update the configuration.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager hetzner update [OPTIONS]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager hetzner reset`
|
||||||
|
|
||||||
|
Reset the configuration to defaults.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager hetzner reset [OPTIONS]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager hetzner token`
|
||||||
|
|
||||||
|
Set Hetzner API token.
|
||||||
|
|
||||||
|
Warning: If token_str is specified, it will be save in plaintext in the database.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager hetzner token [OPTIONS] [TOKEN_STR]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `[TOKEN_STR]`: The Hetzner API token.
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--use-env`: Use the `HETZNER_TOKEN` environment variable.
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager hetzner firewall-info`
|
||||||
|
|
||||||
|
Pull the firewalls from the server.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager hetzner firewall-info [OPTIONS] FIREWALL_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `FIREWALL_ID`: The ID of the firewall to pull from the server. [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager hetzner whitelist`
|
||||||
|
|
||||||
|
Whitelist an IP address.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
fail2ban-manager hetzner whitelist <FIREWALLID> --self
|
||||||
|
fail2ban-manager hetzner whitelist <FIREWALLID> <IP>
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager hetzner whitelist [OPTIONS] FIREWALL_ID [IP] [PORT]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `FIREWALL_ID`: The ID of the firewall to pull from the server. [required]
|
||||||
|
- `[IP]`: An IP to whitelist.
|
||||||
|
- `[PORT]`: any, 443, 80-85 [default: any]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--self`: Whitelist the External IP of the current machine.
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager hetzner whitelist-cloudflare`
|
||||||
|
|
||||||
|
Whitelist Cloudflare IP addresses.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager hetzner whitelist-cloudflare [OPTIONS] FIREWALL_ID [PORT]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `FIREWALL_ID`: The ID of the firewall to pull from the server. [required]
|
||||||
|
- `[PORT]`: any, 443, 80-85 [default: 443]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager hetzner delete-rule`
|
||||||
|
|
||||||
|
Delete a firewall rule.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
fail2ban-manager hetzner delete-rule <FIREWALLID> "fail2ban-manager whitelist - <PORT>"
|
||||||
|
fail2ban-manager hetzner delete-rule <FIREWALLID> "fail2ban-manager cloudflare whitelist - <PORT>"
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager hetzner delete-rule [OPTIONS] FIREWALL_ID DESCRIPTION
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `FIREWALL_ID`: The ID of the firewall to pull from the server. [required]
|
||||||
|
- `DESCRIPTION`: The description of the rule to delete. [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
144
utilities/fail2ban-manager/plugins/telegram.md
Normal file
144
utilities/fail2ban-manager/plugins/telegram.md
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
---
|
||||||
|
title: fail2ban-manager plugin - telegram
|
||||||
|
draft: true
|
||||||
|
date: 2025-01-16
|
||||||
|
---
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager telegram [OPTIONS] COMMAND [ARGS]...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--install-completion`: Install completion for the current shell.
|
||||||
|
- `--show-completion`: Show completion for the current shell, to copy it or customize the installation.
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
**Commands**:
|
||||||
|
|
||||||
|
- `ls`: List all jails with Telegram notifications.
|
||||||
|
- `add-to-jail`: Add the Telegram provider to a jail.
|
||||||
|
- `remove-from-jail`: Remove the Telegram provider from a jail.
|
||||||
|
- `edit-message`: Set the message for a specific action.
|
||||||
|
- `enable-message`: Enable a message for a specific action in...
|
||||||
|
- `send`: Send a messague to a Telegram bot.
|
||||||
|
|
||||||
|
## `fail2ban-manager telegram ls`
|
||||||
|
|
||||||
|
List all jails with Telegram notifications.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager telegram ls [OPTIONS]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager telegram add-to-jail`
|
||||||
|
|
||||||
|
Add the Telegram provider to a jail.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager telegram add-to-jail [OPTIONS] JAILNAME TOKEN CHATID
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `JAILNAME`: [required]
|
||||||
|
- `TOKEN`: [env var: TELEGRAM_API_TOKEN; required]
|
||||||
|
- `CHATID`: [env var: TELEGRAM_CHAT_ID; required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager telegram remove-from-jail`
|
||||||
|
|
||||||
|
Remove the Telegram provider from a jail.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager telegram remove-from-jail [OPTIONS] JAILNAME
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `JAILNAME`: [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager telegram edit-message`
|
||||||
|
|
||||||
|
Set the message for a specific action.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager telegram edit-message [OPTIONS] JAILNAME ACTION MESSAGE
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `JAILNAME`: [required]
|
||||||
|
- `ACTION`: The action to send a message for. [required]
|
||||||
|
- `MESSAGE`: The action to send. [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager telegram enable-message`
|
||||||
|
|
||||||
|
Enable a message for a specific action in a jail.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager telegram enable-message [OPTIONS] JAILNAME
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `JAILNAME`: [required]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--start`: Enable message for start action.
|
||||||
|
- `--stop`: Enable message for stop action.
|
||||||
|
- `--check`: Enable message for check action.
|
||||||
|
- `--ban`: Enable message for ban action.
|
||||||
|
- `--unban`: Enable message for unban action.
|
||||||
|
- `--help`: Show this message and exit.
|
||||||
|
|
||||||
|
## `fail2ban-manager telegram send`
|
||||||
|
|
||||||
|
Send a messague to a Telegram bot.
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ fail2ban-manager telegram send [OPTIONS] ACTION JAILNAME [TOKEN] [CHATID]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `ACTION`: The action to send a message for. [required]
|
||||||
|
- `JAILNAME`: Name of the jail sending the message [required]
|
||||||
|
- `[TOKEN]`: [env var: TELEGRAM_API_TOKEN]
|
||||||
|
- `[CHATID]`: [env var: TELEGRAM_CHAT_ID]
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--ip TEXT`: IP address of the banned/unbanned IP
|
||||||
|
- `--failures INTEGER`: Number of failures before ban [default: 0]
|
||||||
|
- `--help`: Show this message and exit.
|
Loading…
Reference in a new issue