24 Commits

Author SHA1 Message Date
de1f9d6ab6 feat(ui): change inputnumber to snap to values when alt is pressed
Some checks failed
🚀 Lint & Test & Deploy / release (push) Has been cancelled
2026-02-06 15:44:24 +01:00
6acce72fb8 fix(ui): correctly initialize InputNumber
When the value is outside min/max the value should not be clamped.
2026-02-06 15:25:18 +01:00
cf8943b205 chore: pnpm update 2026-02-06 15:18:32 +01:00
9e03d36482 chore: use newest ci image
All checks were successful
🚀 Lint & Test & Deploy / release (push) Successful in 3m55s
2026-02-06 15:06:39 +01:00
fd7268d620 ci: make dockerfile work
Some checks failed
Build & Push CI Image / build-and-push (push) Successful in 8m42s
🚀 Lint & Test & Deploy / release (push) Failing after 2m31s
2026-02-06 14:43:31 +01:00
6358c22a85 ci: use tagged own image for ci
Some checks failed
🚀 Lint & Test & Deploy / release (push) Failing after 3m17s
2026-02-06 13:22:33 +01:00
655b6a18b2 ci: make dockerfile work
Some checks failed
Build & Push CI Image / build-and-push (push) Successful in 9m11s
🚀 Lint & Test & Deploy / release (push) Has been cancelled
2026-02-06 13:10:41 +01:00
37b2bdc8bd ci: update ci Dockerfile to work
Some checks failed
Build & Push CI Image / build-and-push (push) Failing after 1m24s
🚀 Lint & Test & Deploy / release (push) Failing after 2m23s
2026-02-06 12:52:42 +01:00
94e01d4ea8 ci: correctly build and push ci image
Some checks failed
Build & Push CI Image / build-and-push (push) Failing after 1m0s
🚀 Lint & Test & Deploy / release (push) Has been cancelled
2026-02-06 12:50:34 +01:00
35f5177884 feat: try to optimize the Dockerfile
Some checks failed
Build & Push CI Image / build-and-push (push) Failing after 9s
🚀 Lint & Test & Deploy / release (push) Failing after 2m31s
2026-02-06 12:33:36 +01:00
ac2c61f221 ci: use actual git url in ci
Some checks failed
🚀 Lint & Test & Deploy / release (push) Failing after 2m16s
2026-02-06 12:22:51 +01:00
ef3d46279f fix(ci): build before testing 2026-02-06 11:59:43 +01:00
703da324fa ci: automatically build ci image and store locally
Some checks failed
Build & Push CI Image / build-and-push (push) Failing after 3m6s
🚀 Lint & Test & Deploy / release (push) Failing after 1m59s
2026-02-06 11:57:44 +01:00
1dae472253 ci: add a git.json metadata file during build
Some checks failed
🚀 Lint & Test & Deploy / release (push) Failing after 3m14s
2026-02-06 11:48:12 +01:00
09fdfb88cd chore: update test screenshots
All checks were successful
🚀 Lint & Test & Deploy / release (push) Successful in 4m35s
2026-02-06 00:53:58 +01:00
04b63cc7e2 feat: add changelog to sidebar
Some checks failed
🚀 Lint & Test & Deploy / release (push) Failing after 4m36s
2026-02-06 00:45:33 +01:00
cb6a35606d feat(ci): also cache cargo stuff
All checks were successful
🚀 Lint & Test & Deploy / release (push) Successful in 4m15s
2026-02-04 21:13:10 +01:00
9c9f3ba3b7 fix(ci): use GITHUB_ instead of GITEA_ for env vars
Some checks failed
🚀 Lint & Test & Deploy / release (push) Has been cancelled
2026-02-04 20:39:22 +01:00
08dda2b2cb chore: pnpm format
Some checks failed
🚀 Lint & Test & Deploy / release (push) Failing after 4m46s
2026-02-04 20:30:59 +01:00
059129a738 fix(ci): deploy prs and main
Some checks failed
🚀 Lint & Test & Deploy / release (push) Failing after 1m45s
2026-02-04 20:28:41 +01:00
437c9f4a25 feat(ci): add list of all commits to changelog entry 2026-02-04 20:14:38 +01:00
48bf447ce1 docs: straighten up changelog a bit 2026-02-04 20:08:29 +01:00
548fa4f0a1 fix(app): correctly initialize vec3 inputs in nestedsettings
Closes #32
2026-02-04 20:08:04 +01:00
release-bot
642cca30ad chore(release): v0.0.2 2026-02-04 18:37:09 +00:00
25 changed files with 824 additions and 846 deletions

View File

@@ -13,16 +13,12 @@
"markdown": {}, "markdown": {},
"toml": {}, "toml": {},
"dockerfile": {}, "dockerfile": {},
"ruff": {},
"jupyter": {},
"malva": {},
"markup": { "markup": {
// https://dprint.dev/plugins/markup_fmt/config/ // https://dprint.dev/plugins/markup_fmt/config/
"scriptIndent": true, "scriptIndent": true,
"styleIndent": true, "styleIndent": true,
}, },
"yaml": {}, "yaml": {},
"graphql": {},
"exec": { "exec": {
"cwd": "${configDir}", "cwd": "${configDir}",
"commands": [ "commands": [
@@ -53,13 +49,8 @@
"https://plugins.dprint.dev/markdown-0.20.0.wasm", "https://plugins.dprint.dev/markdown-0.20.0.wasm",
"https://plugins.dprint.dev/toml-0.7.0.wasm", "https://plugins.dprint.dev/toml-0.7.0.wasm",
"https://plugins.dprint.dev/dockerfile-0.3.3.wasm", "https://plugins.dprint.dev/dockerfile-0.3.3.wasm",
"https://plugins.dprint.dev/ruff-0.6.11.wasm",
"https://plugins.dprint.dev/jupyter-0.2.1.wasm",
"https://plugins.dprint.dev/g-plane/malva-v0.15.1.wasm",
"https://plugins.dprint.dev/g-plane/markup_fmt-v0.25.3.wasm", "https://plugins.dprint.dev/g-plane/markup_fmt-v0.25.3.wasm",
"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.1.wasm", "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.1.wasm",
"https://plugins.dprint.dev/g-plane/pretty_graphql-v0.2.3.wasm",
"https://plugins.dprint.dev/exec-0.6.0.json@a054130d458f124f9b5c91484833828950723a5af3f8ff2bd1523bd47b83b364", "https://plugins.dprint.dev/exec-0.6.0.json@a054130d458f124f9b5c91484833828950723a5af3f8ff2bd1523bd47b83b364",
"https://plugins.dprint.dev/biome-0.11.10.wasm",
], ],
} }

28
.gitea/scripts/build.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
mkdir -p app/static
cp CHANGELOG.md app/static/CHANGELOG.md
cat >app/static/git.json <<EOF
{
"ref": "${GITEA_REF:-}",
"ref_name": "${GITEA_REF_NAME:-}",
"ref_type": "${GITEA_REF_TYPE:-}",
"sha": "${GITEA_SHA:-}",
"run_number": "${GITEA_RUN_NUMBER:-}",
"server_url": "${GITEA_SERVER_URL:-}",
"event_name": "${GITEA_EVENT_NAME:-}",
"workflow": "${GITEA_WORKFLOW:-}",
"job": "${GITEA_JOB:-}",
"commit_message": "${GITEA_COMMIT_MESSAGE:-}",
"commit_timestamp": "${GITEA_COMMIT_TIMESTAMP:-}",
"branch": "${GITEA_HEAD_REF:-}",
"base_branch": "${GITEA_BASE_REF:-}"
}
EOF
pnpm build
cp -R packages/ui/build app/build/ui

18
.gitea/scripts/ci-checks.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail
pnpm build
pnpm lint &
LINT_PID=$!
pnpm format:check &
FORMAT_PID=$!
pnpm check &
TYPE_PID=$!
xvfb-run --auto-servernum --server-args="-screen 0 1280x1024x24" pnpm test &
TEST_PID=$!
wait $LINT_PID
wait $FORMAT_PID
wait $TYPE_PID
wait $TEST_PID

View File

@@ -33,7 +33,20 @@ find . -name package.json ! -path "*/node_modules/*" | while read file; do
done done
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# 3. Update CHANGELOG.md (prepend) # 3. Generate commit list since last release
# -------------------------------------------------------------------
# Get the previous tag (fallback to empty if none)
LAST_TAG=$(git tag --sort=-creatordate | grep -v "^$TAG$" | head -n 1 || echo "")
if [ -n "$LAST_TAG" ]; then
COMMITS=$(git log "$LAST_TAG"..HEAD --pretty=format:'[%h](https://git.max-richter.dev/max/nodarium/commit/%H) %s')
else
COMMITS=$(git log HEAD --pretty=format:'[%h](https://git.max-richter.dev/max/nodarium/commit/%H) %s')
fi
# -------------------------------------------------------------------
# 4. Update CHANGELOG.md (prepend)
# ------------------------------------------------------------------- # -------------------------------------------------------------------
tmp_changelog="CHANGELOG.tmp" tmp_changelog="CHANGELOG.tmp"
@@ -42,6 +55,11 @@ tmp_changelog="CHANGELOG.tmp"
echo "" echo ""
echo "$NOTES" echo "$NOTES"
echo "" echo ""
if [ -n "$COMMITS" ]; then
echo "All Commits:"
echo "$COMMITS"
echo ""
fi
echo "---" echo "---"
echo "" echo ""
if [ -f CHANGELOG.md ]; then if [ -f CHANGELOG.md ]; then
@@ -54,7 +72,7 @@ mv "$tmp_changelog" CHANGELOG.md
pnpm exec dprint fmt CHANGELOG.md pnpm exec dprint fmt CHANGELOG.md
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# 4. Create release commit # 5. Create release commit
# ------------------------------------------------------------------- # -------------------------------------------------------------------
git config user.name "release-bot" git config user.name "release-bot"
@@ -71,7 +89,7 @@ fi
git commit -m "chore(release): $TAG" git commit -m "chore(release): $TAG"
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# 5. Push changes # 6. Push changes
# ------------------------------------------------------------------- # -------------------------------------------------------------------
git push origin main git push origin main

43
.gitea/scripts/deploy-files.sh Executable file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env bash
set -euo pipefail
echo "Configuring rclone"
KEY_FILE="$(mktemp)"
echo "${SSH_PRIVATE_KEY}" >"${KEY_FILE}"
chmod 600 "${KEY_FILE}"
mkdir -p ~/.config/rclone
cat >~/.config/rclone/rclone.conf <<EOF
[sftp-remote]
type = sftp
host = ${SSH_HOST}
user = ${SSH_USER}
port = ${SSH_PORT}
key_file = ${KEY_FILE}
EOF
if [[ "${GITHUB_REF_TYPE:-}" == "tag" ]]; then
TARGET_DIR="${REMOTE_DIR}"
elif [[ "${GITHUB_EVENT_NAME:-}" == "pull_request" ]]; then
SAFE_PR_NAME="${GITHUB_HEAD_REF//\//-}"
TARGET_DIR="${REMOTE_DIR}_${SAFE_PR_NAME}"
elif [[ "${GITHUB_REF_NAME:-}" == "main" ]]; then
TARGET_DIR="${REMOTE_DIR}_main"
else
SAFE_REF="${GITHUB_REF_NAME//\//-}"
TARGET_DIR="${REMOTE_DIR}_${SAFE_REF}"
fi
echo "Deploying to ${TARGET_DIR}"
rclone sync \
--update \
--verbose \
--progress \
--exclude _astro/** \
--stats 2s \
--stats-one-line \
--transfers 4 \
./app/build/ \
"sftp-remote:${TARGET_DIR}"

View File

@@ -0,0 +1,39 @@
name: Build & Push CI Image
on:
push:
paths:
- "Dockerfile.ci"
- ".gitea/workflows/build-ci-image.yaml"
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v2
with:
config-inline: |
[registry."git.max-richter.dev"]
https = true
insecure = false
- name: Login to Gitea Registry
uses: docker/login-action@v3
with:
registry: git.max-richter.dev
username: ${{ gitea.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.ci
push: true
tags: |
git.max-richter.dev/${{ gitea.repository }}-ci:latest
git.max-richter.dev/${{ gitea.repository }}-ci:${{ github.sha }}

View File

@@ -1,4 +1,4 @@
name: 🚀 Release name: 🚀 Lint & Test & Deploy
on: on:
push: push:
@@ -8,19 +8,21 @@ on:
branches: ["*"] branches: ["*"]
env: env:
PNPM_CACHE_FOLDER: ~/.pnpm-store PNPM_CACHE_FOLDER: /.pnpm-store
CARGO_HOME: /.cargo
CARGO_TARGET_DIR: target
jobs: jobs:
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: jimfx/nodes:latest container: git.max-richter.dev/max/nodarium-ci:fd7268d6208aede435e1685817ae6b271c68bd83
steps: steps:
- name: 📑 Checkout Code - name: 📑 Checkout Code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITEA_TOKEN }}
- name: 💾 Setup pnpm Cache - name: 💾 Setup pnpm Cache
uses: actions/cache@v4 uses: actions/cache@v4
@@ -30,55 +32,45 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-pnpm- ${{ runner.os }}-pnpm-
- name: 🦀 Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: 📦 Install Dependencies - name: 📦 Install Dependencies
run: pnpm install --frozen-lockfile --store-dir ${{ env.PNPM_CACHE_FOLDER }} run: pnpm install --frozen-lockfile --store-dir ${{ env.PNPM_CACHE_FOLDER }}
- name: 🧹 Lint - name: 🧹 Quality Control
run: pnpm lint run: ./.gitea/scripts/ci-checks.sh
- name: 🎨 Format Check
run: pnpm format:check
- name: 🧬 Type Check
run: pnpm check
- name: 🛠️ Build - name: 🛠️ Build
run: pnpm build:deploy run: ./.gitea/scripts/build.sh
- name: 🔬 Tests
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x1024x24" pnpm test
- name: 🚀 Create Release Commit - name: 🚀 Create Release Commit
if: github.ref_type == 'tag' if: gitea.ref_type == 'tag'
run: ./.gitea/scripts/create-release.sh run: ./.gitea/scripts/create-release.sh
- name: 🏷️ Create Gitea Release - name: 🏷️ Create Gitea Release
if: github.ref_type == 'tag' if: gitea.ref_type == 'tag'
uses: akkuman/gitea-release-action@v1 uses: akkuman/gitea-release-action@v1
with: with:
tag_name: ${{ github.ref_name }} tag_name: ${{ gitea.ref_name }}
release_name: Release ${{ github.ref_name }} release_name: Release ${{ gitea.ref_name }}
body_path: CHANGELOG.md body_path: CHANGELOG.md
draft: false draft: false
prerelease: false prerelease: false
- name: 🔑 Configure rclone - name: 🚀 Deploy Changed Files via rclone
if: github.ref_type == 'tag' run: ./.gitea/scripts/deploy-files.sh
run: |
echo "$SSH_PRIVATE_KEY" > /tmp/id_rsa
chmod 600 /tmp/id_rsa
mkdir -p ~/.config/rclone
echo -e "[sftp-remote]\ntype = sftp\nhost = ${SSH_HOST}\nuser = ${SSH_USER}\nport = ${SSH_PORT}\nkey_file = /tmp/id_rsa" > ~/.config/rclone/rclone.conf
env: env:
REMOTE_DIR: ${{ vars.REMOTE_DIR }}
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SSH_HOST: ${{ vars.SSH_HOST }} SSH_HOST: ${{ vars.SSH_HOST }}
SSH_PORT: ${{ vars.SSH_PORT }} SSH_PORT: ${{ vars.SSH_PORT }}
SSH_USER: ${{ vars.SSH_USER }} SSH_USER: ${{ vars.SSH_USER }}
- name: 🚀 Deploy Changed Files via rclone
if: github.ref_type == 'tag'
run: |
echo "Uploading the rest"
rclone sync --update -v --progress --exclude _astro/** --stats 2s --stats-one-line ./app/build/ sftp-remote:${REMOTE_DIR} --transfers 4
env:
REMOTE_DIR: ${{ vars.REMOTE_DIR }}

View File

@@ -1,14 +1,8 @@
## v0.0.2 (2026-02-03) ## v0.0.2 (2026-02-04)
fix(ci): actually deploy on tags fix(ci): actually deploy on tags
---
## v0.0.2 (2026-02-03)
fix(app): correctly handle false value in settings fix(app): correctly handle false value in settings
-> This caused a bug where random seed could not be false.
This caused a bug where random seed could not be false.
--- ---

View File

@@ -1,29 +0,0 @@
# FROM jacoblincool/playwright:chromium-light
FROM jacoblincool/playwright:firefox
# RUN apk add --no-cache curl git jq g++ make
RUN apt update && apt install -y curl git jq g++ make \
libgl1-mesa-dri \
libglapi-mesa \
libosmesa6 \
mesa-utils \
xvfb \
&& rm -rf /var/lib/apt/lists/*
# Set Rust paths
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
# Install Rust, wasm target, and pnpm
RUN curl --silent --show-error --location --fail --retry 3 \
--proto '=https' --tlsv1.2 \
--output /tmp/rustup-init.sh https://sh.rustup.rs \
&& sh /tmp/rustup-init.sh -y --no-modify-path --profile minimal \
&& rm /tmp/rustup-init.sh \
&& rustup target add wasm32-unknown-unknown \
&& rm -rf /usr/local/rustup/toolchains/*/share/doc \
&& npm i -g pnpm \
&& pnpx playwright install firefox

30
Dockerfile.ci Normal file
View File

@@ -0,0 +1,30 @@
FROM node:25-bookworm-slim
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH
RUN apt-get update && apt-get install -y \
ca-certificates=20230311+deb12u1 \
curl=7.88.1-10+deb12u14 \
git=1:2.39.5-0+deb12u3 \
jq=1.6-2.1+deb12u1 \
g++=4:12.2.0-3 \
rclone=1.60.1+dfsg-2+b5 \
xvfb=2:21.1.7-3+deb12u11 \
xauth=1:1.1.2-1 \
--no-install-recommends \
# Install Rust
&& curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
--default-toolchain stable \
--profile minimal \
&& rustup target add wasm32-unknown-unknown \
# Setup Playwright
&& npm i -g pnpm \
&& pnpm dlx playwright install --with-deps firefox \
# Final Cleanup
&& rm -rf /usr/local/rustup/downloads /usr/local/rustup/tmp \
&& rm -rf /usr/local/cargo/registry/index /usr/local/cargo/registry/cache \
&& rm -rf /usr/local/rustup/toolchains/*/share/doc \
&& apt-get purge -y --auto-remove \
&& rm -rf /var/lib/apt/lists/*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -18,7 +18,7 @@
"dependencies": { "dependencies": {
"@nodarium/ui": "workspace:*", "@nodarium/ui": "workspace:*",
"@nodarium/utils": "workspace:*", "@nodarium/utils": "workspace:*",
"@sveltejs/kit": "^2.50.0", "@sveltejs/kit": "^2.50.2",
"@tailwindcss/vite": "^4.1.18", "@tailwindcss/vite": "^4.1.18",
"@threlte/core": "8.3.1", "@threlte/core": "8.3.1",
"@threlte/extras": "9.7.1", "@threlte/extras": "9.7.1",
@@ -34,11 +34,11 @@
"@eslint/js": "^9.39.2", "@eslint/js": "^9.39.2",
"@iconify-json/tabler": "^1.2.26", "@iconify-json/tabler": "^1.2.26",
"@iconify/tailwind4": "^1.2.1", "@iconify/tailwind4": "^1.2.1",
"@nodarium/types": "workspace:", "@nodarium/types": "workspace:^",
"@playwright/test": "^1.58.1", "@playwright/test": "^1.58.1",
"@sveltejs/adapter-static": "^3.0.10", "@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/vite-plugin-svelte": "^6.2.4", "@sveltejs/vite-plugin-svelte": "^6.2.4",
"@tsconfig/svelte": "^5.0.6", "@tsconfig/svelte": "^5.0.7",
"@types/file-saver": "^2.0.7", "@types/file-saver": "^2.0.7",
"@types/three": "^0.182.0", "@types/three": "^0.182.0",
"@vitest/browser-playwright": "^4.0.18", "@vitest/browser-playwright": "^4.0.18",
@@ -46,8 +46,8 @@
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-plugin-svelte": "^3.14.0", "eslint-plugin-svelte": "^3.14.0",
"globals": "^17.3.0", "globals": "^17.3.0",
"svelte": "^5.46.4", "svelte": "^5.49.2",
"svelte-check": "^4.3.5", "svelte-check": "^4.3.6",
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.54.0", "typescript-eslint": "^8.54.0",
@@ -55,7 +55,7 @@
"vite-plugin-comlink": "^5.3.0", "vite-plugin-comlink": "^5.3.0",
"vite-plugin-glsl": "^1.5.5", "vite-plugin-glsl": "^1.5.5",
"vite-plugin-wasm": "^3.5.0", "vite-plugin-wasm": "^3.5.0",
"vitest": "^4.0.17", "vitest": "^4.0.18",
"vitest-browser-svelte": "^2.0.2" "vitest-browser-svelte": "^2.0.2"
} }
} }

View File

@@ -56,6 +56,10 @@
return 0; return 0;
} }
if (Array.isArray(inputValue) && node.type === 'vec3') {
return inputValue;
}
// If the component is supplied with a default value use that // If the component is supplied with a default value use that
if (inputValue !== undefined && typeof inputValue !== 'object') { if (inputValue !== undefined && typeof inputValue !== 'object') {
return inputValue; return inputValue;

View File

@@ -0,0 +1,79 @@
<script lang="ts">
type Change = { type: string; content: string };
async function fetchChangelog() {
const res = await fetch('/CHANGELOG.md');
return await res.text();
}
const typeMap: Record<string, string> = {
fix: 'bg-layer-2 bg-red-800',
feat: 'bg-layer-2 bg-green-800',
chore: 'bg-layer-2 bg-gray-800',
docs: 'bg-layer-2 bg-blue-800',
refactor: 'bg-layer-2 bg-purple-800',
default: 'bg-layer-2 text-text'
};
function parseChangelog(md: string) {
const lines = md.split('\n');
const parsed: (string | Change)[] = [];
for (let line of lines) {
line = line.trim();
if (!line) continue;
if (line === '---') {
parsed.push({ type: 'hr', content: '' });
continue;
}
// Headers
if (line.startsWith('## ')) {
parsed.push(line.replace('## ', ''));
continue;
}
// Commit type
const match = line.match(/^(fix|feat|chore|docs|refactor)(\(|:)/i);
if (match) {
parsed.push({ type: match[1].toLowerCase(), content: line });
continue;
}
// Other lines
parsed.push({ type: 'default', content: line });
}
let lastLine = parsed.at(-1);
if (lastLine !== undefined && typeof lastLine !== 'string' && lastLine.type === 'hr') {
parsed.pop();
}
return parsed;
}
</script>
<div class="p-4 font-mono text-text">
{#await fetchChangelog()}
<p>Loading...</p>
{:then md}
{#each parseChangelog(md) as item (item)}
{#if typeof item === 'string'}
<h2 class="text-xl font-semibold mt-4 mb-4 text-layer-1">{item}</h2>
{:else if item.type === 'hr'}
<p></p>
{:else}
<p class="px-3 py-1 mb-1 leading-8 border-b-1 border-b-outline last:border-b-0">
{#if item.type !== 'default'}
<span class="p-1 rounded-sm font-semibold {typeMap[item.type]}">
{item.content.split(':')[0]}
</span> {item.content.split(':').slice(1).join(':').trim()}
{:else}
{item.content}
{/if}
</p>
{/if}
{/each}
{/await}
</div>

View File

@@ -7,7 +7,7 @@
return JSON.stringify( return JSON.stringify(
{ {
...g, ...g,
nodes: g.nodes.map((n: object) => ({ ...n, tmp: undefined })) nodes: g.nodes.map((n: object) => ({ ...n, tmp: undefined, state: undefined }))
}, },
null, null,
2 2

View File

@@ -17,6 +17,7 @@
import Panel from '$lib/sidebar/Panel.svelte'; import Panel from '$lib/sidebar/Panel.svelte';
import ActiveNodeSettings from '$lib/sidebar/panels/ActiveNodeSettings.svelte'; import ActiveNodeSettings from '$lib/sidebar/panels/ActiveNodeSettings.svelte';
import BenchmarkPanel from '$lib/sidebar/panels/BenchmarkPanel.svelte'; import BenchmarkPanel from '$lib/sidebar/panels/BenchmarkPanel.svelte';
import Changelog from '$lib/sidebar/panels/Changelog.svelte';
import ExportSettings from '$lib/sidebar/panels/ExportSettings.svelte'; import ExportSettings from '$lib/sidebar/panels/ExportSettings.svelte';
import GraphSource from '$lib/sidebar/panels/GraphSource.svelte'; import GraphSource from '$lib/sidebar/panels/GraphSource.svelte';
import Keymap from '$lib/sidebar/panels/Keymap.svelte'; import Keymap from '$lib/sidebar/panels/Keymap.svelte';
@@ -249,6 +250,13 @@
/> />
<ActiveNodeSettings {manager} bind:node={activeNode} /> <ActiveNodeSettings {manager} bind:node={activeNode} />
</Panel> </Panel>
<Panel
id="changelog"
title="Changelog"
icon="i-[tabler--file-text-spark] bg-green-400"
>
<Changelog />
</Panel>
</Sidebar> </Sidebar>
</Grid.Cell> </Grid.Cell>
</Grid.Row> </Grid.Row>

View File

@@ -1 +1,2 @@
nodes/ nodes/
CHANGELOG.md

View File

@@ -2,16 +2,14 @@
"version": "0.0.2", "version": "0.0.2",
"scripts": { "scripts": {
"postinstall": "pnpm run -r --filter 'ui' build", "postinstall": "pnpm run -r --filter 'ui' build",
"build": "pnpm build:nodes && pnpm build:app",
"lint": "pnpm run -r --parallel lint", "lint": "pnpm run -r --parallel lint",
"format": "pnpm dprint fmt", "format": "pnpm dprint fmt",
"format:check": "pnpm dprint check", "format:check": "pnpm dprint check",
"test": "pnpm run -r test", "test": "pnpm run -r test",
"check": "pnpm run -r check", "check": "pnpm run -r check",
"build:story": "pnpm -r --filter 'ui' story:build", "build": "pnpm build:nodes && pnpm build:app",
"build:app": "BASE_PATH=/ui pnpm -r --filter 'ui' build && pnpm -r --filter 'app' build", "build:app": "BASE_PATH=/ui pnpm -r --filter 'ui' build && pnpm -r --filter 'app' build",
"build:nodes": "cargo build --workspace --target wasm32-unknown-unknown --release && rm -rf ./app/static/nodes/max/plantarium/ && mkdir -p ./app/static/nodes/max/plantarium/ && cp -R ./target/wasm32-unknown-unknown/release/*.wasm ./app/static/nodes/max/plantarium/", "build:nodes": "cargo build --workspace --target wasm32-unknown-unknown --release && rm -rf ./app/static/nodes/max/plantarium/ && mkdir -p ./app/static/nodes/max/plantarium/ && cp -R ./target/wasm32-unknown-unknown/release/*.wasm ./app/static/nodes/max/plantarium/",
"build:deploy": "pnpm build && cp -R packages/ui/build app/build/ui",
"dev:nodes": "chokidar './nodes/**' --initial -i '/pkg/' -c 'pnpm build:nodes'", "dev:nodes": "chokidar './nodes/**' --initial -i '/pkg/' -c 'pnpm build:nodes'",
"dev:app_ui": "pnpm -r --parallel --filter 'app' --filter './packages/ui' dev", "dev:app_ui": "pnpm -r --parallel --filter 'app' --filter './packages/ui' dev",
"dev_ui": "pnpm -r --filter 'ui' dev:ui", "dev_ui": "pnpm -r --filter 'ui' dev:ui",

View File

@@ -17,7 +17,7 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"zod": "^4.3.5" "zod": "^4.3.6"
}, },
"devDependencies": { "devDependencies": {
"dprint": "^0.51.1" "dprint": "^0.51.1"

View File

@@ -33,25 +33,25 @@
"@eslint/compat": "^2.0.2", "@eslint/compat": "^2.0.2",
"@eslint/eslintrc": "^3.3.3", "@eslint/eslintrc": "^3.3.3",
"@eslint/js": "^9.39.2", "@eslint/js": "^9.39.2",
"@nodarium/types": "workspace:", "@nodarium/types": "workspace:^",
"@playwright/test": "^1.58.1", "@playwright/test": "^1.58.1",
"@sveltejs/adapter-static": "^3.0.10", "@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/kit": "^2.50.0", "@sveltejs/kit": "^2.50.2",
"@sveltejs/package": "^2.5.7", "@sveltejs/package": "^2.5.7",
"@sveltejs/vite-plugin-svelte": "^6.2.4", "@sveltejs/vite-plugin-svelte": "^6.2.4",
"@testing-library/svelte": "^5.3.1", "@testing-library/svelte": "^5.3.1",
"@types/eslint": "^9.6.1", "@types/eslint": "^9.6.1",
"@types/three": "^0.182.0", "@types/three": "^0.182.0",
"@typescript-eslint/eslint-plugin": "^8.53.0", "@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.53.0", "@typescript-eslint/parser": "^8.54.0",
"@vitest/browser-playwright": "^4.0.18", "@vitest/browser-playwright": "^4.0.18",
"dprint": "^0.51.1", "dprint": "^0.51.1",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-plugin-svelte": "^3.14.0", "eslint-plugin-svelte": "^3.14.0",
"globals": "^17.3.0", "globals": "^17.3.0",
"publint": "^0.3.16", "publint": "^0.3.17",
"svelte": "^5.46.4", "svelte": "^5.49.2",
"svelte-check": "^4.3.5", "svelte-check": "^4.3.6",
"svelte-eslint-parser": "^1.4.1", "svelte-eslint-parser": "^1.4.1",
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.9.3", "typescript": "^5.9.3",

View File

@@ -19,7 +19,6 @@
// normalize bounds // normalize bounds
if (min > max) [min, max] = [max, min]; if (min > max) [min, max] = [max, min];
if (value > max) max = value;
let inputEl: HTMLInputElement | undefined = $state(); let inputEl: HTMLInputElement | undefined = $state();
@@ -27,11 +26,18 @@
return Math.min(max, Math.max(min, v)); return Math.min(max, Math.max(min, v));
} }
function snap(v: number) { function snap(v: number, s = step) {
if (step) v = Math.round(v / step) * step; if (s) v = Math.round(v / s) * s;
return +v.toFixed(3); return +v.toFixed(3);
} }
function getAutoStep(v: number): number {
const abs = Math.abs(v);
if (abs === 0) return 0.1; // fallback for 0
const exponent = Math.floor(Math.log10(abs));
return Math.pow(10, exponent);
}
let dragging = $state(false); let dragging = $state(false);
let startValue = 0; let startValue = 0;
let rect: DOMRect; let rect: DOMRect;
@@ -59,7 +65,8 @@
value = snap( value = snap(
e.ctrlKey e.ctrlKey
? startValue + delta ? startValue + delta
: clamp(startValue + delta) : clamp(startValue + delta),
(e.altKey && !step) ? getAutoStep(value) : step
); );
} }
@@ -105,7 +112,7 @@
} }
onMount(() => { onMount(() => {
value = snap(clamp(value)); value = snap(value);
}); });
</script> </script>
@@ -130,7 +137,7 @@
<div <div
class="absolute inset-y-0 left-0 bg-layer-3 opacity-30 pointer-events-none transition-[width]" class="absolute inset-y-0 left-0 bg-layer-3 opacity-30 pointer-events-none transition-[width]"
class:transition-none={dragging} class:transition-none={dragging}
style={`width: ${Math.min((value - min) / (max - min), 1) * 100}%`} style={`width: ${Math.max(0, Math.min((value - min) / (max - min), 1) * 100)}%`}
> >
</div> </div>

View File

@@ -42,4 +42,18 @@ describe('InputNumber', () => {
await expect.element(input).toHaveAttribute('min', '0'); await expect.element(input).toHaveAttribute('min', '0');
await expect.element(input).toHaveAttribute('max', '10'); await expect.element(input).toHaveAttribute('max', '10');
}); });
it('should not clamp value on init when value exceeds min/max', async () => {
render(InputNumber, { value: 100, min: 0, max: 10 });
const input = page.getByRole('spinbutton');
await expect.element(input).toHaveValue(100);
});
it('should not clamp value on init when value is below min', async () => {
render(InputNumber, { value: -50, min: 0, max: 10 });
const input = page.getByRole('spinbutton');
await expect.element(input).toHaveValue(-50);
});
}); });

View File

@@ -12,11 +12,11 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@nodarium/types": "workspace:" "@nodarium/types": "workspace:^"
}, },
"devDependencies": { "devDependencies": {
"dprint": "^0.51.1", "dprint": "^0.51.1",
"vite": "^7.3.1", "vite": "^7.3.1",
"vitest": "^4.0.17" "vitest": "^4.0.18"
} }
} }

1211
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,8 +2,8 @@ packages:
- app - app
- packages/* - packages/*
- nodes/** - nodes/**
- "!**/.template/**" - '!**/.template/**'
- "!**/pkg/**" - '!**/pkg/**'
catalog: catalog:
chokidar-cli: github:open-cli-tools/chokidar-cli#semver:v4.0.0 chokidar-cli: github:open-cli-tools/chokidar-cli#semver:v4.0.0