diff --git a/README.md b/README.md new file mode 100644 index 0000000..a9307c5 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Jam Tools Docs diff --git a/assets/images/deployment-diagram-multiplayer.png b/assets/images/deployment-diagram-multiplayer.png new file mode 100644 index 0000000..fd99048 Binary files /dev/null and b/assets/images/deployment-diagram-multiplayer.png differ diff --git a/assets/images/deployment-diagram-singleplayer.png b/assets/images/deployment-diagram-singleplayer.png new file mode 100644 index 0000000..4e15931 Binary files /dev/null and b/assets/images/deployment-diagram-singleplayer.png differ diff --git a/assets/scss/common/_custom.scss b/assets/scss/common/_custom.scss index f7c1361..75563e9 100644 --- a/assets/scss/common/_custom.scss +++ b/assets/scss/common/_custom.scss @@ -1 +1,75 @@ // Put your custom SCSS code here + +svg[data-bs-theme-value="dark"], svg[data-bs-theme-value="light"] { + display: none !important; +} + +#offcanvasNavMainLabel { + display: none !important; +} + +.marquee { + --gap: .5rem; + display: flex; + overflow: hidden; + user-select: none; + gap: var(--gap); + padding-block: var(--space-m); + margin-block-start: var(--space-2xl) +} + +@media(prefers-reduced-motion:reduce) { + .marquee-content { + animation-play-state: paused + } +} + +@media (min-width: 768px) { + .marquee:hover .marquee-content { + animation-play-state: paused + } +} + +.marquee-item { + display: flex; + align-items: center +} + +.marquee-text { + margin-inline-start: 1rem; + text-wrap: nowrap +} + +.marquee-content { + flex-shrink: 0; + display: flex; + justify-content: space-around; + min-width: 100%; + gap: 3rem; + + // transform: translateX(-50%); + // padding-left: 15vw; + animation: scroll 25s linear infinite; +} + +@media(prefers-reduced-motion:no-preference) { + .marquee-content { + animation-name: scroll; + animation-duration: 2 5s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-play-state: running; + will-change: transform + } +} + +@keyframes scroll { + 0% { + transform: translateX(0) + } + + 100% { + transform: translateX(calc(-100% - var(--gap))) + // transform: translateX(calc(-100% - 15vw - var(--gap))) + } +} diff --git a/config/_default/hugo.toml b/config/_default/hugo.toml index 78fd124..6649cf6 100644 --- a/config/_default/hugo.toml +++ b/config/_default/hugo.toml @@ -1,4 +1,4 @@ -title = "My Docs" +title = "Jam Tools" baseurl = "http://localhost/" canonifyURLs = false disableAliases = true diff --git a/config/_default/languages.toml b/config/_default/languages.toml index 523b236..abb4d02 100644 --- a/config/_default/languages.toml +++ b/config/_default/languages.toml @@ -5,7 +5,7 @@ [en.params] languageISO = "EN" languageTag = "en-US" - footer = 'Brought to you by Thulite' + footer = '' alertText = 'Doks version 1.0 just shipped!' [de] diff --git a/config/_default/menus/menus.en.toml b/config/_default/menus/menus.en.toml index a2854f1..67c6de2 100644 --- a/config/_default/menus/menus.en.toml +++ b/config/_default/menus/menus.en.toml @@ -54,7 +54,7 @@ [[social]] name = "GitHub" pre = '' - url = "https://github.com/thuliteio/doks" + url = "https://github.com/jamtools/jamtools" post = "v0.1.0" weight = 30 @@ -73,10 +73,10 @@ # pageRef = "/docs/resources" # weight = 30 -[[footer]] - name = "Privacy Policy" - url = "/privacy/" - weight = 10 +# [[footer]] +# name = "Privacy Policy" +# url = "/privacy/" +# weight = 10 # [[footer]] # name = "Terms of Service" diff --git a/config/_default/menus/menus.nl.toml b/config/_default/menus/menus.nl.toml index ba96f72..6b902dc 100644 --- a/config/_default/menus/menus.nl.toml +++ b/config/_default/menus/menus.nl.toml @@ -23,15 +23,15 @@ [[social]] name = "GitHub" pre = "" - url = "https://github.com/h-enk/doks" + url = "https://github.com/jamtools/jamtools" post = "v0.1.0" weight = 10 -[[social]] - name = "X" - pre = "" - url = "https://twitter.com/getdoks" - weight = 20 +# [[social]] +# name = "X" +# pre = "" +# url = "https://twitter.com/getdoks" +# weight = 20 # [[footer]] # name = "Privacy" diff --git a/config/_default/params.toml b/config/_default/params.toml index 649a9ff..a38f5f1 100644 --- a/config/_default/params.toml +++ b/config/_default/params.toml @@ -1,5 +1,5 @@ # Hugo -title = "My Docs" +title = "Jam Tools" description = "Congrats on setting up a new Doks project!" images = ["cover.png"] @@ -74,7 +74,7 @@ mainSections = ["docs"] editPage = false # false (default) or true lastMod = false # false (default) or true repoHost = "GitHub" # GitHub (default), Gitea, GitLab, Bitbucket, or BitbucketServer - docsRepo = "https://github.com/h-enk/doks" + docsRepo = "https://github.com/jamtools/jamtools" docsRepoBranch = "main" # main (default), master, or docsRepoSubPath = "" # "" (none, default) or diff --git a/content/_index.md b/content/_index.md index 5ab054d..cfc3663 100644 --- a/content/_index.md +++ b/content/_index.md @@ -1,12 +1,12 @@ --- -title: "Welcome to Doks" +title: "Jam Tools" description: "" -lead: "Congrats on setting up a new Doks project!" +lead: "A streamlined framework to create real-time multiplayer applications, with first-class support for MIDI and IO devices." date: 2023-09-07T16:33:54+02:00 lastmod: 2023-09-07T16:33:54+02:00 draft: false seo: - title: "Welcome to Doks" # custom title (optional) + title: "Jam Tools" # custom title (optional) description: "" # custom description (recommended) canonical: "" # custom canonical URL (optional) robots: "" # custom robot tags (optional) diff --git a/content/docs/build-process-and-platform-entrypoints.md b/content/docs/build-process-and-platform-entrypoints.md new file mode 100644 index 0000000..e69de29 diff --git a/content/docs/changelog.md b/content/docs/changelog.md new file mode 100644 index 0000000..e69de29 diff --git a/content/docs/code-examples/_index.md b/content/docs/code-examples/_index.md new file mode 100644 index 0000000..9ee43a5 --- /dev/null +++ b/content/docs/code-examples/_index.md @@ -0,0 +1,17 @@ +--- +title: "Code Examples" +description: "" +summary: "" +date: 2023-09-07T16:06:50+02:00 +lastmod: 2023-09-07T16:06:50+02:00 +draft: false +weight: 1000 +toc: true +sidebar: + collapsed: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- diff --git a/content/docs/code-examples/examples-games/_index.md b/content/docs/code-examples/examples-games/_index.md new file mode 100644 index 0000000..a232270 --- /dev/null +++ b/content/docs/code-examples/examples-games/_index.md @@ -0,0 +1,17 @@ +--- +title: "Games" +description: "" +summary: "" +date: 2023-09-07T16:06:50+02:00 +lastmod: 2023-09-07T16:06:50+02:00 +draft: false +weight: 1100 +toc: true +sidebar: + collapsed: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- diff --git a/content/docs/guides/example.md b/content/docs/code-examples/examples-games/euchre.md similarity index 79% rename from content/docs/guides/example.md rename to content/docs/code-examples/examples-games/euchre.md index aacf37f..5762712 100644 --- a/content/docs/guides/example.md +++ b/content/docs/code-examples/examples-games/euchre.md @@ -1,21 +1,21 @@ ---- -title: "Example Guide" -description: "Guides lead a user through a specific task they want to accomplish, often with a sequence of steps." -summary: "" -date: 2023-09-07T16:04:48+02:00 -lastmod: 2023-09-07T16:04:48+02:00 -draft: false -weight: 810 -toc: true -seo: - title: "" # custom title (optional) - description: "" # custom description (recommended) - canonical: "" # custom canonical URL (optional) - robots: "" # custom robot tags (optional) ---- - -Guides lead a user through a specific task they want to accomplish, often with a sequence of steps. Writing a good guide requires thinking about what your users are trying to do. - -## Further reading - -- Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework +--- +title: "Euchre" +description: "A simple MIDI arpeggiator" +summary: "" +date: 2023-09-07T16:04:48+02:00 +lastmod: 2023-09-07T16:04:48+02:00 +draft: false +weight: 810 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +Guides lead a user through a specific task they want to accomplish, often with a sequence of steps. Writing a good guide requires thinking about what your users are trying to do. + +## Further reading + +- Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework diff --git a/content/docs/code-examples/examples-games/tic-tac-toe.md b/content/docs/code-examples/examples-games/tic-tac-toe.md new file mode 100644 index 0000000..1c0d8c5 --- /dev/null +++ b/content/docs/code-examples/examples-games/tic-tac-toe.md @@ -0,0 +1,21 @@ +--- +title: "Tic-Tac-Toe" +description: "A simple MIDI arpeggiator" +summary: "" +date: 2023-09-07T16:04:48+02:00 +lastmod: 2023-09-07T16:04:48+02:00 +draft: false +weight: 810 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +Guides lead a user through a specific task they want to accomplish, often with a sequence of steps. Writing a good guide requires thinking about what your users are trying to do. + +## Further reading + +- Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework diff --git a/content/docs/code-examples/examples-music/_index.md b/content/docs/code-examples/examples-music/_index.md new file mode 100644 index 0000000..24f7169 --- /dev/null +++ b/content/docs/code-examples/examples-music/_index.md @@ -0,0 +1,17 @@ +--- +title: "Music" +description: "" +summary: "" +date: 2023-09-07T16:06:50+02:00 +lastmod: 2023-09-07T16:06:50+02:00 +draft: false +weight: 1000 +toc: true +sidebar: + collapsed: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- diff --git a/content/docs/code-examples/examples-music/control-daw-from-phone.md b/content/docs/code-examples/examples-music/control-daw-from-phone.md new file mode 100644 index 0000000..1c05d56 --- /dev/null +++ b/content/docs/code-examples/examples-music/control-daw-from-phone.md @@ -0,0 +1,29 @@ +--- +title: "DAW Control from Phone" +description: "A simple MIDI arpeggiator" +summary: "" +date: 2023-09-07T16:04:48+02:00 +lastmod: 2023-09-07T16:04:48+02:00 +draft: false +weight: 810 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +Basic recording, and arming tracks using midi + +and messing around with effects + +and some additional stuff about robotjs + +have a youtube video demoing each of these examples. or maybe on youtube video with the timestamp seeked. or something like that + + + +## Further reading + +- Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework diff --git a/content/docs/code-examples/examples-music/gutar-hero-guitar.md b/content/docs/code-examples/examples-music/gutar-hero-guitar.md new file mode 100644 index 0000000..e69de29 diff --git a/content/docs/code-examples/examples-music/midi-arpeggiator.md b/content/docs/code-examples/examples-music/midi-arpeggiator.md new file mode 100644 index 0000000..e5f6453 --- /dev/null +++ b/content/docs/code-examples/examples-music/midi-arpeggiator.md @@ -0,0 +1,21 @@ +--- +title: "MIDI Arpeggiator" +description: "A simple MIDI arpeggiator" +summary: "" +date: 2023-09-07T16:04:48+02:00 +lastmod: 2023-09-07T16:04:48+02:00 +draft: false +weight: 810 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +Go from simple to advanced MIDI arpeggiators + +## Further reading + +- Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework diff --git a/content/docs/introduction/_index.md b/content/docs/introduction/_index.md new file mode 100644 index 0000000..65dc140 --- /dev/null +++ b/content/docs/introduction/_index.md @@ -0,0 +1,17 @@ +--- +title: "Introduction" +description: "" +summary: "" +date: 2023-09-07T16:12:37+02:00 +lastmod: 2023-09-07T16:12:37+02:00 +draft: false +weight: 1 +toc: true +sidebar: + collapsed: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- diff --git a/content/docs/introduction/overview.md b/content/docs/introduction/overview.md new file mode 100644 index 0000000..fae4646 --- /dev/null +++ b/content/docs/introduction/overview.md @@ -0,0 +1,24 @@ +--- +title: "Overview" +description: "Reference pages are ideal for outlining how things work in terse and clear terms." +summary: "" +date: 2023-09-07T16:13:18+02:00 +lastmod: 2023-09-07T16:13:18+02:00 +draft: false +weight: 10 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +Check out [Get Started]() and [Code Examples]() to start building your amazing application! + +--- + +The framework behind Jam Tools applications consists of the following components: + +- [Springboard](../../springboard/overview) - Full stack React framework that supports compilation into multiple server and client platforms, provided by the [`springboard`](https://npmjs.com/package/springboard) package. +- [Jam Tools](./jamtools/overview) - The functionality on top of Springboard related to interacting with MIDI and IO devices, provided by the [`@jamtools/core`](https://npmjs.com/package/@jamtools/core) package. diff --git a/content/docs/jamtools/_index.md b/content/docs/jamtools/_index.md new file mode 100644 index 0000000..20544ff --- /dev/null +++ b/content/docs/jamtools/_index.md @@ -0,0 +1,17 @@ +--- +title: "Jam Tools" +description: "" +summary: "" +date: 2023-09-07T16:12:37+02:00 +lastmod: 2023-09-07T16:12:37+02:00 +draft: false +weight: 100 +toc: true +sidebar: + collapsed: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- diff --git a/content/docs/jamtools/midi/_index.md b/content/docs/jamtools/midi/_index.md new file mode 100644 index 0000000..77c7b7b --- /dev/null +++ b/content/docs/jamtools/midi/_index.md @@ -0,0 +1,17 @@ +--- +title: "MIDI Modules" +description: "" +summary: "" +date: 2023-09-07T16:12:37+02:00 +lastmod: 2023-09-07T16:12:37+02:00 +draft: false +weight: 100 +toc: true +sidebar: + collapsed: false +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- diff --git a/content/docs/jamtools/midi/macro-module.md b/content/docs/jamtools/midi/macro-module.md new file mode 100644 index 0000000..0871063 --- /dev/null +++ b/content/docs/jamtools/midi/macro-module.md @@ -0,0 +1,103 @@ +--- +title: "Macros" +description: "Reference pages are ideal for outlining how things work in terse and clear terms." +summary: "" +date: 2023-09-07T16:13:18+02:00 +lastmod: 2023-09-07T16:13:18+02:00 +draft: false +weight: 100 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +The Macro module exposes functionality to allow the user to configure their own MIDI devices to use a given feature of your application. This means there are no hardcoded MIDI device names in your code, so your application can be used by any user. A macro can capture unlimited mapped inputs from different instruments, so it remembers every mapping that you've had mapped to that feature, and they can be individually added/deleted by the user. + +Macros can be used as primitives to compose simple or complex features. They provide a way to interact with MIDI devices, while having the feature-level code be agnostic to the MIDI device being used. + +The following macro types are exposed by the Macro module: + +- Inputs: + - `musical_keyboard_input` - Receive MIDI events for a MIDI keyboard chosen by the user. This is useful for receiving tonal input from a MIDI keyboard. The stored mapping consists of the midi controller, and a midi channel. Feature-level code generally doesn't need to know these details. + - `midi_control_change_input` - Receive MIDI events for a specific [controller change](https://cmtext.indiana.edu/MIDI/chapter3_controller_change.php) input chosen by the user, like a slider or knob. This is useful for receiving a spectrum of data from one control. + - `midi_button_input` - Receive MIDI events for a specific MIDI controller button or note chosen by the user. This is useful for running a specific action when the given button is pressed. +- Outputs + - `musical_keyboard_output` - Send MIDI events to a MIDI keyboard chosen by the user. This is useful for sending tonal information to a DAW or hardware synth. + - `midi_control_change_output` - Send MIDI events for a controller change output chosen by the user. This is useful for changing things on a DAW effect like the dry/wet or intensity setting. + - `midi_button_output` - Send MIDI events for a specific button/note chosen by the user. This is useful to send discrete messages to a toggleable setting in a DAW. + +## Examples + +Here is a basic "MIDI thru" example, where we simply proxy MIDI events between two MIDI keyboards chosen by the user: + +```jsx +import springboard from 'springboard'; + +import '@jamtools/core/modules/macro_module/macro_module'; + +springboard.registerModule('MidiThru', {}, async (moduleAPI) => { + // Get the Macro module + const macroModule = moduleAPI.deps.module.moduleRegistry.getModule('macro'); + + // Create macros + const inputMacro = await macroModule.createMacro(moduleAPI, 'My MIDI Input', 'musical_keyboard_input', {}); + const outputMacro = await macroModule.createMacro(moduleAPI, 'My MIDI Output', 'musical_keyboard_output', {}); + + // Subscribe to events from the MIDI keyboard(s) chosen by the user for the "My MIDI Input" macro defined above + inputMacro.subject.subscribe(evt => { + outputMacro.send(evt.event); // Send the same MIDI event to the configured MIDI output device + }); + + moduleAPI.registerRoute('', {}, () => { + return ( +
+ + +
+ ); + }); +}); +``` + +We would normally do something more useful here like an arpeggio or chord extension, but for this example we are just doing a "MIDI Thru" by proxying the events from one MIDI device to another. + +Instead of explicitly calling `input.subject.subscribe`, we can also use the shorthand `onTrigger` option to listen for events: + +```jsx +const outputMacro = await macroModule.createMacro(moduleAPI, 'My MIDI Output', 'musical_keyboard_output', {}); + +const inputMacro = await macroModule.createMacro(moduleAPI, 'My MIDI Input', 'musical_keyboard_input', { + onTrigger: (evt) => { + outputMacro.send(evt.event); + }, +}); +``` + +--- + +Here is an example where we extend the note pressed to be a major triad: + +```jsx +const outputMacro = await macroModule.createMacro(moduleAPI, 'My MIDI Output', 'musical_keyboard_output', {}); + +const inputMacro = await macroModule.createMacro(moduleAPI, 'My MIDI Input', 'musical_keyboard_input', { + onTrigger: (evt) => { + // Send 3 MIDI events. One for each note of the triad. + + outputMacro.send(evt.event); // Send the original note pressed + + outputMacro.send({ + ...evt.event, + number: evt.event.number + 4, // Major third interval + }); + + outputMacro.send({ + ...evt.event, + number: evt.event.number + 7, // Perfect fifth interval + }); + }, +}); +``` diff --git a/content/docs/jamtools/midi/midi-io-module.md b/content/docs/jamtools/midi/midi-io-module.md new file mode 100644 index 0000000..b3cc28f --- /dev/null +++ b/content/docs/jamtools/midi/midi-io-module.md @@ -0,0 +1,52 @@ +--- +title: "MIDI IO" +description: "Reference pages are ideal for outlining how things work in terse and clear terms." +summary: "" +date: 2023-09-07T16:13:18+02:00 +lastmod: 2023-09-07T16:13:18+02:00 +draft: false +weight: 100 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +The MIDI IO module exposes functionality for listening to MIDI input devices and communicating to MIDI output devices. Using this module, the application needs no boilerplate code for MIDI device initialization or polling. + +Example of using the MIDI IO module's `midiInputSubject` to subscribe to all incoming midi events for any plugged in MIDI devices. + +```jsx +import React from 'react'; + +import springboard from 'springboard'; +import {MidiEventFull} from '@jamtools/core/modules/macro_module/macro_module_types'; + +import '@jamtools/core/modules/io/io_module'; + +springboard.registerModule('Main', {}, async (moduleAPI) => { + const mostRecentMidiEvent = await moduleAPI.statesAPI.createSharedState('mostRecentMidiEvent', null); + const ioModule = moduleAPI.deps.module.moduleRegistry.getModule('io'); + + // Subscribe to all midi input events + ioModule.midiInputSubject.subscribe(event => { + mostRecentMidiEvent.setState(event); + }); + + moduleAPI.registerRoute('', {}, () => { + const midiEvent = mostRecentMidiEvent.useState(); + + return ( +
+ {midiEvent && ( +
+                        {JSON.stringify(midiEvent, null, 2)}
+                    
+ )} +
+ ); + }); +}); +``` diff --git a/content/docs/jamtools/midi/overview.md b/content/docs/jamtools/midi/overview.md new file mode 100644 index 0000000..a9cca92 --- /dev/null +++ b/content/docs/jamtools/midi/overview.md @@ -0,0 +1,29 @@ +--- +title: "Overview" +description: "Reference pages are ideal for outlining how things work in terse and clear terms." +summary: "" +date: 2023-09-07T16:13:18+02:00 +lastmod: 2023-09-07T16:13:18+02:00 +draft: false +weight: 10 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +Springboard is heavily influenced by the requirements of Jam Tools, which entails: + +- Easily interact with MIDI instruments +- Configure the MIDI instruments from a device other than the device with the MIDI instruments connected +- Be able to run the application as a fullstack app or as a standalone browser application +- Have the feature-level and UI code "right next" to the related MIDI functionality (ideally it's possible to have them in the same file) + +The modules provided by the [`@jamtools/core`](https://npmjs.com/package/@jamtools/core) package contain the relevant functionality develop your MIDI application. In particular: + +- [Macro module](./macro-module.md) - Exposes functionality to allow the user to configure their own MIDI devices to use a given feature of your application. The Macro module uses the MIDI IO module to interact with MIDI devices. +- [MIDI IO module](./midi-io-module.md) - Exposes functionality to listen to MIDI input devices and communicate to MIDI output devices. This module makes it so the application needs no MIDI device initialization or polling. + +Coming soon is the "Snacks" module, which will allow MIDI-related features to scale better. At the moment, a module has to implement its whole feature once, whereas with the Snack system the user will be able to compose their own features using smaller pieces implemented by modules. The current system is analogous to having an effect rack hardcoded, where as the Snack system allows the user to construct their own effect racks, and make several effect racks arbitrarily. diff --git a/content/docs/springboard/_index.md b/content/docs/springboard/_index.md new file mode 100644 index 0000000..2d480a1 --- /dev/null +++ b/content/docs/springboard/_index.md @@ -0,0 +1,17 @@ +--- +title: "Springboard" +description: "" +summary: "" +date: 2023-09-07T16:12:37+02:00 +lastmod: 2023-09-07T16:12:37+02:00 +draft: false +weight: 10 +toc: true +sidebar: + collapsed: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- diff --git a/content/docs/springboard/dependency-injection.md b/content/docs/springboard/dependency-injection.md new file mode 100644 index 0000000..b96e064 --- /dev/null +++ b/content/docs/springboard/dependency-injection.md @@ -0,0 +1,4 @@ +Springboard has first-class support for dependency injection. + +TODO: describe the process of comment-based platform-specific compile-time conditionals +and also implement the proper dependency injection stuff diff --git a/content/docs/springboard/deployment-contexts/_index.md b/content/docs/springboard/deployment-contexts/_index.md new file mode 100644 index 0000000..a3a5123 --- /dev/null +++ b/content/docs/springboard/deployment-contexts/_index.md @@ -0,0 +1,17 @@ +--- +title: "Deployment Contexts" +description: "" +summary: "" +date: 2023-09-07T16:12:37+02:00 +lastmod: 2023-09-07T16:12:37+02:00 +draft: false +weight: 100 +toc: true +sidebar: + collapsed: false +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- diff --git a/content/docs/springboard/deployment-contexts/deployment-contexts.md b/content/docs/springboard/deployment-contexts/deployment-contexts.md new file mode 100644 index 0000000..c1cd3c2 --- /dev/null +++ b/content/docs/springboard/deployment-contexts/deployment-contexts.md @@ -0,0 +1,19 @@ +## Deployment contexts + +A Springboard application can be deployed and run in multiple ways. The framework abstracts this away, so that feature-level code can be agnostic to the deployment context. + +An application deployment can be single-player-only, multi-player-only, or a hybrid where the user swaps between contexts. + +### Multi-player + +The framework helps facilitate realtime communication between clients behind-the-scenes using [WebSockets](https://en.wikipedia.org/wiki/WebSocket) and [JSON-RPC](https://en.wikipedia.org/wiki/JSON-RPC#Version_2.0). By defining shared actions and states in your application, user actions are sent to the correct device to process the action, and any shared state that changes as a consequence from the action is automatically synchronized across devices in realtime. + +![Multi-player deployment](/images/deployment-diagram-multiplayer.png) + +### Single-player + +In the single-player (or local-only, offline) mode, all code runs locally, and any data storage happens locally. + +When the user chooses to go local-only, the browser is refreshed to process the change. This may not be a requirement in the future. + +![Single-player deployment](/images/deployment-diagram-singleplayer.png) diff --git a/content/docs/springboard/deployment-contexts/desktop-app.md b/content/docs/springboard/deployment-contexts/desktop-app.md new file mode 100644 index 0000000..e50fbd0 --- /dev/null +++ b/content/docs/springboard/deployment-contexts/desktop-app.md @@ -0,0 +1,20 @@ +--- +title: "Desktop App" +description: "Reference pages are ideal for outlining how things work in terse and clear terms." +summary: "" +date: 2023-09-07T16:13:18+02:00 +lastmod: 2023-09-07T16:13:18+02:00 +draft: false +weight: 100 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +Springboard's desktop app deployment uses [Tauri](https://v2.tauri.app) as the application shell, and packages the app with: + +- A Hono server sidecar to host the application +- A separate Maestro node script to make it so IO operations (like MIDI events) can happen with minimal latency diff --git a/content/docs/springboard/deployment-contexts/mobile-app.md b/content/docs/springboard/deployment-contexts/mobile-app.md new file mode 100644 index 0000000..0ac8dd5 --- /dev/null +++ b/content/docs/springboard/deployment-contexts/mobile-app.md @@ -0,0 +1,19 @@ +--- +title: "Mobile App" +description: "Reference pages are ideal for outlining how things work in terse and clear terms." +summary: "" +date: 2023-09-07T16:13:18+02:00 +lastmod: 2023-09-07T16:13:18+02:00 +draft: false +weight: 100 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +Springboard's mobile app deployment uses [React Native](https://reactnative.dev) as the application shell, and runs the frontend Springboard app in a webview. + +For offline actions, the webview uses RPC actions to communicate to the React Native process for file and data management. diff --git a/content/docs/springboard/deployment-contexts/raspberry-pi.md b/content/docs/springboard/deployment-contexts/raspberry-pi.md new file mode 100644 index 0000000..d32b84b --- /dev/null +++ b/content/docs/springboard/deployment-contexts/raspberry-pi.md @@ -0,0 +1,15 @@ +--- +title: "Raspberry Pi" +description: "Reference pages are ideal for outlining how things work in terse and clear terms." +summary: "" +date: 2023-09-07T16:13:18+02:00 +lastmod: 2023-09-07T16:13:18+02:00 +draft: false +weight: 100 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- diff --git a/content/docs/springboard/get-started.md b/content/docs/springboard/get-started.md new file mode 100644 index 0000000..917f278 --- /dev/null +++ b/content/docs/springboard/get-started.md @@ -0,0 +1,6 @@ +Note that dependencies in a springboard project must be kept in lockstep +The springboard CLI provides a way to do this +sb upgrade 0.15.0 --packages package.json,apps/mobile/package.json + + +This is because all code in this monorepo is released at the same time, to ensure any breaking situations between version mismatches diff --git a/content/docs/guides/_index.md b/content/docs/springboard/guides/_index.md similarity index 88% rename from content/docs/guides/_index.md rename to content/docs/springboard/guides/_index.md index 94f781c..a4d9eec 100644 --- a/content/docs/guides/_index.md +++ b/content/docs/springboard/guides/_index.md @@ -7,6 +7,8 @@ lastmod: 2023-09-07T16:06:50+02:00 draft: false weight: 800 toc: true +sidebar: + collapsed: false seo: title: "" # custom title (optional) description: "" # custom description (recommended) diff --git a/content/docs/springboard/guides/accessing-a-database.md b/content/docs/springboard/guides/accessing-a-database.md new file mode 100644 index 0000000..a44c676 --- /dev/null +++ b/content/docs/springboard/guides/accessing-a-database.md @@ -0,0 +1,25 @@ +--- +title: "Accessing a database" +description: "Guides lead a user through a specific task they want to accomplish, often with a sequence of steps." +summary: "" +date: 2023-09-07T16:04:48+02:00 +lastmod: 2023-09-07T16:04:48+02:00 +draft: false +weight: 810 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +Use Prisma schemas and Kysely for ubiquitous database access. Different platforms/modules can extend the schema as needed. + +Be sure to get some observability tooling around your queries. + +It's up to you how you choose to implement your database access. It's a good idea to put the boilerplate for setting up the database in its own module. Queries can be defined as actions, so you can have type-safety and universal support for the logic. + +## Further reading + +- Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework diff --git a/content/docs/springboard/guides/registering-ui-routes.md b/content/docs/springboard/guides/registering-ui-routes.md new file mode 100644 index 0000000..e69de29 diff --git a/content/docs/springboard/module-development.md b/content/docs/springboard/module-development.md new file mode 100644 index 0000000..f0a984c --- /dev/null +++ b/content/docs/springboard/module-development.md @@ -0,0 +1,42 @@ +A Springboard application is typically comprised of several modules. A given module uses the [ModuleAPI](../typedoc_docs/module_api/classes/ModuleAPI.md) to register components, actions, and states with the application. When a module is initialized, it's given its own copy of the `ModuleAPI`. Any actions or states created will be scoped to the given module's state namespace behind the scenes. + +The ModuleAPI allows modules to: + +- Register routes and render React components +- Create remote actions and shared states +- Receive dependencies through dependency injection +- Expose functions and utilties for other modules to use. This typcially includes actions, states, or reusable React components. +- Interact with other modules by consuming their exposed functions/properties +- When the same module is defined on two devices, any actions defined can be called between devices. + +Modules can play a few different types of roles: + +- Feature - A feature-level module implements certain features for your application. These modules typically register routes and interact with other modules to faciliate cohesion and "get stuff done". +- Utility - A utility module exposes things for other modules to use, and likely does not register any routes. An example utility module is the [Macro Module](https://github.com/jamtools/jamtools/blob/main/packages/jamtools/core/modules/io/io_module.tsx), which exposes functions for other modules to declare MIDI entrypoints for a given feature. +- Initializer - An initializer module does some initial setup for an application. This kind of module is typically not needed for small applications, though these modules become useful when deployment use cases are complicated. You may want to have some specific initialization on a mobile app versus desktop app, for instance. + +--- + +## Writing a feature module + +When registering a module, the module provides a callback to run on app initialization. The callback the module provides essentially _is_ the module. The callback receives an instance of the `ModuleAPI`. The namespacing of states and actions for this particular module are automatically managed by the framework. Some useful methods/properties from the `ModuleAPI` are documented in the [ModuleAPI docs](../typedoc_docs/module_api/classes/ModuleAPI.md). + +(explain some methods briefly here, a link to individual api reference methods. probably good to have youtube videos walking through code too, though you can do the same in text form probably) + +## Writing a utility module + +By default it is assumed a module is a feature or initializer module, meaning it is assumed that the module does not expose anything for other modules to use. In order for other modules to be aware of any exposed functions/properties, we need to perform [interface merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces) to register the module's return value with the Springboard framework's module system. + +Here's an [example]( +https://github.com/jamtools/jamtools/blob/cea35258c6d7e495a68148c4a9e61ac06dcca609/packages/jamtools/core/modules/macro_module/macro_module.tsx#L31-L35) of the Macro module declaring its return type: + + +```ts +declare module 'springboard/module_registry/module_registry' { + interface AllModules { + macro: MacroModule; + } +} +``` + +This makes it so any other module that interacts with this module knows what is available from that module, and typescript can provide the relevant autocompletions and type checking for consuming this module. When the module is registered with the framework, the type checker will ensure that the return value matches what is defined in the `AllModules` interface. diff --git a/content/docs/springboard/overview.md b/content/docs/springboard/overview.md new file mode 100644 index 0000000..ee3fd11 --- /dev/null +++ b/content/docs/springboard/overview.md @@ -0,0 +1,30 @@ +--- +title: "Overview" +description: "Reference pages are ideal for outlining how things work in terse and clear terms." +summary: "" +date: 2023-09-07T16:13:18+02:00 +lastmod: 2023-09-07T16:13:18+02:00 +draft: false +weight: 10 +toc: true +seo: + title: "" # custom title (optional) + description: "" # custom description (recommended) + canonical: "" # custom canonical URL (optional) + robots: "" # custom robot tags (optional) +--- + +Springboard was built out of the necessity of creating MIDI applications deployed in a fullstack web application context, and simultaneously allowing the application to run standalone in the browser for maximum portability. This dual requirement has shaped how the Springboard framework works, by maximizing the amount of code reuse across the different platforms. + +Springboard uses the concept of [modules](./module-development.md) to encapsulate responsibilities of different pieces of code. A new Springboard project contains no modules by default. There are some predefined modules that you can import into your code, namely the modules defined by the [`@jamtools/core`](https://github.com/jamtools/jamtools/tree/main/packages/jamtools/core/modules) package at the time of writing. + +What is a full-stack MIDI application? An application where there is a client-server architecture involved, and: +- there are MIDI instruments plugged into the desktop/server computer, and mobile/browser clients can interact via a user interface. +- or there are MIDI instruments connected to clients, and interacting with other clients that potentially also have MIDI instruments connected to them. + +--- + +More information: + +- [Developing a module](./module-development.md) +- [Deployment contexts](./deployment-contexts/deployment-contexts.md) - Single-player and multi-player diff --git a/content/docs/reference/_index.md b/content/docs/springboard/reference/_index.md similarity index 100% rename from content/docs/reference/_index.md rename to content/docs/springboard/reference/_index.md diff --git a/content/docs/reference/example.md b/content/docs/springboard/reference/example.md similarity index 100% rename from content/docs/reference/example.md rename to content/docs/springboard/reference/example.md diff --git a/content/docs/springboard/server-modules.md b/content/docs/springboard/server-modules.md new file mode 100644 index 0000000..1f27feb --- /dev/null +++ b/content/docs/springboard/server-modules.md @@ -0,0 +1,3 @@ +You can hook into the framework's Hono server using the server module system. + +Most business logic should go in the application/Maestro portion, though if http is ever directly required, you'll need an http server, which is where this comes in handy. diff --git a/hugo_stats.json b/hugo_stats.json index 4b9a90e..0956a7b 100644 --- a/hugo_stats.json +++ b/hugo_stats.json @@ -9,8 +9,12 @@ "button", "circle", "code", + "desc", "details", "div", + "em", + "figcaption", + "figure", "footer", "form", "g", @@ -20,7 +24,9 @@ "h5", "head", "header", + "hr", "html", + "img", "input", "kbd", "label", @@ -33,6 +39,8 @@ "noscript", "p", "path", + "pre", + "rect", "script", "section", "small", @@ -53,6 +61,8 @@ "bg-dots", "blog", "blog-header", + "border-2", + "border-black", "btn", "btn-close", "btn-cta", @@ -63,10 +73,15 @@ "card-body", "card-list", "categories", + "chroma", + "col-lg-1", "col-lg-10", "col-lg-11", "col-lg-12", + "col-lg-2", + "col-lg-3", "col-lg-5", + "col-lg-6", "col-lg-8", "col-lg-9", "col-md-12", @@ -88,6 +103,7 @@ "d-none", "d-xl-block", "d-xl-none", + "dark:border-none", "docs", "docs-content", "docs-links", @@ -95,6 +111,7 @@ "docs-toc", "doks-sidebar", "error404", + "expressive-code", "flex-column", "flex-grow-1", "flex-lg-row", @@ -105,11 +122,15 @@ "footer", "form-control", "form-control-lg", + "frame", "fs-5", "h-auto", + "h2", "h3", "h4", "h5", + "header", + "highlight", "home", "icon", "icon-tabler", @@ -135,6 +156,10 @@ "list-unstyled", "list-view", "m-2", + "marquee", + "marquee-content", + "marquee-item", + "marquee-text", "mb-0", "me-2", "me-auto", @@ -158,6 +183,7 @@ "mt-n3", "mx-2", "mx-auto", + "mx-xl-auto", "my-3", "nav", "nav-item", @@ -166,6 +192,7 @@ "navbar-brand", "navbar-expand-lg", "navbar-nav", + "not-content", "offcanvas", "offcanvas-body", "offcanvas-end", @@ -174,6 +201,7 @@ "offcanvas-title", "order-3", "order-lg-4", + "overflow-hidden", "p-0", "p-2", "page-footer-meta", @@ -185,6 +213,7 @@ "px-0", "query-no-results", "reading-time", + "rounded-full", "rounded-pill", "row", "search-form", @@ -222,13 +251,24 @@ "toc-mobile", "visually-hidden", "w-100", + "w-[30px]", "wrap" ], "ids": [ + "Esbuild--Streamline-Svg-Logos", + "Expo--Streamline-Simple-Icons", + "Hono--Streamline-Svg-Logos", + "React--Streamline-Simple-Icons", "TableOfContents", + "Tauri--Streamline-Svg-Logos", "buttonColorMode", + "deployment-contexts", "doks-docs-nav", + "examples", "further-reading", + "h-rh-i-0", + "h-rh-i-1", + "multi-player", "offcanvasNavMain", "offcanvasNavMainLabel", "offcanvasNavSection", @@ -240,8 +280,11 @@ "searchResults", "searchToggleDesktop", "searchToggleMobile", + "single-player", "socialMenu", - "toc" + "toc", + "writing-a-feature-module", + "writing-a-utility-module" ] } } diff --git a/layouts/index.html b/layouts/index.html index d519ea2..6d1ab8e 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -6,11 +6,68 @@

{{ .Title }}

{{ .Params.lead | safeHTML }}

- {{ i18n "get_started" }} + + Get Started + {{ .Content }}
+ + + + + {{ end }} {{ define "sidebar-prefooter" }} @@ -19,30 +76,129 @@

{{ .Title }}

{{ end -}} - {{ if eq $.Site.Language.LanguageName "English" }}
-

Update content

-

Edit content/_index.md to see this page change.

+

Write once, run everywhere

+

Run the application in a browser, mobile app, desktop app, and on a server, with zero-config managed GitHub Actions workflows to compile and deploy/update each platform.

-

Add new content

-

Add Markdown files to content to create new pages.

+

Built for Multiplayer

+

Real-time state synchronization is available out-of-the-box between clients in the same session. Focus on your feature-level code, rather than the complexity of managing state.

-

Configure your site

-

Edit your config in config/_default/params.toml.

+

Built for Local-only/Local-first

+

Applications can run entirely offline in the client, and pivot when it needs to interact with a remote server, or optionally rely entirely on the server for business logic.

+

+ An isomorphic and flexible RPC layer allows actions to be run in the right context, determined by feature-level userland code, to fit the use case of the user's current action. +

-
-

Read the docs

-

Learn more in the Docs.

+
+
+
+ + + +
+
- {{ end }} {{ end }} {{ define "sidebar-footer" }}