···11<script lang="ts">
22-import type { ElementAttributes } from '$lib/types'
32import type { WithChildren } from 'bits-ui'
43import { tv } from 'tailwind-variants'
44+import type { ElementAttributes } from '$lib/types'
5566const card = tv({
77 base: [
···11-<script lang="ts">
22-import type { Mechanic } from '@starlight/types/hsr'
33-import type { WithChildren } from 'bits-ui'
44-import type { SvelteHTMLElements } from 'svelte/elements'
55-import Heading from '$ui/Heading/Heading.svelte'
66-import HeadingGroup from '$ui/Heading/HeadingGroup.svelte'
77-import SubHeading from '$ui/Heading/SubHeading.svelte'
88-import MechanicChip from '$patterns/Mechanic/MechanicChip.svelte'
99-1010-export type AbilityCardHeadingRootElement = SvelteHTMLElements['header']
1111-export type AbilityCardHeadingProps = WithChildren<AbilityCardHeadingRootElement> & {
1212- mechanic: Mechanic,
1313-}
1414-let { children, mechanic, ...other }: AbilityCardHeadingProps = $props()
1515-</script>
1616-1717-<header {...other} class="flex flex-row items-center justify-between">
1818- <HeadingGroup>
1919- <Heading level={3} withEllipsis class={["whitespace-nowrap selection:bg-hsr-gold/50 selection:text-hsr-dark"]}>
2020- {@render children?.()}
2121- </Heading>
2222- <SubHeading isScript class="-mt-1">{@render children?.()}</SubHeading>
2323- </HeadingGroup>
2424- <MechanicChip mechanic={mechanic} style="outline" size="sm" />
2525-</header>
-2
app/src/lib/patterns/AbilityCard/exports.ts
···11-export { default as Heading } from './AbilityCardHeading.svelte'
22-export type { AbilityCardHeadingProps as HeadingProps } from './AbilityCardHeading.svelte'
···11<script lang="ts">
22-import { ChevronsUpIcon } from '@lucide/svelte'
22+import ChevronsUpIcon from '@lucide/svelte/icons/chevrons-up'
33import type { AbilityMod } from '@starlight/types/dnd'
44import type { SvelteHTMLElements } from 'svelte/elements'
55
···11<script lang="ts">
22-import { DraftingCompassIcon } from '@lucide/svelte'
22+import DraftingCompassIcon from '@lucide/svelte/icons/drafting-compass'
33import type { WithChildren } from 'bits-ui'
44import type { SvelteHTMLElements } from 'svelte/elements'
55import Card from '$ui/Card/Card.svelte'
···11-import { SpeciesArray, type Species } from '@starlight/types/hsr'
11+import { SpeciesEnum, type Species } from '@starlight/types/hsr'
22import type { ParamMatcher } from '@sveltejs/kit'
3344-const set = new Set(SpeciesArray) as Set<string>
55-export const match: ParamMatcher = (p): p is Species => set.has(p)
44+export const match: ParamMatcher = (p): p is Species =>
55+ SpeciesEnum.safeParse(p).success
···11+<script lang="ts">
22+import FormButton from '$ui/Button/FormButton.svelte'
33+import CheckboxCard from '$ui/ChoiceField/CheckboxCard.svelte'
44+import Heading from '$ui/Heading/Heading.svelte'
55+import HeadingGroup from '$ui/Heading/HeadingGroup.svelte'
66+import SubHeading from '$ui/Heading/SubHeading.svelte'
77+import PageLayout from '$ui/Site/PageLayout.svelte'
88+import MultiTextField from '$ui/TextField/MultiTextField.svelte'
99+import TextField from '$ui/TextField/TextField.svelte'
1010+1111+type AbilityRecord = {
1212+ name: string,
1313+ abbr: string,
1414+ desc: string,
1515+}
1616+const abilityList: AbilityRecord[] = [
1717+ {
1818+ name: 'Strength',
1919+ abbr: 'STR',
2020+ desc: 'A quality of physical prowess and vigor.',
2121+ },
2222+ {
2323+ name: 'Dexterity',
2424+ abbr: 'DEX',
2525+ desc: 'A quality of agility and swiftness.',
2626+ },
2727+ {
2828+ name: 'Constitution',
2929+ abbr: 'CON',
3030+ desc: 'A quality of resistance and endurance.',
3131+ },
3232+ {
3333+ name: 'Intelligence',
3434+ abbr: 'INT',
3535+ desc: 'A quality of reasoning and comprehension.',
3636+ },
3737+ {
3838+ name: 'Wisdom',
3939+ abbr: 'WIS',
4040+ desc: 'A quality of perceptive attunement.',
4141+ },
4242+ {
4343+ name: 'Charisma',
4444+ abbr: 'CHA',
4545+ desc: 'A quality of social appeal and glamour.',
4646+ },
4747+]
4848+</script>
4949+5050+<PageLayout display="flex" direction="col" class="gap-8 mx-auto" items="stretch">
5151+ <HeadingGroup>
5252+ <Heading level={1}>Register a new species</Heading>
5353+ <SubHeading isScript>Register a new species</SubHeading>
5454+ </HeadingGroup>
5555+ <form class="flex flex-col items-start gap-10 max-w-100ch">
5656+ <TextField
5757+ name="name"
5858+ label="Species name"
5959+ placeholder="Enter species name..."
6060+ maxlength={20} />
6161+ <MultiTextField
6262+ name="description"
6363+ label="Species description"
6464+ placeholder="Describe the species..."
6565+ help="What do they look like? What are they capable of?"
6666+ rows={7} />
6767+ <div class="flex flex-col gap-6">
6868+ <div class="leading-relaxed">
6969+ <span class="font-medium">Proficient abilities</span>
7070+ <p class="text-sm text-zinc-500">Select up to 2 abilities.</p>
7171+ </div>
7272+ <div class="grid grid-cols-3 gap-4">
7373+ {#each abilityList as ability}
7474+ <CheckboxCard
7575+ name={'ability'}
7676+ title={ability.name}
7777+ desc={ability.desc} />
7878+ {/each}
7979+ </div>
8080+ </div>
8181+ <div class="flex flex-col gap-6">
8282+ <span class="font-medium">Movement</span>
8383+ <div class="grid grid-cols-3 gap-4">
8484+8585+ </div>
8686+ </div>
8787+ <div class="flex flex-row gap-4">
8888+ <FormButton type="submit" value="Register species" />
8989+ </div>
9090+ </form>
9191+</PageLayout>
+1-2
app/src/stories/AbilityCard.stories.svelte
···11<script module>
22import { defineMeta } from '@storybook/addon-svelte-csf'
33-import AbilityCard from '$patterns/AbilityCard/AbilityCard.svelte'
33+import AbilityCard from '$patterns/AbilityCard.svelte'
44import { ElementInputType } from './utils'
5566-// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
76const { Story } = defineMeta({
87 title: 'Patterns/AbilityCard',
98 component: AbilityCard,
-1
app/src/stories/Chip.stories.svelte
···33import Chip from '$ui/Chip/Chip.svelte'
44import { ColorInputType, SizeInputType, StyleInputType } from './utils'
5566-// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
76const { Story } = defineMeta({
87 title: 'Components/Chip',
98 component: Chip,
-1
app/src/stories/ElementChip.stories.svelte
···33import ElementChip from '$patterns/Element/ElementChip.svelte'
44import { ElementInputType, SizeInputType, StyleInputType } from './utils'
5566-// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
76const { Story } = defineMeta({
87 title: 'Patterns/ElementChip',
98 component: ElementChip,
-1
app/src/stories/MechanicChip.stories.svelte
···33import MechanicChip from '$patterns/Mechanic/MechanicChip.svelte'
44import { MechanicInputType, SizeInputType, StyleInputType } from './utils'
5566-// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
76const { Story } = defineMeta({
87 title: 'Patterns/MechanicChip',
98 component: MechanicChip,
···11import type { IntClosedRange } from 'type-fest'
22+import z from 'zod'
2334export type CharacterLevel = IntClosedRange<1, 20>
45export type D4 = IntClosedRange<1, 4>
···2324 | D12Notation
2425 | D20Notation
25262626-export const AbilityModArray = ['STR', 'DEX', 'CON', 'INT', 'WIS', 'CHA'] as const
2727-export type AbilityMod = typeof AbilityModArray[number]
2828-2927export type HitDc = `${AbilityMod} ${number}`
30282929+export const AbilityModArray = ['STR', 'DEX', 'CON', 'INT', 'WIS', 'CHA'] as const
3130export const SpellComponentArray = ['V', 'S', 'M'] as const
3131+export const SpellSchoolArray = [
3232+ 'abjuration',
3333+ 'conjuration',
3434+ 'divination',
3535+ 'enchantment',
3636+ 'evocation',
3737+ 'illusion',
3838+ 'necromany',
3939+ 'transmutation',
4040+] as const
4141+4242+/* runtime types */
4343+export const AbilityModEnum = z.enum(AbilityModArray)
4444+export const SpellComponentEnum = z.enum(SpellComponentArray)
4545+export const SpellSchoolEnum = z.enum(SpellSchoolArray)
4646+4747+/* compile-time types */
4848+export type AbilityMod = typeof AbilityModArray[number]
3249export type SpellComponent = typeof SpellComponentArray[number]
5050+export type SpellSchool = typeof SpellSchoolArray[number]
+9-2
packages/types/src/hsr.ts
···11-/* array backings */
11+import { z } from 'zod'
22+23export const ElementArray = [
34 'fire',
45 'ice',
···3334 'defense',
3435] as const
35363636-/* types */
3737+/* runtime types */
3838+export const ElementEnum = z.enum(ElementArray)
3939+export const SpeciesEnum = z.enum(SpeciesArray)
4040+export const OffenseMechanicEnum = z.enum(OffenseMechanicArray)
4141+export const DefenseMechanicEnum = z.enum(DefenseMechanicArray)
4242+4343+/* compile-time types */
3744export type Element = typeof ElementArray[number]
3845export type Species = typeof SpeciesArray[number]
3946export type OffenseMechanic = typeof OffenseMechanicArray[number]
+1
scripts/build.sh
···11#!/bin/sh
2233npm i
44+npm run build --workspace=packages/color
45npm run build --workspace=packages/icons
56npm run build --workspace=packages/types
67npm run build --workspace=packages/tokenizer