I guess it so happens with my background -- it was the debounce that stood out. I've seen tricks with the url bar and the compression is not too much more than a call to the browser API.
Most of the frontend I've written (that was complicated enough to warrant a timeout) was in a framework or in elm -- so I guess I hadn't really seen a debouncer written like that before. And now that you've said this and I've poked around - i see it's a conventional implementation.
But as I read through the (delightfully simple and short) implementation for this project - that's what popped out to me. So much - that I even had to comment!
Every time that you want to create a new debouncer, a new local variable (named timer) gets created, and a function using that timer via capturing from the lexical scope is returned. Whenever the returned function is called, it schedules the real work to be done ms milliseconds later, cancelling the currently scheduled execution if it has not yet happened. Thus if the same thing is triggered multiple times with intervals smaller than ms milliseconds between the attempts, it only gets executed once, approximately ms milliseconds after the last attempt to trigger it.
You also sometimes need the opposite of a debounce, which is called a throttle, where things happen immediately but the same event can't happen again until at least X milliseconds later. That is typically something like
function throttle(ms, fn) {
let shouldRun = true
return (...args) => {
if (shouldRun) {
shouldRun = false
setTimeout(() => { shouldRun = true }, ms)
fn()
}
}
There are two dimensions of opposite here: whether to do work at the beginning or at the end, and whether to do work periodically or once per long dense series! (Of course any of the combinations can be the best fit, and it is always good to choose consciously between them)
The debounce thing, the browser encryption APIs, patterns using ? and ??, the fact that contenteditable can be plain text only. The null byte delimiter trick is neat too.
Just a note for anyone thinking of storing massive amounts of data in the URL for anything substantial: browsers typically support only a couple of thousand characters in the URL. See: https://stackoverflow.com/a/417184.
That classic StackOverflow answer is talking about the bit that comes before the # - the bit that's communicated to servers and through HTTP proxies and suchlike.
Before clicking, I was gonna be like "i can do it in one line, har har, <textarea> gg" but curious, why didn't you use the textarea here? Content editable is legit cool too, fun to show that off to people who have no idea you can do it, but curious what your specific thought was in choosing it here here since the code and functionality would be pretty much the same (maybe like a one line diff to change the border? lol).
Oh ok, yeah, that makes sense. I was thinking just making the textarea be full screen (like height: 100vh; width: 100vw;) but the contenteditable does work better, the textarea never quite seems to fit perfectly even with my css stuff so that's a nice benefit.
CompressionStream is nice.. Didn't know about that. Back when I used to do more JS that would have been so nice compared to adding additional dependencies. But seems like browsers only added that API a couple of years ago.
An odd comment when the thing seems to work fine in safari, and the only other mention of safari in the comments is the fragment size limit being unspecified?
strickinato | a day ago
Source code does a neat trick for event debouncing 😀 --
https://github.com/antonmedv/textarea/blob/23193b0a5e84a6d0cce53a9173d7867c906ff2a8/index.html#L103C1-L109C4
[OP] antonmedv | a day ago
I thought debounce was kinda normal)
strickinato | a day ago
LOL -- 🤷. The whole thing is a neat trick!
I guess it so happens with my background -- it was the debounce that stood out. I've seen tricks with the url bar and the compression is not too much more than a call to the browser API.
Most of the frontend I've written (that was complicated enough to warrant a timeout) was in a framework or in elm -- so I guess I hadn't really seen a debouncer written like that before. And now that you've said this and I've poked around - i see it's a conventional implementation.
But as I read through the (delightfully simple and short) implementation for this project - that's what popped out to me. So much - that I even had to comment!
carlana | a day ago
That debounce is pretty standard JS. See for example in my code here from eight years ago: https://github.com/golang/tools/blame/5ca1b5722305658ddc54bb061ac34eb3c0c29cf4/cmd/present/static/slides.js#L462-L470
[OP] antonmedv | a day ago
I think a neat trick is compression)
xnacly | a day ago
What even is that
k749gtnc9l3w | a day ago
Every time that you want to create a new debouncer, a new local variable (named
timer) gets created, and a function using that timer via capturing from the lexical scope is returned. Whenever the returned function is called, it schedules the real work to be donemsmilliseconds later, cancelling the currently scheduled execution if it has not yet happened. Thus if the same thing is triggered multiple times with intervals smaller thanmsmilliseconds between the attempts, it only gets executed once, approximatelymsmilliseconds after the last attempt to trigger it.carlana | a day ago
You also sometimes need the opposite of a debounce, which is called a throttle, where things happen immediately but the same event can't happen again until at least X milliseconds later. That is typically something like
k749gtnc9l3w | a day ago
There are two dimensions of opposite here: whether to do work at the beginning or at the end, and whether to do work periodically or once per long dense series! (Of course any of the combinations can be the best fit, and it is always good to choose consciously between them)
[OP] antonmedv | a day ago
In case you missed it: it is possible to style textarea via CSS and share it.
jrgtt | 12 hours ago
Color me impressed -- this is neat!
simonw | a day ago
I love this. I picked up several new tricks from reading the source code.
louwers | a day ago
Which ones?
simonw | 19 hours ago
The debounce thing, the browser encryption APIs, patterns using ? and ??, the fact that contenteditable can be plain text only. The null byte delimiter trick is neat too.
[OP] antonmedv | a day ago
Thanks!)
metahost | a day ago
Just a note for anyone thinking of storing massive amounts of data in the URL for anything substantial: browsers typically support only a couple of thousand characters in the URL. See: https://stackoverflow.com/a/417184.
[OP] antonmedv | a day ago
Here is the Crime and Punishment by Fyodor Dostoevsky
dz4k | a day ago
wow that makes my browser hang for a solid two seconds when I click the URL bar.
[OP] antonmedv | a day ago
Works surprisingly good on iphone
strongoose | 12 hours ago
😅
simonw | a day ago
That classic StackOverflow answer is talking about the bit that comes before the
#- the bit that's communicated to servers and through HTTP proxies and suchlike.This notes app is using the
#fragmentportion.Chrome documents that limit as 2MB: https://chromium.googlesource.com/chromium/src/%2B/HEAD/docs/security/url_display_guidelines/url_display_guidelines.md?utm_source=chatgpt.com#url-length - it looks like antonmedv's Chrime and Punishment demo is 534KB thanks to compression.
I couldn't find documented limits for Firefox and Safari.
Update: I had Claude Code for web checkout Firefox and WebKit and Chromium and go hunting for URL length limits, here's the result from that: https://github.com/simonw/research/blob/main/url-limits-investigation/README.md
Chrome limit is 2MB, Firefox is 1MB, WebKit is apparently String::MaxLength (INT32_MAX) aka 2GB.
jaredkrinke | a day ago
op | a day ago
similarly: https://github.com/topaz/paste, in about 200 LOC. try here: https://topaz.github.io/paste/.
dz4k | a day ago
Could you fit
word-break: break-wordin there?https://textarea.my/#7YrBDYNAEANbMf80hcQaDu9yaEE6pbE0kMayKSH_-DGSx-4bjv7A2J5oF9pd7cbI-TzbsU4T5muRF94vGwtdoMJlyZBbBgsEtJewXlsWYSnn92O5u1iSFiH-80s-
[OP] antonmedv | a day ago
For me the long word is correctly wrapped into pieces. What browser do you use?
Please try with overflow-wrap: break-word;
dz4k | a day ago
overflow-wrap: break-word; works. I'm on Firefox.
cgc373 | 14 hours ago
A while ago I set up this JavaScript bookmarklet:
data:text/html,<body contenteditable style="line-height:1.5;font-size:20px;">It's zserge's idea., discussed on lobste.rs some years ago.
apg | 35 minutes ago
But where do I store the notes URLs? Hmm.
[OP] antonmedv | 26 minutes ago
Bookmarks!
JamieTanna | 21 hours ago
Is it possible to out a license on it? Right now it'll default to
All-Rights-Reservedwithout an explicit license ☺️adam_d_ruppe | 7 hours ago
Before clicking, I was gonna be like "i can do it in one line, har har,
<textarea>gg" but curious, why didn't you use the textarea here? Content editable is legit cool too, fun to show that off to people who have no idea you can do it, but curious what your specific thought was in choosing it here here since the code and functionality would be pretty much the same (maybe like a one line diff to change the border? lol).[OP] antonmedv | 6 hours ago
textarea can’t grow with content automatically. Contenteditable plaintext-only basically is the same as textarea but grows with content.
adam_d_ruppe | an hour ago
Oh ok, yeah, that makes sense. I was thinking just making the textarea be full screen (like
height: 100vh; width: 100vw;) but the contenteditable does work better, the textarea never quite seems to fit perfectly even with my css stuff so that's a nice benefit.reezer | 4 hours ago
CompressionStream is nice.. Didn't know about that. Back when I used to do more JS that would have been so nice compared to adding additional dependencies. But seems like browsers only added that API a couple of years ago.
alper | a day ago
It's fun how increasingly more things just don't work anymore on Safari.
I don't keep up with web development. How bad is this browser?
masklinn | a day ago
An odd comment when the thing seems to work fine in safari, and the only other mention of safari in the comments is the fragment size limit being unspecified?