@wrnrlr/prelude 0.0.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +46 -5
- package/deno.json +18 -9
- package/package.json +4 -6
- package/readme.md +163 -41
- package/src/constants.ts +2 -1
- package/src/{controlflow.js → controlflow.ts} +63 -50
- package/src/hyperscript.ts +7 -6
- package/src/mod.ts +19 -17
- package/src/reactive.ts +42 -14
- package/src/resource.js +184 -0
- package/src/router.js +65 -0
- package/src/runtime.ts +9 -8
- package/test/hyperscript.js +2 -2
- package/test/reactive.js +12 -4
- package/www/assets/css/presets.css +504 -0
- package/www/assets/css/style.css +90 -0
- package/www/demo.html +15 -0
- package/www/index.html +211 -0
- package/www/playground.html +183 -0
- package/www/public/example/admin.html +88 -0
- package/{example → www/public/example}/counter.html +1 -1
- package/{example → www/public/example}/greeting.html +1 -1
- package/{example → www/public/example}/show.html +1 -1
- package/{example → www/public/example}/todo.html +1 -1
- package/www/public/fonts/fab.ttf +0 -0
- package/www/public/fonts/fab.woff2 +0 -0
- package/www/public/fonts/far.ttf +0 -0
- package/www/public/fonts/far.woff2 +0 -0
- package/www/public/fonts/fas.ttf +0 -0
- package/www/public/fonts/fas.woff2 +0 -0
- package/www/public/logo.svg +16 -0
- package/www/typedoc.json +13 -0
- package/www/vite.config.js +106 -0
- package/example/paint.html +0 -22
- package/index.html +0 -230
- package/presets.css +0 -284
- package/public/logo.svg +0 -5
- package/test/runtime.js +0 -7
- package/typedoc.jsonc +0 -31
- /package/{public → www/public}/banner.svg +0 -0
    
        package/www/index.html
    ADDED
    
    | @@ -0,0 +1,211 @@ | |
| 1 | 
            +
            <!DOCTYPE html>
         | 
| 2 | 
            +
            <head>
         | 
| 3 | 
            +
            <title>Prelude</title>
         | 
| 4 | 
            +
            <link href="https://unpkg.com/prismjs@1.29.0/themes/prism.css" rel="stylesheet" />
         | 
| 5 | 
            +
            <script src="https://unpkg.com/prismjs@1.29.0/components/prism-core.min.js"></script>
         | 
| 6 | 
            +
            <script src="https://unpkg.com/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
         | 
| 7 | 
            +
            <script async src="https://www.googletagmanager.com/gtag/js?id=G-EPMJYD1EK8"></script>
         | 
| 8 | 
            +
            <script>window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'G-EPMJYD1EK8');</script>
         | 
| 9 | 
            +
            </head>
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            <nav class="flex p-2 justify-between">
         | 
| 12 | 
            +
              <div class="flex items-center gap-3">
         | 
| 13 | 
            +
                <svg viewBox="0 0 709 709" style="width: 50px; --color:var(--neutral-900)"><use xlink:href="./logo.svg#loop"></use></svg>
         | 
| 14 | 
            +
                <span class="prelude text-xl">Prelude</span>
         | 
| 15 | 
            +
              </div>
         | 
| 16 | 
            +
              <div class="flex items-center gap-5">
         | 
| 17 | 
            +
                <a href="/prelude/docs/index.html">Get Started</a>
         | 
| 18 | 
            +
                <a href="/prelude/docs/modules.html">API</a>
         | 
| 19 | 
            +
                <a class="px-3 py-2 rounded-md bg-green-600 text-white font-weight-500 text-shadow-green shadow" href="/prelude/playground">Playground</a>
         | 
| 20 | 
            +
              </div>
         | 
| 21 | 
            +
            </nav>
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            <main>
         | 
| 24 | 
            +
            <section class="flex column items-center py-8">
         | 
| 25 | 
            +
              <svg class="py-8" viewBox="0 0 709 709" style="width:300px;--color:var(--neutral-700)"><use xlink:href="./logo.svg#loop"></use></svg>
         | 
| 26 | 
            +
              <div class="flex column center items-center gap-6">
         | 
| 27 | 
            +
                <span class="prelude text-6xl">Prelude</span>
         | 
| 28 | 
            +
                <span class="text-3xl">A signal-based web library that lets you <br> develop in a familiar functional style</span>
         | 
| 29 | 
            +
                <div class="flex gap-4">
         | 
| 30 | 
            +
                  <a class="px-3 py-2 rounded-md bg-neutral-200 text-xl font-weight-500" href="/prelude/docs/index.html">Get Started</a>
         | 
| 31 | 
            +
                  <a class="px-3 py-2 rounded-md bg-green-600 text-xl text-white font-weight-500 shadow" href="/prelude/playground">Open Playground</a>
         | 
| 32 | 
            +
                </div>
         | 
| 33 | 
            +
              </div>
         | 
| 34 | 
            +
            </section>
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            <section class="flex column items-center py-8 gap-8">
         | 
| 37 | 
            +
              <h1 class="center text-5xl">Just write JavaScript!</h1>
         | 
| 38 | 
            +
              <p class="center text-2xl">Instead of JSX, Prelude uses HyperScript,<br>
         | 
| 39 | 
            +
                  a DSL that lets you define components in JavaScript.</p>
         | 
| 40 | 
            +
              <div class="flex items-center content-center gap-8">
         | 
| 41 | 
            +
                <pre><code class="language-js">import {h, signal, render} from 'prelude'
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            function Counter() {
         | 
| 44 | 
            +
              const n = signal(1)
         | 
| 45 | 
            +
              const onClick = e => n(i => i+1)
         | 
| 46 | 
            +
              return h('button', {onClick}, n)
         | 
| 47 | 
            +
            }
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            render(Counter, window.counter)</code></pre>
         | 
| 50 | 
            +
                <div class="card">
         | 
| 51 | 
            +
                    <div class="title center">Counter</div>
         | 
| 52 | 
            +
                    <div class="flex justify-center content-center flex-wrap content" id="counter"></div>
         | 
| 53 | 
            +
                </div>
         | 
| 54 | 
            +
              </div>
         | 
| 55 | 
            +
              <div class="flex column gap-4 items-start text-xl pl-10">
         | 
| 56 | 
            +
                <p>Create a reactive value with <code class="language-js">const n = signal(1)</code>.</p>
         | 
| 57 | 
            +
                <p>Define an event handler that increments the signal <code class="language-js">n(i=>i+1)</code>.</p>
         | 
| 58 | 
            +
                <p>The function returned from <code class="language-js">h(...)</code> will render a HTML button <br>
         | 
| 59 | 
            +
                    that handles <code class="language-js">onclick</code> events and displays the counter value.</p>
         | 
| 60 | 
            +
                <p>Prelude has fine-grained reactivity and will only update<br>those parts of the DOM that needs to be changed.</p>
         | 
| 61 | 
            +
              </div>
         | 
| 62 | 
            +
            </section>
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            <section class="flex column items-center py-8">
         | 
| 65 | 
            +
                <p class="center text-2xl">Try in your Browser</p>
         | 
| 66 | 
            +
                <div class="flex items-center gap-4 pt-6">
         | 
| 67 | 
            +
                  <a class="px-3 py-2 rounded-md bg-green-600 text-xl text-white font-weight-500 text-shadow-green shadow" href="/prelude/playground">Open Playground</a>
         | 
| 68 | 
            +
                </div>
         | 
| 69 | 
            +
            </section>
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            <footer class="flex px-2 py-2 gap-8" style="background-color: var(--neutral-100);">
         | 
| 72 | 
            +
              <div class="flex column justify-center gap-3">
         | 
| 73 | 
            +
                <a href="https://www.npmjs.com/package/@wrnrlr/prelude">NPM</a>
         | 
| 74 | 
            +
                <a href="https://jsr.io/@wrnrlr/prelude">JSR</a>
         | 
| 75 | 
            +
              </div>
         | 
| 76 | 
            +
              <div class="flex column justify-center gap-3">
         | 
| 77 | 
            +
                <a href="https://x.com/wrnrlr">Twitter</a>
         | 
| 78 | 
            +
                <a href="https://discord.gg/N3gPZnhdKV">Discord</a>
         | 
| 79 | 
            +
              </div>
         | 
| 80 | 
            +
              <div class="flex column-reverse center grow">Some rights reserved © 2024</div>
         | 
| 81 | 
            +
              <svg viewBox="0 0 709 709" style="width: 100px; --color:var(--neutral-700)"><use xlink:href="./logo.svg#loop"></use></svg>
         | 
| 82 | 
            +
            </footer>
         | 
| 83 | 
            +
            </main>
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            <style>
         | 
| 86 | 
            +
            @import url('./assets/css/style.css');
         | 
| 87 | 
            +
             | 
| 88 | 
            +
            html {
         | 
| 89 | 
            +
              font-size: 20px;
         | 
| 90 | 
            +
              /* font-family: "Noto Sans Warang Citi", sans-serif; */
         | 
| 91 | 
            +
              font-weight: 400;
         | 
| 92 | 
            +
              font-style: normal;
         | 
| 93 | 
            +
              margin: 0;
         | 
| 94 | 
            +
              display: flex;
         | 
| 95 | 
            +
              width: 100%;
         | 
| 96 | 
            +
              height: 100%;
         | 
| 97 | 
            +
              color: var(--neutral-900);
         | 
| 98 | 
            +
            }
         | 
| 99 | 
            +
             | 
| 100 | 
            +
            body {
         | 
| 101 | 
            +
              display: flex;
         | 
| 102 | 
            +
              flex-direction: column;
         | 
| 103 | 
            +
              margin: 0;
         | 
| 104 | 
            +
              flex-grow: 2;
         | 
| 105 | 
            +
              width: 100%;
         | 
| 106 | 
            +
              height: 100%;
         | 
| 107 | 
            +
            }
         | 
| 108 | 
            +
             | 
| 109 | 
            +
            main {
         | 
| 110 | 
            +
              display: flex;
         | 
| 111 | 
            +
              flex-direction: column;
         | 
| 112 | 
            +
              gap: 1rem;
         | 
| 113 | 
            +
            }
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            /* a:visited { color: inherit; } */
         | 
| 116 | 
            +
             | 
| 117 | 
            +
            section {
         | 
| 118 | 
            +
            }
         | 
| 119 | 
            +
            section:nth-child(1) { background-color: var(--neutral-100); }
         | 
| 120 | 
            +
            /* section:nth-child(even) { background-color: var(--neutral-100); } */
         | 
| 121 | 
            +
            /* section:nth-child(odd) { background-color: var(--neutral-200); } */
         | 
| 122 | 
            +
             | 
| 123 | 
            +
            section > .wrapper { width: 700px }
         | 
| 124 | 
            +
            section > .wide.wrapper { width: 900px }
         | 
| 125 | 
            +
             | 
| 126 | 
            +
            .button.playground {
         | 
| 127 | 
            +
                color: var(--neutral-50);
         | 
| 128 | 
            +
                background-color: var(--green-600);
         | 
| 129 | 
            +
                text-shadow: 0px 0px 3px var(--emrald-800);
         | 
| 130 | 
            +
                cursor: pointer;
         | 
| 131 | 
            +
            }
         | 
| 132 | 
            +
             | 
| 133 | 
            +
            #playground {
         | 
| 134 | 
            +
                display: flex;
         | 
| 135 | 
            +
                width: 100%;
         | 
| 136 | 
            +
                height: 100%;
         | 
| 137 | 
            +
                background-color: var(--neutral-50);
         | 
| 138 | 
            +
            }
         | 
| 139 | 
            +
             | 
| 140 | 
            +
            #playground.playing {
         | 
| 141 | 
            +
                display: flex;
         | 
| 142 | 
            +
            }
         | 
| 143 | 
            +
             | 
| 144 | 
            +
            .prelude {
         | 
| 145 | 
            +
              color: var(--neutral-700);
         | 
| 146 | 
            +
              font-weight: norma;
         | 
| 147 | 
            +
            }
         | 
| 148 | 
            +
             | 
| 149 | 
            +
            .button {
         | 
| 150 | 
            +
              /* padding: 0.75rem 1rem; */
         | 
| 151 | 
            +
              /* border-radius: 0.5rem; */
         | 
| 152 | 
            +
              /* background-color: var(--neutral-200); */
         | 
| 153 | 
            +
              /* font-weight: 600; */
         | 
| 154 | 
            +
            }
         | 
| 155 | 
            +
             | 
| 156 | 
            +
            #counter > button {
         | 
| 157 | 
            +
                color: var(--neutral-50);
         | 
| 158 | 
            +
                font-weight: 800;
         | 
| 159 | 
            +
                padding: 1rem 1.5rem;
         | 
| 160 | 
            +
                border: 1px solid var(--blue-600);
         | 
| 161 | 
            +
                border-radius: 0.5rem;
         | 
| 162 | 
            +
                background-color: var(--blue-600);
         | 
| 163 | 
            +
                height: fit-content;
         | 
| 164 | 
            +
                min-width: 4.2rem;
         | 
| 165 | 
            +
                text-align: center;
         | 
| 166 | 
            +
                text-shadow: 0px 0px 3px var(--blue-800);
         | 
| 167 | 
            +
            }
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            #counter > button:hover {
         | 
| 170 | 
            +
                background-color: var(--blue-500);
         | 
| 171 | 
            +
            }
         | 
| 172 | 
            +
             | 
| 173 | 
            +
            .card {
         | 
| 174 | 
            +
              width: 20rem;
         | 
| 175 | 
            +
              display: flex;
         | 
| 176 | 
            +
              flex-direction: column;
         | 
| 177 | 
            +
            }
         | 
| 178 | 
            +
             | 
| 179 | 
            +
            .card > .title {
         | 
| 180 | 
            +
              font-weight: bold;
         | 
| 181 | 
            +
              color: var(--gray-700);
         | 
| 182 | 
            +
              padding: 0.5em;
         | 
| 183 | 
            +
              background-color: var(--gray-300);
         | 
| 184 | 
            +
              border-radius: 0.5em 0.5em 0 0;
         | 
| 185 | 
            +
            }
         | 
| 186 | 
            +
             | 
| 187 | 
            +
            .card > .content {
         | 
| 188 | 
            +
              height: 220px;
         | 
| 189 | 
            +
              background-color: var(--gray-200);
         | 
| 190 | 
            +
            }
         | 
| 191 | 
            +
             | 
| 192 | 
            +
            a.card {
         | 
| 193 | 
            +
              text-decoration: none;
         | 
| 194 | 
            +
            }
         | 
| 195 | 
            +
             | 
| 196 | 
            +
            .w-half { width: 50% }
         | 
| 197 | 
            +
            .CodeMirror { height: 100%; }
         | 
| 198 | 
            +
            </style>
         | 
| 199 | 
            +
             | 
| 200 | 
            +
            <script type="module">
         | 
| 201 | 
            +
            import {h, signal, render} from '../src/mod.ts'
         | 
| 202 | 
            +
             | 
| 203 | 
            +
            function Counter() {
         | 
| 204 | 
            +
              const n = signal(1)
         | 
| 205 | 
            +
              const onClick = e => n(i => i+1)
         | 
| 206 | 
            +
              return h('button', {onClick}, n)
         | 
| 207 | 
            +
            }
         | 
| 208 | 
            +
             | 
| 209 | 
            +
            render(Counter, window.counter)
         | 
| 210 | 
            +
             | 
| 211 | 
            +
            </script>
         | 
| @@ -0,0 +1,183 @@ | |
| 1 | 
            +
            <!DOCTYPE html>
         | 
| 2 | 
            +
            <head>
         | 
| 3 | 
            +
            <title>Prelude</title>
         | 
| 4 | 
            +
            <link href="https://unpkg.com/prismjs@1.29.0/themes/prism.css" rel="stylesheet" />
         | 
| 5 | 
            +
            <script src="https://unpkg.com/prismjs@1.29.0/components/prism-core.min.js"></script>
         | 
| 6 | 
            +
            <script src="https://unpkg.com/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
         | 
| 7 | 
            +
            <script async src="https://www.googletagmanager.com/gtag/js?id=G-EPMJYD1EK8"></script>
         | 
| 8 | 
            +
            <script>window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'G-EPMJYD1EK8');</script>
         | 
| 9 | 
            +
            </head>
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            <script type="module">
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            import {h, signal, effect, untrack, resource, render, onMount, Show, List, Router} from '../src/mod.ts'
         | 
| 14 | 
            +
            import {EditorView, basicSetup} from "codemirror"
         | 
| 15 | 
            +
            import {html} from "@codemirror/lang-html"
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            const playing = signal(true)
         | 
| 18 | 
            +
            const examples = [
         | 
| 19 | 
            +
              {name:'counter',title:'Counter'},
         | 
| 20 | 
            +
              {name:'todo',title:'Todo App'},
         | 
| 21 | 
            +
              // {name:'admin',title:'Admin'}
         | 
| 22 | 
            +
            ]
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            function Editor(props) {
         | 
| 25 | 
            +
              const theme = EditorView.baseTheme({
         | 
| 26 | 
            +
                '&.cm-editor': {height: '100%', backgroundColor:'var(--stone-100)'},
         | 
| 27 | 
            +
                '&.cm-editor.cm-focused': {outline: 'none'},
         | 
| 28 | 
            +
                '&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': { backgroundColor: 'var(--pink-200)'},
         | 
| 29 | 
            +
                '&.cm-scroller': {overflow: 'auto'},
         | 
| 30 | 
            +
                '&.cm-gutter': {backgroundColor:'var(--stone-200)'},
         | 
| 31 | 
            +
                '&.cm-content': {height: "100%", backgroundColor:'var(--stone-100)'},
         | 
| 32 | 
            +
              })
         | 
| 33 | 
            +
              const ref = parent => {
         | 
| 34 | 
            +
                let updateListener = EditorView.updateListener.of((update) => {
         | 
| 35 | 
            +
                  if (update.docChanged) {
         | 
| 36 | 
            +
                    console.log('doc change')
         | 
| 37 | 
            +
                    props.setValue(update.state.doc.toString())
         | 
| 38 | 
            +
                    // props.value(update.state.doc.toString())
         | 
| 39 | 
            +
                  }
         | 
| 40 | 
            +
                });
         | 
| 41 | 
            +
                let editor = new EditorView({
         | 
| 42 | 
            +
                  doc:'', parent,
         | 
| 43 | 
            +
                  extensions: [theme, basicSetup, html(), updateListener]
         | 
| 44 | 
            +
                })
         | 
| 45 | 
            +
                effect(()=>{
         | 
| 46 | 
            +
                  // console.log('loading', props.value.loading);
         | 
| 47 | 
            +
                  if (props.value.loading) return
         | 
| 48 | 
            +
                  const insert = props.value()
         | 
| 49 | 
            +
                  // console.log('set content',insert);
         | 
| 50 | 
            +
                  editor.dispatch({changes: {from: 0, to: editor.state.doc.length, insert}})
         | 
| 51 | 
            +
                })
         | 
| 52 | 
            +
              }
         | 
| 53 | 
            +
              return h('.p-1.w-half.h-full.bg-stone-200',{ref,style:'position:relative'})
         | 
| 54 | 
            +
            }
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            function Preview(props) {
         | 
| 57 | 
            +
              return h('iframe.w-half.bg-stone-200', {srcdoc:props.value})
         | 
| 58 | 
            +
            }
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            function TopMenu(props) {
         | 
| 61 | 
            +
              return h('.flex.items-center.p-1.gap-2.bg-neutral-100',[
         | 
| 62 | 
            +
                h('.button.rounded.text-xl.bg-neutral-100.p-1', {onClick:e=>(props.show(b=>!b))},
         | 
| 63 | 
            +
                  h('i.icon.menu')),
         | 
| 64 | 
            +
                h('input.grow', {value:props.file, readonly:true}),
         | 
| 65 | 
            +
                // h('.flex.px-2.py-1.rounded.text-md.bg-gray-300.font-weight-500.text-neutral-700.gap-2.shadow',
         | 
| 66 | 
            +
                //   [h('i.icon.plus.text-md'),'New']),
         | 
| 67 | 
            +
                // h('.flex.px-2.py-1.rounded.text-md.bg-gray-300.font-weight-500.text-neutral-700.gap-2.shadow',
         | 
| 68 | 
            +
                //   [h('i.icon.clone.text-md'),'Clone']),
         | 
| 69 | 
            +
                h('a.px-3.py-1.rounded-full.text-md.bg-blue-500.text-white.shadow', {href:'/prelude'},
         | 
| 70 | 
            +
                  h('i.icon.info.text-shadow-black'))])
         | 
| 71 | 
            +
            }
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            function SideMenu(props) {
         | 
| 74 | 
            +
              return h('.sidemenu.flex.column.px-2.py-2.gap-4.bg-stone-300',[
         | 
| 75 | 
            +
                h('.flex.justify-around', [
         | 
| 76 | 
            +
                  h(PreludeLogo)
         | 
| 77 | 
            +
                ]),
         | 
| 78 | 
            +
                h('.flex.column.gap-2', [
         | 
| 79 | 
            +
                  h(SrcMenu,{name:'example',title:'Example',file:()=>props.file,items:()=>()=>examples,src:()=>props.src}),
         | 
| 80 | 
            +
                  // h(SrcMenu,{name:'local',title:'Local Storage',file:()=>props.file,items:()=>props.local,src:()=>props.src})
         | 
| 81 | 
            +
                ])
         | 
| 82 | 
            +
              ])
         | 
| 83 | 
            +
            }
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            function SrcMenu(props) {
         | 
| 86 | 
            +
              effect(()=>console.log(props.src()===props.name))
         | 
| 87 | 
            +
              return h('.flex.column.gap-1',[
         | 
| 88 | 
            +
                h('button.flex.font-weight-600', {onClick:e=>props.src(props.name)}, props.title),
         | 
| 89 | 
            +
                h(Show, {when:()=>props.src()===props.name},
         | 
| 90 | 
            +
                  h('.flex.column.gap-1.pl-2',
         | 
| 91 | 
            +
                    h(List, {each:()=>props.items}, (value,i) =>
         | 
| 92 | 
            +
                      h('button.flex', {onClick:e=>props.file(value().name)}, h('',()=>value().title))
         | 
| 93 | 
            +
                    )
         | 
| 94 | 
            +
                  )
         | 
| 95 | 
            +
                )])
         | 
| 96 | 
            +
            }
         | 
| 97 | 
            +
             | 
| 98 | 
            +
            function Playground() {
         | 
| 99 | 
            +
              const show = signal(true)
         | 
| 100 | 
            +
              const src = signal('example')
         | 
| 101 | 
            +
              const file = signal('counter')
         | 
| 102 | 
            +
              const local = signal(examples)
         | 
| 103 | 
            +
              const content = signal('')
         | 
| 104 | 
            +
              const exs = ()=>examples
         | 
| 105 | 
            +
              const doodle = resource(()=>({src:src(),file:file()}), async r => {
         | 
| 106 | 
            +
                console.log('change resource',r)
         | 
| 107 | 
            +
                if (r.src==='example') {
         | 
| 108 | 
            +
                  console.log('fetch',r.file)
         | 
| 109 | 
            +
                  const resp = await fetch('/prelude/example/' + r.file + '.html')
         | 
| 110 | 
            +
                  const text = await resp.text()
         | 
| 111 | 
            +
                  // console.log(text)
         | 
| 112 | 
            +
                  return text
         | 
| 113 | 
            +
                  // content(html)
         | 
| 114 | 
            +
                } else {
         | 
| 115 | 
            +
                  console.log('local')
         | 
| 116 | 
            +
                }
         | 
| 117 | 
            +
              })
         | 
| 118 | 
            +
              const setContent = s => {
         | 
| 119 | 
            +
                console.log('set content')
         | 
| 120 | 
            +
                content(s)
         | 
| 121 | 
            +
              }
         | 
| 122 | 
            +
              return h('.flex.w-full.h-full',{style:'background-color:var(--neutral-100)'},[
         | 
| 123 | 
            +
                h(Show, {when:show}, h(SideMenu, {show:()=>show, src:()=>src, file:()=>file, local:()=>local})),
         | 
| 124 | 
            +
                h('.flex.column.grow.items-stretch', [
         | 
| 125 | 
            +
                  h(TopMenu, {playing:()=>playing, show:()=>show, file:()=>file}),
         | 
| 126 | 
            +
                  h(Show, {when:()=>!doodle.loading, fallback: 'Loading...'},
         | 
| 127 | 
            +
                    h('.flex', {style:'overflow:auto;height:100%'}, [
         | 
| 128 | 
            +
                      h(Editor, {value:()=>doodle, setValue:setContent, src:()=>src, file:()=>file}),
         | 
| 129 | 
            +
                      h(Preview, {value:()=>content})
         | 
| 130 | 
            +
                    ]))
         | 
| 131 | 
            +
                ])
         | 
| 132 | 
            +
              ])
         | 
| 133 | 
            +
            }
         | 
| 134 | 
            +
             | 
| 135 | 
            +
            render(Playground, document.body)
         | 
| 136 | 
            +
             | 
| 137 | 
            +
            function PreludeLogo() {
         | 
| 138 | 
            +
              return h('svg', {viewBox:'0 0 709 709',style:'width: 50px; --color:var(--neutral-700)'},
         | 
| 139 | 
            +
                h('use', {'xlink:href':'./logo.svg#loop'}))
         | 
| 140 | 
            +
            }
         | 
| 141 | 
            +
             | 
| 142 | 
            +
            </script>
         | 
| 143 | 
            +
             | 
| 144 | 
            +
            <style>
         | 
| 145 | 
            +
            @import url('./assets/css/style.css');
         | 
| 146 | 
            +
             | 
| 147 | 
            +
            html {
         | 
| 148 | 
            +
              font-size: 20px;
         | 
| 149 | 
            +
              /* font-family: "Noto Sans Warang Citi", sans-serif; */
         | 
| 150 | 
            +
              font-weight: 400;
         | 
| 151 | 
            +
              font-style: normal;
         | 
| 152 | 
            +
              margin: 0;
         | 
| 153 | 
            +
              display: flex;
         | 
| 154 | 
            +
              width: 100%;
         | 
| 155 | 
            +
              height: 100%;
         | 
| 156 | 
            +
              color: var(--neutral-900);
         | 
| 157 | 
            +
            }
         | 
| 158 | 
            +
             | 
| 159 | 
            +
            body {
         | 
| 160 | 
            +
              display: flex;
         | 
| 161 | 
            +
              flex-direction: column;
         | 
| 162 | 
            +
              margin: 0;
         | 
| 163 | 
            +
              flex-grow: 2;
         | 
| 164 | 
            +
              width: 100%;
         | 
| 165 | 
            +
              height: 100%;
         | 
| 166 | 
            +
            }
         | 
| 167 | 
            +
             | 
| 168 | 
            +
            .prelude {
         | 
| 169 | 
            +
              color: var(--neutral-700);
         | 
| 170 | 
            +
              font-weight: normal;
         | 
| 171 | 
            +
            }
         | 
| 172 | 
            +
             | 
| 173 | 
            +
            .sidemenu {
         | 
| 174 | 
            +
              min-width: max-content;
         | 
| 175 | 
            +
            }
         | 
| 176 | 
            +
             | 
| 177 | 
            +
            .button {
         | 
| 178 | 
            +
            }
         | 
| 179 | 
            +
            .button:hover {
         | 
| 180 | 
            +
                background-color:var(--neutral-300)
         | 
| 181 | 
            +
            }
         | 
| 182 | 
            +
             | 
| 183 | 
            +
            </style>
         | 
| @@ -0,0 +1,88 @@ | |
| 1 | 
            +
            <!DOCTYPE html>
         | 
| 2 | 
            +
            <title>Admin</title>
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            <script type="module">
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            import {h,signal,effect,render,resource,memo,Router,List,Show} from '/prelude/bundle.js'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            function makeApi(base,method='GET') {
         | 
| 9 | 
            +
              return async (s='') => await (await fetch('https://dummyjson.com/'+base+s,{method})).json()
         | 
| 10 | 
            +
            }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            const getPosts = makeApi('posts')
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            function ResourceList(props) {
         | 
| 15 | 
            +
              const when = () => !props.result.loading,
         | 
| 16 | 
            +
                list = () => props.result()?.[props.key];
         | 
| 17 | 
            +
              return h(Show,{when,fallback:()=>'loading...'},
         | 
| 18 | 
            +
                h('ul', h(List, {each:()=>list}, props.children)))
         | 
| 19 | 
            +
            }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            function PostItem(props) {
         | 
| 22 | 
            +
              const item = () => props.children(),
         | 
| 23 | 
            +
                    href = () => '#post/' + item().id
         | 
| 24 | 
            +
              return h('li',h('a',{href},()=>item().title))
         | 
| 25 | 
            +
            }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            function Posts(props) {
         | 
| 28 | 
            +
              const result = resource(async ()=>getPosts())
         | 
| 29 | 
            +
              return [
         | 
| 30 | 
            +
                h('h1','Posts'),
         | 
| 31 | 
            +
                h(ResourceList, {key:'posts',result:()=>result}, (v,i) => h(PostItem,v))
         | 
| 32 | 
            +
              ]
         | 
| 33 | 
            +
            }
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            function UserItem(props) {
         | 
| 36 | 
            +
              const item = () => props.children()
         | 
| 37 | 
            +
              return h('li',()=>item().name)
         | 
| 38 | 
            +
            }
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            function Users(props) {
         | 
| 41 | 
            +
              const result = resource(async ()=>getPosts())
         | 
| 42 | 
            +
              return [
         | 
| 43 | 
            +
                h('h1','Users'),
         | 
| 44 | 
            +
                h(ResourceList, {key:'users',result:()=>result}, (v,i) => h(UserItem,v))
         | 
| 45 | 
            +
              ]
         | 
| 46 | 
            +
            }
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            function Post(props) {
         | 
| 49 | 
            +
              return h('h1','Post #123')
         | 
| 50 | 
            +
            }
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            function App(props) {
         | 
| 53 | 
            +
              return [
         | 
| 54 | 
            +
                h('nav',[
         | 
| 55 | 
            +
                  h('a',{href:'/example/admin.html#'},'Posts'),
         | 
| 56 | 
            +
                  h('a',{href:'/example/admin.html#user'},'Users')
         | 
| 57 | 
            +
                ]),
         | 
| 58 | 
            +
                h(Router,[
         | 
| 59 | 
            +
                  {path:'/', component:Posts},
         | 
| 60 | 
            +
                  {path:'/user', component:Users},
         | 
| 61 | 
            +
                  {path:'/post/:id', component:Post}
         | 
| 62 | 
            +
                ])
         | 
| 63 | 
            +
              ]
         | 
| 64 | 
            +
            }
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            render(App, document.body)
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            </script>
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            <style>
         | 
| 71 | 
            +
            nav {
         | 
| 72 | 
            +
                display: flex;
         | 
| 73 | 
            +
                gap: 1rem;
         | 
| 74 | 
            +
            }
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            ul {
         | 
| 77 | 
            +
                display: flex;
         | 
| 78 | 
            +
                flex-direction: column;
         | 
| 79 | 
            +
                padding:0;
         | 
| 80 | 
            +
                gap: 0.5rem;
         | 
| 81 | 
            +
            }
         | 
| 82 | 
            +
             | 
| 83 | 
            +
            li {
         | 
| 84 | 
            +
              list-style: none;
         | 
| 85 | 
            +
            }
         | 
| 86 | 
            +
             | 
| 87 | 
            +
             | 
| 88 | 
            +
            </style>
         | 
| Binary file | 
| Binary file | 
| Binary file | 
| Binary file | 
| Binary file | 
| Binary file | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            <?xml version="1.0" encoding="UTF-8"?>
         | 
| 2 | 
            +
            <svg
         | 
| 3 | 
            +
              id="loop"
         | 
| 4 | 
            +
              width="709"
         | 
| 5 | 
            +
              height="709"
         | 
| 6 | 
            +
              viewBox="0 0 709 709"
         | 
| 7 | 
            +
              xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
         | 
| 8 | 
            +
                <path
         | 
| 9 | 
            +
                  id="Rectangle"
         | 
| 10 | 
            +
                  style="stroke:var(--color,#444444); stroke-width:var(--width,40)"
         | 
| 11 | 
            +
                  fill="none"
         | 
| 12 | 
            +
                  stroke-linecap="round"
         | 
| 13 | 
            +
                  stroke-linejoin="round"
         | 
| 14 | 
            +
                  d="M 76 617 L 631 617 L 631 94 C 631 94 26 450 358 450 C 682 450 76 94 76 94 L 76 617 Z"
         | 
| 15 | 
            +
                />
         | 
| 16 | 
            +
            </svg>
         | 
    
        package/www/typedoc.json
    ADDED
    
    | @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            {
         | 
| 2 | 
            +
              "name": "prelude",
         | 
| 3 | 
            +
              "out":"./docs/",
         | 
| 4 | 
            +
              "compilerOptions": {
         | 
| 5 | 
            +
                "strict": false,
         | 
| 6 | 
            +
                "allowJs": true,
         | 
| 7 | 
            +
                "checkJs": false,
         | 
| 8 | 
            +
                "noImplicitThis": false,
         | 
| 9 | 
            +
                "allowImportingTsExtensions": true,
         | 
| 10 | 
            +
                "downlevelIteration": true
         | 
| 11 | 
            +
              },
         | 
| 12 | 
            +
              "entryPoints": ["../src/mod.ts"]
         | 
| 13 | 
            +
            }
         | 
| @@ -0,0 +1,106 @@ | |
| 1 | 
            +
            import * as path from '@std/path'
         | 
| 2 | 
            +
            import * as fs from '@std/fs'
         | 
| 3 | 
            +
            import {defineConfig} from 'vite'
         | 
| 4 | 
            +
            import * as esbuild from 'esbuild'
         | 
| 5 | 
            +
            import { Application, TSConfigReader, TypeDocReader } from 'typedoc'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            export default defineConfig(({ command, mode }) => {
         | 
| 8 | 
            +
              const root = path.join(__dirname, '')
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              const define = {}
         | 
| 11 | 
            +
              let build
         | 
| 12 | 
            +
              const optimizeDeps = {
         | 
| 13 | 
            +
                // exclude: ['dist/','public/'],
         | 
| 14 | 
            +
              }
         | 
| 15 | 
            +
              const assetsInclude = [
         | 
| 16 | 
            +
                // 'public/example/**'
         | 
| 17 | 
            +
              ]
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              const input = {
         | 
| 20 | 
            +
                'index': path.join(root, 'index.html'),
         | 
| 21 | 
            +
                'playground': path.join(root, 'playground.html'),
         | 
| 22 | 
            +
              }
         | 
| 23 | 
            +
              // const docs = path.join(root, 'distdocs/index.html')
         | 
| 24 | 
            +
              // if (fs.existsSync(docs)) input.docs = docs
         | 
| 25 | 
            +
              // console.log('docs',input.docs)
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              build = {
         | 
| 28 | 
            +
                lib: {
         | 
| 29 | 
            +
                  entry: path.join(root, '../src/mod.ts'),
         | 
| 30 | 
            +
                  formats: ['es'],
         | 
| 31 | 
            +
                  name: 'mod',
         | 
| 32 | 
            +
                  fileName: (name) => 'prelude.js'
         | 
| 33 | 
            +
                },
         | 
| 34 | 
            +
                rollupOptions: {
         | 
| 35 | 
            +
                  input,
         | 
| 36 | 
            +
                  exports: 'named',
         | 
| 37 | 
            +
                  output: {
         | 
| 38 | 
            +
                    manualChunks(id) {
         | 
| 39 | 
            +
                      // if (id.endsWith('src/mod.ts')) return 'prelude'
         | 
| 40 | 
            +
                    }
         | 
| 41 | 
            +
                  }
         | 
| 42 | 
            +
                }
         | 
| 43 | 
            +
              }
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              return {
         | 
| 46 | 
            +
                root,
         | 
| 47 | 
            +
                define,
         | 
| 48 | 
            +
                build,
         | 
| 49 | 
            +
                optimizeDeps,
         | 
| 50 | 
            +
                assetsInclude,
         | 
| 51 | 
            +
                base: '/prelude',
         | 
| 52 | 
            +
                plugins: [
         | 
| 53 | 
            +
                  esbuildPlugin(), typedocPlugin()
         | 
| 54 | 
            +
                ]
         | 
| 55 | 
            +
              }
         | 
| 56 | 
            +
            })
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            function esbuildPlugin() {
         | 
| 59 | 
            +
              let ctx
         | 
| 60 | 
            +
              return {
         | 
| 61 | 
            +
                name: 'typedoc-plugin',
         | 
| 62 | 
            +
                async buildStart() {
         | 
| 63 | 
            +
                  const entry = path.join(__dirname, '../src/mod.ts')
         | 
| 64 | 
            +
                  const options = {
         | 
| 65 | 
            +
                    entryPoints: [entry],
         | 
| 66 | 
            +
                    outfile: path.join(__dirname, './public/bundle.js'),
         | 
| 67 | 
            +
                    bundle: true,
         | 
| 68 | 
            +
                    format:'esm',
         | 
| 69 | 
            +
                    // minify: true,
         | 
| 70 | 
            +
                    // sourcemap: true,
         | 
| 71 | 
            +
                    target: ["es2020"],
         | 
| 72 | 
            +
                  }
         | 
| 73 | 
            +
                  ctx = await  esbuild.context(options)
         | 
| 74 | 
            +
                  await ctx.watch()
         | 
| 75 | 
            +
                },
         | 
| 76 | 
            +
                async buildEnd() {
         | 
| 77 | 
            +
                  await ctx.dispose()
         | 
| 78 | 
            +
                }
         | 
| 79 | 
            +
              }
         | 
| 80 | 
            +
            }
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            function typedocPlugin() {
         | 
| 83 | 
            +
              let _config
         | 
| 84 | 
            +
              return {
         | 
| 85 | 
            +
                name: 'typedoc-plugin',
         | 
| 86 | 
            +
                apply: 'build',
         | 
| 87 | 
            +
                configResolved(config) { _config = config },
         | 
| 88 | 
            +
                async writeBundle() {
         | 
| 89 | 
            +
                  const name = path.join(__dirname, 'typedoc.json')
         | 
| 90 | 
            +
                  const config = JSON.parse(await Deno.readTextFile(name))
         | 
| 91 | 
            +
                  config.hostedBaseUrl = 'https://wrnrlr.github.io/prelude/docs'
         | 
| 92 | 
            +
                  config.useHostedBaseUrlForAbsoluteLinks = true
         | 
| 93 | 
            +
                  config.out = path.join(__dirname, './dist/docs')
         | 
| 94 | 
            +
                  config.entryPoints = [path.join(__dirname, '../src/mod.ts')]
         | 
| 95 | 
            +
                  const app = await Application.bootstrap(config)
         | 
| 96 | 
            +
                  if (!app) Deno.exit()
         | 
| 97 | 
            +
                  const project = await app.convert()
         | 
| 98 | 
            +
                  if (!project) Deno.exit()
         | 
| 99 | 
            +
                  try {
         | 
| 100 | 
            +
                    await app.generateDocs(project, config.out)
         | 
| 101 | 
            +
                  } catch (e) {
         | 
| 102 | 
            +
                    console.error(e)
         | 
| 103 | 
            +
                  }
         | 
| 104 | 
            +
                }
         | 
| 105 | 
            +
              }
         | 
| 106 | 
            +
            }
         |