Skip to content

Instantly share code, notes, and snippets.

@tobek
Last active April 3, 2024 15:55
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tobek/a1458928168627714e91c5ff6cf3f690 to your computer and use it in GitHub Desktop.
Save tobek/a1458928168627714e91c5ff6cf3f690 to your computer and use it in GitHub Desktop.
Roam bookmarklet for rich link embeds, read later, and bookmark management

Setup:

  1. Create a browser bookmark (e.g. in bookmarks bar) where the URL is the text in bookmarklet below (open this "raw" link so it's not truncated). Yes it's huge, that's fine. You can name the bookmark "Roam Link" or whatever you like.
    • Alternately, go to this page for easier drag-and-drop bookmarklet setup (GitHub doesn't allow javascript links in gists, sorry)
  2. Go to or create a Roam page called roam/css, and create a CSS code block (``` for code block, then change the language selector dropdown to CSS) and paste the CSS below.

Usage:

  1. On the page you want to create a link embed of, hit the bookmarklet (optional: highlight some text first and it'll be included as a quote)
  2. Go to Roam and paste
  3. If you want, add more tags at the beginning, turn it into a todo for reading later (ctrl/cmd + enter), etc.

By default this link embed uses a #link tag, and the CSS styling targets this. If you want to change this make sure to change in both the bookmarklet code and the CSS.

Keywords picked up from metadata on the page are automatically included as tags. Also, an iframe embed of the actual page is included (only if URL is HTTPS, otherwise iframe won't work - and sometimes iframe doesn't work anyway due to X-Frame-Options header). Collapse the block (ctrl/cmd + up) if you don't want to see it, or change the code to exclude it.

With the iframe you can in theory then read the page inside Roam while taking notes alongside it - sort of a mobile view of the page. Note that creating a new sibling block will reload the iframe, so if you want to take multiple blocks of notes while reading, you'll need to nest them under a parent block that is a sibling of the iframe block. If you highlight text to quote, it is automatically nested like this.

For "Read Later" functionality, convert the link embeds into TODOs and create (and star!) a page with the following query:

{{query: {and: [[TODO]] [[link]]}}}

Suggestions/updates welcome. Would be very easy to make this into a browser extension, or add this functionality to an existing one like roam-highlighter. Could also make a Roam42 SmartBlock (though you'd lose highlight feature, and fetching the page metadata would be harder).

Read later example:

roam read later bookmarks

Inline notes using iframe:

roam inline notes

javascript:var%20%24jscomp%3D%24jscomp%7C%7C%7B%7D%3B%24jscomp.scope%3D%7B%7D%3B%24jscomp.arrayIteratorImpl%3Dfunction(b)%7Bvar%20c%3D0%3Breturn%20function()%7Breturn%20c%3Cb.length%3F%7Bdone%3A!1%2Cvalue%3Ab%5Bc%2B%2B%5D%7D%3A%7Bdone%3A!0%7D%7D%7D%3B%24jscomp.arrayIterator%3Dfunction(b)%7Breturn%7Bnext%3A%24jscomp.arrayIteratorImpl(b)%7D%7D%3B%24jscomp.ASSUME_ES5%3D!1%3B%24jscomp.ASSUME_NO_NATIVE_MAP%3D!1%3B%24jscomp.ASSUME_NO_NATIVE_SET%3D!1%3B%24jscomp.SIMPLE_FROUND_POLYFILL%3D!1%3B%24jscomp.ISOLATE_POLYFILLS%3D!1%3B%24jscomp.FORCE_POLYFILL_PROMISE%3D!1%3B%24jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION%3D!1%3B%24jscomp.defineProperty%3D%24jscomp.ASSUME_ES5%7C%7C%22function%22%3D%3Dtypeof%20Object.defineProperties%3FObject.defineProperty%3Afunction(b%2Cc%2Cd)%7Bif(b%3D%3DArray.prototype%7C%7Cb%3D%3DObject.prototype)return%20b%3Bb%5Bc%5D%3Dd.value%3Breturn%20b%7D%3B%24jscomp.getGlobal%3Dfunction(b)%7Bb%3D%5B%22object%22%3D%3Dtypeof%20globalThis%26%26globalThis%2Cb%2C%22object%22%3D%3Dtypeof%20window%26%26window%2C%22object%22%3D%3Dtypeof%20self%26%26self%2C%22object%22%3D%3Dtypeof%20global%26%26global%5D%3Bfor(var%20c%3D0%3Bc%3Cb.length%3B%2B%2Bc)%7Bvar%20d%3Db%5Bc%5D%3Bif(d%26%26d.Math%3D%3DMath)return%20d%7Dthrow%20Error(%22Cannot%20find%20global%20object%22)%3B%7D%3B%24jscomp.global%3D%24jscomp.getGlobal(this)%3B%24jscomp.IS_SYMBOL_NATIVE%3D%22function%22%3D%3D%3Dtypeof%20Symbol%26%26%22symbol%22%3D%3D%3Dtypeof%20Symbol(%22x%22)%3B%24jscomp.TRUST_ES6_POLYFILLS%3D!%24jscomp.ISOLATE_POLYFILLS%7C%7C%24jscomp.IS_SYMBOL_NATIVE%3B%24jscomp.polyfills%3D%7B%7D%3B%24jscomp.propertyToPolyfillSymbol%3D%7B%7D%3B%24jscomp.POLYFILL_PREFIX%3D%22%24jscp%24%22%3Bvar%20%24jscomp%24lookupPolyfilledValue%3Dfunction(b%2Cc)%7Bvar%20d%3D%24jscomp.propertyToPolyfillSymbol%5Bc%5D%3Bif(null%3D%3Dd)return%20b%5Bc%5D%3Bd%3Db%5Bd%5D%3Breturn%20void%200!%3D%3Dd%3Fd%3Ab%5Bc%5D%7D%3B%24jscomp.polyfill%3Dfunction(b%2Cc%2Cd%2Ce)%7Bc%26%26(%24jscomp.ISOLATE_POLYFILLS%3F%24jscomp.polyfillIsolated(b%2Cc%2Cd%2Ce)%3A%24jscomp.polyfillUnisolated(b%2Cc%2Cd%2Ce))%7D%3B%24jscomp.polyfillUnisolated%3Dfunction(b%2Cc%2Cd%2Ce)%7Bd%3D%24jscomp.global%3Bb%3Db.split(%22.%22)%3Bfor(e%3D0%3Be%3Cb.length-1%3Be%2B%2B)%7Bvar%20f%3Db%5Be%5D%3Bif(!(f%20in%20d))return%3Bd%3Dd%5Bf%5D%7Db%3Db%5Bb.length-1%5D%3Be%3Dd%5Bb%5D%3Bc%3Dc(e)%3Bc!%3De%26%26null!%3Dc%26%26%24jscomp.defineProperty(d%2Cb%2C%7Bconfigurable%3A!0%2Cwritable%3A!0%2Cvalue%3Ac%7D)%7D%3B%24jscomp.polyfillIsolated%3Dfunction(b%2Cc%2Cd%2Ce)%7Bvar%20f%3Db.split(%22.%22)%3Bb%3D1%3D%3D%3Df.length%3Be%3Df%5B0%5D%3Be%3D!b%26%26e%20in%20%24jscomp.polyfills%3F%24jscomp.polyfills%3A%24jscomp.global%3Bfor(var%20h%3D0%3Bh%3Cf.length-1%3Bh%2B%2B)%7Bvar%20g%3Df%5Bh%5D%3Bif(!(g%20in%20e))return%3Be%3De%5Bg%5D%7Df%3Df%5Bf.length-1%5D%3Bd%3D%24jscomp.IS_SYMBOL_NATIVE%26%26%22es6%22%3D%3D%3Dd%3Fe%5Bf%5D%3Anull%3Bc%3Dc(d)%3Bnull!%3Dc%26%26(b%3F%24jscomp.defineProperty(%24jscomp.polyfills%2Cf%2C%7Bconfigurable%3A!0%2Cwritable%3A!0%2Cvalue%3Ac%7D)%3Ac!%3D%3Dd%26%26(void%200%3D%3D%3D%24jscomp.propertyToPolyfillSymbol%5Bf%5D%26%26(%24jscomp.propertyToPolyfillSymbol%5Bf%5D%3D%24jscomp.IS_SYMBOL_NATIVE%3F%24jscomp.global.Symbol(f)%3A%24jscomp.POLYFILL_PREFIX%2Bf)%2C%24jscomp.defineProperty(e%2C%24jscomp.propertyToPolyfillSymbol%5Bf%5D%2C%7Bconfigurable%3A!0%2Cwritable%3A!0%2Cvalue%3Ac%7D)))%7D%3B%24jscomp.initSymbol%3Dfunction()%7B%7D%3B%24jscomp.polyfill(%22Symbol%22%2Cfunction(b)%7Bif(b)return%20b%3Bvar%20c%3Dfunction(f%2Ch)%7Bthis.%24jscomp%24symbol%24id_%3Df%3B%24jscomp.defineProperty(this%2C%22description%22%2C%7Bconfigurable%3A!0%2Cwritable%3A!0%2Cvalue%3Ah%7D)%7D%3Bc.prototype.toString%3Dfunction()%7Breturn%20this.%24jscomp%24symbol%24id_%7D%3Bvar%20d%3D0%2Ce%3Dfunction(f)%7Bif(this%20instanceof%20e)throw%20new%20TypeError(%22Symbol%20is%20not%20a%20constructor%22)%3Breturn%20new%20c(%22jscomp_symbol_%22%2B(f%7C%7C%22%22)%2B%22_%22%2Bd%2B%2B%2Cf)%7D%3Breturn%20e%7D%2C%22es6%22%2C%22es3%22)%3B%24jscomp.polyfill(%22Symbol.iterator%22%2Cfunction(b)%7Bif(b)return%20b%3Bb%3DSymbol(%22Symbol.iterator%22)%3Bfor(var%20c%3D%22Array%20Int8Array%20Uint8Array%20Uint8ClampedArray%20Int16Array%20Uint16Array%20Int32Array%20Uint32Array%20Float32Array%20Float64Array%22.split(%22%20%22)%2Cd%3D0%3Bd%3Cc.length%3Bd%2B%2B)%7Bvar%20e%3D%24jscomp.global%5Bc%5Bd%5D%5D%3B%22function%22%3D%3D%3Dtypeof%20e%26%26%22function%22!%3Dtypeof%20e.prototype%5Bb%5D%26%26%24jscomp.defineProperty(e.prototype%2Cb%2C%7Bconfigurable%3A!0%2Cwritable%3A!0%2Cvalue%3Afunction()%7Breturn%20%24jscomp.iteratorPrototype(%24jscomp.arrayIteratorImpl(this))%7D%7D)%7Dreturn%20b%7D%2C%22es6%22%2C%22es3%22)%3B%24jscomp.iteratorPrototype%3Dfunction(b)%7Bb%3D%7Bnext%3Ab%7D%3Bb%5BSymbol.iterator%5D%3Dfunction()%7Breturn%20this%7D%3Breturn%20b%7D%3B%24jscomp.createTemplateTagFirstArg%3Dfunction(b)%7Breturn%20b.raw%3Db%7D%3B%24jscomp.createTemplateTagFirstArgWithRaw%3Dfunction(b%2Cc)%7Bb.raw%3Dc%3Breturn%20b%7D%3B(function()%7Bvar%20b%3Dfunction(h)%7Bfunction%20g(v)%7Bif(n%5Bv%5D)return%20n%5Bv%5D.exports%3Bvar%20t%3Dn%5Bv%5D%3D%7Bexports%3A%7B%7D%2Cid%3Av%2Cloaded%3A!1%7D%3Breturn%20h%5Bv%5D.call(t.exports%2Ct%2Ct.exports%2Cg)%2Ct.loaded%3D!0%2Ct.exports%7Dvar%20n%3D%7B%7D%3Breturn%20g.m%3Dh%2Cg.c%3Dn%2Cg.p%3D%22%22%2Cg(0)%7D(%5Bfunction(h%2Cg%2Cn)%7Bh.exports%3Dn(1)%7D%2Cfunction(h%2Cg%2Cn)%7Bfunction%20v(a)%7Breturn%20function(k%2Cl)%7Bfor(var%20p%3D0%2Cm%3Dvoid%200%2Cq%3D0%3Bq%3Ca.rules.length%3Bq%2B%2B)%7Bvar%20r%3Dt(a.rules%5Bq%5D%2C2)%2Cw%3Dr%5B1%5D%2Cx%3DArray.from(k.querySelectorAll(r%5B0%5D))%3Bif(x.length)%7Br%3D!0%3Bvar%20E%3D!1%2CF%3Dvoid%200%3Btry%7Bfor(var%20G%2CB%3Dx%5BSymbol.iterator%5D()%3B!(r%3D(G%3DB.next()).done)%3Br%3D!0)%7Bvar%20H%3DG.value%2CA%3Da.rules.length-q%3Bif(a.scorers)%7Bx%3D!0%3Bvar%20I%3D!1%2CJ%3Dvoid%200%3Btry%7Bfor(var%20K%2CC%3Da.scorers%5BSymbol.iterator%5D()%3B!(x%3D(K%3DC.next()).done)%3Bx%3D!0)%7Bvar%20N%3DK.value%2CL%3DN(H%2CA)%3BL%26%26(A%3DL)%7D%7Dcatch(z)%7BI%3D!0%2CJ%3Dz%7Dfinally%7Btry%7B!x%26%26C%5B%22return%22%5D%26%26C%5B%22return%22%5D()%7Dfinally%7Bif(I)throw%20J%3B%7D%7D%7DA%3Ep%26%26(p%3DA%2Cm%3Dw(H))%7D%7Dcatch(z)%7BE%3D!0%2CF%3Dz%7Dfinally%7Btry%7B!r%26%26B%5B%22return%22%5D%26%26B%5B%22return%22%5D()%7Dfinally%7Bif(E)throw%20F%3B%7D%7D%7D%7Dif(!m%26%26a.defaultValue%26%26(m%3Da.defaultValue(l))%2Cm)%7Bif(a.processors)%7Bp%3D!0%3Bq%3D!1%3Bw%3Dvoid%200%3Btry%7Bfor(var%20M%2CD%3Da.processors%5BSymbol.iterator%5D()%3B!(p%3D(M%3DD.next()).done)%3Bp%3D!0)%7Bvar%20O%3DM.value%3Bm%3DO(m%2Cl)%7D%7Dcatch(z)%7Bq%3D!0%2Cw%3Dz%7Dfinally%7Btry%7B!p%26%26D%5B%22return%22%5D%26%26D%5B%22return%22%5D()%7Dfinally%7Bif(q)throw%20w%3B%7D%7D%7Dreturn%20m.trim%26%26(m%3Dm.trim())%2Cm%7D%7D%7Dvar%20t%3Dfunction()%7Breturn%20function(a%2Ck)%7Bif(Array.isArray(a))return%20a%3Bif(Symbol.iterator%20in%20Object(a))%7Bvar%20l%3D%5B%5D%2Cp%3D!0%2Cm%3D!1%2Cq%3Dvoid%200%3Btry%7Bfor(var%20r%2Cw%3Da%5BSymbol.iterator%5D()%3B!(p%3D(r%3Dw.next()).done)%26%26(l.push(r.value)%2C!k%7C%7Cl.length!%3D%3Dk)%3Bp%3D!0)%3B%7Dcatch(x)%7Bm%3D!0%2Cq%3Dx%7Dfinally%7Btry%7B!p%26%26w%5B%22return%22%5D%26%26w%5B%22return%22%5D()%7Dfinally%7Bif(m)throw%20q%3B%7D%7Dreturn%20l%7Dthrow%20new%20TypeError(%22Invalid%20attempt%20to%20destructure%20non-iterable%20instance%22)%3B%7D%7D()%3Bg%3Dn(2)%3Bvar%20u%3Dg.makeUrlAbsolute%2Cy%3D(g.parseUrl%2C%7Bdescription%3A%7Brules%3A%5B%5B'meta%5Bproperty%3D%22og%3Adescription%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%2C%5B'meta%5Bname%3D%22description%22%20i%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%5D%7D%2Cicon%3A%7Brules%3A%5B%5B'link%5Brel%3D%22apple-touch-icon%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22href%22)%7D%5D%2C%5B'link%5Brel%3D%22apple-touch-icon-precomposed%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22href%22)%7D%5D%2C%5B'link%5Brel%3D%22icon%22%20i%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22href%22)%7D%5D%2C%5B'link%5Brel%3D%22fluid-icon%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22href%22)%7D%5D%2C%5B'link%5Brel%3D%22shortcut%20icon%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22href%22)%7D%5D%2C%5B'link%5Brel%3D%22Shortcut%20Icon%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22href%22)%7D%5D%2C%5B'link%5Brel%3D%22mask-icon%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22href%22)%7D%5D%5D%2Cscorers%3A%5Bfunction(a%2Ck)%7Bvar%20l%3Da.getAttribute(%22sizes%22)%3Bif(l%26%26(l%3Dl.match(%2F%5Cd%2B%2Fg)))return%20l%5B0%5D%7D%5D%2CdefaultValue%3Afunction(a)%7Breturn%22favicon.ico%22%7D%2Cprocessors%3A%5Bfunction(a%2Ck)%7Breturn%20u(k.url%2Ca)%7D%5D%7D%2Cimage%3A%7Brules%3A%5B%5B'meta%5Bproperty%3D%22og%3Aimage%3Asecure_url%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%2C%5B'meta%5Bproperty%3D%22og%3Aimage%3Aurl%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%2C%5B'meta%5Bproperty%3D%22og%3Aimage%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%2C%5B'meta%5Bname%3D%22twitter%3Aimage%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%2C%5B'meta%5Bproperty%3D%22twitter%3Aimage%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%2C%5B'meta%5Bname%3D%22thumbnail%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%5D%2Cprocessors%3A%5Bfunction(a%2Ck)%7Breturn%20u(k.url%2Ca)%7D%5D%7D%2Ckeywords%3A%7Brules%3A%5B%5B'meta%5Bname%3D%22keywords%22%20i%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%5D%2Cprocessors%3A%5Bfunction(a%2Ck)%7Breturn%20a.split(%22%2C%22).map(function(l)%7Breturn%20l.trim()%7D)%7D%5D%7D%2Ctitle%3A%7Brules%3A%5B%5B'meta%5Bproperty%3D%22og%3Atitle%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%2C%5B'meta%5Bname%3D%22twitter%3Atitle%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%2C%5B'meta%5Bproperty%3D%22twitter%3Atitle%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%2C%5B'meta%5Bname%3D%22hdl%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%2C%5B%22title%22%2Cfunction(a)%7Breturn%20a.text%7D%5D%5D%7D%2Clanguage%3A%7Brules%3A%5B%5B%22html%5Blang%5D%22%2Cfunction(a)%7Breturn%20a.getAttribute(%22lang%22)%7D%5D%2C%5B'meta%5Bname%3D%22language%22%20i%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%5D%2Cprocessors%3A%5Bfunction(a%2Ck)%7Breturn%20a.split(%22-%22)%5B0%5D%7D%5D%7D%2Ctype%3A%7Brules%3A%5B%5B'meta%5Bproperty%3D%22og%3Atype%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%5D%7D%2Curl%3A%7Brules%3A%5B%5B%22a.amp-canurl%22%2Cfunction(a)%7Breturn%20a.getAttribute(%22href%22)%7D%5D%2C%5B'link%5Brel%3D%22canonical%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22href%22)%7D%5D%2C%5B'meta%5Bproperty%3D%22og%3Aurl%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%5D%2CdefaultValue%3Afunction(a)%7Breturn%20a.url%7D%2Cprocessors%3A%5Bfunction(a%2Ck)%7Breturn%20u(k.url%2Ca)%7D%5D%7D%2Cprovider%3A%7Brules%3A%5B%5B'meta%5Bproperty%3D%22og%3Asite_name%22%5D'%2Cfunction(a)%7Breturn%20a.getAttribute(%22content%22)%7D%5D%5D%2CdefaultValue%3Afunction(a)%7B%7D%7D%7D)%3Bh.exports%3D%7BbuildRuleSet%3Av%2CgetMetadata%3Afunction(a%2Ck%2Cl)%7Bvar%20p%3D%7B%7D%2Cm%3D%7Burl%3Ak%7D%2Cq%3Dl%7C%7Cy%3Breturn%20Object.keys(q).map(function(r)%7Bvar%20w%3Dv(q%5Br%5D)%3Bp%5Br%5D%3Dw(a%2Cm)%7D)%2Cp%7D%2CmetadataRuleSets%3Ay%7D%7D%2Cfunction(h%2Cg%2Cn)%7B(function(v)%7Bif(void%200!%3D%3Dv.URL)h.exports%3D%7BmakeUrlAbsolute%3Afunction(u%2Cy)%7Breturn(new%20URL(y%2Cu)).href%7D%2CparseUrl%3Afunction(u)%7Breturn(new%20URL(u)).host%7D%7D%3Belse%7Bvar%20t%3Dn(3)%3Bh.exports%3D%7BmakeUrlAbsolute%3Afunction(u%2Cy)%7Breturn%20null%3D%3D%3Dt.parse(y).host%3Ft.resolve(u%2Cy)%3Ay%7D%2CparseUrl%3Afunction(u)%7Breturn%20t.parse(u).hostname%7D%7D%7D%7D).call(g%2Cfunction()%7Breturn%20this%7D())%7D%2Cfunction(h%2Cg)%7Bh.exports%3Dwindow%7D%5D).getMetadata(window.document%2Cwindow.location)%2Cc%3Dwindow.location.protocol%2B%22%2F%2F%22%2Bwindow.location.host%2Cd%3Ddocument.location.host.replace(%22www.%22%2C%22%22)%2Ce%3Db.keywords%3F%22%23%5B%5B%22%2Bb.keywords.join(%22%5D%5D%20%23%5B%5B%22).toLowerCase()%2B%22%5D%5D%20%22%3A%22%22%2Cf%3Dwindow.getSelection().toString()%3Bc%3De%2B%22%20%23link%20%5B%22%2Bb.title.replaceAll('%22'%2C'%5C%5C%22')%2B%22%5D(%22%2Bb.url%2B%22)!%5B%5D(%22%2B(b.image%7C%7C%22data%3Aimage%2Fpng%3Bbase64%2Chideme%22)%2B%22)!%5B%5D(%22%2B(b.icon%7C%7C%22data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mM88x8AAp0BzdNtlUkAAAAASUVORK5CYII%3D%22)%2B%22)%5B%22%2B(b.provider%7C%7Cd)%2B%22%5D(%22%2Bc%2B%22)%22%2B(b.description%7C%7C%22%22)%3Be%3D%22%22%3B-1!%3D%3Dd.indexOf(%22youtube.com%22)%3Fe%3D%22%5Cn%20%20%20%20%7B%7Bvideo%3A%22%2Bb.url%2B%22%7D%7D%22%3A0%3D%3D%3Db.url.indexOf(%22https%22)%26%26(e%3D%22%5Cn%20%20%20%20%7B%7Biframe%3A%22%2Bb.url%2B%22%7D%7D%22)%3Bf%26%26(b%3Df.trim().replaceAll(%22%5Cn%5Cn%22%2C%22%5Cn%22).split(%22%5Cn%22)%2Ce%3F(b%3Db.join(%22%5Cn%20%20%20%20%20%20%20%20%3E%20%22)%2Cc%2B%3D%22%5Cn%20%20%20%20%5Cud83d%5Cudcdd%5Cn%20%20%20%20%20%20%20%20%3E%20%22%2Bb)%3A(b%3Db.join(%22%5Cn%20%20%20%20%3E%20%22)%2Cc%2B%3D%22%5Cn%20%20%20%20%3E%20%22%2Bb))%3B(function(h)%7Bvar%20g%3Ddocument.createElement(%22textarea%22)%2Cn%3Ddocument.getSelection()%3Bg.textContent%3Dh%3Bdocument.body.appendChild(g)%3Bn.removeAllRanges()%3Bg.select()%3Bdocument.execCommand(%22copy%22)%3Bn.removeAllRanges()%3Bdocument.body.removeChild(g)%7D)(c%2Be)%7D)()%3Bvoid+0
#[[urban planning]] #[[social media]] #[[society]] #[[ethics]] #[[internet]] #[[tech]] #link [To Mend a Broken Internet, Create Online Parks](https://www.wired.com/story/to-mend-a-broken-internet-create-online-parks/)![](https://media.wired.com/photos/5f84c1d34dbf8ba438224a83/191:100/w_1280,c_limit/OpEd-Internet-Digital-Parks-690130756.jpg)![](https://www.wired.com/verso/static/wired/assets/favicon.ico)[Wired](https://www.wired.com)We need public spaces, built in the spirit of Walt Whitman, that allow us to gather, communicate, and share in something bigger than ourselves.
{{iframe:https://www.wired.com/story/to-mend-a-broken-internet-create-online-parks/}}
(function() {
// metadata parser from https://github.com/mozilla/page-metadata-parser
var metadataparser=function(t){function r(n){if(e[n])return e[n].exports;var u=e[n]={exports:{},id:n,loaded:!1};return t[n].call(u.exports,u,u.exports,r),u.loaded=!0,u.exports}var e={};return r.m=t,r.c=e,r.p="",r(0)}([function(t,r,e){t.exports=e(1)},function(t,r,e){"use strict";function n(t){return function(r,e){for(var n=0,u=void 0,i=0;i<t.rules.length;i++){var a=o(t.rules[i],2),c=a[0],l=a[1],f=Array.from(r.querySelectorAll(c));if(f.length){var s=!0,p=!1,m=void 0;try{for(var g,b=f[Symbol.iterator]();!(s=(g=b.next()).done);s=!0){var y=g.value,d=t.rules.length-i;if(t.scorers){var v=!0,h=!1,A=void 0;try{for(var w,k=t.scorers[Symbol.iterator]();!(v=(w=k.next()).done);v=!0){var x=w.value,S=x(y,d);S&&(d=S)}}catch(t){h=!0,A=t}finally{try{!v&&k.return&&k.return()}finally{if(h)throw A}}}d>n&&(n=d,u=l(y))}}catch(t){p=!0,m=t}finally{try{!s&&b.return&&b.return()}finally{if(p)throw m}}}}if(!u&&t.defaultValue&&(u=t.defaultValue(e)),u){if(t.processors){var U=!0,R=!1,V=void 0;try{for(var L,j=t.processors[Symbol.iterator]();!(U=(L=j.next()).done);U=!0){var I=L.value;u=I(u,e)}}catch(t){R=!0,V=t}finally{try{!U&&j.return&&j.return()}finally{if(R)throw V}}}return u.trim&&(u=u.trim()),u}}}function u(t,r,e){var u={},o={url:r},i=e||c;return Object.keys(i).map(function(r){var e=i[r],a=n(e);u[r]=a(t,o)}),u}var o=function(){function t(t,r){var e=[],n=!0,u=!1,o=void 0;try{for(var i,a=t[Symbol.iterator]();!(n=(i=a.next()).done)&&(e.push(i.value),!r||e.length!==r);n=!0);}catch(t){u=!0,o=t}finally{try{!n&&a.return&&a.return()}finally{if(u)throw o}}return e}return function(r,e){if(Array.isArray(r))return r;if(Symbol.iterator in Object(r))return t(r,e);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),i=e(2),a=i.makeUrlAbsolute,c=(i.parseUrl,{description:{rules:[['meta[property="og:description"]',function(t){return t.getAttribute("content")}],['meta[name="description" i]',function(t){return t.getAttribute("content")}]]},icon:{rules:[['link[rel="apple-touch-icon"]',function(t){return t.getAttribute("href")}],['link[rel="apple-touch-icon-precomposed"]',function(t){return t.getAttribute("href")}],['link[rel="icon" i]',function(t){return t.getAttribute("href")}],['link[rel="fluid-icon"]',function(t){return t.getAttribute("href")}],['link[rel="shortcut icon"]',function(t){return t.getAttribute("href")}],['link[rel="Shortcut Icon"]',function(t){return t.getAttribute("href")}],['link[rel="mask-icon"]',function(t){return t.getAttribute("href")}]],scorers:[function(t,r){var e=t.getAttribute("sizes");if(e){var n=e.match(/\d+/g);if(n)return n[0]}}],defaultValue:function(t){return"favicon.ico"},processors:[function(t,r){return a(r.url,t)}]},image:{rules:[['meta[property="og:image:secure_url"]',function(t){return t.getAttribute("content")}],['meta[property="og:image:url"]',function(t){return t.getAttribute("content")}],['meta[property="og:image"]',function(t){return t.getAttribute("content")}],['meta[name="twitter:image"]',function(t){return t.getAttribute("content")}],['meta[property="twitter:image"]',function(t){return t.getAttribute("content")}],['meta[name="thumbnail"]',function(t){return t.getAttribute("content")}]],processors:[function(t,r){return a(r.url,t)}]},keywords:{rules:[['meta[name="keywords" i]',function(t){return t.getAttribute("content")}]],processors:[function(t,r){return t.split(",").map(function(t){return t.trim()})}]},title:{rules:[['meta[property="og:title"]',function(t){return t.getAttribute("content")}],['meta[name="twitter:title"]',function(t){return t.getAttribute("content")}],['meta[property="twitter:title"]',function(t){return t.getAttribute("content")}],['meta[name="hdl"]',function(t){return t.getAttribute("content")}],["title",function(t){return t.text}]]},language:{rules:[["html[lang]",function(t){return t.getAttribute("lang")}],['meta[name="language" i]',function(t){return t.getAttribute("content")}]],processors:[function(t,r){return t.split("-")[0]}]},type:{rules:[['meta[property="og:type"]',function(t){return t.getAttribute("content")}]]},url:{rules:[["a.amp-canurl",function(t){return t.getAttribute("href")}],['link[rel="canonical"]',function(t){return t.getAttribute("href")}],['meta[property="og:url"]',function(t){return t.getAttribute("content")}]],defaultValue:function(t){return t.url},processors:[function(t,r){return a(r.url,t)}]},provider:{rules:[['meta[property="og:site_name"]',function(t){return t.getAttribute("content")}]],defaultValue:function(t){}}});t.exports={buildRuleSet:n,getMetadata:u,metadataRuleSets:c}},function(t,r,e){(function(r){"use strict";if(void 0!==r.URL)t.exports={makeUrlAbsolute:function(t,r){return new URL(r,t).href},parseUrl:function(t){return new URL(t).host}};else{var n=e(3);t.exports={makeUrlAbsolute:function(t,r){var e=n.parse(r);return null===e.host?n.resolve(t,r):r},parseUrl:function(t){return n.parse(t).hostname}}}}).call(r,function(){return this}())},function(t,r){t.exports=window}]);
var metadata = metadataparser.getMetadata(window.document, window.location)
var domainUrl = `${window.location.protocol}//${window.location.host}`;
var domain = document.location.host.replace("www.", "");
var fallbackImage = "data:image/png;base64,hideme"; /* Works in Chrome (haven't tried other browsers) to hide the image, easier than trying to target with CSS, and excluding the image markup altogether messes up CSS targeting the favicon. */
var fallbackGray = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mM88x8AAp0BzdNtlUkAAAAASUVORK5CYII="
var keywords = metadata.keywords ? "#[[" + metadata.keywords.join("]] #[[").toLowerCase() + "]] " : "";
var highlightedText = window.getSelection().toString();
var roamLink = `${keywords} #link [${metadata.title.replaceAll('"', '\\"')}](${metadata.url})![](${metadata.image || fallbackImage})![](${metadata.icon || fallbackGray})[${metadata.provider || domain}](${domainUrl})${metadata.description || ""}`;
var embed = '';
if (domain.indexOf("youtube.com") !== -1) {
embed = `
{{video:${metadata.url}}}`;
} else if (metadata.url.indexOf("https") === 0) {
embed = `
{{iframe:${metadata.url}}}`;
}
if (highlightedText) {
var quoteLines = highlightedText.trim().replaceAll("\n\n", "\n").split("\n");
var quote;
if (embed) {
/* Nest the quote under a "notes" block so that editing t and adding more won't cause the iframe to constantly refresh. */
quote = quoteLines.join("\n > ");
roamLink += `
📝
> ${quote}`;
} else {
quote = quoteLines.join("\n > ");
roamLink += `
> ${quote}`;
}
}
roamLink += embed;
(function copyToClipboard(text) {
var node = document.createElement('textarea')
var selection = document.getSelection()
node.textContent = text
document.body.appendChild(node)
selection.removeAllRanges()
node.select()
document.execCommand('copy')
selection.removeAllRanges()
document.body.removeChild(node)
})(roamLink);
})();
// for bookmarklet, run through:
// https://www.yourjs.com/bookmarklet/
@ykessler
Copy link

ykessler commented Apr 3, 2021

This is awesome, thanks for sharing.

Slight tweak for your CSS: Change .roam-block-container[data-page-links*='link'] to .roam-block-container[data-page-links*='link'] > .rm-block-main to limit the bookmark styles to the primary block, so that those styles don't get applied to the child blocks as well.

So for example this:

/* Image */
.roam-block-container[data-page-links*='link'] div.rm-inline-img__resize:first-of-type {
  float: right;
  margin-left: 6px;
}

becomes this:

/* Image */
.roam-block-container[data-page-links*='link'] > .rm-block-main div.rm-inline-img__resize:first-of-type {
  float: right;
  margin-left: 6px;
}

otherwise images in child blocks will also be floated.

@tobek
Copy link
Author

tobek commented Apr 6, 2021

@ykessler good call, thanks for that! I hadn't run into this cause I haven't yet used images nested under a bookmark and hadn't noticed the other changes, but definitely makes sense. Just updated all the selectors in this gist to restrict by direct descendant like that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment