storyloco 1.0.0

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.
Files changed (34) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/package.json +77 -0
  4. package/packages/heading/README.md +68 -0
  5. package/packages/heading/src/app.svelte +64 -0
  6. package/packages/mux/README.md +68 -0
  7. package/packages/mux/src/app.svelte +576 -0
  8. package/packages/shared/README.md +58 -0
  9. package/packages/shared/dist/components/ui/input/index.d.ts +2 -0
  10. package/packages/shared/dist/components/ui/input/index.js +4 -0
  11. package/packages/shared/dist/components/ui/input/input.svelte +51 -0
  12. package/packages/shared/dist/components/ui/input/input.svelte.d.ts +13 -0
  13. package/packages/shared/dist/components/ui/label/index.d.ts +2 -0
  14. package/packages/shared/dist/components/ui/label/index.js +4 -0
  15. package/packages/shared/dist/components/ui/label/label.svelte +16 -0
  16. package/packages/shared/dist/components/ui/label/label.svelte.d.ts +4 -0
  17. package/packages/shared/dist/components/ui/skeleton/index.d.ts +2 -0
  18. package/packages/shared/dist/components/ui/skeleton/index.js +4 -0
  19. package/packages/shared/dist/components/ui/skeleton/skeleton.svelte +17 -0
  20. package/packages/shared/dist/components/ui/skeleton/skeleton.svelte.d.ts +5 -0
  21. package/packages/shared/dist/components/ui/switch/index.d.ts +2 -0
  22. package/packages/shared/dist/components/ui/switch/index.js +4 -0
  23. package/packages/shared/dist/components/ui/switch/switch.svelte +29 -0
  24. package/packages/shared/dist/components/ui/switch/switch.svelte.d.ts +4 -0
  25. package/packages/shared/dist/global.css +161 -0
  26. package/packages/shared/dist/index.d.ts +5 -0
  27. package/packages/shared/dist/index.js +7 -0
  28. package/packages/shared/dist/setup.d.ts +2 -0
  29. package/packages/shared/dist/setup.js +25 -0
  30. package/packages/shared/dist/utils.d.ts +12 -0
  31. package/packages/shared/dist/utils.js +5 -0
  32. package/packages/theme/README.md +68 -0
  33. package/packages/theme/src/app.svelte +114 -0
  34. package/src/index.ts +3 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Storyloco
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # Storyloco
2
+
3
+ A collection of slick Storyblok field plugins built with Svelte 5, TypeScript, and Tailwind CSS. Each plugin is designed to be lightweight, performant, and a joy to use.
4
+
5
+ ## 🚀 What's Included
6
+
7
+ ### Field Plugins
8
+
9
+ - **🎬 Mux Video** - Upload, manage, and configure videos with Mux integration
10
+ - **📝 Heading** - Simple heading editor with level selection (H1-H6)
11
+ - **🎨 Theme** - Color theme selector with visual previews
12
+
13
+ ### Shared Components
14
+
15
+ A comprehensive UI component library built with:
16
+
17
+ - **Svelte 5** - Latest reactive framework
18
+ - **Tailwind CSS** - Utility-first styling
19
+ - **Bits UI** - Headless component primitives
20
+ - **TypeScript** - Full type safety
21
+
22
+ ## 📦 Installation
23
+
24
+ ```bash
25
+ bun add storyloco
26
+ ```
27
+
28
+ ## 🎯 Usage
29
+
30
+ ### Import Types
31
+
32
+ ```typescript
33
+ // Import specific plugin types
34
+ import type { Video } from 'storyloco/mux'
35
+ import type { Heading } from 'storyloco/heading'
36
+
37
+ // Or import everything
38
+ import type { Video, Heading } from 'storyloco'
39
+ ```
40
+
41
+ ### Use Shared Components
42
+
43
+ ```typescript
44
+ import { Input, Label, Switch, Skeleton } from 'storyloco/shared'
45
+ import { cn } from 'storyloco/shared/utils'
46
+ ```
47
+
48
+ ## 🔧 Development
49
+
50
+ ### Prerequisites
51
+
52
+ - **Bun** (recommended) or Node.js 18+
53
+ - **Storyblok** account for field plugin development
54
+
55
+ ### Setup
56
+
57
+ ```bash
58
+ # Clone the repo
59
+ git clone <your-repo-url>
60
+ cd storyloco
61
+
62
+ # Install dependencies
63
+ bun install
64
+
65
+ # Start development
66
+ bun run dev
67
+ ```
68
+
69
+ ### Project Structure
70
+
71
+ ```
72
+ storyloco/
73
+ ├── packages/
74
+ │ ├── mux/ # Video upload & management plugin
75
+ │ ├── heading/ # Heading editor plugin
76
+ │ ├── theme/ # Theme selector plugin
77
+ │ └── shared/ # UI component library
78
+ ├── src/
79
+ │ └── index.ts # Type exports
80
+ └── package.json # Root workspace config
81
+ ```
82
+
83
+ ### Adding New Plugins
84
+
85
+ 1. **Create plugin package**:
86
+
87
+ ```bash
88
+ bun run add-plugin
89
+ ```
90
+
91
+ 2. **Export types** in your plugin:
92
+
93
+ ```svelte
94
+ <script module lang="ts">
95
+ export interface YourType {
96
+ // your interface here
97
+ }
98
+ </script>
99
+ ```
100
+
101
+ 3. **Add to root exports** in `package.json`:
102
+
103
+ ```json
104
+ {
105
+ "exports": {
106
+ "./your-plugin": {
107
+ "types": "./packages/your-plugin/src/app.svelte",
108
+ "import": "./packages/your-plugin/src/app.svelte"
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ 4. **Re-export in `src/index.ts`**:
115
+ ```typescript
116
+ export type { YourType } from '../packages/your-plugin/src/app.svelte'
117
+ ```
118
+
119
+ ## 🎨 Shared Components
120
+
121
+ The shared package provides a comprehensive set of UI components:
122
+
123
+ ### Form Components
124
+
125
+ - `Input` - Text input with proper styling
126
+ - `Label` - Accessible form labels
127
+ - `Switch` - Toggle switch component
128
+
129
+ ### Display Components
130
+
131
+ - `Skeleton` - Loading skeleton component
132
+
133
+ ### Utilities
134
+
135
+ - `cn()` - Class name utility (clsx + tailwind-merge)
136
+ - `setup()` - Global setup function
137
+
138
+ ## 🔌 Field Plugin Development
139
+
140
+ Each plugin follows the Storyblok field plugin pattern:
141
+
142
+ ```svelte
143
+ <script lang="ts">
144
+ import { createFieldPlugin, type FieldPluginResponse } from '@storyblok/field-plugin'
145
+
146
+ interface YourData {
147
+ // your data structure
148
+ }
149
+
150
+ type Plugin = FieldPluginResponse<YourData | null>
151
+
152
+ let plugin: Plugin | null = $state(null)
153
+ let content: YourData = $state(initial_value)
154
+
155
+ onMount(() => {
156
+ createFieldPlugin({
157
+ enablePortalModal: true,
158
+ validateContent: (c) => {
159
+ // validation logic
160
+ return { content: validated_content }
161
+ },
162
+ onUpdateState: (state) => {
163
+ plugin = state as Plugin
164
+ },
165
+ })
166
+ })
167
+ </script>
168
+ ```
169
+
170
+ ## 🚀 Deployment
171
+
172
+ ### Deploy Individual Plugins
173
+
174
+ ```bash
175
+ # From the plugin directory
176
+ cd packages/mux
177
+ bun run deploy
178
+ ```
179
+
180
+ ### Build Shared Package
181
+
182
+ ```bash
183
+ cd packages/shared
184
+ bun run build
185
+ ```
186
+
187
+ ## 🛠️ Tech Stack
188
+
189
+ - **Framework**: Svelte 5 with TypeScript
190
+ - **Styling**: Tailwind CSS
191
+ - **Components**: Shadcn Svelte, Bits UI primitives
192
+ - **Build Tool**: Vite
193
+ - **Package Manager**: Bun (recommended)
194
+ - **Linting**: ESLint + Prettier
195
+
196
+ ## 📄 License
197
+
198
+ MIT License - feel free to use this however you want, you absolute legend! 🎯
199
+
200
+ ## 🤝 Contributing
201
+
202
+ 1. Fork the repo
203
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
204
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
205
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
206
+ 5. Open a Pull Request
207
+
208
+ ---
209
+
210
+ Built with ❤️ and a lot of swearing by the Storyloco team
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "storyloco",
3
+ "version": "1.0.0",
4
+ "description": "A collection of slick Storyblok field plugins built with Svelte 5, TypeScript, and Tailwind CSS",
5
+ "keywords": [
6
+ "storyblok",
7
+ "field-plugin",
8
+ "svelte",
9
+ "typescript",
10
+ "tailwind",
11
+ "cms",
12
+ "headless"
13
+ ],
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/darbymanning/storyloco.git"
18
+ },
19
+ "main": "./src/index.ts",
20
+ "types": "./src/index.ts",
21
+ "files": [
22
+ "src",
23
+ "packages/mux/src/app.svelte",
24
+ "packages/heading/src/app.svelte",
25
+ "packages/theme/src/app.svelte",
26
+ "packages/shared/dist"
27
+ ],
28
+ "exports": {
29
+ ".": {
30
+ "types": "./src/index.ts",
31
+ "import": "./src/index.ts"
32
+ },
33
+ "./mux": {
34
+ "types": "./packages/mux/src/app.svelte",
35
+ "import": "./packages/mux/src/app.svelte"
36
+ },
37
+ "./heading": {
38
+ "types": "./packages/heading/src/app.svelte",
39
+ "import": "./packages/heading/src/app.svelte"
40
+ },
41
+ "./theme": {
42
+ "types": "./packages/theme/src/app.svelte",
43
+ "import": "./packages/theme/src/app.svelte"
44
+ },
45
+ "./shared": {
46
+ "types": "./packages/shared/dist/index.d.ts",
47
+ "svelte": "./packages/shared/dist/index.js"
48
+ },
49
+ "./shared/utils": {
50
+ "types": "./packages/shared/dist/utils.d.ts",
51
+ "svelte": "./packages/shared/dist/utils.js"
52
+ }
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "24.0.0",
56
+ "@typescript-eslint/eslint-plugin": "8.34.0",
57
+ "@typescript-eslint/parser": "8.34.0",
58
+ "eslint": "9.28.0",
59
+ "eslint-plugin-prettier": "5.4.1",
60
+ "kitto": "1.0.0-rc.12",
61
+ "prettier": "3.5.3",
62
+ "typescript": "5.8.3",
63
+ "vite": "6.3.5"
64
+ },
65
+ "scripts": {
66
+ "add-plugin": "npx @storyblok/field-plugin-cli@latest add --dir \"./packages\" --structure monorepo",
67
+ "lint": "eslint --ext .js .",
68
+ "format": "prettier . --write"
69
+ },
70
+ "type": "module",
71
+ "workspaces": [
72
+ "packages/*"
73
+ ],
74
+ "dependencies": {
75
+ "color": "5.0.0"
76
+ }
77
+ }
@@ -0,0 +1,68 @@
1
+ # Storyblok Field Plugin with "Vanilla" JavaScript
2
+
3
+ This project was generated with `@storyblok/field-plugin-cli`.
4
+
5
+ ## Usage
6
+
7
+ For development, run the application locally with
8
+
9
+ ```shell
10
+ npm run dev
11
+ ```
12
+
13
+ and open the [Sandbox](https://plugin-sandbox.storyblok.com/field-plugin/).
14
+
15
+ To build the project, run
16
+
17
+ ```shell
18
+ npm run build
19
+ ```
20
+
21
+ Deploy the field plugin with the CLI. Issue a [personal access token](https://app.storyblok.com/#/me/account?tab=token), rename `.env.local.example` to `.env.example`, open the file, set the value `STORYBLOK_PERSONAL_ACCESS_TOKEN`, and run
22
+
23
+ ```shell
24
+ npm run deploy
25
+ ```
26
+
27
+ ## Manifest File for Field Plugins
28
+
29
+ The manifest file is a configuration that enhances the functionality of your field plugin. This JSON file, named `field-plugin.config.json` and located in your project's root folder, is optional but highly recommended.
30
+
31
+ The manifest file allows you to configure [options](https://www.storyblok.com/docs/plugins/field-plugins/introduction#options) for your field plugin. When developing your field plugin with the [Sandbox](https://plugin-sandbox.storyblok.com/field-plugin/), the options are applied by default. Also, the deploy command automatically applies the options in production. So, you no longer need to configure the options manually.
32
+
33
+ ### Configuring a Manifest File
34
+
35
+ The options list within the file `field-plugin.config.json` should consist of key-value objects representing the essential options required for your field plugin to function correctly, along with their corresponding values. This is an example of how it should be structured:
36
+
37
+ ```json
38
+ {
39
+ "options": [
40
+ {
41
+ "name": "myPluginInitialValue",
42
+ "value": 100
43
+ }
44
+ ]
45
+ }
46
+ ```
47
+
48
+ Now, you just need to access these options in your code like in the example below:
49
+
50
+ ```js
51
+ console.log(data.options.myPluginInitialValue)
52
+ ```
53
+
54
+ ## Clean up the boilerplate
55
+
56
+ To start from a blank state, delete the following files:
57
+
58
+ - src/components/components.js
59
+
60
+ And search for comments `DELETE THIS BOILERPLATE` comments and delete the code around them.
61
+
62
+ ## Continuous delivery
63
+
64
+ Set up [continuous delivery](https://www.storyblok.com/docs/plugins/field-plugins/continuous-delivery) with the CLI. Define an environmental variable `STORYBLOK_PERSONAL_ACCESS_TOKEN`, and use the `--name` and `--skipPrompts` options as such:
65
+
66
+ ```shell
67
+ npm run deploy --name $NAME --skipPrompts
68
+ ```
@@ -0,0 +1,64 @@
1
+ <script module lang="ts">
2
+ export interface Heading {
3
+ text: string
4
+ level: 1 | 2 | 3 | 4 | 5 | 6
5
+ }
6
+ </script>
7
+
8
+ <script lang="ts">
9
+ import { createFieldPlugin, type FieldPluginResponse } from '@storyblok/field-plugin'
10
+ import { onMount } from 'svelte'
11
+ import { Input } from 'shared'
12
+
13
+ type Plugin = FieldPluginResponse<Heading | null>
14
+
15
+ let plugin: Plugin | null = $state(null)
16
+ let content: Heading = $state({ text: '', level: 1 })
17
+
18
+ onMount(() => {
19
+ createFieldPlugin({
20
+ enablePortalModal: true,
21
+ validateContent: (content) => {
22
+ if (typeof content !== 'object' || content === null) {
23
+ return { content: { text: '', level: 1 } as Heading }
24
+ }
25
+
26
+ const validated = content as Heading
27
+ // ensure level is one of the allowed values
28
+ if (![1, 2, 3, 4, 5, 6].includes(validated.level)) {
29
+ validated.level = 1
30
+ }
31
+ // ensure text is a string
32
+ if (typeof validated.text !== 'string') {
33
+ validated.text = ''
34
+ }
35
+
36
+ return { content: validated }
37
+ },
38
+ onUpdateState: (state) => {
39
+ plugin = state as Plugin
40
+ },
41
+ })
42
+ })
43
+
44
+ function update() {
45
+ if (plugin?.type !== 'loaded') return
46
+ const state = $state.snapshot(content)
47
+ plugin.actions.setContent(state)
48
+ }
49
+ </script>
50
+
51
+ {#if plugin?.type === 'loaded'}
52
+ <fieldset class="flex gap-2">
53
+ <Input class="flex-1" bind:value={content.text} oninput={update} />
54
+ <select
55
+ class="rounded outline-none focus-visible:border-ring border-input bg-white border dark:bg-input/30"
56
+ bind:value={() => content.level, (v) => (content.level = Number(v) as Heading['level'])}
57
+ onchange={update}
58
+ >
59
+ {#each [1, 2, 3, 4, 5, 6] as level}
60
+ <option value={level}>H{level}</option>
61
+ {/each}
62
+ </select>
63
+ </fieldset>
64
+ {/if}
@@ -0,0 +1,68 @@
1
+ # Storyblok Field Plugin with "Vanilla" JavaScript
2
+
3
+ This project was generated with `@storyblok/field-plugin-cli`.
4
+
5
+ ## Usage
6
+
7
+ For development, run the application locally with
8
+
9
+ ```shell
10
+ npm run dev
11
+ ```
12
+
13
+ and open the [Sandbox](https://plugin-sandbox.storyblok.com/field-plugin/).
14
+
15
+ To build the project, run
16
+
17
+ ```shell
18
+ npm run build
19
+ ```
20
+
21
+ Deploy the field plugin with the CLI. Issue a [personal access token](https://app.storyblok.com/#/me/account?tab=token), rename `.env.local.example` to `.env.example`, open the file, set the value `STORYBLOK_PERSONAL_ACCESS_TOKEN`, and run
22
+
23
+ ```shell
24
+ npm run deploy
25
+ ```
26
+
27
+ ## Manifest File for Field Plugins
28
+
29
+ The manifest file is a configuration that enhances the functionality of your field plugin. This JSON file, named `field-plugin.config.json` and located in your project's root folder, is optional but highly recommended.
30
+
31
+ The manifest file allows you to configure [options](https://www.storyblok.com/docs/plugins/field-plugins/introduction#options) for your field plugin. When developing your field plugin with the [Sandbox](https://plugin-sandbox.storyblok.com/field-plugin/), the options are applied by default. Also, the deploy command automatically applies the options in production. So, you no longer need to configure the options manually.
32
+
33
+ ### Configuring a Manifest File
34
+
35
+ The options list within the file `field-plugin.config.json` should consist of key-value objects representing the essential options required for your field plugin to function correctly, along with their corresponding values. This is an example of how it should be structured:
36
+
37
+ ```json
38
+ {
39
+ "options": [
40
+ {
41
+ "name": "myPluginInitialValue",
42
+ "value": 100
43
+ }
44
+ ]
45
+ }
46
+ ```
47
+
48
+ Now, you just need to access these options in your code like in the example below:
49
+
50
+ ```js
51
+ console.log(data.options.myPluginInitialValue)
52
+ ```
53
+
54
+ ## Clean up the boilerplate
55
+
56
+ To start from a blank state, delete the following files:
57
+
58
+ - src/components/components.js
59
+
60
+ And search for comments `DELETE THIS BOILERPLATE` comments and delete the code around them.
61
+
62
+ ## Continuous delivery
63
+
64
+ Set up [continuous delivery](https://www.storyblok.com/docs/plugins/field-plugins/continuous-delivery) with the CLI. Define an environmental variable `STORYBLOK_PERSONAL_ACCESS_TOKEN`, and use the `--name` and `--skipPrompts` options as such:
65
+
66
+ ```shell
67
+ npm run deploy --name $NAME --skipPrompts
68
+ ```