Experiment to rebuild Diffuse using web applets.
0
fork

Configure Feed

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

chore: More reorg

+140 -117
+4
src/applets/themes/pilot/ui/audio/applet.astro
··· 19 19 line-height: 100px; 20 20 text-align: center; 21 21 } 22 + 23 + button { 24 + line-height: 1.5; 25 + } 22 26 </style> 23 27 24 28 <script>
+1 -1
src/layouts/applet.astro
··· 10 10 11 11 <title>{title}</title> 12 12 13 - <style> 13 + <style is:global> 14 14 @import "../styles/reset.css"; 15 15 @import "../styles/fonts.css"; 16 16 @import "../styles/variables.css";
+6
src/layouts/page.astro
··· 8 8 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 9 9 10 10 <title>{title}</title> 11 + 12 + <style is:global> 13 + @import "../styles/reset.css"; 14 + @import "../styles/fonts.css"; 15 + @import "../styles/variables.css"; 16 + </style> 11 17 </head> 12 18 <body> 13 19 <slot />
-2
src/pages/index.astro
··· 2 2 import Page from "../layouts/page.astro"; 3 3 4 4 // import "@picocss/pico/css/pico.colors.css"; 5 - import "../styles/reset.css"; 6 - import "../styles/fonts.css"; 7 5 import "../styles/pages/index.css"; 8 6 9 7 const engines = [{ url: "engine/audio/", title: "Audio" }];
+3 -108
src/pages/themes/pilot/index.astro
··· 2 2 import Page from "../../../layouts/page.astro"; 3 3 --- 4 4 5 - <Page title="Diffuse Applets Usage Example"> 5 + <Page title="Diffuse"> 6 + <script src="../../../scripts/themes/pilot/index.js"></script> 7 + 6 8 <!-- Theme applets --> 7 9 <iframe id="#applet__ui__audio" src="ui/audio/" frameborder="0" style="width: 100%"></iframe> 8 10 9 11 <!-- Other applets --> 10 12 <iframe id="#applet__engine__audio" src="../../engine/audio/" frameborder="0" height="0" width="0" 11 13 ></iframe> 12 - 13 - <div>Testing</div> 14 14 </Page> 15 - 16 - <style is:global> 17 - @import "../../../styles/reset.css"; 18 - @import "../../../styles/fonts.css"; 19 - @import "../../../styles/variables.css"; 20 - @import "../../../styles/themes/pilot/variables.css"; 21 - </style> 22 - 23 - <style> 24 - /*********************************** 25 - * Fonts 26 - ***********************************/ 27 - :root { 28 - font-family: "Inter", sans-serif; 29 - font-size: var(--fs-base); 30 - } 31 - 32 - @supports (font-variation-settings: normal) { 33 - :root { 34 - font-family: "InterVariable", sans-serif; 35 - font-feature-settings: 36 - "ss03" 2, 37 - "ss02" 2; 38 - font-optical-sizing: auto; 39 - } 40 - } 41 - 42 - body { 43 - background-color: var(--delicate-cloud); 44 - color: var(--made-in-the-shade); 45 - overflow: hidden; 46 - } 47 - 48 - /*********************************** 49 - * Applets | Position 50 - ***********************************/ 51 - #applet__ui__audio { 52 - } 53 - 54 - /* Position engines outside the viewframe (no UI for these) */ 55 - #applet__engine__audio { 56 - left: 110vw; 57 - position: absolute; 58 - top: 110vh; 59 - } 60 - </style> 61 - 62 - <script> 63 - import { type Applet, type AppletEvent, applets } from "@web-applets/sdk"; 64 - import { effect, signal } from "spellcaster/spellcaster.js"; 65 - 66 - //////////////////////////////////////////// 67 - // 🗂️ Applets 68 - //////////////////////////////////////////// 69 - const engine = { 70 - audio: await applet("../../engine/audio"), 71 - }; 72 - 73 - const ui = { 74 - audio: await applet("ui/audio", { setHeight: true }), 75 - }; 76 - 77 - //////////////////////////////////////////// 78 - // ⚡ Connect applets 79 - //////////////////////////////////////////// 80 - reactive(ui.audio, (isPlaying: boolean) => 81 - engine.audio.sendAction(isPlaying ? "play" : "pause", null), 82 - ); 83 - 84 - reactive(engine.audio, (isPlaying: boolean) => ui.audio.sendAction("set_is_playing", isPlaying)); 85 - reactive(engine.audio, (progress: number) => ui.audio.sendAction("set_progress", progress)); 86 - 87 - //////////////////////////////////////////// 88 - // 🪟 Applet initialiser 89 - //////////////////////////////////////////// 90 - async function applet(src: string, opts: { setHeight?: boolean } = {}) { 91 - const frame: HTMLIFrameElement = document.querySelector( 92 - `[src="${src}${src.endsWith("/") ? "" : "/"}"]`, 93 - ); 94 - 95 - const applet = await applets.connect(frame.contentWindow); 96 - if (opts.setHeight) applet.onresize = () => (frame.height = `${applet.height}px`); 97 - 98 - return applet; 99 - } 100 - 101 - //////////////////////////////////////////// 102 - // 🔮 Reactive state management 103 - //////////////////////////////////////////// 104 - // TODO: Applet should have a subtype 105 - function reactive<T>(applet: Applet, effectFn: (t: T) => void) { 106 - const property = effectFn 107 - .toString() 108 - .slice(1) 109 - .match(/([^(\)|,|\s)]+)/)[1]; 110 - 111 - const [getter, setter] = signal(applet.data[property] as T); 112 - 113 - effect(() => effectFn(getter())); 114 - 115 - applet.addEventListener("data", (event: AppletEvent) => { 116 - setter(event.data[property]); 117 - }); 118 - } 119 - </script>
+76
src/scripts/themes/pilot/index.ts
··· 1 + import { type Applet, type AppletEvent, applets } from "@web-applets/sdk"; 2 + import { effect, signal } from "spellcaster/spellcaster.js"; 3 + 4 + import "../../../styles/themes/pilot/index.css"; 5 + 6 + //////////////////////////////////////////// 7 + // 🗂️ Applets 8 + //////////////////////////////////////////// 9 + const engine = { 10 + audio: await applet("../../engine/audio"), 11 + }; 12 + 13 + const ui = { 14 + audio: await applet("ui/audio", { setHeight: true }), 15 + }; 16 + 17 + //////////////////////////////////////////// 18 + // ⚡ Connect applets 19 + //////////////////////////////////////////// 20 + reactive( 21 + ui.audio, 22 + (isPlaying: boolean) => 23 + engine.audio.sendAction(isPlaying ? "play" : "pause", null), 24 + ); 25 + 26 + reactive( 27 + engine.audio, 28 + (isPlaying: boolean) => ui.audio.sendAction("set_is_playing", isPlaying), 29 + ); 30 + 31 + reactive( 32 + engine.audio, 33 + (progress: number) => ui.audio.sendAction("set_progress", progress), 34 + ); 35 + 36 + //////////////////////////////////////////// 37 + // 🪟 Applet initialiser 38 + //////////////////////////////////////////// 39 + async function applet(src: string, opts: { setHeight?: boolean } = {}) { 40 + const frame: HTMLIFrameElement | null = document.querySelector( 41 + `[src="${src}${src.endsWith("/") ? "" : "/"}"]`, 42 + ); 43 + 44 + if (frame === null) throw new Error("iframe element not found, src: " + src); 45 + if (frame.contentWindow === null) { 46 + throw new Error("iframe does not have a contentWindow"); 47 + } 48 + 49 + const applet = await applets.connect(frame.contentWindow); 50 + if (opts.setHeight) { 51 + applet.onresize = () => (frame.height = `${applet.height}px`); 52 + } 53 + 54 + return applet; 55 + } 56 + 57 + //////////////////////////////////////////// 58 + // 🔮 Reactive state management 59 + //////////////////////////////////////////// 60 + // TODO: Applet should have a subtype 61 + function reactive<T>(applet: Applet, effectFn: (t: T) => void) { 62 + const property = effectFn 63 + .toString() 64 + .slice(1) 65 + .match(/([^(\)|,|\s)]+)/); 66 + 67 + const [getter, setter] = signal( 68 + (applet.data as any)[property ? property[1] : "ignore"] as T, 69 + ); 70 + 71 + effect(() => effectFn(getter())); 72 + 73 + applet.addEventListener("data", (event: AppletEvent) => { 74 + setter(event.data[property ? property[1] : "ignore"]); 75 + }); 76 + }
+12 -6
src/styles/pages/index.css
··· 36 36 main { 37 37 display: flex; 38 38 flex-wrap: wrap; 39 - gap: 0 var(--space-xl); 39 + gap: 0 var(--space-3xl); 40 40 } 41 41 42 42 main > section { 43 - flex: 1; 43 + /* flex: 1; */ 44 44 min-width: min(var(--container-xs), 100%); 45 - width: 50%; 45 + width: 32.5%; 46 46 } 47 47 48 48 a { ··· 58 58 color: var(--beyond-the-pines); 59 59 font-size: var(--fs-xl); 60 60 font-weight: 900; 61 - margin: var(--space-xl) 0 var(--space-sm); 61 + line-height: 1; 62 + margin: var(--space-2xl) 0 var(--space-md); 62 63 text-transform: uppercase; 63 64 } 64 65 65 66 h3 { 66 - font-size: var(--fs-lg); 67 + font-size: var(--fs-md); 67 68 font-weight: 800; 68 - margin: var(--space-lg) 0 var(--space-sm); 69 + line-height: 1; 70 + margin: var(--space-xl) 0 var(--space-sm); 69 71 text-transform: uppercase; 72 + } 73 + 74 + h2 + h3 { 75 + margin-top: var(--space-md); 70 76 } 71 77 72 78 ul,
+38
src/styles/themes/pilot/index.css
··· 1 + @import "./variables.css"; 2 + 3 + /*********************************** 4 + * Fonts 5 + ***********************************/ 6 + :root { 7 + font-family: "Inter", sans-serif; 8 + font-size: var(--fs-base); 9 + } 10 + 11 + @supports (font-variation-settings: normal) { 12 + :root { 13 + font-family: "InterVariable", sans-serif; 14 + font-feature-settings: 15 + "ss03" 2, 16 + "ss02" 2; 17 + font-optical-sizing: auto; 18 + } 19 + } 20 + 21 + body { 22 + background-color: var(--delicate-cloud); 23 + color: var(--made-in-the-shade); 24 + overflow: hidden; 25 + } 26 + 27 + /*********************************** 28 + * Applets | Position 29 + ***********************************/ 30 + #applet__ui__audio { 31 + } 32 + 33 + /* Position engines outside the viewframe (no UI for these) */ 34 + #applet__engine__audio { 35 + left: 110vw; 36 + position: absolute; 37 + top: 110vh; 38 + }