A music player that connects to your cloud/distributed storage.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

wip: new your-facets-collection page

+223 -197
+2 -2
src/_components/facets/grid.vto
··· 1 - <ul class="grid"> 1 + <ul class="grid" style="margin-top: var(--space-lg);"> 2 2 {{ for index, item of items }} 3 3 <li data-uri="{{ item.url |> facetOrThemeURI }}" data-name="{{item.title}}"> 4 4 <div style="position: relative;"> ··· 14 14 </button> 15 15 </div> 16 16 <div class="list-description"> 17 - {{item.desc |> md(true)}} 17 + {{ item.desc |> md(true) }} 18 18 </div> 19 19 20 20 <!-- Dropdown Menu -->
+3 -1
src/_includes/layouts/diffuse.vto
··· 28 28 <meta name="msapplication-TileColor" content="#8a90a9" /> 29 29 <meta name="theme-color" content="#8a90a9" /> 30 30 31 - <!-- Preload fonts so they're ready before first paint (prevents flash during view transitions) --> 31 + <!-- Preload items so they're ready before first paint (prevents flash during view transitions) --> 32 32 <link rel="preload" as="font" type="font/woff2" crossorigin href="fonts/InterVariable.woff2" /> 33 33 <link rel="preload" as="font" type="font/woff2" crossorigin href="fonts/InterVariable-Italic.woff2" /> 34 34 <link rel="preload" as="font" type="font/woff2" crossorigin href="fonts/CommitMonoVariable.woff2" /> 35 35 <link rel="preload" as="font" type="font/woff2" crossorigin href="vendor/@phosphor-icons/bold/Phosphor-Bold.woff2" /> 36 36 <link rel="preload" as="font" type="font/woff2" crossorigin href="vendor/@phosphor-icons/fill/Phosphor-Fill.woff2" /> 37 + 38 + <link rel="preload" as="image" crossorigin href="images/diffuse-current.svg" /> 37 39 38 40 <!-- Styles --> 39 41 {{ for url of styles }}
+2
src/_includes/layouts/facets-category.vto
··· 5 5 <section> 6 6 {{ await comp.facets.grid({ id: slug, items: categoryFacets }) }} 7 7 </section> 8 + 9 + <script src="facets/pages/save-fork.js" type="module"></script>
+2 -176
src/facets/_index.js
··· 1 - import { html, render } from "lit-html"; 2 - import { keyed } from "lit-html/directives/keyed.js"; 3 - import { marked } from "marked"; 4 - import { unsafeHTML } from "lit-html/directives/unsafe-html.js"; 5 - 6 1 import { basicSetup, EditorView } from "codemirror"; 7 2 import { css as langCss } from "@codemirror/lang-css"; 8 3 import { html as langHtml } from "@codemirror/lang-html"; ··· 13 8 14 9 import * as CID from "~/common/cid.js"; 15 10 import foundation from "~/common/facets/foundation.js"; 16 - import { effect, signal } from "~/common/signal.js"; 17 - import { facetFromURI } from "~/common/facets/utils.js"; 18 - import { nothing } from "~/common/element.js"; 11 + import { signal } from "~/common/signal.js"; 19 12 import { loadURI } from "~/common/loader.js"; 20 13 21 14 /** ··· 23 16 */ 24 17 25 18 //////////////////////////////////////////// 26 - // SAVE & FORK 27 - //////////////////////////////////////////// 28 - 29 - document.body.addEventListener( 30 - "click", 31 - /** 32 - * @param {MouseEvent} event 33 - */ 34 - async (event) => { 35 - const target = /** @type {HTMLElement} */ (event.target); 36 - const rel = target.getAttribute("rel"); 37 - if (!rel) return; 38 - 39 - const uri = target.closest("li")?.getAttribute("data-uri"); 40 - if (!uri) return; 41 - 42 - const name = target.closest("li")?.getAttribute("data-name"); 43 - if (!name) return; 44 - 45 - switch (rel) { 46 - case "fork": { 47 - const facet = await facetFromURI({ name, uri }, { fetchHTML: true }); 48 - editFacet(facet); 49 - document.querySelector("#build")?.scrollIntoView(); 50 - break; 51 - } 52 - case "save": { 53 - const facet = await facetFromURI({ name, uri }, { fetchHTML: false }); 54 - const out = foundation.orchestrator.output(); 55 - 56 - out.facets.save([ 57 - ...out.facets.collection(), 58 - facet, 59 - ]); 60 - break; 61 - } 62 - } 63 - }, 64 - ); 65 - 66 - //////////////////////////////////////////// 67 - // YOUR COLLECTION 68 - //////////////////////////////////////////// 69 - 70 - /** @type {HTMLElement | null} */ 71 - const listEl = document.querySelector("#list"); 72 - if (!listEl) throw new Error("List element not found"); 73 - 74 - const output = foundation.orchestrator.output(); 75 - 76 - listEl.innerHTML = ""; 77 - 78 - effect(() => { 79 - const col = output.facets.collection().sort((a, b) => { 80 - return a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase()); 81 - }); 82 - 83 - const state = output.facets.state(); 84 - 85 - const h = col.length && state === "loaded" 86 - ? html` 87 - <ul> 88 - ${col.map((c) => 89 - keyed( 90 - c.id, 91 - html` 92 - <li> 93 - <div style="position: relative;"> 94 - <a href="facets/l/?id=${c.id}"> 95 - ${c.name} 96 - </a> 97 - <button 98 - class="button--fixed button--transparent" 99 - popovertarget="facet-menu-col-${c.id}" 100 - style="anchor-name: --facet-anchor-col-${c 101 - .id}; position: absolute; right: 0; top: 50%; transform: translateY(-50%);" 102 - > 103 - <i class="ph-fill ph-dots-three-circle"></i> 104 - </button> 105 - </div> 106 - <div class="list-description"> 107 - <div> 108 - ${c.description?.trim().length 109 - ? unsafeHTML( 110 - marked.parse(c.description, { async: false }), 111 - ) 112 - : nothing} 113 - </div> 114 - <div> 115 - ${c.uri && !c.html 116 - ? html` 117 - <span class="with-icon"> 118 - <i class="ph-fill ph-binoculars"></i> 119 - <span>Tracking the original <a href="${c 120 - .uri}">URI</a></span> 121 - </span> 122 - ` 123 - : html` 124 - <span class="with-icon"> 125 - <i class="ph-fill ph-code"></i> 126 - <span>Custom code</span> 127 - </span> 128 - `} 129 - </div> 130 - </div> 131 - 132 - <!-- Dropdown Menu --> 133 - <div 134 - id="facet-menu-col-${c.id}" 135 - class="dropdown" 136 - style="position-anchor: --facet-anchor-col-${c.id}" 137 - popover 138 - > 139 - <a href="facets/l/?id=${c.id}"> 140 - <span class="with-icon"> 141 - <i class="ph-fill ph-globe"></i> Open 142 - </span> 143 - </a> 144 - <a @click="${() => editFacet(c)}"> 145 - <span class="with-icon"> 146 - <i class="ph-fill ph-cursor-text"></i> Edit 147 - </span> 148 - </a> 149 - <a @click="${deleteFacet({ id: c.id })}"> 150 - <span class="with-icon"> 151 - <i class="ph-fill ph-eraser"></i> Delete 152 - </span> 153 - </a> 154 - </div> 155 - </li> 156 - `, 157 - ) 158 - )} 159 - </ul> 160 - ` 161 - : state === "loaded" 162 - ? emptyFacetsList 163 - : html` 164 - <div class="with-icon" style="font-size: var(--fs-sm);"> 165 - <i class="ph-bold ph-spinner-gap"></i> 166 - Loading items 167 - </div> 168 - `; 169 - 170 - render(h, listEl); 171 - }); 172 - 173 - const emptyFacetsList = html` 174 - <p style="margin-bottom: 0;"> 175 - <i class="ph-fill ph-info"></i> You have not saved any facets yet. 176 - </p> 177 - `; 178 - 179 - /** 180 - * @param {{ id: string }} _ 181 - */ 182 - function deleteFacet({ id }) { 183 - return () => { 184 - const c = confirm("Are you sure you want to delete this facet?"); 185 - if (!c) return; 186 - 187 - output.facets.save( 188 - output.facets.collection().filter((c) => !(c.id === id)), 189 - ); 190 - }; 191 - } 192 - 193 - //////////////////////////////////////////// 194 19 // BUILD 195 20 //////////////////////////////////////////// 196 21 22 + const output = foundation.orchestrator.output(); 197 23 const $editingFacet = signal(/** @type {Facet | null} */ (null)); 198 24 199 25 // Code editor
-11
src/facets/_index.vto
··· 51 51 <div class="dither-mask filler"></div> 52 52 </header> 53 53 <main> 54 - <!-- YOUR COLLECTION --> 55 - <section> 56 - <h2 id="collection">Your collection</h2> 57 - <div id="list"> 58 - <div class="with-icon" style="font-size: var(--fs-sm);"> 59 - <i class="ph-bold ph-spinner-gap"></i> 60 - Loading items 61 - </div> 62 - </div> 63 - </section> 64 - 65 54 <!-- FEATURED + USAGE --> 66 55 <div class="columns"> 67 56 <section class="flex">
+51
src/facets/pages/save-fork.js
··· 1 + import * as Output from "~/common/output.js"; 2 + import foundation from "~/common/facets/foundation.js"; 3 + import { facetFromURI } from "~/common/facets/utils.js"; 4 + 5 + //////////////////////////////////////////// 6 + // SAVE & FORK 7 + //////////////////////////////////////////// 8 + 9 + document.body.addEventListener( 10 + "click", 11 + /** 12 + * @param {MouseEvent} event 13 + */ 14 + async (event) => { 15 + const target = /** @type {HTMLElement} */ (event.target); 16 + const rel = target.getAttribute("rel"); 17 + if (!rel) return; 18 + 19 + const uri = target.closest("li")?.getAttribute("data-uri"); 20 + if (!uri) return; 21 + 22 + const name = target.closest("li")?.getAttribute("data-name"); 23 + if (!name) return; 24 + 25 + switch (rel) { 26 + case "fork": { 27 + // TODO: Go to build page and load this facet into the editor. 28 + // 29 + // const facet = await facetFromURI({ name, uri }, { fetchHTML: true }); 30 + // editFacet(facet); 31 + // document.querySelector("#build")?.scrollIntoView(); 32 + break; 33 + } 34 + case "save": { 35 + const facet = await facetFromURI({ name, uri }, { fetchHTML: false }); 36 + const out = foundation.orchestrator.output(); 37 + 38 + await Output.waitUntilLoaded(out.facets); 39 + 40 + out.facets.save([ 41 + ...out.facets.collection(), 42 + facet, 43 + ]); 44 + break; 45 + } 46 + } 47 + 48 + // @ts-ignore No types exist yet for this I think? 49 + target.closest("[popover]")?.hidePopover(); 50 + }, 51 + );
+138
src/facets/you.js
··· 1 + import { html, render } from "lit-html"; 2 + import { keyed } from "lit-html/directives/keyed.js"; 3 + import { marked } from "marked"; 4 + import { unsafeHTML } from "lit-html/directives/unsafe-html.js"; 5 + 6 + import * as Output from "~/common/output.js"; 7 + import foundation from "~/common/facets/foundation.js"; 8 + import { effect } from "~/common/signal.js"; 9 + import { nothing } from "~/common/element.js"; 10 + 11 + //////////////////////////////////////////// 12 + // YOUR COLLECTION 13 + //////////////////////////////////////////// 14 + 15 + /** @type {HTMLElement | null} */ 16 + const listEl = document.querySelector("#list"); 17 + if (!listEl) throw new Error("List element not found"); 18 + 19 + const output = foundation.orchestrator.output(); 20 + 21 + listEl.innerHTML = ""; 22 + 23 + effect(() => { 24 + const col = output.facets.collection().sort((a, b) => { 25 + return a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase()); 26 + }); 27 + 28 + const state = output.facets.state(); 29 + 30 + const h = col.length && state === "loaded" 31 + ? html` 32 + <ul style="margin: 0; max-width: none;"> 33 + ${col.map((c) => 34 + keyed( 35 + c.id, 36 + html` 37 + <li style="margin-bottom: var(--space-sm)"> 38 + <div style="position: relative;"> 39 + <a href="facets/l/?id=${c.id}"> 40 + ${c.name} 41 + </a> 42 + <button 43 + class="button--fixed button--transparent" 44 + popovertarget="facet-menu-col-${c.id}" 45 + style="anchor-name: --facet-anchor-col-${c 46 + .id}; position: absolute; right: 0; top: 50%; transform: translateY(-50%);" 47 + > 48 + <i class="ph-fill ph-dots-three-circle"></i> 49 + </button> 50 + </div> 51 + <div class="list-description"> 52 + <div> 53 + ${c.description?.trim().length 54 + ? unsafeHTML( 55 + marked.parse(c.description, { async: false }), 56 + ) 57 + : nothing} 58 + </div> 59 + <div> 60 + ${c.uri && !c.html 61 + ? html` 62 + <span class="with-icon"> 63 + <i class="ph-fill ph-binoculars"></i> 64 + <span>Tracking the original <a href="${c 65 + .uri}">URI</a></span> 66 + </span> 67 + ` 68 + : html` 69 + <span class="with-icon"> 70 + <i class="ph-fill ph-code"></i> 71 + <span>Custom code</span> 72 + </span> 73 + `} 74 + </div> 75 + </div> 76 + 77 + <!-- Dropdown Menu --> 78 + <div 79 + id="facet-menu-col-${c.id}" 80 + class="dropdown" 81 + style="position-anchor: --facet-anchor-col-${c.id}" 82 + popover 83 + > 84 + <a href="facets/l/?id=${c.id}"> 85 + <span class="with-icon"> 86 + <i class="ph-fill ph-globe"></i> Open 87 + </span> 88 + </a> 89 + <a @click="${() => editFacet(c)}"> 90 + <span class="with-icon"> 91 + <i class="ph-fill ph-cursor-text"></i> Edit 92 + </span> 93 + </a> 94 + <a @click="${deleteFacet({ id: c.id })}"> 95 + <span class="with-icon"> 96 + <i class="ph-fill ph-eraser"></i> Delete 97 + </span> 98 + </a> 99 + </div> 100 + </li> 101 + `, 102 + ) 103 + )} 104 + </ul> 105 + ` 106 + : state === "loaded" 107 + ? emptyFacetsList 108 + : html` 109 + <div class="with-icon"> 110 + <i class="ph-bold ph-spinner-gap"></i> 111 + Loading items 112 + </div> 113 + `; 114 + 115 + render(h, listEl); 116 + }); 117 + 118 + const emptyFacetsList = html` 119 + <p style="margin-bottom: 0;"> 120 + <i class="ph-fill ph-info"></i> You have not saved any facets yet. 121 + </p> 122 + `; 123 + 124 + /** 125 + * @param {{ id: string }} _ 126 + */ 127 + function deleteFacet({ id }) { 128 + return async () => { 129 + const c = confirm("Are you sure you want to delete this facet?"); 130 + if (!c) return; 131 + 132 + await Output.waitUntilLoaded(output.facets); 133 + 134 + output.facets.save( 135 + output.facets.collection().filter((c) => !(c.id === id)), 136 + ); 137 + }; 138 + }
+17 -3
src/facets/you.vto
··· 4 4 title: Your collection | Facets | Diffuse 5 5 --- 6 6 7 - <section> 8 - <h1 hidden>Your collection</h1> 9 - </section> 7 + <h1 hidden>Your collection</h1> 8 + 9 + <div class="columns" style="margin-top: var(--space-xl);"> 10 + <section class="flex"> 11 + <div id="list"> 12 + <div class="with-icon"> 13 + <i class="ph-bold ph-spinner-gap"></i> 14 + Loading items 15 + </div> 16 + </div> 17 + </section> 18 + 19 + <section class="flex"> 20 + </section> 21 + </div> 22 + 23 + <script src="facets/you.js" type="module"></script>
+8 -4
src/styles/diffuse/page.css
··· 141 141 */ 142 142 143 143 .dropdown { 144 - background: var(--bg-color); 144 + background: oklch(from var(--bg-color) calc(l - 0.05) c h); 145 145 border: 0; 146 146 border-radius: var(--radius-md); 147 147 box-shadow: var(--box-shadow-lg); 148 148 color: var(--text-color); 149 - font-size: var(--fs-xs); 149 + font-size: var(--fs-sm); 150 150 margin: 0; 151 151 margin-top: var(--space-3xs); 152 152 padding: 0; ··· 161 161 & > a { 162 162 cursor: pointer; 163 163 display: block; 164 - padding: var(--space-3xs) var(--space-2xs); 164 + min-width: var(--space-3xl); 165 + padding: var(--space-xs) var(--space-sm); 165 166 text-decoration: none; 166 167 167 168 & > * { ··· 180 181 181 182 a:hover i { 182 183 opacity: 1; 184 + } 185 + 186 + .with-icon { 187 + gap: var(--space-2xs); 183 188 } 184 189 } 185 190 ··· 493 498 display: flex; 494 499 flex-wrap: wrap; 495 500 gap: var(--space-xs); 496 - margin-bottom: var(--space-lg); 497 501 margin-top: var(--space-md); 498 502 padding-bottom: var(--space-md); 499 503 }