2026-09-19 02:04:00
I often could not remember little things about my tags, like is it "#book" or "#books", SO I made a plugin. Your available tags are listed under the "Attributes" dropdown on your new/edit post page. So as you type a tag, the plugin pulls from that list and compares what you typed to what's in the afformentioned list.
By default, as long as the typed-word is IN the tag, the tag will be suggested. Like "blog" will match #blog, #blogger, and #bearblog (and will be sorted in that order).
There are various configurations at the top of the script, including a block of CSS styles. The default orientation is vertical, but you can change --direction: to column to make it horizontal. For more on the configs, see the documentation written within the script's comments. You can also set styles in your dashboard theme.
This plugin is fairly complex, dealing with dynamic user input and a dynamic header section. While I think I've fixed all the bugs, I will not be surprised if I left some hanging around.
Report Bugs: reed [at] reedybear [dot] com
Note: This plugin has been submitted to Herman's Bearblog Plugins
USAGE: You can tab or shift+tab to cycle through suggestions. You choose a selection with space or enter. You can also click on a suggestion to select it. Arrow keys are not supported. Escape closes autosuggestion. Clicking outside of the area where you type tags closes the autosuggest.
For the most up-to-date version, see my gitlab. If getting code from the link, you MUST wrap it in <script></script>.
Copy+Paste the code below into your dashboard page. (the code below is already wrapped in <script> tags so you will not need to make any changes, just copy+paste)
<script>
/*
Plugin name: Tag Autocomplete
Description: Adds autocomplete when you're typing tags on your edit post page
Author: ReedyBear
Author URI: https://reedybear.bearblog.dev/bearblog/
Author URI 2: https://gitlab.com/taeluf/other/bearblog-stuff
*/
(function() {
'use strict';
////// CONFIGURATIONS //////
const config = {
/* if 'false', then longer tags are displayed first in the list */
short_tags_come_first: true,
/** maximum number of suggestions to display. */
max_suggestions: 10,
/** set 'true' to show autosuggestion even when you the word-under-cursor is empty */
allow_empty_autosuggest: false,
/* As long as the word you typed is contained in your tag, it is displayed. Set to 'true' and only tags STARTING with what you typed will be displayed */
start_match_only: false,
/** When the autosuggestion box is visible, it is set to display: grid (*or whatever you configure here*) */
display_visible: "grid",
/** The styles defined on the autosuggestion box. You can also set this blank and put these styles on your theme. */
styles:
`
.autosuggest_box {
--background_color: #fff;
--text_color: #000;
--divider_color: #000;
--direction: row; /* 'column' for horizontal layout, 'row' for vertical layout */
display:none;
position: absolute;
grid-template-rows: auto;
grid-template-columns: auto;
grid-auto-flow: var(--direction);
grid-gap: 1px;
border: 1px solid var(--divider_color);
border-radius: 2px;
min-width: 10ch;
padding: 0px;
margin: 0px;
background: var(--divider_color);
}
.autosuggest_box > button {
background: var(--background_color);
border: none;
color: var(--text_color);
border-radius: 0px;
margin: 0px;
padding: 4px 8px;
text-wrap: nowrap;
}
.autosuggest_box > button:focus, .autosuggest_box > button:hover {
background-color: var(--text_color);
color: var(--background_color);
}
`,
};
const Plugin = {
/** the text node where you type your tags list */
text_node: null,
/** The start of the word (*tag*) your cursor is on */
start_index: null,
/** the end of the word (*tag*) your cursor is on */
end_index: null,
/** the node used to display autosuggestions */
autosuggest_box: null,
/* list of all tags printed in the attributes section */
tags: null,
};
/** Sets Plugin.tags to array of all tags defined in the attributes section */
Plugin.make_tag_list = function(){
const all_attributes = document.querySelector('form.post-form > details > p').textContent.split("\n");
const tags = [];
for (const entry of all_attributes){
if (entry.trim().startsWith('tags:')){
const all_tags = entry
.trim()
.substring(5)
.split(',')
.map(function(v){return v.trim()});
Plugin.tags = all_tags;
return;
}
}
console.log("Could not load taglist for tag autocomplete.");
Plugin.tags = [];
}
/** replace the currently-typed text with the selected tag's text. */
Plugin.select_current_tag = function(tag_text){
Plugin.text_node.textContent =
Plugin.text_node.textContent.substring(0, Plugin.start_index)
+ tag_text
+ Plugin.text_node.textContent.substring(Plugin.end_index);
const position = Plugin.start_index + tag_text.length;
// setting caret position modified from https://stackoverflow.com/questions/6249095/how-to-set-the-caret-cursor-position-in-a-contenteditable-element-div
const range = document.createRange();
const selection = window.getSelection();
range.setStart(Plugin.text_node, position);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
}
/**
* Generate the ui component and add it to the document, returning the node. Also setup the styles.
*/
Plugin.make_autosuggest_box = function(){
const post_header = document.querySelector('div#header_content');
const box = document.createElement('div');
Plugin.autosuggest_box = box;
box.contentEditable = false;
box.classList.add('autosuggest_box');
post_header.appendChild(box);
box.addEventListener('keydown',
function(event){
// prevent printing a new line in the header content box
if (event.key == 'Enter'
||event.key == ' '
||event.key == 'Backspace'
){
event.stopPropagation();
event.preventDefault();
}
}
);
box.addEventListener('keyup',
function(event){
if (event.target.tagName !== 'BUTTON')return;
if (event.key != 'Enter'
&& event.key != ' ')return;
if (event.key == 'Backspace'){
event.stopPropagation();
event.preventDefault();
return;
}
event.stopPropagation();
event.preventDefault();
Plugin.select_current_tag(event.target.innerText);
box.style.display = "none";
}
);
box.addEventListener('click',
function(event){
if (event.target.tagName !== 'BUTTON')return;
event.stopPropagation();
event.preventDefault();
Plugin.select_current_tag(event.target.innerText);
box.style.display = "none";
}
);
const head = document.querySelector('head');
const style_node = document.createElement('style');
style_node.appendChild(document.createTextNode(config.styles));
head.insertBefore(style_node, head.firstElementChild);
return box;
}
/** Move the autosuggestion box to appear beneath the node containing the taglist node */
Plugin.position_autosuggest = function(){
/* textnode positioning solution copied from https://stackoverflow.com/questions/16209153/how-to-get-the-position-and-size-of-a-html-text-node-using-javascript */
var range = document.createRange();
range.selectNodeContents(Plugin.text_node);
var rects = range.getClientRects();
Plugin.autosuggest_box.style.top = (rects[0].y+rects[0].height)+'px';
}
/** Show the list of suggested tags */
Plugin.fill_autosuggest = function(tags){
if (tags.length == 0){
Plugin.autosuggest_box.style.display = "none";
return;
}
if (tags.length > config.max_suggestions){
tags = tags.slice(0, config.max_suggestions);
}
Plugin.autosuggest_box.style.display = config.display_visible;
Plugin.autosuggest_box.innerHTML = '';
for (const tag of tags){
const node = document.createElement('button');
node.innerText = tag;
Plugin.autosuggest_box.appendChild(node);
}
}
/* If Escape is pressed, then hide the autocomplete box */
Plugin.escape_hide_autocomplete = function(event){
if (event.key == 'Escape') {
if (Plugin.autosuggest_box.style.display == "none") return;
Plugin.autosuggest_box.style.display = "none";
const range = document.createRange();
const selection = window.getSelection();
range.setStart(Plugin.text_node, Plugin.end_index);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
event.preventDefault();
event.stopPropagation();
return;
}
}
/** hide autocomplete if a click happens anywhere outside of the spot where you type tags */
Plugin.click_hide_autocomplete = function(event){
const header = document.querySelector('div#header_content');
if (!header.contains(event.target)){
Plugin.autosuggest_box.style.display = "none";
return;
}
if (!Plugin.is_tags_entry()){
Plugin.autosuggest_box.style.display = "none";
}
}
/** Check if the caret is int he portion of #header_content where the taglist is typed */
Plugin.is_tags_entry = function(){
/// NOTE: Plugin.text_node must be set in this function because its node is dynamic. User could erase the node, then make a newline and write "tags: ".
const sel = document.getSelection();
const node = sel.anchorNode;
const text = node.textContent;
if (text.startsWith('tags:')){
Plugin.text_node = node;
return true;
}
const prev_node = node.previousSibling;
if (prev_node == null)return false;
if (prev_node.innerText.indexOf("tags:") !== -1){
Plugin.text_node = node;
return true;
}
return false;
}
Plugin.show_autocomplete = function(event){
if (event.key == 'Tab'
|| event.key == 'Escape'
){
return;
} else if (event.key == 'Backspace'
&& event.target.tagName == 'BUTTON'){
return;
}
if (!Plugin.is_tags_entry()){
Plugin.autosuggest_box.style.display = "none";
return;
}
//////// GET WORD BEING CURRENTLY TYPED (and selection range) ////////
const sel = document.getSelection();
const node = sel.anchorNode;
const text = node.textContent;
// Find the beginning of the word currently being typed
// There should be a space, a comma, or a colon before any tag
const search_list = [' ', ',', ':'];
// we get the positions of all the potential start characters, relative to cursor position
// and use the position closest to where the cursor is
let start_index = -1;
for (const searchable of search_list){
const pos = text.lastIndexOf(searchable, sel.anchorOffset-1);
if (pos === -1)continue;
if (start_index < pos) start_index = pos;
}
if (start_index == -1){
// can't find a valid position before to start from
Plugin.autosuggest_box.style.display = "none";
return;
}
start_index = start_index+1;
Plugin.start_index = start_index;
// find the end of the tag being currently-typed.
let end_index = 999;
if (text.charAt(sel.anchorOffset) === ''){
// this means we're at the end of the string
end_index = sel.anchorOffset;
} else {
// the tag being currently-typed ends with a space or comma
const end_search_list = [' ', ','];
for (const searchable of end_search_list){
const pos = text.indexOf(searchable, sel.anchorOffset);
if (pos === -1)continue;
if (end_index > pos) end_index = pos;
}
}
const word_being_typed = text.substring(start_index, end_index).trim();
if (word_being_typed === ''
&& config.allow_empty_autosuggest === false
){
Plugin.autosuggest_box.style.display = "none";
return;
}
Plugin.end_index = end_index;
const matching = Plugin.tags
.filter(
function(v){
if (config.start_match_only === true){
if (v.indexOf(word_being_typed) === 0)return true;
} else if (v.indexOf(word_being_typed)!==-1){
return true;
}
}
).sort(
function(a,b){
if (config.short_tags_come_first === false){
// swap a & b to swap the sort order
const c = a;
a = b;
b = c;
}
// When the typed-word appears earlier in the tag, the tag appears first in the list of suggestions
const diff = a.indexOf(word_being_typed) - b.indexOf(word_being_typed);
if (diff == 0){
// When two tags would appear in the same spot, the shorter tag appears first
return a.length - b.length;
}
return diff
}
);
Plugin.position_autosuggest();
Plugin.fill_autosuggest(matching);
}
/** setup the plugin */
Plugin.activate = function(){
const is_edit_post_page = document.querySelector('body.edit-page');
if (is_edit_post_page == null)return;
const body_content = document.querySelector('textarea#body_content');
if (body_content == null) return;
const post_header = document.querySelector('div#header_content');
if (post_header == null) return;
Plugin.make_tag_list();
Plugin.make_autosuggest_box();
post_header.addEventListener('keyup', Plugin.show_autocomplete);
document.addEventListener('keyup', Plugin.escape_hide_autocomplete);
document.addEventListener('click', Plugin.click_hide_autocomplete);
};
if (document.readyState === "loading"){
document.addEventListener('DOMContentLoaded', Plugin.activate);
} else {
Plugin.activate();
}
})();
</script>
2026-09-17 06:44:00
I'm writing my legislators about this. Below is my email about this, which I've sent to my state reps. I sent a similar letter to my rep in U.S. Congress, and I wrote a similar Letter To The Editor to be (hopefully) published in my local paper. I've also sent a similar message to my state's attorney general.
Apple announced a feature for their upcoming smart watch which will listen to everything 24/7 and transcribe notes for the wearer. In [my state], we have a two-party consent rule for recording, and this always-on feature would likely violate that law in many contexts, and it would violate basic privacy rights we have, to not be recorded without our consent. Apple is arguing that the "listening" is not technically recording because they are just "processing" the audio data then deleting the data. This is, perhaps, debateable. But the end result is to produce transcriptions and summaries of conversations, which is very near recording without consent.
Please act quickly to protect our privacy rights in [my sate], before a feature like this becomes ubiquitous. This kind of feature should be outright banned in [my state]. There should be clear legislation to this effect, and the question of "is this an illegal recording" should not be left to the courts. By the time it hits the courts, our privacy rights will have already been violated, and many thousands of people will already be wearing these watches and "recording" people without consent. Our legislature should be proactive in protecting us from new technology that violates our rights, and it should not be up to the courts to do the legislature's job of governing.
Section added for my Attorney General:
The Attorney General should promptly publish an opinion on this matter with their legal interpretation, identifying this as a form of recording protected by our existing laws regarding audio recording. Our AG should also work with the legislature to craft policy that will clearly ban this practice of always-listening and transcribing. Regardless of the AG's official opinion with regard to our existing laws, new legislation should be passed that does not leave any room for interpretation by the courts.
2026-09-16 04:34:00
In the U.S., we have allowed the courts (particularly the Supereme Court) to legislate matters that should be decided by Congress.
A recent one is the question of whether "prediction markets" are financial things regulated federally by the SEC or if they are gambling, which is regulated by each state individually. There are two active court cases right now, where one court says it's federal authority (investment b.s.), and the other says it is state (gambling).
This is an issue that should have been resolved by Congress quite awhile ago, because jesus fucking christ, placing a "prediction" on whether a politician will say [some word] at [some conference] is a fucking bet. It is gambling. You are guessing about what will happen in the future and hoping to win based on your guess being right. It is fucking gambling.
And when these "prediction market" companies tried to use legal loopholes to dodge gambling regulations ... Congress should have stepped in within the week and said "FUCK NO". They should have immediately passed a bill saying: That's gambling, bro.
But our Congress (collectively) does not do it's job, and so courts are looking at laws already on the books, along with legal history, and deciding what the law actually is. They're interpreting laws come to conclusions that are not clearly prescribed by Congress. And that's a problem.
I don't mind this kind of behavior for individual cases with narrow outcomes. I don't mind this kind of behavior for temporary resolution while Congress resolves complex things. But I have a huge problem with courts deciding what the laws of our land are, beyond the narrow scope of their interpretive powers - even when they get it (morally) right.
Another legislative failure is Roe v Wade (Casey v Planned Parenthood was a subsequent case that actually superceded Roe and still protected abortion rights). The fact that our country leaned on a supreme court ruling for nearly 50 years is ABHORRENT. Abortion rights absolutely should be protected. And the courts should make a ruling to protect an individual who's rights are being violated. But the courts should not decide what the law of the land is.
Congress should have passed clear legislation enforcing abortion rights FIFTY YEARS AGO. But they didn't. So when we got a new court, these 9 unelected people just ... changed their fucking minds.
It's a shame, and it's an utter failure of our democracy.
2026-09-13 02:01:12
It's a very good book. I started reading it probably around 2 years ago. Then I lost interest and I put it down.
I was waiting on Book 2 of Jemisin's Dreamblood series to come into my library, so I picked up this book to finish during my pre-bed reading time.
I don't know why I lost interest before. I think I kinda just wasn't as good of a reader then? I'm way more consistent and enjoy it more now than I did when I read this book.
But the story is good. The main character is interesting - other characters aren't all that developed. It's a fairly short book, ~290 pages. It's a pretty easy read.
When I returned, I found myself very quickly interested in the plot, and yearning to read more, and find out what's going to happen. Most of the individual developments in the story feel somewhat small, and I think that's what maybe dulled it for me before? But now that I've finished it, I think that's one of it's strengths. It's dry, in a way, I guess. But it's good. Am a fan.
It doesn't make my "favorite" books list, but it's close.
2026-09-11 04:42:05
I'm 34 now. When I was in my mid-20s, I was meditating a lot, doing a lot of yoga. I did some psychedelics, and I was very interested in the philosophical questions of WHAT IS REALITY.
Most of my lucid dreams back then involved me taking control of the dream. I'd test the physics (by jumping), and I'd run around really fast and jump high and idk. Most of my lucid dreams were ones I took control of. And most of the time, becoming lucid would cause me to wake up.
Over the last few years, I've began to become lucid a few times, but I almost immediately try to take control, and then it wakes me up, so I don't really get to experience a lucid dream.
I've again been meditating and doing yoga consistently for many months now. It's been really good for me. Good for my mental health, good for my quality of sleep, good for my physical wellbeing (I have very little chronic pain now!)
Well. My interest in lucid dreaming had sparked again recently (probably in part because of the book i just read). For a long time I was reluctant to try because my mental health has been poor, my sleep has been poor, and I didn't wanna mess with things (sometimes lucid dreaming would make me wake up so much tired).
But two nights ago, I started talking with myself about lucid dreaming, trying to encourage my mind to do it.
Then last night, while I was reading, I stopped and asked myself "Am I dreaming?" then I looked around the room, kind of took stock of everything, and assessed everything as normal. I will say, there's this trippy sense that maybe this is a dream, from a version of my self in another, "higher" plane. Like life is the dream that my soul dreams.
But no, I was not dreaming.
So then for my meditation before bed, I prepped myself some more. I started with my deep breathing, with my breath-awareness, with my current mantra-of-choice: (inhale) Joy within, (exhale) Joy around. (or did I do "love" last night?)
Then I went into dream-related mantra.
(inhale) I am a dreamer (exhale) Am I dreaming?
and later
(inhale) Am I dreaming? (exhale) Am I awake?
and so-on. I did the breathwork and the mantra for a little bit. Whole meditation was probably 10 mins or less.
But then, during one of my dreams last night, I hear "Am I dreaming?"
And yes, I fucking was. And then I knew I was. My old instinct kicked in, to try to drive the dream. But I talked myself down, settled myself. My intent did affect the dream some, but it was mild, and I didn't let the intent take over.
Mostly, I just "sat" there, inside my dream-self, and just watched the dream happen.
It was nice. I want to do more of this. I want to practice the watching-while-lucid. At some point, I do want to engage and drive the dreams, too. But I need to practice the calm, patient awareness first. And I also just want to watch things happen sometimes.
Sidenote: It's an open question in my mind whether my soul actually has any will. The body and mind function in the world, and are clearly dynamic. I experience intention and choice. Thoughts and actions feel like they are mine, like I am doing them. But I often wonder whether I - the soul, the part that experiences the body and mind - have any influence over my body/mind. It might just be an illusion. I may not be a creator of action, but merely an observer of a body/mind that acts.
2026-09-11 04:28:34
I LOVE this book. I'm excited for the second book - waiting for it to come into my library.
It's setting is largely based on Ancient Egypt (she talks about this in an "interview" at the end of the book). The magic system revolves around dream-things.
I don't want to spoil anything. I want you to read it. READ IT. It's so good.
Goodbye.