Compare commits
31 Commits
v26.03.21-
...
v26.03.30.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b75beb607 | ||
|
21f97b7f78
|
|||
|
cc5473f648
|
|||
| b4fc72c46a | |||
| 338e8665ec | |||
|
8ce81afdaf
|
|||
|
f70120c2a0
|
|||
|
|
7fa57cb782 | ||
| 6dc6eea401 | |||
|
|
4302f6bbba | ||
|
|
2deba8ba2f | ||
| 5aa972ab7f | |||
|
|
1b9cf4b388 | ||
|
|
50d97b4e55 | ||
|
59c0b50396
|
|||
|
f83331ed35
|
|||
|
|
7a5d423dcb | ||
|
c4296137b3
|
|||
|
d980671266
|
|||
|
|
52d129f50a | ||
|
c8b43a3f8f
|
|||
| 63e2c267fb | |||
|
|
14c2dfdb20 | ||
|
68f1ed5d81
|
|||
|
|
875ab670d5 | ||
| 43cef42c3b | |||
|
|
29dd867bcb | ||
|
dba93b67fd
|
|||
|
5d23d0639e
|
|||
|
535c62b838
|
|||
| bcac9f3310 |
@@ -1,194 +0,0 @@
|
||||
name: Pre-release on non-main branches
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore: [ main ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
prerelease:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout (full history + tags)
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Stop if this is the bot changelog commit
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
msg="$(git log -1 --pretty=%B)"
|
||||
echo "$msg" | tr -d '\r' | grep -qi "\[skip ci\]" && {
|
||||
echo "Skipping (bot commit with [skip ci])"
|
||||
exit 0
|
||||
} || true
|
||||
|
||||
- name: Install git-cliff
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
GIT_CLIFF_VERSION="2.11.0"
|
||||
URL="https://github.com/orhun/git-cliff/releases/download/v${GIT_CLIFF_VERSION}/git-cliff-${GIT_CLIFF_VERSION}-x86_64-unknown-linux-gnu.tar.gz"
|
||||
curl -L "$URL" -o /tmp/git-cliff.tar.gz
|
||||
tar -xzf /tmp/git-cliff.tar.gz -C /tmp
|
||||
sudo install /tmp/git-cliff-*/git-cliff /usr/local/bin/git-cliff
|
||||
git-cliff --version
|
||||
|
||||
- name: Generate CHANGELOG.md (in runner only)
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
git-cliff --config cliff.toml --output CHANGELOG.md
|
||||
test -s CHANGELOG.md
|
||||
|
||||
- name: Extract newest changelog section for pre-release body
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
awk '
|
||||
/^## / { if (seen) exit; seen=1 }
|
||||
seen { print }
|
||||
' CHANGELOG.md > RELEASE_NOTES.md
|
||||
|
||||
sed -i 's/[[:space:]]*$//' RELEASE_NOTES.md
|
||||
test -s RELEASE_NOTES.md
|
||||
|
||||
- name: Create export zip (Computing:Box Website.zip)
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
if [ ! -d "dist" ]; then
|
||||
echo "❌ dist/ folder not found in repo root"
|
||||
ls -la
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "Computing:Box Website.zip"
|
||||
(cd dist && zip -r "../Computing:Box Website.zip" .)
|
||||
test -s "Computing:Box Website.zip"
|
||||
ls -lh "Computing:Box Website.zip"
|
||||
|
||||
- name: Prepare pre-release tag + name
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
|
||||
# Get branch name from ref: refs/heads/feature/x -> feature/x
|
||||
ref="${GITHUB_REF#refs/heads/}"
|
||||
# Make it tag-safe: lowercase, / -> -, remove invalid chars, collapse repeats
|
||||
safe_branch="$(echo "$ref" | tr '[:upper:]' '[:lower:]' | sed -E 's#[^a-z0-9._-]+#-#g; s#-+#-#g; s#(^-|-$)##g')"
|
||||
|
||||
VERSION="$(date -u +'%y.%m.%d')"
|
||||
SHORT_SHA="$(git rev-parse --short HEAD)"
|
||||
|
||||
# Pre-release tag format:
|
||||
# vYY.MM.DD-pre.<branch>.<sha>
|
||||
TAG="v${VERSION}-pre.${safe_branch}.${SHORT_SHA}"
|
||||
|
||||
# Release name shown in UI
|
||||
RELEASE_NAME="Computing:Box pre-release (${ref}) v${VERSION}"
|
||||
|
||||
echo "TAG=$TAG" >> "$GITHUB_ENV"
|
||||
echo "RELEASE_NAME=$RELEASE_NAME" >> "$GITHUB_ENV"
|
||||
echo "ZIP_PATH=Computing:Box Website.zip" >> "$GITHUB_ENV"
|
||||
echo "BRANCH_NAME=$ref" >> "$GITHUB_ENV"
|
||||
|
||||
echo "Using tag: $TAG"
|
||||
echo "Release name: $RELEASE_NAME"
|
||||
|
||||
- name: Create and push tag (CHANGELOG_PAT)
|
||||
shell: bash
|
||||
env:
|
||||
CHANGELOG_PAT: ${{ secrets.CHANGELOG_PAT }}
|
||||
run: |
|
||||
set -e
|
||||
|
||||
git tag -f "$TAG"
|
||||
|
||||
origin_url="$(git remote get-url origin)"
|
||||
|
||||
# Convert SSH origin to HTTPS if needed
|
||||
if echo "$origin_url" | grep -q "^git@"; then
|
||||
host="$(echo "$origin_url" | sed -E 's#git@([^:]+):.*#\1#')"
|
||||
path="$(echo "$origin_url" | sed -E 's#git@[^:]+:(.*)#\1#')"
|
||||
origin_url="https://$host/$path"
|
||||
fi
|
||||
|
||||
authed_url="$(echo "$origin_url" | sed -E "s#^https://#https://oauth2:${CHANGELOG_PAT}@#")"
|
||||
git push "$authed_url" "refs/tags/$TAG" --force
|
||||
|
||||
- name: Create Gitea pre-release + upload asset (CHANGELOG_PAT)
|
||||
shell: bash
|
||||
env:
|
||||
CHANGELOG_PAT: ${{ secrets.CHANGELOG_PAT }}
|
||||
run: |
|
||||
set -e
|
||||
|
||||
origin_url="$(git remote get-url origin)"
|
||||
if echo "$origin_url" | grep -q "^git@"; then
|
||||
host="$(echo "$origin_url" | sed -E 's#git@([^:]+):.*#\1#')"
|
||||
path="$(echo "$origin_url" | sed -E 's#git@[^:]+:(.*)#\1#')"
|
||||
origin_url="https://$host/$path"
|
||||
fi
|
||||
|
||||
base="$(echo "$origin_url" | sed -E 's#(https?://[^/]+)/.*#\1#')"
|
||||
repo_path="$(echo "$origin_url" | sed -E 's#https?://[^/]+/##')"
|
||||
repo_path="$(echo "$repo_path" | sed -E 's/\.git$//')"
|
||||
|
||||
owner="$(echo "$repo_path" | cut -d/ -f1)"
|
||||
repo="$(echo "$repo_path" | cut -d/ -f2-)"
|
||||
|
||||
api="$base/api/v1"
|
||||
|
||||
python3 - <<'PY'
|
||||
import json, os
|
||||
tag = os.environ["TAG"]
|
||||
name = os.environ["RELEASE_NAME"]
|
||||
branch = os.environ.get("BRANCH_NAME", "")
|
||||
|
||||
with open("RELEASE_NOTES.md", "r", encoding="utf-8") as f:
|
||||
body = f.read()
|
||||
|
||||
# Add a small pre-release banner at the top
|
||||
banner = f"⚠️ Pre-release build from branch `{branch}`\n\n"
|
||||
payload = {
|
||||
"tag_name": tag,
|
||||
"target_commitish": branch if branch else "main",
|
||||
"name": name,
|
||||
"body": banner + body,
|
||||
"draft": False,
|
||||
"prerelease": True,
|
||||
}
|
||||
|
||||
with open("release.json", "w", encoding="utf-8") as f:
|
||||
json.dump(payload, f)
|
||||
PY
|
||||
|
||||
curl -sS -X POST \
|
||||
-H "Authorization: Bearer ${CHANGELOG_PAT}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${api}/repos/${owner}/${repo}/releases" \
|
||||
--data-binary @release.json \
|
||||
-o release_response.json
|
||||
|
||||
release_id="$(python3 - <<'PY'
|
||||
import json
|
||||
with open("release_response.json","r",encoding="utf-8") as f:
|
||||
data=json.load(f)
|
||||
rid=data.get("id")
|
||||
if not rid:
|
||||
raise SystemExit("No release id returned. Response:\n" + json.dumps(data, indent=2))
|
||||
print(rid)
|
||||
PY
|
||||
)"
|
||||
echo "Created pre-release id: $release_id"
|
||||
|
||||
curl -sS -X POST \
|
||||
-H "Authorization: Bearer ${CHANGELOG_PAT}" \
|
||||
"${api}/repos/${owner}/${repo}/releases/${release_id}/assets?name=Computing%3ABox%20Website.zip" \
|
||||
-F "attachment=@${ZIP_PATH}" \
|
||||
>/dev/null
|
||||
|
||||
echo "✅ Pre-release created: ${RELEASE_NAME} (tag: ${TAG}) with asset uploaded"
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Stop if this is the bot changelog commit
|
||||
- name: Stop if this is the bot changelog/version commit
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
@@ -36,82 +36,11 @@ jobs:
|
||||
sudo install /tmp/git-cliff-*/git-cliff /usr/local/bin/git-cliff
|
||||
git-cliff --version
|
||||
|
||||
- name: Generate CHANGELOG.md (Keep a Changelog)
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
git-cliff --config cliff.toml --output CHANGELOG.md
|
||||
test -s CHANGELOG.md
|
||||
|
||||
- name: Commit and push CHANGELOG.md if changed (CHANGELOG_PAT)
|
||||
shell: bash
|
||||
env:
|
||||
CHANGELOG_PAT: ${{ secrets.CHANGELOG_PAT }}
|
||||
run: |
|
||||
set -e
|
||||
|
||||
if git diff --quiet -- CHANGELOG.md; then
|
||||
echo "No changelog changes."
|
||||
else
|
||||
git config user.name "changelog-bot"
|
||||
git config user.email "changelog-bot@users.noreply.local"
|
||||
|
||||
git add CHANGELOG.md
|
||||
git commit -m "docs(changelog): update changelog [skip ci]"
|
||||
|
||||
origin_url="$(git remote get-url origin)"
|
||||
|
||||
# Convert SSH origin to HTTPS if needed
|
||||
if echo "$origin_url" | grep -q "^git@"; then
|
||||
host="$(echo "$origin_url" | sed -E 's#git@([^:]+):.*#\1#')"
|
||||
path="$(echo "$origin_url" | sed -E 's#git@[^:]+:(.*)#\1#')"
|
||||
origin_url="https://$host/$path"
|
||||
fi
|
||||
|
||||
authed_url="$(echo "$origin_url" | sed -E "s#^https://#https://oauth2:${CHANGELOG_PAT}@#")"
|
||||
git push "$authed_url" HEAD:main
|
||||
fi
|
||||
|
||||
- name: Extract newest changelog section for release body
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
# Extract the first "## ..." section (newest section) from CHANGELOG.md
|
||||
# Includes the "## ..." heading and everything until the next "## ..." heading.
|
||||
awk '
|
||||
/^## / { if (seen) exit; seen=1 }
|
||||
seen { print }
|
||||
' CHANGELOG.md > RELEASE_NOTES.md
|
||||
|
||||
# Clean trailing whitespace/newlines a bit
|
||||
sed -i 's/[[:space:]]*$//' RELEASE_NOTES.md
|
||||
|
||||
test -s RELEASE_NOTES.md
|
||||
echo "---- RELEASE_NOTES.md ----"
|
||||
head -n 60 RELEASE_NOTES.md
|
||||
echo "--------------------------"
|
||||
|
||||
- name: Create export zip (Computing:Box Website.zip)
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
if [ ! -d "dist" ]; then
|
||||
echo "❌ dist/ folder not found in repo root"
|
||||
ls -la
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "Computing:Box Website.zip"
|
||||
(cd dist && zip -r "../Computing:Box Website.zip" .)
|
||||
test -s "Computing:Box Website.zip"
|
||||
ls -lh "Computing:Box Website.zip"
|
||||
|
||||
- name: Prepare YY.MM.DD letter-suffix tag + release name
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
|
||||
# Version: YY.MM.DD (UTC). Swap to `date +...` if you prefer UK-local runner time.
|
||||
VERSION="$(date -u +'%y.%m.%d')"
|
||||
PREFIX="v${VERSION}."
|
||||
|
||||
@@ -135,12 +64,216 @@ jobs:
|
||||
TAG="${PREFIX}${next_letter}"
|
||||
RELEASE_NAME="Computing:Box v${VERSION}.${next_letter}"
|
||||
|
||||
origin_url="$(git remote get-url origin)"
|
||||
|
||||
if echo "$origin_url" | grep -q "^git@"; then
|
||||
host="$(echo "$origin_url" | sed -E 's#git@([^:]+):.*#\1#')"
|
||||
path="$(echo "$origin_url" | sed -E 's#git@[^:]+:(.*)#\1#')"
|
||||
origin_url="https://$host/$path"
|
||||
fi
|
||||
|
||||
base="$(echo "$origin_url" | sed -E 's#(https?://[^/]+)/.*#\1#')"
|
||||
repo_path="$(echo "$origin_url" | sed -E 's#https?://[^/]+/##')"
|
||||
repo_path="$(echo "$repo_path" | sed -E 's/\.git$//')"
|
||||
|
||||
RELEASE_URL="${base}/${repo_path}/releases/tag/${TAG}"
|
||||
|
||||
echo "TAG=$TAG" >> "$GITHUB_ENV"
|
||||
echo "RELEASE_NAME=$RELEASE_NAME" >> "$GITHUB_ENV"
|
||||
echo "ZIP_PATH=Computing:Box Website.zip" >> "$GITHUB_ENV"
|
||||
echo "RELEASE_URL=$RELEASE_URL" >> "$GITHUB_ENV"
|
||||
|
||||
echo "Using tag: $TAG"
|
||||
echo "Release name: $RELEASE_NAME"
|
||||
echo "Release URL: $RELEASE_URL"
|
||||
|
||||
- name: Find previous release tag
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
|
||||
PREV_TAG="$(
|
||||
git tag --list 'v*' \
|
||||
| grep -E '^v[0-9]{2}\.[0-9]{2}\.[0-9]{2}[a-z]$' \
|
||||
| grep -Fxv "$TAG" \
|
||||
| sort -V \
|
||||
| tail -n 1 \
|
||||
|| true
|
||||
)"
|
||||
|
||||
if [ -n "$PREV_TAG" ]; then
|
||||
echo "PREV_TAG=$PREV_TAG" >> "$GITHUB_ENV"
|
||||
echo "Previous release tag: $PREV_TAG"
|
||||
else
|
||||
echo "PREV_TAG=" >> "$GITHUB_ENV"
|
||||
echo "No previous release tag found."
|
||||
fi
|
||||
|
||||
- name: Generate CHANGELOG.md from previous release to HEAD
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
|
||||
if [ -n "${PREV_TAG}" ]; then
|
||||
echo "Generating changelog from ${PREV_TAG}..HEAD"
|
||||
git-cliff --config cliff.toml "${PREV_TAG}..HEAD" --output CHANGELOG.md
|
||||
else
|
||||
echo "Generating changelog from full history"
|
||||
git-cliff --config cliff.toml --output CHANGELOG.md
|
||||
fi
|
||||
|
||||
test -s CHANGELOG.md
|
||||
echo "---- CHANGELOG.md ----"
|
||||
head -n 120 CHANGELOG.md
|
||||
echo "----------------------"
|
||||
|
||||
- name: Commit and push CHANGELOG.md if changed (CHANGELOG_PAT)
|
||||
shell: bash
|
||||
env:
|
||||
CHANGELOG_PAT: ${{ secrets.CHANGELOG_PAT }}
|
||||
run: |
|
||||
set -e
|
||||
|
||||
if git diff --quiet -- CHANGELOG.md; then
|
||||
echo "No changelog changes."
|
||||
else
|
||||
git config user.name "changelog-bot"
|
||||
git config user.email "changelog-bot@users.noreply.local"
|
||||
|
||||
git add CHANGELOG.md
|
||||
git commit -m "docs(changelog): update changelog [skip ci]"
|
||||
|
||||
origin_url="$(git remote get-url origin)"
|
||||
|
||||
if echo "$origin_url" | grep -q "^git@"; then
|
||||
host="$(echo "$origin_url" | sed -E 's#git@([^:]+):.*#\1#')"
|
||||
path="$(echo "$origin_url" | sed -E 's#git@[^:]+:(.*)#\1#')"
|
||||
origin_url="https://$host/$path"
|
||||
fi
|
||||
|
||||
authed_url="$(echo "$origin_url" | sed -E "s#^https://#https://oauth2:${CHANGELOG_PAT}@#")"
|
||||
git push "$authed_url" HEAD:main
|
||||
fi
|
||||
|
||||
- name: Prepare release notes
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
cp CHANGELOG.md RELEASE_NOTES.md
|
||||
test -s RELEASE_NOTES.md
|
||||
echo "---- RELEASE_NOTES.md ----"
|
||||
head -n 120 RELEASE_NOTES.md
|
||||
echo "--------------------------"
|
||||
|
||||
- name: Derive semver package version from tag
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
|
||||
PACKAGE_VERSION="$(echo "$TAG" | sed -E 's/^v([0-9]{2})\.0?([0-9]{1,2})\.0?([0-9]{1,2})([a-z])$/\1.\2.\3-\4/')"
|
||||
|
||||
if [ -z "$PACKAGE_VERSION" ]; then
|
||||
echo "❌ Failed to derive PACKAGE_VERSION from TAG=$TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> "$GITHUB_ENV"
|
||||
echo "Using package version: $PACKAGE_VERSION"
|
||||
|
||||
- name: Generate version file for Astro footer
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
mkdir -p src/generated
|
||||
cat > src/generated/version.json <<EOF
|
||||
{
|
||||
"version": "${TAG}",
|
||||
"url": "${RELEASE_URL}"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Generated src/generated/version.json"
|
||||
cat src/generated/version.json
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 25
|
||||
cache: npm
|
||||
|
||||
- name: Check Node version
|
||||
shell: bash
|
||||
run: |
|
||||
node -v
|
||||
npm -v
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
npm ci
|
||||
|
||||
- name: Update package.json and package-lock.json version
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
npm version "$PACKAGE_VERSION" --no-git-tag-version
|
||||
|
||||
echo "package.json version:"
|
||||
node -p "require('./package.json').version"
|
||||
|
||||
echo "package-lock.json version:"
|
||||
node -p "require('./package-lock.json').version"
|
||||
|
||||
- name: Commit and push version bump (CHANGELOG_PAT)
|
||||
shell: bash
|
||||
env:
|
||||
CHANGELOG_PAT: ${{ secrets.CHANGELOG_PAT }}
|
||||
run: |
|
||||
set -e
|
||||
|
||||
if git diff --quiet -- package.json package-lock.json; then
|
||||
echo "No version changes to commit."
|
||||
else
|
||||
git config user.name "release-bot"
|
||||
git config user.email "release-bot@users.noreply.local"
|
||||
|
||||
git add package.json package-lock.json
|
||||
git commit -m "chore(release): bump version to ${PACKAGE_VERSION} [skip ci]"
|
||||
|
||||
origin_url="$(git remote get-url origin)"
|
||||
|
||||
if echo "$origin_url" | grep -q "^git@"; then
|
||||
host="$(echo "$origin_url" | sed -E 's#git@([^:]+):.*#\1#')"
|
||||
path="$(echo "$origin_url" | sed -E 's#git@[^:]+:(.*)#\1#')"
|
||||
origin_url="https://$host/$path"
|
||||
fi
|
||||
|
||||
authed_url="$(echo "$origin_url" | sed -E "s#^https://#https://oauth2:${CHANGELOG_PAT}@#")"
|
||||
git push "$authed_url" HEAD:main
|
||||
fi
|
||||
|
||||
- name: Build Astro site
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
npm run build
|
||||
test -d dist
|
||||
|
||||
- name: Create export zip (Computing:Box Website.zip)
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
if [ ! -d "dist" ]; then
|
||||
echo "❌ dist/ folder not found in repo root"
|
||||
ls -la
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "Computing:Box Website.zip"
|
||||
(cd dist && zip -r "../Computing:Box Website.zip" .)
|
||||
test -s "Computing:Box Website.zip"
|
||||
ls -lh "Computing:Box Website.zip"
|
||||
|
||||
- name: Create and push tag (CHANGELOG_PAT)
|
||||
shell: bash
|
||||
@@ -153,7 +286,6 @@ jobs:
|
||||
|
||||
origin_url="$(git remote get-url origin)"
|
||||
|
||||
# Convert SSH origin to HTTPS if needed
|
||||
if echo "$origin_url" | grep -q "^git@"; then
|
||||
host="$(echo "$origin_url" | sed -E 's#git@([^:]+):.*#\1#')"
|
||||
path="$(echo "$origin_url" | sed -E 's#git@[^:]+:(.*)#\1#')"
|
||||
@@ -198,7 +330,7 @@ jobs:
|
||||
"tag_name": tag,
|
||||
"target_commitish": "main",
|
||||
"name": name,
|
||||
"body": body, # newest section only
|
||||
"body": body,
|
||||
"draft": False,
|
||||
"prerelease": False,
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
35
cliff.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
[changelog]
|
||||
header = """
|
||||
# Changelog
|
||||
"""
|
||||
|
||||
body = """
|
||||
{% for group, commits in commits | group_by(attribute="group") %}
|
||||
## {{ group }}
|
||||
{% for commit in commits %}
|
||||
- {{ commit.message | upper_first }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
"""
|
||||
|
||||
footer = ""
|
||||
|
||||
[git]
|
||||
conventional_commits = false
|
||||
filter_unconventional = false
|
||||
split_commits = false
|
||||
topo_order = true
|
||||
|
||||
# IMPORTANT: match your tag format
|
||||
tag_pattern = "^v[0-9]{2}\\.[0-9]{2}\\.[0-9]{2}[a-z]$"
|
||||
|
||||
commit_parsers = [
|
||||
{ message = "^feat", group = "🚀 Features" },
|
||||
{ message = "^fix", group = "🐛 Fixes" },
|
||||
{ message = "^refactor", group = "♻️ Refactoring" },
|
||||
{ message = "^docs", group = "📚 Documentation" },
|
||||
{ message = "^chore", group = "💼 Other" },
|
||||
|
||||
# catch-all so NOTHING is dropped
|
||||
{ message = ".*", group = "💼 Other" },
|
||||
]
|
||||
@@ -1,12 +0,0 @@
|
||||
(()=>{const d=document.getElementById("bitsGrid"),h=document.getElementById("denaryNumber"),M=document.getElementById("binaryNumber"),f=document.getElementById("bitsInput"),m=document.getElementById("modeToggle"),E=document.getElementById("modeHint"),T=document.getElementById("lblUnsigned"),k=document.getElementById("lblTwos"),H=document.getElementById("btnCustomBinary"),G=document.getElementById("btnCustomDenary"),P=document.getElementById("btnShiftLeft"),V=document.getElementById("btnShiftRight"),q=document.getElementById("btnDec"),Z=document.getElementById("btnInc"),z=document.getElementById("btnClear"),I=document.getElementById("btnRandom"),O=document.getElementById("btnBitsUp"),W=document.getElementById("btnBitsDown"),R=document.getElementById("toolboxToggle"),w=document.getElementById("binaryPage");let i=b(Number(f?.value??8),1,64),s=new Array(i).fill(!1),u=null;function b(t,n,e){return Number.isFinite(t)?Math.max(n,Math.min(e,Math.trunc(t))):n}function l(){return!!m?.checked}function a(t){return 1n<<BigInt(t)}function y(t){return a(t)}function $(t){return a(t)-1n}function B(t){return-a(t-1)}function p(t){return a(t-1)-1n}function v(){let t=0n;for(let n=0;n<i;n++)s[n]&&(t+=a(n));return t}function g(t){const n=y(i),e=(t%n+n)%n;for(let o=0;o<i;o++)s[o]=(e>>BigInt(o)&1n)===1n}function L(){const t=v();return s[i-1]===!0?t-a(i):t}function x(t){const n=a(i);let e=t;e=(e%n+n)%n,g(e)}function j(){let t="";for(let n=i-1;n>=0;n--){t+=s[n]?"1":"0";const e=i-n;n!==0&&e%4===0&&(t+=" ")}return t.trimEnd()}function D(){E&&(l()?E.textContent="Tip: In two's complement, the left-most bit (MSB) represents a negative value.":E.textContent="Tip: In unsigned binary, all bits represent positive values.")}function A(){if(!d)return;const t=d.parentElement;if(!t)return;const n=t.getBoundingClientRect().width,o=b(Math.floor(n/100),1,12);d.style.setProperty("--cols",String(Math.min(o,i)))}function C(t){i=b(t,1,64),f&&(f.value=String(i));const n=s.slice();s=new Array(i).fill(!1);for(let e=0;e<Math.min(n.length,i);e++)s[e]=n[e];d.innerHTML="",d.classList.toggle("bitsFew",i<8);for(let e=i-1;e>=0;e--){const o=document.createElement("div");o.className="bit",o.innerHTML=`
|
||||
<div class="bulb" id="bulb-${e}" aria-hidden="true">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2.25a6.75 6.75 0 0 0-6.75 6.75c0 2.537 1.393 4.75 3.493 5.922l.507.282v1.546h5.5v-1.546l.507-.282A6.75 6.75 0 0 0 12 2.25Zm-2.25 16.5v.75a2.25 2.25 0 0 0 4.5 0v-.75h-4.5Z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="bitVal" id="bitLabel-${e}"></div>
|
||||
<label class="switch" aria-label="Toggle bit ${e}">
|
||||
<input type="checkbox" data-index="${e}">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
`,d.appendChild(o)}d.querySelectorAll('input[type="checkbox"]').forEach(e=>{e.addEventListener("change",()=>{const o=Number(e.dataset.index);s[o]=e.checked,r()})}),A(),r()}function J(){for(let t=0;t<i;t++){const n=document.getElementById(`bitLabel-${t}`);if(!n)continue;let e;l()&&t===i-1?e=`-${a(i-1).toString()}`:e=a(t).toString(),n.textContent=e,n.style.setProperty("--len",e.length)}}function K(){d.querySelectorAll('input[type="checkbox"]').forEach(t=>{const n=Number(t.dataset.index);t.checked=!!s[n]})}function Q(){for(let t=0;t<i;t++){const n=document.getElementById(`bulb-${t}`);n&&n.classList.toggle("on",s[t]===!0)}}function X(){!h||!M||(l()?h.textContent=L().toString():h.textContent=v().toString(),M.textContent=j())}function r(){D(),T&&k&&(T.classList.toggle("activeMode",!l()),k.classList.toggle("activeMode",l())),J(),K(),Q(),X()}function Y(t){const n=String(t??"").replace(/\s+/g,"");if(!/^[01]+$/.test(n))return!1;const e=n.slice(-i).padStart(i,"0");for(let o=0;o<i;o++){const c=e[e.length-1-o];s[o]=c==="1"}return r(),!0}function _(t){const n=String(t??"").trim();if(!n)return!1;let e;try{if(!/^-?\d+$/.test(n))return!1;e=BigInt(n)}catch{return!1}if(l()){const o=B(i),c=p(i);if(e<o||e>c)return!1;x(e)}else{if(e<0n||e>$(i))return!1;g(e)}return r(),!0}function tt(){for(let t=i-1;t>=1;t--)s[t]=s[t-1];s[0]=!1,r()}function nt(){const t=s[i-1];for(let n=0;n<i-1;n++)s[n]=s[n+1];s[i-1]=l()?t:!1,r()}function et(){s=[],m&&(m.checked=!1),C(8)}function it(){if(l()){const t=B(i),n=p(i);let e=L()+1n;e>n&&(e=t),x(e)}else{const t=y(i);g((v()+1n)%t)}r()}function ot(){if(l()){const t=B(i),n=p(i);let e=L()-1n;e<t&&(e=n),x(e)}else{const t=y(i);g((v()-1n+t)%t)}r()}function st(t){if(t<=0n)return 0n;const n=t.toString(2).length,e=Math.ceil(n/8);for(;;){const o=new Uint8Array(e);crypto.getRandomValues(o);let c=0n;for(const ct of o)c=c<<8n|BigInt(ct);const U=BigInt(e*8-n);if(U>0n&&(c=c>>U),c<t)return c}}function lt(){const t=y(i),n=st(t);g(n),r()}function N(t){I&&I.classList.toggle("btnRandomRunning",!!t)}function rt(){u&&(clearInterval(u),u=null),N(!0);const t=Date.now(),n=1125;u=setInterval(()=>{lt(),Date.now()-t>=n&&(clearInterval(u),u=null,N(!1))},80)}function S(t){const n=b(t,1,64);C(n)}function F(t){if(!w)return;w.classList.toggle("toolboxCollapsed",!!t);const n=!t;R?.setAttribute("aria-expanded",n?"true":"false")}m?.addEventListener("change",r),H?.addEventListener("click",()=>{const t=prompt(`Enter binary (spaces allowed). Current width: ${i} bits`);t!==null&&(Y(t)||alert("Invalid binary"))}),G?.addEventListener("click",()=>{const t=prompt(l()?`Enter denary (${B(i).toString()} to ${p(i).toString()}):`:`Enter denary (0 to ${$(i).toString()}):`);t!==null&&(_(t)||alert("Invalid denary for current mode/bit width"))}),P?.addEventListener("click",tt),V?.addEventListener("click",nt),Z?.addEventListener("click",it),q?.addEventListener("click",ot),z?.addEventListener("click",et),I?.addEventListener("click",rt),O?.addEventListener("click",()=>S(i+1)),W?.addEventListener("click",()=>S(i-1)),f?.addEventListener("change",()=>S(Number(f.value))),R?.addEventListener("click",()=>{const t=w?.classList.contains("toolboxCollapsed");F(!t)}),window.addEventListener("resize",()=>{A()}),D(),C(i),F(!1)})();
|
||||
80
dist/binary/index.html
vendored
@@ -13,7 +13,81 @@
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_astro/about.DM-NXsTj.css">
|
||||
<link rel="stylesheet" href="/_astro/binary.9peKc0z2.css"></head> <body> <header class="siteNav"> <div class="navInner"> <a class="brand" href="/"> <img class="brandLogo" src="/images/computing-box-logo.svg" alt="Computing:Box logo"> <span class="brandName">Computing:Box</span> </a> <nav class="navLinks" aria-label="Site navigation"> <a href="/about">About</a> <a href="/binary">Binary</a> <a href="/hexadecimal">Hexadecimal</a> <a href="/hex-colours">Hex Colours</a> <a href="/logic-gates">Logic Gates</a> <a href="/pc-builder">PC Components</a> </nav> </div> </header> <main class="pageWrap"> <div class="binaryPage" id="binaryPage"> <button id="toolboxToggle" class="toolboxToggle" type="button" aria-expanded="true"> <span class="toolboxIcon" aria-hidden="true">🧰</span> <span class="toolboxText">TOOLBOX</span> </button> <section class="topGrid"> <div class="leftCol"> <div class="readout"> <div class="label">Denary</div> <div id="denaryNumber" class="num denaryValue">0</div> <div class="label">Binary</div> <div id="binaryNumber" class="num binaryValue">00000000</div> </div> <div class="divider"></div> <section class="bitsWrap" aria-label="Bit switches"> <div class="bitsGrid" id="bitsGrid"></div> </section> </div> <aside id="toolboxPanel" class="panelCol" aria-label="Toolbox"> <div class="card"> <div class="cardTitle">Settings</div> <div class="toggleRow"> <div class="toggleLabel" id="lblUnsigned">Unsigned</div> <label class="switch" aria-label="Toggle mode"> <input id="modeToggle" type="checkbox"> <span class="slider"></span> </label> <div class="toggleLabel" id="lblTwos">Two's complement</div> </div> <div class="hint" id="modeHint">
|
||||
</script><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet"><link rel="icon" type="image/x-icon" href="/images/favicon.ico"><link rel="stylesheet" href="/_astro/BaseLayout.B8W3SO34.css">
|
||||
<link rel="stylesheet" href="/_astro/number-simulators.6IzVRJBu.css"></head> <body> <header class="siteNav"> <div class="navInner"> <a class="brand" href="/"> <img class="brandLogo" src="/images/computing-box-logo-small.webp" alt="Computing:Box logo"> <span class="brandName">Computing:Box</span> </a> <nav class="navLinks" aria-label="Site navigation"> <a href="/">Home</a> <a href="/about">About</a> <a href="/binary">Binary</a> <a href="/hexadecimal">Hexadecimal</a> <a href="/hex-colours">Hex Colours</a> <a href="/logic-gates">Logic Gates</a> <a href="/pc-builder">PC Components</a> </nav> </div> </header> <main class="pageWrap"> <div class="binaryPage" id="binaryPage"> <button id="toolboxToggle" class="toolboxToggle" type="button" aria-expanded="true"> <span class="toolboxIcon" aria-hidden="true">🧰</span> <span class="toolboxText">TOOLBOX</span> </button> <section class="topGrid"> <div class="leftCol"> <div class="readout"> <div class="label">Denary</div> <div id="denaryNumber" class="num denaryValue">0</div> <div class="label">Binary</div> <div id="binaryNumber" class="num binaryValue">00000000</div> </div> <div class="divider"></div> <section class="bitsWrap" aria-label="Bit switches"> <div class="bitsGrid" id="bitsGrid"></div> </section> </div> <aside id="toolboxPanel" class="panelCol" aria-label="Toolbox"> <div class="card"> <div class="cardTitle">Settings</div> <div class="toggleRow"> <div class="toggleLabel" id="lblUnsigned">Unsigned</div> <label class="switch" aria-label="Toggle mode"> <input id="modeToggle" type="checkbox"> <span class="slider"></span> </label> <div class="toggleLabel" id="lblTwos">Two's complement</div> </div> <div class="hint" id="modeHint">
|
||||
Tip: In unsigned binary, all bits represent positive values.
|
||||
</div> <div class="subCard"> <div class="subTitle">Bit width</div> <div class="bitWidthRow"> <button class="miniBtn" id="btnBitsDown" type="button" aria-label="Decrease bits">−</button> <div class="bitInputWrap"> <div class="bitInputLabel">Bits</div> <input id="bitsInput" class="bitInput" type="number" inputmode="numeric" min="1" max="64" step="1" value="8" aria-label="Number of bits"> </div> <button class="miniBtn" id="btnBitsUp" type="button" aria-label="Increase bits">+</button> </div> </div> </div> <div class="card"> <div class="cardTitle">Custom Number</div> <div class="controlsRow"> <button class="btn btnAccent btnHalf" id="btnCustomBinary" type="button">Custom Binary</button> <button class="btn btnAccent btnHalf" id="btnCustomDenary" type="button">Custom Denary</button> </div> <button class="btn btnWide" id="btnRandom" type="button">Random</button> <div class="hint">Random runs briefly then stops automatically.</div> </div> <div class="card"> <div class="cardTitle">Tools</div> <div class="toolRowCentered"> <button class="toolBtn toolSpin toolDec" id="btnDec" type="button" aria-label="Decrement">▼</button> <button class="toolBtn toolSpin toolInc" id="btnInc" type="button" aria-label="Increment">▲</button> </div> <div class="toolRow2"> <button class="btn btnHalf" id="btnShiftLeft" type="button">Left Shift</button> <button class="btn btnHalf" id="btnShiftRight" type="button">Right Shift</button> </div> <button class="btn btnReset btnWide" id="btnClear" type="button">Reset</button> </div> </aside> </section> </div> <script type="module" src="/_astro/binary.astro_astro_type_script_index_0_lang.C_c_A3x5.js"></script> </main> <footer class="siteFooter"> <div class="footerInner"> <div style="margin-top: 5px; display: flex; justify-content: center;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> <a href="/legal-code" style="margin-left: 32px; color: var(--muted); text-decoration: underline;">Legal Code</a> </div> <div>Computer Science Concept Simulators</div> <div>© 2026 Computing:Box • Created with ♥ by Mr A Lyall</div> </div> </footer> </body></html>
|
||||
</div> <div class="subCard"> <div class="subTitle">Bit width</div> <div class="bitWidthRow"> <button class="miniBtn" id="btnBitsDown" type="button" aria-label="Decrease bits">−</button> <div class="bitInputWrap"> <div class="bitInputLabel">Bits</div> <input id="bitsInput" class="bitInput" type="number" inputmode="numeric" min="1" max="64" step="1" value="8" aria-label="Number of bits"> </div> <button class="miniBtn" id="btnBitsUp" type="button" aria-label="Increase bits">+</button> </div> </div> </div> <div class="card"> <div class="cardTitle">Custom Number</div> <div class="controlsRow"> <button class="btn btnAccent btnHalf" id="btnCustomBinary" type="button">Custom Binary</button> <button class="btn btnAccent btnHalf" id="btnCustomDenary" type="button">Custom Denary</button> </div> <button class="btn btnWide" id="btnRandom" type="button">Random</button> <div class="hint">Random runs briefly then stops automatically.</div> </div> <div class="card"> <div class="cardTitle">Tools</div> <div class="toolRowCentered"> <button class="toolBtn toolSpin toolDec" id="btnDec" type="button" aria-label="Decrement">▼</button> <button class="toolBtn toolSpin toolInc" id="btnInc" type="button" aria-label="Increment">▲</button> </div> <div class="controlsRow"> <button class="btn btnHalf" id="btnShiftLeft" type="button">Left Shift</button> <button class="btn btnHalf" id="btnShiftRight" type="button">Right Shift</button> </div> <button class="btn btnReset btnWide" id="btnClear" type="button">Reset</button> </div> </aside> </section> </div> <script type="module" src="/_astro/binary.astro_astro_type_script_index_0_lang.CNqn-vvz.js"></script> </main> <footer class="siteFooter"> <div class="footerInner"> <div style="margin-top: 5px; display: flex; justify-content: center;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> <a href="/legal-code" style="margin-left: 32px; color: var(--muted); text-decoration: underline;">Legal Code</a> </div> <div>Computer Science Concept Simulators</div> <div> Version:
|
||||
<a href="#" target="_blank" rel="noopener noreferrer">dev</a> • © 2026 Computing:Box • Created with ♥ by Mr A Lyall</div> </div> </footer> <script>
|
||||
function setupToolboxAccordions() {
|
||||
// Look for cards inside ANY of the three toolboxes!
|
||||
const cards = document.querySelectorAll('.panelCol .card, .pb-toolbox .card, .lg-toolbox .card');
|
||||
if (!cards.length) return;
|
||||
|
||||
// Your primary cards for each page
|
||||
const primaryCardNames = ['settings', 'info', 'components', 'system diagnostics'];
|
||||
|
||||
cards.forEach(card => {
|
||||
const titleEl = card.querySelector('.cardTitle');
|
||||
if (!titleEl) return;
|
||||
|
||||
const titleText = titleEl.textContent.trim().toLowerCase();
|
||||
const isPrimary = primaryCardNames.includes(titleText);
|
||||
|
||||
// 1. DYNAMICALLY WRAP THE CONTENT
|
||||
if (!card.querySelector('.cardBody')) {
|
||||
const body = document.createElement('div');
|
||||
body.className = 'cardBody';
|
||||
const inner = document.createElement('div');
|
||||
inner.className = 'cardBodyInner';
|
||||
body.appendChild(inner);
|
||||
|
||||
Array.from(card.childNodes).forEach(node => {
|
||||
if (node !== titleEl) {
|
||||
inner.appendChild(node);
|
||||
}
|
||||
});
|
||||
card.appendChild(body);
|
||||
}
|
||||
|
||||
// 2. APPLY DEFAULT STATE
|
||||
if (isPrimary) {
|
||||
card.classList.remove('collapsed');
|
||||
} else {
|
||||
card.classList.add('collapsed');
|
||||
}
|
||||
|
||||
// 3. CLICK LISTENERS
|
||||
if (card.dataset.accordionInit) return;
|
||||
card.dataset.accordionInit = "true";
|
||||
|
||||
titleEl.addEventListener('click', () => {
|
||||
const isCollapsing = !card.classList.contains('collapsed');
|
||||
|
||||
if (isCollapsing) {
|
||||
card.classList.add('collapsed');
|
||||
} else {
|
||||
if (isPrimary) {
|
||||
cards.forEach(c => {
|
||||
// Only close cards that share the same parent toolbox
|
||||
if (c !== card && c.closest(card.parentElement.tagName) === card.closest(card.parentElement.tagName)) {
|
||||
c.classList.add('collapsed');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cards.forEach(c => {
|
||||
if (c.closest(card.parentElement.tagName) !== card.closest(card.parentElement.tagName)) return;
|
||||
const cTitle = c.querySelector('.cardTitle')?.textContent.trim().toLowerCase() || '';
|
||||
if (primaryCardNames.includes(cTitle)) {
|
||||
c.classList.add('collapsed');
|
||||
}
|
||||
});
|
||||
}
|
||||
card.classList.remove('collapsed');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setupToolboxAccordions();
|
||||
document.addEventListener('astro:page-load', setupToolboxAccordions);
|
||||
</script> </body> </html>
|
||||
9
dist/favicon.svg
vendored
@@ -1,9 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||
<style>
|
||||
path { fill: #000; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #FFF; }
|
||||
}
|
||||
</style>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 749 B |
80
dist/hexadecimal/index.html
vendored
@@ -13,7 +13,81 @@
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_astro/about.DM-NXsTj.css">
|
||||
<link rel="stylesheet" href="/_astro/binary.9peKc0z2.css"></head> <body> <header class="siteNav"> <div class="navInner"> <a class="brand" href="/"> <img class="brandLogo" src="/images/computing-box-logo.svg" alt="Computing:Box logo"> <span class="brandName">Computing:Box</span> </a> <nav class="navLinks" aria-label="Site navigation"> <a href="/about">About</a> <a href="/binary">Binary</a> <a href="/hexadecimal">Hexadecimal</a> <a href="/hex-colours">Hex Colours</a> <a href="/logic-gates">Logic Gates</a> <a href="/pc-builder">PC Components</a> </nav> </div> </header> <main class="pageWrap"> <div class="binaryPage" id="hexPage"> <button id="toolboxToggle" class="toolboxToggle" type="button" aria-expanded="true"> <span class="toolboxIcon" aria-hidden="true">🧰</span> <span class="toolboxText">TOOLBOX</span> </button> <section class="topGrid"> <div class="leftCol"> <div class="readout"> <div class="label">Denary</div> <div id="denaryNumber" class="num denaryValue">0</div> <div class="label">Hexadecimal</div> <div id="hexNumber" class="num hexValue">00</div> <div class="label">Binary</div> <div id="binaryNumber" class="num binaryValue">00000000</div> </div> <div class="divider"></div> <section class="bitsWrap" aria-label="Hexadecimal sliders"> <div class="hexGrid" id="hexGrid"></div> </section> </div> <aside id="toolboxPanel" class="panelCol" aria-label="Toolbox"> <div class="card"> <div class="cardTitle">Settings</div> <div class="hint" style="margin-top: 0; margin-bottom: 14px;">
|
||||
</script><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet"><link rel="icon" type="image/x-icon" href="/images/favicon.ico"><link rel="stylesheet" href="/_astro/BaseLayout.B8W3SO34.css">
|
||||
<link rel="stylesheet" href="/_astro/number-simulators.6IzVRJBu.css"></head> <body> <header class="siteNav"> <div class="navInner"> <a class="brand" href="/"> <img class="brandLogo" src="/images/computing-box-logo-small.webp" alt="Computing:Box logo"> <span class="brandName">Computing:Box</span> </a> <nav class="navLinks" aria-label="Site navigation"> <a href="/">Home</a> <a href="/about">About</a> <a href="/binary">Binary</a> <a href="/hexadecimal">Hexadecimal</a> <a href="/hex-colours">Hex Colours</a> <a href="/logic-gates">Logic Gates</a> <a href="/pc-builder">PC Components</a> </nav> </div> </header> <main class="pageWrap"> <div class="binaryPage" id="hexPage"> <button id="toolboxToggle" class="toolboxToggle" type="button" aria-expanded="true"> <span class="toolboxIcon" aria-hidden="true">🧰</span> <span class="toolboxText">TOOLBOX</span> </button> <section class="topGrid"> <div class="leftCol"> <div class="readout"> <div class="label">Denary</div> <div id="denaryNumber" class="num denaryValue">0</div> <div class="label">Hexadecimal</div> <div id="hexNumber" class="num hexValue">00</div> <div class="label">Binary</div> <div id="binaryNumber" class="num binaryValue">00000000</div> </div> <div class="divider"></div> <section class="bitsWrap" aria-label="Hexadecimal sliders"> <div class="hexGrid" id="hexGrid"></div> </section> </div> <aside id="toolboxPanel" class="panelCol" aria-label="Toolbox"> <div class="card"> <div class="cardTitle">Settings</div> <div class="hint" style="margin-top: 0; margin-bottom: 14px;">
|
||||
Hexadecimal represents numbers using base 16 (0-9, A-F).
|
||||
</div> <div class="subCard"> <div class="subTitle">Digit width</div> <div class="bitWidthRow"> <button class="miniBtn" id="btnDigitsDown" type="button" aria-label="Decrease digits">−</button> <div class="bitInputWrap"> <div class="bitInputLabel">Digits</div> <input id="digitsInput" class="bitInput" type="number" inputmode="numeric" min="1" max="16" step="1" value="2" aria-label="Number of hex digits"> </div> <button class="miniBtn" id="btnDigitsUp" type="button" aria-label="Increase digits">+</button> </div> </div> </div> <div class="card"> <div class="cardTitle">Custom Number</div> <div class="controlsRow"> <button class="btn btnAccent btnHalf" id="btnCustomHex" type="button">Custom Hex</button> <button class="btn btnAccent btnHalf" id="btnCustomDenary" type="button">Custom Denary</button> </div> <div class="controlsRow"> <button class="btn btnAccent btnWide" id="btnCustomBinary" type="button">Custom Binary</button> </div> <button class="btn btnWide" id="btnRandom" type="button">Random</button> <div class="hint">Random runs briefly then stops automatically.</div> </div> <div class="card"> <div class="cardTitle">Tools</div> <div class="toolRowCentered"> <button class="toolBtn toolSpin toolDec" id="btnDec" type="button" aria-label="Decrement">▼</button> <button class="toolBtn toolSpin toolInc" id="btnInc" type="button" aria-label="Increment">▲</button> </div> <button class="btn btnReset btnWide" id="btnClear" type="button">Reset</button> </div> </aside> </section> </div> <script type="module" src="/_astro/hexadecimal.astro_astro_type_script_index_0_lang.C4Wx7oaX.js"></script> </main> <footer class="siteFooter"> <div class="footerInner"> <div style="margin-top: 5px; display: flex; justify-content: center;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> <a href="/legal-code" style="margin-left: 32px; color: var(--muted); text-decoration: underline;">Legal Code</a> </div> <div>Computer Science Concept Simulators</div> <div>© 2026 Computing:Box • Created with ♥ by Mr A Lyall</div> </div> </footer> </body></html>
|
||||
</div> <div class="subCard"> <div class="subTitle">Digit width</div> <div class="bitWidthRow"> <button class="miniBtn" id="btnDigitsDown" type="button" aria-label="Decrease digits">−</button> <div class="bitInputWrap"> <div class="bitInputLabel">Digits</div> <input id="digitsInput" class="bitInput" type="number" inputmode="numeric" min="1" max="16" step="1" value="2" aria-label="Number of hex digits"> </div> <button class="miniBtn" id="btnDigitsUp" type="button" aria-label="Increase digits">+</button> </div> </div> </div> <div class="card"> <div class="cardTitle">Custom Number</div> <div class="controlsRow"> <button class="btn btnAccent btnHalf" id="btnCustomHex" type="button">Custom Hex</button> <button class="btn btnAccent btnHalf" id="btnCustomDenary" type="button">Custom Denary</button> </div> <div class="controlsRow"> <button class="btn btnAccent btnWide" id="btnCustomBinary" type="button">Custom Binary</button> </div> <button class="btn btnWide" id="btnRandom" type="button">Random</button> <div class="hint">Random runs briefly then stops automatically.</div> </div> <div class="card"> <div class="cardTitle">Tools</div> <div class="toolRowCentered"> <button class="toolBtn toolSpin toolDec" id="btnDec" type="button" aria-label="Decrement">▼</button> <button class="toolBtn toolSpin toolInc" id="btnInc" type="button" aria-label="Increment">▲</button> </div> <button class="btn btnReset btnWide" id="btnClear" type="button">Reset</button> </div> </aside> </section> </div> <script type="module" src="/_astro/hexadecimal.astro_astro_type_script_index_0_lang.C4Wx7oaX.js"></script> </main> <footer class="siteFooter"> <div class="footerInner"> <div style="margin-top: 5px; display: flex; justify-content: center;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> <a href="/legal-code" style="margin-left: 32px; color: var(--muted); text-decoration: underline;">Legal Code</a> </div> <div>Computer Science Concept Simulators</div> <div> Version:
|
||||
<a href="#" target="_blank" rel="noopener noreferrer">dev</a> • © 2026 Computing:Box • Created with ♥ by Mr A Lyall</div> </div> </footer> <script>
|
||||
function setupToolboxAccordions() {
|
||||
// Look for cards inside ANY of the three toolboxes!
|
||||
const cards = document.querySelectorAll('.panelCol .card, .pb-toolbox .card, .lg-toolbox .card');
|
||||
if (!cards.length) return;
|
||||
|
||||
// Your primary cards for each page
|
||||
const primaryCardNames = ['settings', 'info', 'components', 'system diagnostics'];
|
||||
|
||||
cards.forEach(card => {
|
||||
const titleEl = card.querySelector('.cardTitle');
|
||||
if (!titleEl) return;
|
||||
|
||||
const titleText = titleEl.textContent.trim().toLowerCase();
|
||||
const isPrimary = primaryCardNames.includes(titleText);
|
||||
|
||||
// 1. DYNAMICALLY WRAP THE CONTENT
|
||||
if (!card.querySelector('.cardBody')) {
|
||||
const body = document.createElement('div');
|
||||
body.className = 'cardBody';
|
||||
const inner = document.createElement('div');
|
||||
inner.className = 'cardBodyInner';
|
||||
body.appendChild(inner);
|
||||
|
||||
Array.from(card.childNodes).forEach(node => {
|
||||
if (node !== titleEl) {
|
||||
inner.appendChild(node);
|
||||
}
|
||||
});
|
||||
card.appendChild(body);
|
||||
}
|
||||
|
||||
// 2. APPLY DEFAULT STATE
|
||||
if (isPrimary) {
|
||||
card.classList.remove('collapsed');
|
||||
} else {
|
||||
card.classList.add('collapsed');
|
||||
}
|
||||
|
||||
// 3. CLICK LISTENERS
|
||||
if (card.dataset.accordionInit) return;
|
||||
card.dataset.accordionInit = "true";
|
||||
|
||||
titleEl.addEventListener('click', () => {
|
||||
const isCollapsing = !card.classList.contains('collapsed');
|
||||
|
||||
if (isCollapsing) {
|
||||
card.classList.add('collapsed');
|
||||
} else {
|
||||
if (isPrimary) {
|
||||
cards.forEach(c => {
|
||||
// Only close cards that share the same parent toolbox
|
||||
if (c !== card && c.closest(card.parentElement.tagName) === card.closest(card.parentElement.tagName)) {
|
||||
c.classList.add('collapsed');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cards.forEach(c => {
|
||||
if (c.closest(card.parentElement.tagName) !== card.closest(card.parentElement.tagName)) return;
|
||||
const cTitle = c.querySelector('.cardTitle')?.textContent.trim().toLowerCase() || '';
|
||||
if (primaryCardNames.includes(cTitle)) {
|
||||
c.classList.add('collapsed');
|
||||
}
|
||||
});
|
||||
}
|
||||
card.classList.remove('collapsed');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setupToolboxAccordions();
|
||||
document.addEventListener('astro:page-load', setupToolboxAccordions);
|
||||
</script> </body> </html>
|
||||
1017
dist/images/computing-box-logo.svg
vendored
|
Before Width: | Height: | Size: 732 KiB |
1017
dist/images/favicon.svg
vendored
|
Before Width: | Height: | Size: 732 KiB |
78
dist/index.html
vendored
@@ -13,6 +13,80 @@
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_astro/about.DM-NXsTj.css"></head> <body> <header class="siteNav"> <div class="navInner"> <a class="brand" href="/"> <img class="brandLogo" src="/images/computing-box-logo.svg" alt="Computing:Box logo"> <span class="brandName">Computing:Box</span> </a> <nav class="navLinks" aria-label="Site navigation"> <a href="/about">About</a> <a href="/binary">Binary</a> <a href="/hexadecimal">Hexadecimal</a> <a href="/hex-colours">Hex Colours</a> <a href="/logic-gates">Logic Gates</a> <a href="/pc-builder">PC Components</a> </nav> </div> </header> <main class="pageWrap"> <div style="display: flex; align-items: center; justify-content: space-between; gap: 40px; min-height: 60vh; padding: 40px 0;"> <div style="flex: 1;"> <p style="color: var(--accent); font-weight: 800; letter-spacing: 2px; text-transform: uppercase; margin-bottom: 10px;">Version 2.0 Now Live</p> <h1 class="brandName" style="font-size: 48px; line-height: 1.1; margin-bottom: 24px;">Understand Computing concepts better.</h1> <p style="font-size: 18px; color: var(--muted);">
|
||||
</script><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet"><link rel="icon" type="image/x-icon" href="/images/favicon.ico"><link rel="stylesheet" href="/_astro/BaseLayout.B8W3SO34.css"></head> <body> <header class="siteNav"> <div class="navInner"> <a class="brand" href="/"> <img class="brandLogo" src="/images/computing-box-logo-small.webp" alt="Computing:Box logo"> <span class="brandName">Computing:Box</span> </a> <nav class="navLinks" aria-label="Site navigation"> <a href="/">Home</a> <a href="/about">About</a> <a href="/binary">Binary</a> <a href="/hexadecimal">Hexadecimal</a> <a href="/hex-colours">Hex Colours</a> <a href="/logic-gates">Logic Gates</a> <a href="/pc-builder">PC Components</a> </nav> </div> </header> <main class="pageWrap"> <div style="display: flex; align-items: center; justify-content: space-between; gap: 40px; min-height: 60vh; padding: 40px 0;"> <div style="flex: 1;"> <p style="color: var(--accent); font-weight: 800; letter-spacing: 2px; text-transform: uppercase; margin-bottom: 10px;">Version 2.0 Now Live</p> <h1 class="brandName" style="font-size: 48px; line-height: 1.1; margin-bottom: 24px;">Understand Computing concepts better.</h1> <p style="font-size: 18px; color: var(--muted);">
|
||||
Interactive simulators for Binary, Hexadecimal, Logic Gates, and Computer Components designed for the UK curriculum.
|
||||
</p> <div style="display: flex; gap: 16px; margin-top: 32px;"> <a href="/about" class="btn btnAccent" style="text-decoration: none; padding: 14px 28px;">Learn More</a> <a href="/binary" class="btn" style="text-decoration: none; padding: 14px 28px;">Get Started</a> </div> </div> <div style="flex: 1; text-align: right;"> <img src="/images/computing-box-logo.svg" alt="Computing Box Logo" style="width: 100%; max-width: 450px; filter: drop-shadow(0 0 50px rgba(40, 240, 122, 0.15));"> </div> </div> </main> <footer class="siteFooter"> <div class="footerInner"> <div style="margin-top: 5px; display: flex; justify-content: center;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> <a href="/legal-code" style="margin-left: 32px; color: var(--muted); text-decoration: underline;">Legal Code</a> </div> <div>Computer Science Concept Simulators</div> <div>© 2026 Computing:Box • Created with ♥ by Mr A Lyall</div> </div> </footer> </body></html>
|
||||
</p> <div style="display: flex; gap: 16px; margin-top: 32px;"> <a href="/about" class="btn btnAccent" style="text-decoration: none; padding: 14px 28px;">Learn More</a> <a href="/binary" class="btn" style="text-decoration: none; padding: 14px 28px;">Get Started</a> </div> </div> <div style="flex: 1; text-align: right;"> <img src="/images/computing-box-logo.webp" alt="Computing Box Logo" style="width: 100%; max-width: 450px; filter: drop-shadow(0 0 50px rgba(40, 240, 122, 0.15));"> </div> </div> </main> <footer class="siteFooter"> <div class="footerInner"> <div style="margin-top: 5px; display: flex; justify-content: center;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> <a href="/legal-code" style="margin-left: 32px; color: var(--muted); text-decoration: underline;">Legal Code</a> </div> <div>Computer Science Concept Simulators</div> <div> Version:
|
||||
<a href="#" target="_blank" rel="noopener noreferrer">dev</a> • © 2026 Computing:Box • Created with ♥ by Mr A Lyall</div> </div> </footer> <script>
|
||||
function setupToolboxAccordions() {
|
||||
// Look for cards inside ANY of the three toolboxes!
|
||||
const cards = document.querySelectorAll('.panelCol .card, .pb-toolbox .card, .lg-toolbox .card');
|
||||
if (!cards.length) return;
|
||||
|
||||
// Your primary cards for each page
|
||||
const primaryCardNames = ['settings', 'info', 'components', 'system diagnostics'];
|
||||
|
||||
cards.forEach(card => {
|
||||
const titleEl = card.querySelector('.cardTitle');
|
||||
if (!titleEl) return;
|
||||
|
||||
const titleText = titleEl.textContent.trim().toLowerCase();
|
||||
const isPrimary = primaryCardNames.includes(titleText);
|
||||
|
||||
// 1. DYNAMICALLY WRAP THE CONTENT
|
||||
if (!card.querySelector('.cardBody')) {
|
||||
const body = document.createElement('div');
|
||||
body.className = 'cardBody';
|
||||
const inner = document.createElement('div');
|
||||
inner.className = 'cardBodyInner';
|
||||
body.appendChild(inner);
|
||||
|
||||
Array.from(card.childNodes).forEach(node => {
|
||||
if (node !== titleEl) {
|
||||
inner.appendChild(node);
|
||||
}
|
||||
});
|
||||
card.appendChild(body);
|
||||
}
|
||||
|
||||
// 2. APPLY DEFAULT STATE
|
||||
if (isPrimary) {
|
||||
card.classList.remove('collapsed');
|
||||
} else {
|
||||
card.classList.add('collapsed');
|
||||
}
|
||||
|
||||
// 3. CLICK LISTENERS
|
||||
if (card.dataset.accordionInit) return;
|
||||
card.dataset.accordionInit = "true";
|
||||
|
||||
titleEl.addEventListener('click', () => {
|
||||
const isCollapsing = !card.classList.contains('collapsed');
|
||||
|
||||
if (isCollapsing) {
|
||||
card.classList.add('collapsed');
|
||||
} else {
|
||||
if (isPrimary) {
|
||||
cards.forEach(c => {
|
||||
// Only close cards that share the same parent toolbox
|
||||
if (c !== card && c.closest(card.parentElement.tagName) === card.closest(card.parentElement.tagName)) {
|
||||
c.classList.add('collapsed');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cards.forEach(c => {
|
||||
if (c.closest(card.parentElement.tagName) !== card.closest(card.parentElement.tagName)) return;
|
||||
const cTitle = c.querySelector('.cardTitle')?.textContent.trim().toLowerCase() || '';
|
||||
if (primaryCardNames.includes(cTitle)) {
|
||||
c.classList.add('collapsed');
|
||||
}
|
||||
});
|
||||
}
|
||||
card.classList.remove('collapsed');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setupToolboxAccordions();
|
||||
document.addEventListener('astro:page-load', setupToolboxAccordions);
|
||||
</script> </body> </html>
|
||||
469
package-lock.json
generated
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "computing-box",
|
||||
"version": "2.0.0",
|
||||
"version": "26.3.3-0.c",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "computing-box",
|
||||
"version": "2.0.0",
|
||||
"version": "26.3.3-0.c",
|
||||
"dependencies": {
|
||||
"astro": "^6.0.0"
|
||||
"astro": "^6.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@astrojs/compiler": {
|
||||
@@ -27,9 +27,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@astrojs/markdown-remark": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.0.1.tgz",
|
||||
"integrity": "sha512-zAfLJmn07u9SlDNNHTpjv0RT4F8D4k54NR7ReRas8CO4OeGoqSvOuKwqCFg2/cqN3wHwdWlK/7Yv/lMXlhVIaw==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.1.0.tgz",
|
||||
"integrity": "sha512-P+HnCsu2js3BoTc8kFmu+E9gOcFeMdPris75g+Zl4sY8+bBRbSQV6xzcBDbZ27eE7yBGEGQoqjpChx+KJYIPYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@astrojs/internal-helpers": "0.8.0",
|
||||
@@ -45,6 +45,7 @@
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.1.2",
|
||||
"remark-smartypants": "^3.0.2",
|
||||
"retext-smartypants": "^6.2.0",
|
||||
"shiki": "^4.0.0",
|
||||
"smol-toml": "^1.6.0",
|
||||
"unified": "^11.0.5",
|
||||
@@ -162,9 +163,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz",
|
||||
"integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==",
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz",
|
||||
"integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@@ -588,9 +589,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/colour": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
|
||||
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
|
||||
"integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
@@ -680,6 +681,9 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -696,6 +700,9 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -712,6 +719,9 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -728,6 +738,9 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -744,6 +757,9 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -760,6 +776,9 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -776,6 +795,9 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -792,6 +814,9 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -808,6 +833,9 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -830,6 +858,9 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -852,6 +883,9 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -874,6 +908,9 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -896,6 +933,9 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -918,6 +958,9 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -940,6 +983,9 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -962,6 +1008,9 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1087,16 +1136,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
|
||||
"integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.1.tgz",
|
||||
"integrity": "sha512-xB0b51TB7IfDEzAojXahmr+gfA00uYVInJGgNNkeQG6RPnCPGr7udsylFLTubuIUSRE6FkcI1NElyRt83PP5oQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1107,9 +1150,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.1.tgz",
|
||||
"integrity": "sha512-XOjPId0qwSDKHaIsdzHJtKCxX0+nH8MhBwvrNsT7tVyKmdTx1jJ4XzN5RZXCdTzMpufLb+B8llTC0D8uCrLhcw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1120,9 +1163,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.1.tgz",
|
||||
"integrity": "sha512-vQuRd28p0gQpPrS6kppd8IrWmFo42U8Pz1XLRjSZXq5zCqyMDYFABT7/sywL11mO1EL10Qhh7MVPEwkG8GiBeg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1133,9 +1176,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.1.tgz",
|
||||
"integrity": "sha512-x6VG6U29+Ivlnajrg1IHdzXeAwSoEHBFVO+CtC9Brugx6de712CUJobRUxsIA0KYrQvCmzNrMPFTT1A4CCqNTg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1146,9 +1189,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.1.tgz",
|
||||
"integrity": "sha512-Sgi0Uo6t1YCHJMNO3Y8+bm+SvOanUGkoZKn/VJPwYUe2kp31X5KnXmzKd/NjW8iA3gFcfNZ64zh14uOGrIllCQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1159,9 +1202,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.1.tgz",
|
||||
"integrity": "sha512-AM4xnwEZwukdhk7laMWfzWu9JGSVnJd+Fowt6Fd7QW1nrf3h0Hp7Qx5881M4aqrUlKBCybOxz0jofvIIfl7C5g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1172,12 +1215,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
|
||||
"integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.1.tgz",
|
||||
"integrity": "sha512-KUizqxpwaR2AZdAUsMWfL/C94pUu7TKpoPd88c8yFVixJ+l9hejkrwoK5Zj3wiNh65UeyryKnJyxL1b7yNqFQA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1185,12 +1231,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
|
||||
"integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.1.tgz",
|
||||
"integrity": "sha512-MZoQ/am77ckJtZGFAtPucgUuJWiop3m2R3lw7tC0QCcbfl4DRhQUBUkHWCkcrT3pqy5Mzv5QQgY6Dmlba6iTWg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1198,12 +1247,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.1.tgz",
|
||||
"integrity": "sha512-Sez95TP6xGjkWB1608EfhCX1gdGrO5wzyN99VqzRtC17x/1bhw5VU1V0GfKUwbW/Xr1J8mSasoFoJa6Y7aGGSA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1211,12 +1263,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.1.tgz",
|
||||
"integrity": "sha512-9Cs2Seq98LWNOJzR89EGTZoiP8EkZ9UbQhBlDgfAkM6asVna1xJ04W2CLYWDN/RpUgOjtQvcv8wQVi1t5oQazA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1224,12 +1279,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.1.tgz",
|
||||
"integrity": "sha512-n9yqttftgFy7IrNEnHy1bOp6B4OSe8mJDiPkT7EqlM9FnKOwUMnCK62ixW0Kd9Clw0/wgvh8+SqaDXMFvw3KqQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1237,12 +1295,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.1.tgz",
|
||||
"integrity": "sha512-SfpNXDzVTqs/riak4xXcLpq5gIQWsqGWMhN1AGRQKB4qGSs4r0sEs3ervXPcE1O9RsQ5bm8Muz6zmQpQnPss1g==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1250,12 +1311,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.1.tgz",
|
||||
"integrity": "sha512-LjaChED0wQnjKZU+tsmGbN+9nN1XhaWUkAlSbTdhpEseCS4a15f/Q8xC2BN4GDKRzhhLZpYtJBZr2NZhR0jvNw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1263,12 +1327,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.1.tgz",
|
||||
"integrity": "sha512-ojW7iTJSIs4pwB2xV6QXGwNyDctvXOivYllttuPbXguuKDX5vwpqYJsHc6D2LZzjDGHML414Tuj3LvVPe1CT1A==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1276,12 +1343,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.1.tgz",
|
||||
"integrity": "sha512-FP+Q6WTcxxvsr0wQczhSE+tOZvFPV8A/mUE6mhZYFW9/eea/y/XqAgRoLLMuE9Cz0hfX5bi7p116IWoB+P237A==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1289,12 +1359,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.1.tgz",
|
||||
"integrity": "sha512-L1uD9b/Ig8Z+rn1KttCJjwhN1FgjRMBKsPaBsDKkfUl7GfFq71pU4vWCnpOsGljycFEbkHWARZLf4lMYg3WOLw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1302,12 +1375,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.1.tgz",
|
||||
"integrity": "sha512-EZc9NGTk/oSUzzOD4nYY4gIjteo2M3CiozX6t1IXGCOdgxJTlVu/7EdPeiqeHPSIrxkLhavqpBAUCfvC6vBOug==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1315,12 +1391,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.1.tgz",
|
||||
"integrity": "sha512-NQ9KyU1Anuy59L8+HHOKM++CoUxrQWrZWXRik4BJFm+7i5NP6q/SW43xIBr80zzt+PDBJ7LeNmloQGfa0JGk0w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1328,12 +1407,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
|
||||
"integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.1.tgz",
|
||||
"integrity": "sha512-GZkLk2t6naywsveSFBsEb0PLU+JC9ggVjbndsbG20VPhar6D1gkMfCx4NfP9owpovBXTN+eRdqGSkDGIxPHhmQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1341,9 +1423,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openbsd-x64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
|
||||
"integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.1.tgz",
|
||||
"integrity": "sha512-1hjG9Jpl2KDOetr64iQd8AZAEjkDUUK5RbDkYWsViYLC1op1oNzdjMJeFiofcGhqbNTaY2kfgqowE7DILifsrA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1354,9 +1436,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
|
||||
"integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.1.tgz",
|
||||
"integrity": "sha512-ARoKfflk0SiiYm3r1fmF73K/yB+PThmOwfWCk1sr7x/k9dc3uGLWuEE9if+Pw21el8MSpp3TMnG5vLNsJ/MMGQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1367,9 +1449,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.1.tgz",
|
||||
"integrity": "sha512-oOST61G6VM45Mz2vdzWMr1s2slI7y9LqxEV5fCoWi2MDONmMvgsJVHSXxce/I2xOSZPTZ47nDPOl1tkwKWSHcw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1380,9 +1462,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.1.tgz",
|
||||
"integrity": "sha512-x5WgLi5dWpRz7WclKBGEF15LcWTh0ewrHM6Cq4A+WUbkysUMZNeqt05bwPonOQ3ihPS/WMhAZV5zB1DfnI4Sxg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -1393,9 +1475,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
|
||||
"integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.1.tgz",
|
||||
"integrity": "sha512-wS+zHAJRVP5zOL0e+a3V3E/NTEwM2HEvvNKoDy5Xcfs0o8lljxn+EAFPkUsxihBdmDq1JWzXmmB9cbssCPdxxw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1406,9 +1488,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
|
||||
"integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.1.tgz",
|
||||
"integrity": "sha512-rhHyrMeLpErT/C7BxcEsU4COHQUzHyrPYW5tOZUeUhziNtRuYxmDWvqQqzpuUt8xpOgmbKa1btGXfnA/ANVO+g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1519,9 +1601,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/debug": {
|
||||
"version": "4.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
||||
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
|
||||
"version": "4.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
|
||||
"integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/ms": "*"
|
||||
@@ -1629,17 +1711,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro": {
|
||||
"version": "6.0.8",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-6.0.8.tgz",
|
||||
"integrity": "sha512-DCPeb8GKOoFWh+8whB7Qi/kKWD/6NcQ9nd1QVNzJFxgHkea3WYrNroQRq4whmBdjhkYPTLS/1gmUAl2iA2Es2g==",
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-6.1.2.tgz",
|
||||
"integrity": "sha512-r3iIvmB6JvQxsdJLvapybKKq7Bojd1iQK6CCx5P55eRnXJIyUpHx/1UB/GdMm+em/lwaCUasxHCmIO0lCLV2uA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^3.0.0",
|
||||
"@astrojs/compiler": "^3.0.1",
|
||||
"@astrojs/internal-helpers": "0.8.0",
|
||||
"@astrojs/markdown-remark": "7.0.1",
|
||||
"@astrojs/markdown-remark": "7.1.0",
|
||||
"@astrojs/telemetry": "3.3.0",
|
||||
"@capsizecss/unpack": "^4.0.0",
|
||||
"@clack/prompts": "^1.0.1",
|
||||
"@clack/prompts": "^1.1.0",
|
||||
"@oslojs/encoding": "^1.1.0",
|
||||
"@rollup/pluginutils": "^5.3.0",
|
||||
"aria-query": "^5.3.2",
|
||||
@@ -1672,11 +1754,11 @@
|
||||
"picomatch": "^4.0.3",
|
||||
"rehype": "^13.0.2",
|
||||
"semver": "^7.7.4",
|
||||
"shiki": "^4.0.0",
|
||||
"shiki": "^4.0.2",
|
||||
"smol-toml": "^1.6.0",
|
||||
"svgo": "^4.0.0",
|
||||
"tinyclip": "^0.1.6",
|
||||
"tinyexec": "^1.0.2",
|
||||
"svgo": "^4.0.1",
|
||||
"tinyclip": "^0.1.12",
|
||||
"tinyexec": "^1.0.4",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"tsconfck": "^3.1.6",
|
||||
"ultrahtml": "^1.6.0",
|
||||
@@ -1706,15 +1788,6 @@
|
||||
"sharp": "^0.34.0"
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/zod": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
||||
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/axobject-query": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
||||
@@ -1892,13 +1965,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
|
||||
"integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
|
||||
"integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mdn-data": "2.12.2",
|
||||
"source-map-js": "^1.0.1"
|
||||
"mdn-data": "2.27.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
@@ -1967,9 +2040,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/decode-named-character-reference": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
|
||||
"integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
|
||||
"integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"character-entities": "^2.0.0"
|
||||
@@ -2011,9 +2084,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/devalue": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.3.tgz",
|
||||
"integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==",
|
||||
"version": "5.6.4",
|
||||
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.4.tgz",
|
||||
"integrity": "sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/devlop": {
|
||||
@@ -2191,10 +2264,16 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
|
||||
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/extend": {
|
||||
@@ -2239,9 +2318,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fontkitten": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.2.tgz",
|
||||
"integrity": "sha512-piJxbLnkD9Xcyi7dWJRnqszEURixe7CrF/efBfbffe2DPyabmuIuqraruY8cXTs19QoM8VJzx47BDRVNXETM7Q==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz",
|
||||
"integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tiny-inflate": "^1.0.3"
|
||||
@@ -2271,9 +2350,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/h3": {
|
||||
"version": "1.15.5",
|
||||
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.5.tgz",
|
||||
"integrity": "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==",
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.9.tgz",
|
||||
"integrity": "sha512-H7UPnyIupUOYUQu7f2x7ABVeMyF/IbJjqn20WSXpMdnQB260luADUkSgJU7QTWLutq8h3tUayMQ1DdbSYX5LkA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie-es": "^1.2.2",
|
||||
@@ -2541,9 +2620,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/is-wsl": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz",
|
||||
"integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
|
||||
"integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-inside-container": "^1.0.0"
|
||||
@@ -2578,9 +2657,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "11.2.6",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
|
||||
"integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
|
||||
"version": "11.2.7",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
|
||||
"integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
@@ -2648,9 +2727,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-from-markdown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
|
||||
"integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz",
|
||||
"integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
@@ -2842,9 +2921,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.12.2",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
|
||||
"integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
|
||||
"version": "2.27.1",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
|
||||
"integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/micromark": {
|
||||
@@ -3646,9 +3725,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"version": "8.5.8",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
|
||||
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -3939,9 +4018,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
|
||||
"integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
|
||||
"version": "4.59.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.1.tgz",
|
||||
"integrity": "sha512-iZKH8BeoCwTCBTZBZWQQMreekd4mdomwdjIQ40GC1oZm6o+8PnNMIxFOiCsGMWeS8iDJ7KZcl7KwmKk/0HOQpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
@@ -3954,39 +4033,42 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.59.0",
|
||||
"@rollup/rollup-android-arm64": "4.59.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.59.0",
|
||||
"@rollup/rollup-darwin-x64": "4.59.0",
|
||||
"@rollup/rollup-freebsd-arm64": "4.59.0",
|
||||
"@rollup/rollup-freebsd-x64": "4.59.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.59.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-loong64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-ppc64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.59.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.59.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.59.0",
|
||||
"@rollup/rollup-openbsd-x64": "4.59.0",
|
||||
"@rollup/rollup-openharmony-arm64": "4.59.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.59.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.59.0",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.59.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.59.0",
|
||||
"@rollup/rollup-android-arm-eabi": "4.59.1",
|
||||
"@rollup/rollup-android-arm64": "4.59.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.59.1",
|
||||
"@rollup/rollup-darwin-x64": "4.59.1",
|
||||
"@rollup/rollup-freebsd-arm64": "4.59.1",
|
||||
"@rollup/rollup-freebsd-x64": "4.59.1",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.59.1",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.59.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.59.1",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.59.1",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.59.1",
|
||||
"@rollup/rollup-linux-loong64-musl": "4.59.1",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.59.1",
|
||||
"@rollup/rollup-linux-ppc64-musl": "4.59.1",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.59.1",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.59.1",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.59.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.59.1",
|
||||
"@rollup/rollup-linux-x64-musl": "4.59.1",
|
||||
"@rollup/rollup-openbsd-x64": "4.59.1",
|
||||
"@rollup/rollup-openharmony-arm64": "4.59.1",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.59.1",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.59.1",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.59.1",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.59.1",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
|
||||
"integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
|
||||
"license": "BlueOak-1.0.0"
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
|
||||
"integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
@@ -4116,9 +4198,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svgo": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz",
|
||||
"integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz",
|
||||
"integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^11.1.0",
|
||||
@@ -4127,7 +4209,7 @@
|
||||
"css-what": "^6.1.0",
|
||||
"csso": "^5.0.5",
|
||||
"picocolors": "^1.1.1",
|
||||
"sax": "^1.4.1"
|
||||
"sax": "^1.5.0"
|
||||
},
|
||||
"bin": {
|
||||
"svgo": "bin/svgo.js"
|
||||
@@ -4156,9 +4238,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
|
||||
"integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz",
|
||||
"integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -4675,6 +4757,15 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
||||
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zwitch": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "computing-box",
|
||||
"type": "module",
|
||||
"version": "2.0.0",
|
||||
"version": "26.3.3-0.c",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
@@ -9,6 +9,6 @@
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^6.0.0"
|
||||
"astro": "^6.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
public/Microsoft_Nostalgic_Windows_Wallpaper_4k.jpg
Normal file
|
After Width: | Height: | Size: 7.3 MiB |
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -1,9 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||
<style>
|
||||
path { fill: #000; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #FFF; }
|
||||
}
|
||||
</style>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 749 B |
BIN
public/favicon.webp
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
public/images/computing-box-logo-small.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/images/computing-box-logo.webp
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
public/images/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 732 KiB |
BIN
public/images/favicon.webp
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
src/assets/Microsoft_Nostalgic_Windows_Wallpaper_4k.jpg
Normal file
|
After Width: | Height: | Size: 7.3 MiB |
4
src/generated/version.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": "dev",
|
||||
"url": "#"
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
---
|
||||
import "../styles/global.css";
|
||||
import versionInfo from "../generated/version.json";
|
||||
|
||||
const version = versionInfo.version;
|
||||
const releaseUrl = versionInfo.url;
|
||||
const { title = "Computing:Box" } = Astro.props;
|
||||
---
|
||||
|
||||
@@ -29,16 +33,18 @@ const { title = "Computing:Box" } = Astro.props;
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet">
|
||||
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<header class="siteNav">
|
||||
<div class="navInner">
|
||||
<a class="brand" href="/">
|
||||
<img class="brandLogo" src="/images/computing-box-logo.svg" alt="Computing:Box logo" />
|
||||
<img class="brandLogo" src="/images/computing-box-logo-small.webp" alt="Computing:Box logo" />
|
||||
<span class="brandName">Computing:Box</span>
|
||||
</a>
|
||||
|
||||
<nav class="navLinks" aria-label="Site navigation">
|
||||
<a href="/">Home</a>
|
||||
<a href="/about">About</a>
|
||||
<a href="/binary">Binary</a>
|
||||
<a href="/hexadecimal">Hexadecimal</a>
|
||||
@@ -60,8 +66,85 @@ const { title = "Computing:Box" } = Astro.props;
|
||||
<a href="/legal-code" style="margin-left: 32px; color: var(--muted); text-decoration: underline;">Legal Code</a>
|
||||
</div>
|
||||
<div>Computer Science Concept Simulators</div>
|
||||
<div>© {new Date().getFullYear()} Computing:Box • Created with ♥ by Mr A Lyall</div>
|
||||
<div> Version:
|
||||
<a href={releaseUrl} target="_blank" rel="noopener noreferrer">{version}</a> • © {new Date().getFullYear()} Computing:Box • Created with ♥ by Mr A Lyall</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script is:inline>
|
||||
function setupToolboxAccordions() {
|
||||
// Look for cards inside ANY of the three toolboxes!
|
||||
const cards = document.querySelectorAll('.panelCol .card, .pb-toolbox .card, .lg-toolbox .card');
|
||||
if (!cards.length) return;
|
||||
|
||||
// Your primary cards for each page
|
||||
const primaryCardNames = ['settings', 'info', 'components', 'system diagnostics'];
|
||||
|
||||
cards.forEach(card => {
|
||||
const titleEl = card.querySelector('.cardTitle');
|
||||
if (!titleEl) return;
|
||||
|
||||
const titleText = titleEl.textContent.trim().toLowerCase();
|
||||
const isPrimary = primaryCardNames.includes(titleText);
|
||||
|
||||
// 1. DYNAMICALLY WRAP THE CONTENT
|
||||
if (!card.querySelector('.cardBody')) {
|
||||
const body = document.createElement('div');
|
||||
body.className = 'cardBody';
|
||||
const inner = document.createElement('div');
|
||||
inner.className = 'cardBodyInner';
|
||||
body.appendChild(inner);
|
||||
|
||||
Array.from(card.childNodes).forEach(node => {
|
||||
if (node !== titleEl) {
|
||||
inner.appendChild(node);
|
||||
}
|
||||
});
|
||||
card.appendChild(body);
|
||||
}
|
||||
|
||||
// 2. APPLY DEFAULT STATE
|
||||
if (isPrimary) {
|
||||
card.classList.remove('collapsed');
|
||||
} else {
|
||||
card.classList.add('collapsed');
|
||||
}
|
||||
|
||||
// 3. CLICK LISTENERS
|
||||
if (card.dataset.accordionInit) return;
|
||||
card.dataset.accordionInit = "true";
|
||||
|
||||
titleEl.addEventListener('click', () => {
|
||||
const isCollapsing = !card.classList.contains('collapsed');
|
||||
|
||||
if (isCollapsing) {
|
||||
card.classList.add('collapsed');
|
||||
} else {
|
||||
if (isPrimary) {
|
||||
cards.forEach(c => {
|
||||
// Only close cards that share the same parent toolbox
|
||||
if (c !== card && c.closest(card.parentElement.tagName) === card.closest(card.parentElement.tagName)) {
|
||||
c.classList.add('collapsed');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cards.forEach(c => {
|
||||
if (c.closest(card.parentElement.tagName) !== card.closest(card.parentElement.tagName)) return;
|
||||
const cTitle = c.querySelector('.cardTitle')?.textContent.trim().toLowerCase() || '';
|
||||
if (primaryCardNames.includes(cTitle)) {
|
||||
c.classList.add('collapsed');
|
||||
}
|
||||
});
|
||||
}
|
||||
card.classList.remove('collapsed');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setupToolboxAccordions();
|
||||
document.addEventListener('astro:page-load', setupToolboxAccordions);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -6,7 +6,7 @@ import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
<article style="max-width: 1100px; margin: 0 auto; width: 100%;">
|
||||
|
||||
<div style="text-align: center; margin-bottom: 60px;">
|
||||
<img src="/images/computing-box-logo.svg" alt="Computing:Box Logo" style="width: 250px; border-radius: 20px; box-shadow: 0 12px 40px rgba(0,0,0,0.5);" />
|
||||
<img src="/images/computing-box-logo.webp" alt="Computing:Box Logo" style="width: 250px; border-radius: 20px; box-shadow: 0 12px 40px rgba(0,0,0,0.5);" />
|
||||
<h1 class="brandName" style="font-size: 42px; margin-top: 20px; color: var(--text);">The New Computing:Box Experience</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ import "../styles/number-simulators.css";
|
||||
<button class="toolBtn toolSpin toolDec" id="btnDec" type="button" aria-label="Decrement">▼</button>
|
||||
<button class="toolBtn toolSpin toolInc" id="btnInc" type="button" aria-label="Increment">▲</button>
|
||||
</div>
|
||||
<div class="toolRow2">
|
||||
<div class="controlsRow">
|
||||
<button class="btn btnHalf" id="btnShiftLeft" type="button">Left Shift</button>
|
||||
<button class="btn btnHalf" id="btnShiftRight" type="button">Right Shift</button>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@ import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
</div>
|
||||
|
||||
<div style="flex: 1; text-align: right;">
|
||||
<img src="/images/computing-box-logo.svg" alt="Computing Box Logo" style="width: 100%; max-width: 450px; filter: drop-shadow(0 0 50px rgba(40, 240, 122, 0.15));" />
|
||||
<img src="/images/computing-box-logo.webp" alt="Computing Box Logo" style="width: 100%; max-width: 450px; filter: drop-shadow(0 0 50px rgba(40, 240, 122, 0.15));" />
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
@@ -39,11 +39,10 @@ import "../styles/logic-gates.css";
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="cardTitle">Live Truth Table</div>
|
||||
<details open>
|
||||
<summary class="tt-summary">Show / Hide Table</summary>
|
||||
<div style="font-family: var(--ui-font); font-size: 12px; color: var(--muted); margin-bottom: 12px;">Auto-generates based on current wiring. (Max 6 inputs)</div>
|
||||
<div class="tt-table-wrap" id="truthTableContainer"></div>
|
||||
</details>
|
||||
<div style="font-family: var(--ui-font); font-size: 12px; color: var(--muted); margin-bottom: 12px;">Auto-generates based on current wiring.</div>
|
||||
<div id="truthTableContainer"> <div class="tt-table-wrap">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="cardTitle">Tools</div>
|
||||
|
||||
@@ -31,20 +31,25 @@ import "../styles/pc-builder.css";
|
||||
</div>
|
||||
|
||||
<aside id="toolboxPanel" class="pb-toolbox" aria-label="Toolbox">
|
||||
<div class="card">
|
||||
<div class="cardTitle">Inventory</div>
|
||||
<div class="tb-icon-grid" id="toolboxGrid"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="cardTitle">System Diagnostics</div>
|
||||
<div style="font-family: var(--ui-font); font-size: 12px; color: var(--muted); margin-bottom: 12px;">Live pre-flight boot analysis.</div>
|
||||
<div class="specs-panel" id="buildSpecsContainer"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="cardTitle">Inventory</div>
|
||||
<div class="tb-icon-grid" id="toolboxGrid"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="cardTitle">Tools</div>
|
||||
<button class="btn btnReset btnWide" id="btnClearBoard" type="button" style="margin-bottom:0;">Disassemble PC</button>
|
||||
<div style="display:grid; grid-template-columns: 1fr; gap: 8px; margin-bottom: 12px;">
|
||||
<button class="btn btnWide" id="btnAssembleHDD" type="button">Assemble (HDD Build)</button>
|
||||
<button class="btn btnWide" id="btnAssembleSATA" type="button">Assemble (SSD Build)</button>
|
||||
<button class="btn btnWide" id="btnAssembleM2" type="button">Assemble (NVMe Build)</button>
|
||||
</div>
|
||||
<button class="btn btnReset btnWide" id="btnClearBoard" type="button">Disassemble PC</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
|
||||
setRandomRunning(true);
|
||||
const start = Date.now();
|
||||
const durationMs = 1125;
|
||||
const durationMs = 1500;
|
||||
const tickMs = 80;
|
||||
|
||||
randomTimer = setInterval(() => {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
let nextNodeId = 1;
|
||||
let nextWireId = 1;
|
||||
let discoveredStates = new Set();
|
||||
|
||||
// Interaction State
|
||||
let isDraggingNode = null;
|
||||
@@ -200,40 +201,96 @@
|
||||
|
||||
/* --- Truth Table Generation --- */
|
||||
function generateTruthTable() {
|
||||
if (!ttContainer) return;
|
||||
// 1. Find the target container
|
||||
let container = document.getElementById("truthTableContainer");
|
||||
|
||||
const inNodes = Object.values(nodes).filter(n => n.type === 'INPUT').sort((a,b) => a.label.localeCompare(b.label));
|
||||
const outNodes = Object.values(nodes).filter(n => n.type === 'OUTPUT').sort((a,b) => a.label.localeCompare(b.label));
|
||||
// Fail-safe: Find the card if the specific ID is missing
|
||||
if (!container) {
|
||||
const cards = document.querySelectorAll('.card');
|
||||
const ttCard = Array.from(cards).find(c => c.innerText.includes('LIVE TRUTH TABLE'));
|
||||
if (ttCard) {
|
||||
container = ttCard.querySelector('.cardBodyInner') || ttCard;
|
||||
}
|
||||
}
|
||||
|
||||
if (!container) return;
|
||||
|
||||
// 2. Identify and sort Inputs and Outputs
|
||||
const inNodes = Object.values(nodes)
|
||||
.filter(n => n.type === 'INPUT')
|
||||
.sort((a,b) => a.label.localeCompare(b.label));
|
||||
const outNodes = Object.values(nodes)
|
||||
.filter(n => n.type === 'OUTPUT')
|
||||
.sort((a,b) => a.label.localeCompare(b.label));
|
||||
|
||||
// 3. Handle Empty State
|
||||
if (inNodes.length === 0 || outNodes.length === 0) {
|
||||
ttContainer.innerHTML = '<div style="padding: 16px; color: var(--muted); text-align:center;">Add inputs and outputs to generate table.</div>'; return;
|
||||
}
|
||||
if (inNodes.length > 6) {
|
||||
ttContainer.innerHTML = '<div style="padding: 16px; color: var(--muted); text-align:center;">Maximum 6 inputs supported.</div>'; return;
|
||||
container.innerHTML = '<div style="padding: 20px; color: var(--muted); text-align:center; font-family: var(--bit-font); font-size: 12px; letter-spacing: 1px;">CONNECT INPUTS & OUTPUTS</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '<table class="tt-table"><thead><tr>';
|
||||
// 4. Build Table within the styled wrapper
|
||||
let html = '<div class="tt-table-wrap"><table class="tt-table"><thead><tr>';
|
||||
|
||||
// Headers
|
||||
inNodes.forEach(n => html += `<th>${n.label}</th>`);
|
||||
outNodes.forEach(n => html += `<th style="color:var(--text);">${n.label}</th>`);
|
||||
outNodes.forEach(n => html += `<th style="color:var(--text); border-left: 1px solid rgba(255,255,255,0.1);">${n.label}</th>`);
|
||||
html += '</tr></thead><tbody>';
|
||||
|
||||
// 5. Generate Rows
|
||||
const numRows = Math.pow(2, inNodes.length);
|
||||
for (let i = 0; i < numRows; i++) {
|
||||
let override = {};
|
||||
inNodes.forEach((n, idx) => { override[n.id] = ((i >> (inNodes.length - 1 - idx)) & 1) === 1; });
|
||||
let outStates = evaluateGraph(override);
|
||||
let stateArr = [];
|
||||
|
||||
// Calculate binary state for this row
|
||||
inNodes.forEach((n, idx) => {
|
||||
let val = ((i >> (inNodes.length - 1 - idx)) & 1) === 1;
|
||||
override[n.id] = val;
|
||||
stateArr.push(val ? '1' : '0');
|
||||
});
|
||||
|
||||
let stateStr = stateArr.join('');
|
||||
let isFound = discoveredStates.has(stateStr);
|
||||
let outResults = evaluateGraph(override); // Simulate the board logic for this state
|
||||
|
||||
html += '<tr>';
|
||||
inNodes.forEach(n => { let val = override[n.id]; html += `<td class="${val ? 'tt-on' : ''}">${val ? 1 : 0}</td>`; });
|
||||
outNodes.forEach(n => { let val = outStates[n.id]; html += `<td class="${val ? 'tt-on' : ''}" style="font-weight:bold;">${val ? 1 : 0}</td>`; });
|
||||
|
||||
// Input Cells
|
||||
inNodes.forEach(n => {
|
||||
let v = override[n.id];
|
||||
html += `<td class="${v ? 'tt-on' : ''}">${v ? 1 : 0}</td>`;
|
||||
});
|
||||
|
||||
// Output Cells (Discovery Logic)
|
||||
outNodes.forEach(n => {
|
||||
if (isFound) {
|
||||
let v = outResults[n.id];
|
||||
html += `<td class="${v ? 'tt-on' : ''}" style="font-weight:bold; border-left: 1px solid rgba(255,255,255,0.05);">${v ? 1 : 0}</td>`;
|
||||
} else {
|
||||
html += `<td style="color: #444; border-left: 1px solid rgba(255,255,255,0.05); opacity: 0.6;">?</td>`;
|
||||
}
|
||||
});
|
||||
html += '</tr>';
|
||||
}
|
||||
html += '</tbody></table>';
|
||||
ttContainer.innerHTML = html;
|
||||
|
||||
html += '</tbody></table></div>';
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
function runSimulation() {
|
||||
function runSimulation(topologyChanged = false) {
|
||||
// If you add/remove wires, reset the table memory because the logic changed
|
||||
if (topologyChanged) discoveredStates.clear();
|
||||
|
||||
evaluateGraph();
|
||||
|
||||
// Check the current board state (e.g., "10") and save it to memory
|
||||
const inNodes = Object.values(nodes).filter(n => n.type === 'INPUT').sort((a,b) => a.label.localeCompare(b.label));
|
||||
if (inNodes.length > 0) {
|
||||
let currentStateStr = inNodes.map(n => n.value ? '1' : '0').join('');
|
||||
discoveredStates.add(currentStateStr);
|
||||
}
|
||||
|
||||
renderWires();
|
||||
generateTruthTable();
|
||||
}
|
||||
@@ -289,18 +346,17 @@
|
||||
node.el = el;
|
||||
|
||||
if (node.type === 'INPUT') {
|
||||
el.querySelector('.switch').addEventListener('click', (e) => {
|
||||
const dist = Math.hypot(e.clientX - clickStartX, e.clientY - clickStartY);
|
||||
if (dist > 3) {
|
||||
e.preventDefault(); // Prevents toggle if it was a drag motion
|
||||
} else {
|
||||
const sw = el.querySelector('.switch');
|
||||
sw.addEventListener('click', (e) => {
|
||||
// ... (keep your clickStartX/Y drag check) ...
|
||||
|
||||
node.value = !node.value;
|
||||
el.querySelector('.switch').classList.toggle('active-sim', node.value);
|
||||
el.querySelector('.slider').style.background = node.value ? 'rgba(40,240,122,.25)' : '';
|
||||
el.querySelector('.slider').style.borderColor = node.value ? 'rgba(40,240,122,.30)' : '';
|
||||
el.querySelector('.slider').innerHTML = node.value ? `<style>#logicPage [data-id="${node.id}"] .slider::before { transform: translateX(28px); }</style>` : '';
|
||||
|
||||
// This targets the exact class your CSS needs for the glow and move
|
||||
sw.classList.toggle('active-sim', node.value);
|
||||
|
||||
// This ensures the table and logic update
|
||||
runSimulation();
|
||||
}
|
||||
});
|
||||
}
|
||||
return el;
|
||||
@@ -312,7 +368,8 @@
|
||||
if (type === 'OUTPUT') label = getNextOutputLabel();
|
||||
if (type === 'GATE') label = gateType;
|
||||
|
||||
const id = `node_${nextNodeId++}`;
|
||||
// Double check this line in logicGates.js
|
||||
const id = `node_${Date.now()}_${nextNodeId++}`;
|
||||
const offset = Math.floor(Math.random() * 40);
|
||||
const x = dropX !== null ? dropX : (type === 'INPUT' ? 50 : (type === 'OUTPUT' ? 600 : 300) + offset);
|
||||
const y = dropY !== null ? dropY : 150 + offset;
|
||||
@@ -320,7 +377,8 @@
|
||||
const node = { id, type, gateType, label, x, y, value: false, el: null };
|
||||
nodes[id] = node;
|
||||
createNodeElement(node);
|
||||
runSimulation();
|
||||
// Change the very last line to:
|
||||
runSimulation(true);
|
||||
}
|
||||
|
||||
/* --- Global Interaction Handlers --- */
|
||||
@@ -433,8 +491,9 @@
|
||||
connections.push({ id: `conn_${nextWireId++}`, fromNode: wiringStart.node, fromPort: 'out', toNode: targetNodeId, toPort: targetPortId });
|
||||
}
|
||||
}
|
||||
// Change the very last line of the if(wiringStart) block to:
|
||||
wiringStart = null; tempWirePath = null;
|
||||
runSimulation();
|
||||
runSimulation(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -451,7 +510,8 @@
|
||||
viewport.removeChild(nodes[selectedNodeId].el);
|
||||
}
|
||||
delete nodes[selectedNodeId];
|
||||
clearSelection(); runSimulation();
|
||||
// Change the two deletion triggers to:
|
||||
clearSelection(); runSimulation(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -473,8 +533,15 @@
|
||||
/* --- Init --- */
|
||||
btnClearBoard?.addEventListener('click', () => {
|
||||
viewport.querySelectorAll('.lg-node').forEach(el => el.remove());
|
||||
nodes = {}; connections = [];
|
||||
runSimulation();
|
||||
|
||||
// Target your specific SVG layer class
|
||||
const svgLayer = document.querySelector('.lg-svg-layer');
|
||||
if (svgLayer) svgLayer.innerHTML = '';
|
||||
|
||||
nodes = {};
|
||||
connections = [];
|
||||
discoveredStates.clear();
|
||||
runSimulation(true);
|
||||
});
|
||||
|
||||
toolboxToggle?.addEventListener("click", () => {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
const toolboxToggle = document.getElementById("toolboxToggle");
|
||||
const pcPage = document.getElementById("pcPage");
|
||||
|
||||
/* --- Extensive PC Component Library --- */
|
||||
/* --- ULTRA-REALISTIC COMPONENT LIBRARY --- */
|
||||
const PC_PARTS = {
|
||||
'CASE': {
|
||||
name: 'ATX PC Case', w: 600, h: 550, z: 5, ports: [],
|
||||
@@ -29,8 +29,7 @@
|
||||
name: 'Motherboard', w: 360, h: 400, z: 10,
|
||||
ports: [
|
||||
{ id: 'atx_pwr', x: 340, y: 150 }, { id: 'sata1', x: 340, y: 300 }, { id: 'sata2', x: 340, y: 330 },
|
||||
{ id: 'usb1', x: 10, y: 40 }, { id: 'usb2', x: 10, y: 70 }, { id: 'usb3', x: 10, y: 100 }, { id: 'usb4', x: 10, y: 130 },
|
||||
{ id: 'audio', x: 10, y: 170 }, { id: 'disp', x: 10, y: 210 }
|
||||
{ id: 'usb1', x: 10, y: 40 }, { id: 'usb2', x: 10, y: 70 }, { id: 'audio', x: 10, y: 170 }, { id: 'disp', x: 10, y: 210 }
|
||||
],
|
||||
slots: {
|
||||
'CPU1': { x: 120, y: 40, accepts: 'CPU' },
|
||||
@@ -40,21 +39,56 @@
|
||||
'M2_1': { x: 120, y: 170, accepts: 'M2_SSD' }, 'M2_2': { x: 120, y: 250, accepts: 'M2_SSD' },
|
||||
'PCIE1': { x: 40, y: 200, accepts: 'GPU' }, 'PCIE2': { x: 40, y: 300, accepts: 'GPU' }
|
||||
},
|
||||
// Uses a lighter slate grey #2C303A to stand out from the case
|
||||
svg: `<rect width="360" height="400" fill="#2C303A" rx="8" stroke="#4b5060" stroke-width="3"/><rect x="120" y="40" width="80" height="80" fill="#1f2229" stroke="#4b5060"/><rect x="230" y="30" width="15" height="100" fill="#1f2229"/><rect x="250" y="30" width="15" height="100" fill="#1f2229"/><rect x="270" y="30" width="15" height="100" fill="#1f2229"/><rect x="290" y="30" width="15" height="100" fill="#1f2229"/><rect x="40" y="200" width="280" height="15" fill="#15171c"/><rect x="40" y="300" width="280" height="15" fill="#15171c"/><rect x="120" y="170" width="80" height="15" fill="#1f2229"/><rect x="120" y="250" width="80" height="15" fill="#1f2229"/>`
|
||||
svg: `<rect width="360" height="400" fill="#2C303A" rx="8" stroke="#4b5060" stroke-width="3"/><rect x="120" y="40" width="80" height="80" fill="#1f2229" stroke="#4b5060"/><rect x="230" y="30" width="15" height="100" fill="#1f2229"/><rect x="250" y="30" width="15" height="100" fill="#1f2229"/><rect x="270" y="30" width="15" height="100" fill="#1f2229"/><rect x="290" y="30" width="15" height="100" fill="#1f2229"/><rect x="40" y="200" width="280" height="15" fill="#15171c"/><rect x="40" y="300" width="280" height="15" fill="#15171c"/><rect x="120" y="170" width="80" height="15" fill="#1f2229" stroke="#4b5060" stroke-dasharray="2 2"/><text x="160" y="182" fill="#555" font-size="10" font-family="sans-serif" text-anchor="middle">M.2_1</text><rect x="120" y="250" width="80" height="15" fill="#1f2229" stroke="#4b5060" stroke-dasharray="2 2"/><text x="160" y="262" fill="#555" font-size="10" font-family="sans-serif" text-anchor="middle">M.2_2</text>`
|
||||
},
|
||||
'CPU': { name: 'Processor', w: 80, h: 80, z: 20, ports: [], slots: {}, svg: `<rect width="80" height="80" fill="#0b381a"/><rect x="10" y="10" width="60" height="60" rx="4" fill="#d4d4d4"/><polygon points="5,75 15,75 5,65" fill="#ffd700"/><text x="40" y="45" fill="#555" font-family="sans-serif" font-size="14" font-weight="bold" text-anchor="middle">CPU</text>` },
|
||||
'COOLER': { name: 'CPU Fan', w: 120, h: 120, z: 30, ports: [], slots: {}, svg: `<rect width="120" height="120" rx="60" fill="#1a1c23" stroke="#aaa" stroke-width="3"/><circle cx="60" cy="60" r="50" fill="#111"/><path d="M60,15 A45,45 0 0,1 105,60 L60,60 Z" fill="#444"/><path d="M105,60 A45,45 0 0,1 60,105 L60,60 Z" fill="#555"/><path d="M60,105 A45,45 0 0,1 15,60 L60,60 Z" fill="#444"/><path d="M15,60 A45,45 0 0,1 60,15 L60,60 Z" fill="#555"/><circle cx="60" cy="60" r="20" fill="#222"/>` },
|
||||
'RAM': { name: 'DDR4 Memory', w: 15, h: 100, z: 20, ports: [], slots: {}, svg: `<rect width="15" height="100" fill="#111"/><rect x="2" y="5" width="11" height="80" fill="#2a2a2a"/><rect x="0" y="90" width="15" height="10" fill="#ffd700"/>` },
|
||||
'GPU': { name: 'Graphics Card', w: 280, h: 60, z: 40, slots: {}, ports: [{ id: 'pwr_in', x: 270, y: 10 }, { id: 'disp_out', x: 10, y: 30 }], svg: `<rect width="280" height="60" rx="5" fill="#1a1a1a"/><circle cx="70" cy="30" r="22" fill="#111" stroke="#333" stroke-width="2"/><circle cx="140" cy="30" r="22" fill="#111" stroke="#333" stroke-width="2"/><circle cx="210" cy="30" r="22" fill="#111" stroke="#333" stroke-width="2"/><rect x="20" y="55" width="80" height="5" fill="#ffd700"/>` },
|
||||
'M2_SSD': { name: 'M.2 NVMe SSD', w: 80, h: 15, z: 20, ports: [], slots: {}, svg: `<rect width="80" height="15" rx="1" fill="#000"/><rect x="10" y="2" width="20" height="11" fill="#1a1a1a"/><rect x="35" y="2" width="20" height="11" fill="#1a1a1a"/><rect x="60" y="2" width="10" height="11" fill="#ccc"/><rect x="0" y="0" width="4" height="15" fill="#ffd700"/>` },
|
||||
'SATA_SSD': { name: '2.5" SATA SSD', w: 100, h: 70, z: 20, slots: {}, ports: [{id:'data', x:90, y:20}, {id:'pwr', x:90, y:50}], svg: `<rect width="100" height="70" fill="#111" rx="4" stroke="#444"/><rect x="10" y="10" width="80" height="50" fill="#1a1a1a" rx="2" stroke="#222"/><text x="50" y="40" fill="#888" font-family="sans-serif" font-size="14" font-weight="bold" text-anchor="middle">SSD</text>` },
|
||||
'HDD': { name: '3.5" Mech HDD', w: 120, h: 140, z: 20, slots: {}, ports: [{id:'data', x:110, y:20}, {id:'pwr', x:110, y:120}], svg: `<rect width="120" height="140" fill="#d0d0d0" rx="4" stroke="#888"/><rect x="10" y="10" width="100" height="100" fill="#e0e0e0" rx="50"/><circle cx="60" cy="60" r="35" fill="#ddd" stroke="#aaa"/><circle cx="60" cy="60" r="10" fill="#999"/><rect x="30" y="120" width="60" height="10" fill="#111"/>` },
|
||||
'PSU': { name: 'Power Supply', w: 160, h: 90, z: 20, slots: {}, ports: [{id:'out1',x:150,y:20}, {id:'out2',x:150,y:40}, {id:'out3',x:150,y:60}, {id:'out4',x:150,y:80}], svg: `<rect width="160" height="90" rx="4" fill="#1a1a1a" stroke="#333" stroke-width="2"/><circle cx="80" cy="45" r="35" fill="#0a0a0a" stroke="#222" stroke-width="2"/><line x1="80" y1="10" x2="80" y2="80" stroke="#333" stroke-width="2"/><line x1="45" y1="45" x2="115" y2="45" stroke="#333" stroke-width="2"/><circle cx="80" cy="45" r="10" fill="#222"/>` },
|
||||
'MONITOR': { name: 'Monitor', w: 240, h: 160, z: 30, slots: {}, ports: [{id:'disp', x:120, y:140}], svg: `<rect width="240" height="160" fill="#111" rx="5"/><rect x="10" y="10" width="220" height="120" fill="#000"/><rect x="100" y="140" width="40" height="20" fill="#222"/><rect x="60" y="150" width="120" height="10" fill="#222"/>` },
|
||||
'KEYBOARD': { name: 'Keyboard', w: 180, h: 60, z: 30, slots: {}, ports: [{id:'usb', x:90, y:10}], svg: `<rect width="180" height="60" fill="#111" rx="3"/><rect x="5" y="5" width="170" height="50" fill="#222" rx="2" stroke="#333" stroke-dasharray="8 8"/>` },
|
||||
'MOUSE': { name: 'Mouse', w: 30, h: 50, z: 30, slots: {}, ports: [{id:'usb', x:15, y:5}], svg: `<rect width="30" height="50" fill="#111" rx="15"/><line x1="15" y1="0" x2="15" y2="20" stroke="#333" stroke-width="2"/><circle cx="15" cy="15" r="4" fill="#333"/>` },
|
||||
'SPEAKER': { name: 'Speakers', w: 40, h: 80, z: 30, slots: {}, ports: [{id:'audio', x:20, y:10}], svg: `<rect width="40" height="80" fill="#111" rx="4"/><circle cx="20" cy="25" r="12" fill="#222"/><circle cx="20" cy="60" r="16" fill="#222"/>` }
|
||||
'CPU': {
|
||||
name: 'Processor', w: 80, h: 80, z: 20, ports: [], slots: {},
|
||||
svg: `<rect width="80" height="80" fill="#0c4a22" rx="4"/><rect x="2" y="2" width="76" height="76" fill="none" stroke="#ffd700" stroke-width="1" stroke-dasharray="2 4"/><rect x="12" y="12" width="56" height="56" fill="#e0e4e8" rx="6" stroke="#b0b5b9" stroke-width="2"/><text x="40" y="35" fill="#666" font-family="sans-serif" font-size="10" font-weight="900" text-anchor="middle">INTEL</text><text x="40" y="50" fill="#555" font-family="sans-serif" font-size="16" font-weight="900" text-anchor="middle">CORE i9</text><text x="40" y="60" fill="#777" font-family="sans-serif" font-size="7" font-weight="bold" text-anchor="middle">14900K</text><polygon points="5,75 15,75 5,65" fill="#ffd700"/>`
|
||||
},
|
||||
'COOLER': {
|
||||
name: 'Liquid AIO', w: 120, h: 120, z: 30, ports: [], slots: {},
|
||||
svg: `<circle cx="60" cy="60" r="55" fill="#15171e" stroke="#2d313d" stroke-width="4"/><circle cx="60" cy="60" r="45" fill="#050505"/><text x="60" y="55" fill="#28f07a" font-family="var(--num-font)" font-size="20" font-weight="bold" text-anchor="middle">32°C</text><text x="60" y="75" fill="#55aaff" font-family="var(--ui-font)" font-size="10" text-anchor="middle">2400 RPM</text><path d="M 110 40 Q 140 40 140 10 M 110 80 Q 150 80 150 110" fill="none" stroke="#111" stroke-width="12" stroke-linecap="round"/><circle cx="60" cy="60" r="50" fill="none" stroke="cyan" stroke-width="2" opacity="0.8"/>`
|
||||
},
|
||||
'RAM': {
|
||||
name: 'RGB Memory', w: 15, h: 100, z: 20, ports: [], slots: {},
|
||||
svg: `<rect width="15" height="100" fill="#111" rx="2"/><rect x="0" y="90" width="15" height="10" fill="#ffd700"/><rect x="0" y="94" width="15" height="1" fill="#b8860b"/><path d="M -2 15 L 17 15 L 17 85 L -2 85 Z" fill="#2d313d" stroke="#111"/><path d="M 0 20 L 15 30 L 15 80 L 0 70 Z" fill="#1a1c23"/><path d="M -2 2 L 17 2 L 17 15 L -2 15 Z" fill="#ff0055"/><path d="M 0 2 L 5 10 L 10 2 L 15 10" fill="none" stroke="#fff" stroke-width="1" opacity="0.5"/>`
|
||||
},
|
||||
'GPU': {
|
||||
name: 'Graphics Card', w: 280, h: 80, z: 40, slots: {}, ports: [{ id: 'pwr_in', x: 270, y: 10 }, { id: 'disp_out', x: 10, y: 40 }],
|
||||
svg: `<rect width="280" height="80" rx="8" fill="#15171e" stroke="#333742" stroke-width="2"/><rect x="5" y="5" width="270" height="70" rx="6" fill="#0f1015"/><path d="M 20 5 L 60 75 M 110 5 L 150 75 M 200 5 L 240 75" stroke="#1a1c23" stroke-width="4"/><g transform="translate(50, 40)"><circle r="32" fill="#111" stroke="#2d313d" stroke-width="2"/><circle r="10" fill="#222"/><path d="M0 -10 L15 -28 L25 -20 Z M0 10 L-15 28 L-25 20 Z M-10 0 L-28 -15 L-20 -25 Z M10 0 L28 15 L20 25 Z" fill="#1a1c23"/></g><g transform="translate(140, 40)"><circle r="32" fill="#111" stroke="#2d313d" stroke-width="2"/><circle r="10" fill="#222"/><path d="M0 -10 L15 -28 L25 -20 Z M0 10 L-15 28 L-25 20 Z M-10 0 L-28 -15 L-20 -25 Z M10 0 L28 15 L20 25 Z" fill="#1a1c23"/></g><g transform="translate(230, 40)"><circle r="32" fill="#111" stroke="#2d313d" stroke-width="2"/><circle r="10" fill="#222"/><path d="M0 -10 L15 -28 L25 -20 Z M0 10 L-15 28 L-25 20 Z M-10 0 L-28 -15 L-20 -25 Z M10 0 L28 15 L20 25 Z" fill="#1a1c23"/></g><rect x="20" y="80" width="160" height="8" fill="#ffd700" rx="2"/><rect x="100" y="32" width="80" height="16" fill="#000" rx="2" opacity="0.8"/><text x="140" y="43" fill="#28f07a" font-family="sans-serif" font-size="10" font-weight="900" text-anchor="middle">GEFORCE RTX</text>`
|
||||
},
|
||||
'M2_SSD': {
|
||||
name: 'M.2 NVMe SSD', w: 80, h: 22, z: 20, ports: [], slots: {},
|
||||
svg: `<rect width="80" height="22" fill="#111" rx="2"/><rect x="0" y="0" width="5" height="22" fill="#ffd700"/><rect x="3" y="14" width="3" height="4" fill="#111"/><rect x="15" y="4" width="18" height="14" fill="#1a1c23" rx="1"/><rect x="38" y="4" width="18" height="14" fill="#1a1c23" rx="1"/><rect x="60" y="6" width="10" height="10" fill="#2d313d" rx="1"/><rect x="10" y="8" width="50" height="6" fill="#fff" opacity="0.8"/><text x="35" y="13" fill="#000" font-family="sans-serif" font-size="4" font-weight="bold" text-anchor="middle">990 PRO 2TB</text><circle cx="76" cy="11" r="3" fill="#222"/>`
|
||||
},
|
||||
'SATA_SSD': {
|
||||
name: '2.5" SATA SSD', w: 100, h: 70, z: 20, slots: {}, ports: [{id:'data', x:90, y:20}, {id:'pwr', x:90, y:50}],
|
||||
svg: `<rect width="100" height="70" fill="#1a1c23" rx="4" stroke="#4b5162" stroke-width="1"/><rect x="2" y="2" width="96" height="66" fill="#2d313d" rx="2"/><rect x="15" y="15" width="70" height="40" fill="#111" rx="2"/><rect x="15" y="45" width="70" height="10" fill="#e74c3c"/><text x="50" y="35" fill="#fff" font-family="sans-serif" font-size="14" font-weight="900" text-anchor="middle" letter-spacing="1px">SAMSUNG</text><circle cx="5" cy="5" r="1.5" fill="#111"/><circle cx="95" cy="5" r="1.5" fill="#111"/><circle cx="5" cy="65" r="1.5" fill="#111"/><circle cx="95" cy="65" r="1.5" fill="#111"/>`
|
||||
},
|
||||
'HDD': {
|
||||
name: '3.5" Mech HDD', w: 120, h: 140, z: 20, slots: {}, ports: [{id:'data', x:110, y:20}, {id:'pwr', x:110, y:120}],
|
||||
svg: `<rect width="120" height="140" fill="#bdc3c7" rx="4" stroke="#7f8c8d" stroke-width="2"/><path d="M 5 5 L 115 5 L 115 110 C 80 120, 40 120, 5 110 Z" fill="#e0e4e8" stroke="#95a5a6" stroke-width="1"/><circle cx="60" cy="55" r="45" fill="none" stroke="#bdc3c7" stroke-width="2"/><circle cx="60" cy="55" r="12" fill="#bdc3c7" stroke="#95a5a6"/><circle cx="100" cy="100" r="8" fill="#bdc3c7" stroke="#95a5a6"/><path d="M 100 100 L 70 60" stroke="#7f8c8d" stroke-width="6" stroke-linecap="round"/><rect x="30" y="80" width="60" height="30" fill="#fff" rx="2"/><text x="60" y="92" fill="#000" font-family="sans-serif" font-size="8" font-weight="bold" text-anchor="middle">WD BLACK</text><text x="60" y="102" fill="#333" font-family="sans-serif" font-size="6" text-anchor="middle">12TB HDD</text><rect x="20" y="120" width="80" height="15" fill="#0b3d21" rx="2"/>`
|
||||
},
|
||||
'PSU': {
|
||||
name: 'Power Supply', w: 160, h: 90, z: 20, slots: {}, ports: [{id:'out1',x:150,y:20}, {id:'out2',x:150,y:40}, {id:'out3',x:150,y:60}, {id:'out4',x:150,y:80}],
|
||||
svg: `<rect width="160" height="90" fill="#15171e" rx="4" stroke="#333742" stroke-width="2"/><rect x="40" y="5" width="80" height="80" fill="#0a0a0a" rx="40"/><circle cx="80" cy="45" r="38" fill="none" stroke="#2d313d" stroke-width="2"/><circle cx="80" cy="45" r="28" fill="none" stroke="#2d313d" stroke-width="2"/><circle cx="80" cy="45" r="18" fill="none" stroke="#2d313d" stroke-width="2"/><path d="M 80 5 L 80 85 M 40 45 L 120 45 M 52 20 L 108 70 M 108 20 L 52 70" stroke="#2d313d" stroke-width="2"/><circle cx="80" cy="45" r="8" fill="#111" stroke="#e74c3c"/><rect x="145" y="10" width="15" height="70" fill="#0a0a0a"/><rect x="5" y="15" width="25" height="60" fill="#333742" rx="2"/><text x="17" y="45" fill="#fff" font-family="sans-serif" font-size="10" font-weight="bold" text-anchor="middle" transform="rotate(-90 17,45)">1200W</text>`
|
||||
},
|
||||
'MONITOR': {
|
||||
name: 'Monitor', w: 240, h: 180, z: 30, slots: {}, ports: [{id:'disp', x:120, y:140}],
|
||||
svg: `<rect width="240" height="160" fill="#1a1a1a" rx="6" stroke="#333"/><rect x="8" y="8" width="224" height="124" fill="#000" id="screen-bg"/><g id="boot-content"></g><rect x="6" y="140" width="228" height="15" fill="#1a1c23"/><text x="120" y="150" fill="#fff" font-family="sans-serif" font-size="6" text-anchor="middle">ASUS</text><circle cx="220" cy="147" r="2" fill="#28f07a"/><path d="M 100 160 L 110 180 L 130 180 L 140 160 Z" fill="#222"/><rect x="80" y="180" width="80" height="5" fill="#333" rx="2"/>`
|
||||
},
|
||||
'KEYBOARD': {
|
||||
name: 'Keyboard', w: 180, h: 60, z: 30, slots: {}, ports: [{id:'usb', x:90, y:10}],
|
||||
svg: `<rect width="180" height="60" fill="#15171e" rx="4" stroke="#2d313d" stroke-width="2"/><rect x="0" y="45" width="180" height="15" fill="#111" rx="2"/><rect x="5" y="5" width="170" height="36" fill="#0a0a0a" rx="2"/><g fill="#222" stroke="#111" stroke-width="1"><rect x="8" y="8" width="10" height="10" rx="2"/><rect x="20" y="8" width="10" height="10" rx="2"/><rect x="32" y="8" width="10" height="10" rx="2"/><rect x="44" y="8" width="10" height="10" rx="2"/><rect x="56" y="8" width="10" height="10" rx="2"/><rect x="68" y="8" width="10" height="10" rx="2"/><rect x="80" y="8" width="10" height="10" rx="2"/><rect x="92" y="8" width="10" height="10" rx="2"/><rect x="104" y="8" width="10" height="10" rx="2"/><rect x="116" y="8" width="10" height="10" rx="2"/><rect x="128" y="8" width="10" height="10" rx="2"/><rect x="140" y="8" width="18" height="10" rx="2"/><rect x="8" y="20" width="14" height="10" rx="2"/><rect x="24" y="20" width="10" height="10" rx="2"/><rect x="36" y="20" width="10" height="10" rx="2"/><rect x="48" y="20" width="10" height="10" rx="2"/><rect x="60" y="20" width="10" height="10" rx="2"/><rect x="72" y="20" width="10" height="10" rx="2"/><rect x="84" y="20" width="10" height="10" rx="2"/><rect x="96" y="20" width="10" height="10" rx="2"/><rect x="108" y="20" width="10" height="10" rx="2"/><rect x="120" y="20" width="10" height="10" rx="2"/><rect x="132" y="20" width="26" height="10" rx="2"/></g><rect x="56" y="32" width="60" height="10" fill="#222" stroke="#111" rx="2"/><rect x="4" y="4" width="172" height="38" fill="none" stroke="cyan" stroke-width="1" opacity="0.3"/>`
|
||||
},
|
||||
'MOUSE': {
|
||||
name: 'Mouse', w: 30, h: 54, z: 30, slots: {}, ports: [{id:'usb', x:15, y:5}],
|
||||
svg: `<rect width="30" height="54" fill="#15171e" rx="15" stroke="#2d313d" stroke-width="2"/><path d="M 15 0 L 15 20 M 5 25 Q 15 30 25 25" stroke="#0a0a0a" stroke-width="2" fill="none"/><rect x="13" y="6" width="4" height="10" fill="#111" rx="2"/><rect x="14" y="7" width="2" height="8" fill="#28f07a"/><path d="M 10 45 Q 15 50 20 45" stroke="cyan" stroke-width="2" fill="none" opacity="0.8"/><path d="M 0 15 Q 4 25 0 35 M 30 15 Q 26 25 30 35" stroke="#111" stroke-width="2" fill="none"/>`
|
||||
},
|
||||
'SPEAKER': {
|
||||
name: 'Speakers', w: 46, h: 90, z: 30, slots: {}, ports: [{id:'audio', x:23, y:10}],
|
||||
svg: `<rect width="46" height="90" fill="#1a1c23" rx="4" stroke="#333742" stroke-width="2"/><rect x="4" y="4" width="38" height="82" fill="#111" rx="2"/><circle cx="23" cy="22" r="10" fill="#2d313d" stroke="#0a0a0a" stroke-width="2"/><circle cx="23" cy="22" r="4" fill="#15171e"/><circle cx="23" cy="58" r="16" fill="#2d313d" stroke="#0a0a0a" stroke-width="3"/><circle cx="23" cy="58" r="6" fill="#15171e"/><circle cx="23" cy="80" r="4" fill="#000"/>`
|
||||
}
|
||||
};
|
||||
|
||||
let nodes = {};
|
||||
@@ -66,27 +100,22 @@
|
||||
let selectedWireId = null, selectedNodeId = null;
|
||||
|
||||
let panX = 0, panY = 0, zoom = 1;
|
||||
let isPanning = false, panStart = { x: 0, y: 0 };
|
||||
let isPanning = false, panStart = { x: 0, y: 0 }, isSystemBooted = false;
|
||||
|
||||
/* --- Setup Toolbox --- */
|
||||
/* --- Toolbox & Base Init --- */
|
||||
function initToolbox() {
|
||||
if(!toolboxGrid) return;
|
||||
let html = '';
|
||||
Object.keys(PC_PARTS).forEach(partKey => {
|
||||
html += `
|
||||
<div draggable="true" data-spawn="${partKey}" class="drag-item tb-icon-box" title="${PC_PARTS[partKey].name}">
|
||||
html += `<div draggable="true" data-spawn="${partKey}" class="drag-item tb-icon-box" title="${PC_PARTS[partKey].name}">
|
||||
<svg viewBox="0 0 ${PC_PARTS[partKey].w} ${PC_PARTS[partKey].h}" style="max-width:80%; max-height:40px; pointer-events:none;">${PC_PARTS[partKey].svg}</svg>
|
||||
<div class="tb-icon-label">${partKey}</div>
|
||||
</div>
|
||||
`;
|
||||
<div class="tb-icon-label">${partKey}</div></div>`;
|
||||
});
|
||||
toolboxGrid.innerHTML = html;
|
||||
document.querySelectorAll('.drag-item').forEach(item => {
|
||||
item.addEventListener('dragstart', (e) => { e.dataTransfer.setData('spawnType', item.dataset.spawn); });
|
||||
});
|
||||
document.querySelectorAll('.drag-item').forEach(item => { item.addEventListener('dragstart', (e) => { e.dataTransfer.setData('spawnType', item.dataset.spawn); }); });
|
||||
}
|
||||
|
||||
/* --- Camera Math --- */
|
||||
/* --- Viewport Math --- */
|
||||
function updateViewport() {
|
||||
viewport.style.transform = `translate(${panX}px, ${panY}px) scale(${zoom})`;
|
||||
workspace.style.backgroundSize = `${32 * zoom}px ${32 * zoom}px`;
|
||||
@@ -94,63 +123,58 @@
|
||||
}
|
||||
function zoomWorkspace(factor, mouseX, mouseY) {
|
||||
const newZoom = Math.min(Math.max(0.1, zoom * factor), 2);
|
||||
panX = mouseX - (mouseX - panX) * (newZoom / zoom);
|
||||
panY = mouseY - (mouseY - panY) * (newZoom / zoom);
|
||||
panX = mouseX - (mouseX - panX) * (newZoom / zoom); panY = mouseY - (mouseY - panY) * (newZoom / zoom);
|
||||
zoom = newZoom; updateViewport();
|
||||
}
|
||||
function getPortCoords(nodeId, portDataAttr) {
|
||||
const node = nodes[nodeId];
|
||||
if (!node || !node.el) return {x:0, y:0};
|
||||
const portEl = node.el.querySelector(`[data-port="${portDataAttr}"]`);
|
||||
if (!portEl) return {x:0, y:0};
|
||||
const wsRect = workspace.getBoundingClientRect();
|
||||
const portRect = portEl.getBoundingClientRect();
|
||||
return {
|
||||
x: (portRect.left - wsRect.left - panX + portRect.width / 2) / zoom,
|
||||
y: (portRect.top - wsRect.top - panY + portRect.height / 2) / zoom
|
||||
};
|
||||
}
|
||||
function drawBezier(x1, y1, x2, y2) {
|
||||
const cpDist = Math.abs(x2 - x1) * 0.6 + 20;
|
||||
return `M ${x1} ${y1} C ${x1 + cpDist} ${y1}, ${x2 - cpDist} ${y2}, ${x2} ${y2}`;
|
||||
const node = nodes[nodeId]; if (!node || !node.el) return {x:0, y:0};
|
||||
const portEl = node.el.querySelector(`[data-port="${portDataAttr}"]`); if (!portEl) return {x:0, y:0};
|
||||
const wsRect = workspace.getBoundingClientRect(); const portRect = portEl.getBoundingClientRect();
|
||||
return { x: (portRect.left - wsRect.left - panX + portRect.width / 2) / zoom, y: (portRect.top - wsRect.top - panY + portRect.height / 2) / zoom };
|
||||
}
|
||||
function drawBezier(x1, y1, x2, y2) { const cpDist = Math.abs(x2 - x1) * 0.6 + 20; return `M ${x1} ${y1} C ${x1 + cpDist} ${y1}, ${x2 - cpDist} ${y2}, ${x2} ${y2}`; }
|
||||
|
||||
/* --- Rendering --- */
|
||||
function renderWires() {
|
||||
let svgHTML = '';
|
||||
connections.forEach(conn => {
|
||||
const from = getPortCoords(conn.fromNode, conn.fromPort);
|
||||
const to = getPortCoords(conn.toNode, conn.toPort);
|
||||
const isSelected = conn.id === selectedWireId;
|
||||
svgHTML += `<path class="pb-wire active ${isSelected ? 'selected' : ''}" d="${drawBezier(from.x, from.y, to.x, to.y)}" data-conn-id="${conn.id}" />`;
|
||||
const from = getPortCoords(conn.fromNode, conn.fromPort); const to = getPortCoords(conn.toNode, conn.toPort);
|
||||
svgHTML += `<path class="pb-wire active ${conn.id === selectedWireId ? 'selected' : ''}" d="${drawBezier(from.x, from.y, to.x, to.y)}" data-conn-id="${conn.id}" />`;
|
||||
});
|
||||
if (wiringStart && tempWirePath) {
|
||||
svgHTML += `<path class="pb-wire pb-wire-temp" d="${drawBezier(wiringStart.x, wiringStart.y, tempWirePath.x, tempWirePath.y)}" />`;
|
||||
}
|
||||
if (wiringStart && tempWirePath) svgHTML += `<path class="pb-wire pb-wire-temp" d="${drawBezier(wiringStart.x, wiringStart.y, tempWirePath.x, tempWirePath.y)}" />`;
|
||||
wireLayer.innerHTML = svgHTML;
|
||||
}
|
||||
function updateNodePositions() { Object.values(nodes).forEach(n => { if (n.el) { n.el.style.left = `${n.x}px`; n.el.style.top = `${n.y}px`; } }); renderWires(); }
|
||||
function clearSelection() { selectedWireId = null; selectedNodeId = null; document.querySelectorAll('.pb-node.selected').forEach(el => el.classList.remove('selected')); renderWires(); }
|
||||
|
||||
function updateNodePositions() {
|
||||
Object.values(nodes).forEach(n => {
|
||||
if (n.el) { n.el.style.left = `${n.x}px`; n.el.style.top = `${n.y}px`; }
|
||||
});
|
||||
renderWires();
|
||||
/* --- Node Logic --- */
|
||||
function createNodeElement(node) {
|
||||
const el = document.createElement('div'); el.className = `pb-node`; el.dataset.id = node.id;
|
||||
el.style.left = `${node.x}px`; el.style.top = `${node.y}px`;
|
||||
el.style.width = `${PC_PARTS[node.type].w}px`; el.style.height = `${PC_PARTS[node.type].h}px`; el.style.zIndex = PC_PARTS[node.type].z;
|
||||
let innerHTML = `<svg class="pb-part-svg" viewBox="0 0 ${PC_PARTS[node.type].w} ${PC_PARTS[node.type].h}">${PC_PARTS[node.type].svg}</svg>`;
|
||||
PC_PARTS[node.type].ports.forEach(p => { innerHTML += `<div class="pb-port" data-port="${p.id}" style="left: ${p.x}px; top: ${p.y}px;"></div>`; });
|
||||
el.innerHTML = innerHTML; viewport.appendChild(el); node.el = el; return el;
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
selectedWireId = null; selectedNodeId = null;
|
||||
document.querySelectorAll('.pb-node.selected').forEach(el => el.classList.remove('selected'));
|
||||
renderWires();
|
||||
function spawnNode(type, dropX = null, dropY = null) {
|
||||
const id = `node_${nextNodeId++}`;
|
||||
const x = dropX !== null ? dropX : 300 + Math.random()*40; const y = dropY !== null ? dropY : 150 + Math.random()*40;
|
||||
const node = { id, type, x, y, snappedTo: null, el: null };
|
||||
if (PC_PARTS[type].slots) { node.slots = { ...PC_PARTS[type].slots }; for(let k in node.slots) { node.slots[k] = null; } }
|
||||
nodes[id] = node; createNodeElement(node); evaluateBuild(); return id;
|
||||
}
|
||||
|
||||
/* --- Seven-Segment Diagnostics Engine --- */
|
||||
function moveNodeRecursive(nodeId, dx, dy) {
|
||||
const n = nodes[nodeId]; if(!n) return; n.x += dx; n.y += dy;
|
||||
if(n.slots) { Object.keys(n.slots).forEach(k => { if(typeof n.slots[k] === 'string') moveNodeRecursive(n.slots[k], dx, dy); }); }
|
||||
}
|
||||
|
||||
/* --- SYSTEM DIAGNOSTICS & VARIABLE BOOT SPEED --- */
|
||||
function evaluateBuild() {
|
||||
if(!specsContainer) return;
|
||||
|
||||
let hasCase = false, hasMB = false, hasCPU = false, hasCooler = false, hasRAM = false, hasPSU = false;
|
||||
let hasStorage = false, hasGPU = false;
|
||||
let mbPwr = false, gpuPwr = false;
|
||||
let usbCount = 0, dispConn = false, audConn = false;
|
||||
let hasCase=false, hasMB=false, hasCPU=false, hasCooler=false, hasRAM=false, hasPSU=false, hasStorage=false, hasGPU=false;
|
||||
let mbPwr=false, gpuPwr=false, storPwr=false, storData=false, dispConn=false, usbCount=0;
|
||||
|
||||
let caseNode = Object.values(nodes).find(n => n.type === 'CASE');
|
||||
let mbNode = Object.values(nodes).find(n => n.type === 'MB');
|
||||
@@ -160,145 +184,92 @@
|
||||
if (caseNode.slots['MB1']) hasMB = true;
|
||||
if (caseNode.slots['PSU1']) hasPSU = true;
|
||||
if (caseNode.slots['HDD1'] || caseNode.slots['HDD2'] || caseNode.slots['SATA_SSD1'] || caseNode.slots['SATA_SSD2']) hasStorage = true;
|
||||
} else if (mbNode) {
|
||||
hasMB = true; // Motherboard exists outside case
|
||||
}
|
||||
} else if (mbNode) { hasMB = true; }
|
||||
|
||||
if (mbNode) {
|
||||
if (mbNode.slots['CPU1']) hasCPU = true;
|
||||
if (mbNode.slots['COOLER1']) hasCooler = true;
|
||||
if (mbNode.slots['RAM1'] || mbNode.slots['RAM2'] || mbNode.slots['RAM3'] || mbNode.slots['RAM4']) hasRAM = true;
|
||||
if (mbNode.slots['PCIE1'] || mbNode.slots['PCIE2']) hasGPU = true;
|
||||
if (mbNode.slots['M2_1'] || mbNode.slots['M2_2']) hasStorage = true;
|
||||
if (mbNode.slots['M2_1'] || mbNode.slots['M2_2']) { hasStorage = true; storPwr = true; storData = true; }
|
||||
}
|
||||
|
||||
// Check Cables
|
||||
connections.forEach(c => {
|
||||
let n1 = nodes[c.fromNode], n2 = nodes[c.toNode];
|
||||
if(!n1 || !n2) return;
|
||||
let types = [n1.type, n2.type];
|
||||
let n1 = nodes[c.fromNode], n2 = nodes[c.toNode]; if(!n1 || !n2) return;
|
||||
let types = [n1.type, n2.type], ports = [c.fromPort, c.toPort];
|
||||
|
||||
if(types.includes('MB') && types.includes('PSU')) mbPwr = true;
|
||||
if(types.includes('GPU') && types.includes('PSU')) gpuPwr = true;
|
||||
if(types.includes('MB') && ['KEYBOARD','MOUSE','WEBCAM','MIC','PRINTER'].some(t => types.includes(t))) usbCount++;
|
||||
if(types.includes('MB') && types.includes('SPEAKER')) audConn = true;
|
||||
if(types.includes('PSU') && (types.includes('HDD') || types.includes('SATA_SSD')) && ports.includes('pwr')) storPwr = true;
|
||||
if(types.includes('MB') && (types.includes('HDD') || types.includes('SATA_SSD')) && ports.includes('data')) storData = true;
|
||||
if(types.includes('MB') && ['KEYBOARD','MOUSE'].some(t => types.includes(t))) usbCount++;
|
||||
if((types.includes('MB') || types.includes('GPU')) && types.includes('MONITOR')) dispConn = true;
|
||||
});
|
||||
|
||||
const isBootable = (hasMB && hasCPU && hasCooler && hasRAM && hasPSU && hasStorage && mbPwr && (hasGPU ? gpuPwr : true) && dispConn);
|
||||
|
||||
// Determine the Boot Speed based on the connected drive
|
||||
let bootSpeed = 8000; // Default slow HDD
|
||||
let activeDrive = 'HDD';
|
||||
if (mbNode && (mbNode.slots['M2_1'] || mbNode.slots['M2_2'])) {
|
||||
activeDrive = 'M2_SSD';
|
||||
} else {
|
||||
Object.values(nodes).forEach(n => {
|
||||
if ((n.type === 'SATA_SSD' || n.type === 'HDD') && n.snappedTo) activeDrive = n.type;
|
||||
});
|
||||
}
|
||||
|
||||
if (activeDrive === 'M2_SSD') bootSpeed = 1500;
|
||||
else if (activeDrive === 'SATA_SSD') bootSpeed = 3500;
|
||||
|
||||
// Auto-Trigger the Boot Animation
|
||||
if (isBootable && !isSystemBooted) { isSystemBooted = true; triggerBootSequence(bootSpeed); }
|
||||
else if (!isBootable) { isSystemBooted = false; resetMonitor(); }
|
||||
|
||||
specsContainer.innerHTML = `
|
||||
<div class="diag-cat">Core System</div>
|
||||
<div class="diag-cat">CORE SYSTEM</div>
|
||||
<div class="diag-row"><span>CHASSIS</span><span style="color: ${hasCase ? '#28f07a' : '#ff5555'}">${hasCase ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>MOTHERBOARD</span><span style="color: ${hasMB ? '#28f07a' : '#ff5555'}">${hasMB ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>CPU</span><span style="color: ${hasCPU ? '#28f07a' : '#ff5555'}">${hasCPU ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>COOLING</span><span style="color: ${hasCooler ? '#28f07a' : '#ff5555'}">${hasCooler ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>MEMORY</span><span style="color: ${hasRAM ? '#28f07a' : '#ff5555'}">${hasRAM ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>POWER SPLY</span><span style="color: ${hasPSU ? '#28f07a' : '#ff5555'}">${hasPSU ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-cat">Connections</div>
|
||||
<div class="diag-cat">CONNECTIONS</div>
|
||||
<div class="diag-row"><span>MB POWER</span><span style="color: ${mbPwr ? '#28f07a' : '#ff5555'}">${mbPwr ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>STORAGE</span><span style="color: ${hasStorage ? '#28f07a' : '#ff5555'}">${hasStorage ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>STORAGE</span><span style="color: ${(hasStorage && storPwr && storData) ? '#28f07a' : '#ff5555'}">${(hasStorage && storPwr && storData) ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>GPU POWER</span><span style="color: ${!hasGPU ? '#888' : (gpuPwr ? '#28f07a' : '#ff5555')}">${!hasGPU ? 'N/A' : (gpuPwr ? 'OK' : 'ERR')}</span></div>
|
||||
<div class="diag-row"><span>DISPLAY</span><span style="color: ${dispConn ? '#28f07a' : '#ff5555'}">${dispConn ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>USB DEVS</span><span style="color: #55aaff">${usbCount}</span></div>
|
||||
<hr style="border-color: rgba(255,255,255,0.1); margin: 12px 0 8px 0;">
|
||||
<div style="text-align:center; font-size: 28px; color: ${isBootable ? '#28f07a' : '#ff5555'}; font-family: var(--bit-font); letter-spacing: 2px;">
|
||||
${isBootable ? 'BOOTING...' : 'HALTED'}
|
||||
</div>
|
||||
<div style="text-align:center; font-size: 28px; color: ${isBootable ? '#28f07a' : '#ff5555'}; font-family: var(--bit-font); letter-spacing: 2px;">${isBootable ? 'BOOTING...' : 'HALTED'}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/* --- Node Creation & Snapping --- */
|
||||
function createNodeElement(node) {
|
||||
const el = document.createElement('div');
|
||||
el.className = `pb-node`; el.dataset.id = node.id;
|
||||
el.style.left = `${node.x}px`; el.style.top = `${node.y}px`;
|
||||
el.style.width = `${PC_PARTS[node.type].w}px`; el.style.height = `${PC_PARTS[node.type].h}px`;
|
||||
el.style.zIndex = PC_PARTS[node.type].z;
|
||||
function triggerBootSequence(duration) {
|
||||
const monitor = Object.values(nodes).find(n => n.type === 'MONITOR'); if (!monitor) return;
|
||||
const bootContent = monitor.el.querySelector('#boot-content');
|
||||
const durSeconds = (duration / 1000).toFixed(1);
|
||||
|
||||
let innerHTML = `<svg class="pb-part-svg" viewBox="0 0 ${PC_PARTS[node.type].w} ${PC_PARTS[node.type].h}">${PC_PARTS[node.type].svg}</svg>`;
|
||||
PC_PARTS[node.type].ports.forEach(p => {
|
||||
innerHTML += `<div class="pb-port" data-port="${p.id}" style="left: ${p.x}px; top: ${p.y}px;"></div>`;
|
||||
});
|
||||
bootContent.innerHTML = `<text x="120" y="70" fill="white" font-family="sans-serif" font-size="12" text-anchor="middle">Starting Windows</text><rect x="85" y="85" width="0" height="4" fill="#28f07a" rx="2"><animate attributeName="width" from="0" to="70" dur="${durSeconds}s" fill="freeze" /></rect>`;
|
||||
|
||||
// Debug Labels for bare parts
|
||||
if(node.type !== 'CASE' && node.type !== 'MB') {
|
||||
innerHTML += `<div style="position:absolute; top:-20px; font-family:var(--ui-font); font-size:12px; color:var(--muted);">${node.type}</div>`;
|
||||
setTimeout(() => {
|
||||
bootContent.innerHTML = `<image href="/Microsoft_Nostalgic_Windows_Wallpaper_4k.jpg" x="10" y="10" width="220" height="120" preserveAspectRatio="xMidYMid slice" />`;
|
||||
}, duration + 300); // Small buffer to let the bar finish
|
||||
}
|
||||
|
||||
el.innerHTML = innerHTML;
|
||||
viewport.appendChild(el);
|
||||
node.el = el;
|
||||
return el;
|
||||
}
|
||||
function resetMonitor() { const monitor = Object.values(nodes).find(n => n.type === 'MONITOR'); if (monitor) monitor.el.querySelector('#boot-content').innerHTML = ''; }
|
||||
|
||||
function spawnNode(type, dropX = null, dropY = null) {
|
||||
const id = `node_${nextNodeId++}`;
|
||||
const x = dropX !== null ? dropX : 300 + Math.random()*40;
|
||||
const y = dropY !== null ? dropY : 150 + Math.random()*40;
|
||||
|
||||
const node = { id, type, x, y, snappedTo: null, el: null };
|
||||
if (PC_PARTS[type].slots) node.slots = { ...PC_PARTS[type].slots }; // Copy slots schema, values will be filled with IDs
|
||||
|
||||
// Reset slot values to null
|
||||
if(node.slots) {
|
||||
for(let k in node.slots) { node.slots[k] = null; }
|
||||
}
|
||||
|
||||
nodes[id] = node;
|
||||
createNodeElement(node);
|
||||
evaluateBuild();
|
||||
}
|
||||
|
||||
// Recursive movement to handle nested snaps (MB inside CASE inside ...)
|
||||
function moveNodeRecursive(nodeId, dx, dy) {
|
||||
const n = nodes[nodeId];
|
||||
if(!n) return;
|
||||
n.x += dx; n.y += dy;
|
||||
if(n.slots) {
|
||||
Object.keys(n.slots).forEach(k => {
|
||||
if(typeof n.slots[k] === 'string') moveNodeRecursive(n.slots[k], dx, dy);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Inspect Mode --- */
|
||||
let inspectZoom = 1, inspectRotX = 0, inspectRotY = 0;
|
||||
workspace.addEventListener('dblclick', (e) => {
|
||||
const nodeEl = e.target.closest('.pb-node');
|
||||
if (nodeEl) {
|
||||
const node = nodes[nodeEl.dataset.id];
|
||||
document.getElementById('inspectModal').classList.add('active');
|
||||
document.getElementById('inspectObject').innerHTML = `<svg viewBox="0 0 ${PC_PARTS[node.type].w} ${PC_PARTS[node.type].h}" style="width:100%; height:100%;">${PC_PARTS[node.type].svg}</svg>`;
|
||||
document.getElementById('inspectName').innerText = PC_PARTS[node.type].name;
|
||||
inspectZoom = 1.5; inspectRotX = 0; inspectRotY = 0; updateInspectTransform(); clearSelection();
|
||||
}
|
||||
});
|
||||
document.getElementById('inspectStage')?.addEventListener('mousemove', (e) => {
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
inspectRotY = (e.clientX - rect.left - rect.width/2) / 5;
|
||||
inspectRotX = -(e.clientY - rect.top - rect.height/2) / 5;
|
||||
updateInspectTransform();
|
||||
});
|
||||
document.getElementById('inspectStage')?.addEventListener('wheel', (e) => {
|
||||
e.preventDefault(); inspectZoom += e.deltaY < 0 ? 0.1 : -0.1;
|
||||
inspectZoom = Math.max(0.5, Math.min(inspectZoom, 4)); updateInspectTransform();
|
||||
});
|
||||
function updateInspectTransform() { const obj = document.getElementById('inspectObject'); if(obj) obj.style.transform = `scale(${inspectZoom}) rotateX(${inspectRotX}deg) rotateY(${inspectRotY}deg)`; }
|
||||
document.getElementById('inspectClose')?.addEventListener('click', () => { document.getElementById('inspectModal').classList.remove('active'); });
|
||||
|
||||
/* --- Interaction --- */
|
||||
/* --- INTERACTION (Drag, Drop, Snap, Wire) --- */
|
||||
document.getElementById("btnZoomIn")?.addEventListener('click', () => { const r = workspace.getBoundingClientRect(); zoomWorkspace(1.2, r.width/2, r.height/2); });
|
||||
document.getElementById("btnZoomOut")?.addEventListener('click', () => { const r = workspace.getBoundingClientRect(); zoomWorkspace(1/1.2, r.width/2, r.height/2); });
|
||||
document.getElementById("btnZoomReset")?.addEventListener('click', () => { panX = 0; panY = 0; zoom = 1; updateViewport(); });
|
||||
|
||||
workspace.addEventListener('wheel', (e) => { e.preventDefault(); const wsRect = workspace.getBoundingClientRect(); zoomWorkspace(e.deltaY < 0 ? 1.1 : (1/1.1), e.clientX - wsRect.left, e.clientY - wsRect.top); });
|
||||
|
||||
workspace.addEventListener('mousedown', (e) => {
|
||||
const port = e.target.closest('.pb-port');
|
||||
if (port) {
|
||||
const nodeEl = port.closest('.pb-node');
|
||||
const portId = port.dataset.port;
|
||||
const nodeEl = port.closest('.pb-node'); const portId = port.dataset.port;
|
||||
const existingIdx = connections.findIndex(c => (c.toNode === nodeEl.dataset.id && c.toPort === portId) || (c.fromNode === nodeEl.dataset.id && c.fromPort === portId));
|
||||
if (existingIdx !== -1) { connections.splice(existingIdx, 1); evaluateBuild(); renderWires(); return; }
|
||||
const coords = getPortCoords(nodeEl.dataset.id, portId);
|
||||
@@ -314,18 +285,14 @@
|
||||
clearSelection(); selectedNodeId = nodeEl.dataset.id; nodeEl.classList.add('selected'); isDraggingNode = nodeEl.dataset.id;
|
||||
const rect = nodeEl.getBoundingClientRect(); dragOffset = { x: (e.clientX - rect.left) / zoom, y: (e.clientY - rect.top) / zoom };
|
||||
|
||||
// Unsnap from parent when picked up
|
||||
const node = nodes[isDraggingNode];
|
||||
if (node.snappedTo) {
|
||||
const parent = nodes[node.snappedTo.id];
|
||||
if (parent && parent.slots[node.snappedTo.key] === node.id) parent.slots[node.snappedTo.key] = null;
|
||||
node.snappedTo = null;
|
||||
node.el.style.zIndex = PC_PARTS[node.type].z; // Reset Z
|
||||
evaluateBuild();
|
||||
node.snappedTo = null; node.el.style.zIndex = PC_PARTS[node.type].z; evaluateBuild();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
clearSelection(); isPanning = true; panStart = { x: e.clientX - panX, y: e.clientY - panY };
|
||||
});
|
||||
|
||||
@@ -334,20 +301,16 @@
|
||||
if (isPanning) { panX = e.clientX - panStart.x; panY = e.clientY - panStart.y; updateViewport(); return; }
|
||||
if (isDraggingNode) {
|
||||
const node = nodes[isDraggingNode];
|
||||
let newX = (e.clientX - wsRect.left - panX) / zoom - dragOffset.x;
|
||||
let newY = (e.clientY - wsRect.top - panY) / zoom - dragOffset.y;
|
||||
moveNodeRecursive(node.id, newX - node.x, newY - node.y);
|
||||
updateNodePositions();
|
||||
let newX = (e.clientX - wsRect.left - panX) / zoom - dragOffset.x; let newY = (e.clientY - wsRect.top - panY) / zoom - dragOffset.y;
|
||||
moveNodeRecursive(node.id, newX - node.x, newY - node.y); updateNodePositions();
|
||||
}
|
||||
if (wiringStart) { tempWirePath = { x: (e.clientX - wsRect.left - panX) / zoom, y: (e.clientY - wsRect.top - panY) / zoom }; renderWires(); }
|
||||
});
|
||||
|
||||
window.addEventListener('mouseup', (e) => {
|
||||
if (isDraggingNode) {
|
||||
const node = nodes[isDraggingNode];
|
||||
let snapped = false;
|
||||
const node = nodes[isDraggingNode]; let snapped = false;
|
||||
|
||||
// Check all other nodes for compatible slots
|
||||
Object.values(nodes).forEach(target => {
|
||||
if (target.slots && !snapped && target.id !== node.id) {
|
||||
for(let slotKey in target.slots) {
|
||||
@@ -358,7 +321,7 @@
|
||||
moveNodeRecursive(node.id, tX - node.x, tY - node.y);
|
||||
node.snappedTo = { id: target.id, key: slotKey };
|
||||
target.slots[slotKey] = node.id;
|
||||
node.el.style.zIndex = PC_PARTS[target.type].z + 5; // Layer above parent
|
||||
node.el.style.zIndex = PC_PARTS[target.type].z + 5;
|
||||
snapped = true; break;
|
||||
}
|
||||
}
|
||||
@@ -371,8 +334,7 @@
|
||||
if (wiringStart) {
|
||||
const port = e.target.closest('.pb-port');
|
||||
if (port) {
|
||||
const targetNodeId = port.closest('.pb-node').dataset.id;
|
||||
const targetPortId = port.dataset.port;
|
||||
const targetNodeId = port.closest('.pb-node').dataset.id; const targetPortId = port.dataset.port;
|
||||
if (targetNodeId !== wiringStart.node) { connections.push({ id: `conn_${nextWireId++}`, fromNode: wiringStart.node, fromPort: wiringStart.port, toNode: targetNodeId, toPort: targetPortId }); }
|
||||
}
|
||||
wiringStart = null; tempWirePath = null; evaluateBuild(); renderWires();
|
||||
@@ -380,7 +342,8 @@
|
||||
isPanning = false;
|
||||
});
|
||||
|
||||
/* --- Deletion (Recursive) --- */
|
||||
|
||||
/* --- Deletion & Toolbox UI --- */
|
||||
function deleteNodeRecursive(id) {
|
||||
const n = nodes[id]; if(!n) return;
|
||||
if(n.slots) { Object.keys(n.slots).forEach(k => { if(typeof n.slots[k] === 'string') deleteNodeRecursive(n.slots[k]); }); }
|
||||
@@ -390,28 +353,17 @@
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if ((e.key === 'Delete' || e.key === 'Backspace') && selectedNodeId) {
|
||||
deleteNodeRecursive(selectedNodeId); clearSelection(); evaluateBuild(); renderWires();
|
||||
}
|
||||
if ((e.key === 'Delete' || e.key === 'Backspace') && selectedWireId) {
|
||||
connections = connections.filter(c => c.id !== selectedWireId); clearSelection(); evaluateBuild(); renderWires();
|
||||
}
|
||||
if ((e.key === 'Delete' || e.key === 'Backspace') && selectedNodeId) { deleteNodeRecursive(selectedNodeId); clearSelection(); evaluateBuild(); renderWires(); }
|
||||
if ((e.key === 'Delete' || e.key === 'Backspace') && selectedWireId) { connections = connections.filter(c => c.id !== selectedWireId); clearSelection(); evaluateBuild(); renderWires(); }
|
||||
});
|
||||
|
||||
workspace.addEventListener('dragover', (e) => { e.preventDefault(); });
|
||||
workspace.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
const type = e.dataTransfer.getData('spawnType');
|
||||
if (type) {
|
||||
const r = workspace.getBoundingClientRect();
|
||||
spawnNode(type, (e.clientX - r.left - panX) / zoom - (PC_PARTS[type].w / 2), (e.clientY - r.top - panY) / zoom - (PC_PARTS[type].h / 2));
|
||||
}
|
||||
e.preventDefault(); const type = e.dataTransfer.getData('spawnType');
|
||||
if (type) { const r = workspace.getBoundingClientRect(); spawnNode(type, (e.clientX - r.left - panX) / zoom - (PC_PARTS[type].w / 2), (e.clientY - r.top - panY) / zoom - (PC_PARTS[type].h / 2)); }
|
||||
});
|
||||
|
||||
btnClearBoard?.addEventListener('click', () => {
|
||||
viewport.querySelectorAll('.pb-node').forEach(el => el.remove());
|
||||
nodes = {}; connections = []; evaluateBuild(); renderWires();
|
||||
});
|
||||
btnClearBoard?.addEventListener('click', () => { viewport.querySelectorAll('.pb-node').forEach(el => el.remove()); nodes = {}; connections = []; evaluateBuild(); renderWires(); });
|
||||
|
||||
toolboxToggle?.addEventListener("click", () => {
|
||||
const c = pcPage?.classList.contains("toolboxCollapsed");
|
||||
@@ -419,5 +371,29 @@
|
||||
toolboxToggle?.setAttribute("aria-expanded", c ? "true" : "false");
|
||||
});
|
||||
|
||||
/* --- Auto-Assemble Engine --- */
|
||||
function autoAssemble(sT) {
|
||||
btnClearBoard.click();
|
||||
const mId = spawnNode('MONITOR', 200, 100), kId = spawnNode('KEYBOARD', 230, 320), moId = spawnNode('MOUSE', 450, 330), spId = spawnNode('SPEAKER', 150, 300);
|
||||
const cId = spawnNode('CASE', 550, 100), mbId = spawnNode('MB', 1250, 250), pId = spawnNode('PSU', 1250, 100), cpId = spawnNode('CPU', 1450, 100), coId = spawnNode('COOLER', 1450, 250), rId = spawnNode('RAM', 1600, 100), gId = spawnNode('GPU', 1450, 400), stId = spawnNode(sT, 1600, 250);
|
||||
|
||||
const plan = [{c:mbId,p:cId,s:'MB1'},{c:pId,p:cId,s:'PSU1'},{c:cpId,p:mbId,s:'CPU1'},{c:coId,p:mbId,s:'COOLER1'},{c:rId,p:mbId,s:'RAM1'},{c:gId,p:mbId,s:'PCIE1'}];
|
||||
if(sT==='HDD') plan.push({c:stId,p:cId,s:'HDD1'}); if(sT==='SATA_SSD') plan.push({c:stId,p:cId,s:'SATA_SSD1'}); if(sT==='M2_SSD') plan.push({c:stId,p:mbId,s:'M2_1'});
|
||||
|
||||
plan.forEach(s => { const ch = nodes[s.c], p = nodes[s.p]; const sD = PC_PARTS[p.type].slots[s.s]; moveNodeRecursive(ch.id, (p.x + sD.x) - ch.x, (p.y + sD.y) - ch.y); ch.snappedTo = { id: p.id, key: s.s }; p.slots[s.s] = ch.id; ch.el.style.zIndex = PC_PARTS[p.type].z + 5; });
|
||||
|
||||
const conn = (n1, p1, n2, p2) => connections.push({ id: `conn_${nextWireId++}`, fromNode: n1, fromPort: p1, toNode: n2, toPort: p2 });
|
||||
conn(pId, 'out1', mbId, 'atx_pwr'); conn(pId, 'out2', gId, 'pwr_in');
|
||||
if (sT !== 'M2_SSD') { conn(pId, 'out3', stId, 'pwr'); conn(mbId, 'sata1', stId, 'data'); }
|
||||
conn(gId, 'disp_out', mId, 'disp'); conn(mbId, 'usb1', kId, 'usb'); conn(mbId, 'usb2', moId, 'usb'); conn(mbId, 'audio', spId, 'audio');
|
||||
|
||||
updateNodePositions();
|
||||
evaluateBuild();
|
||||
}
|
||||
|
||||
document.getElementById('btnAssembleHDD')?.addEventListener('click', () => autoAssemble('HDD'));
|
||||
document.getElementById('btnAssembleSATA')?.addEventListener('click', () => autoAssemble('SATA_SSD'));
|
||||
document.getElementById('btnAssembleM2')?.addEventListener('click', () => autoAssemble('M2_SSD'));
|
||||
|
||||
initToolbox(); evaluateBuild();
|
||||
})();
|
||||
@@ -37,7 +37,7 @@ body { margin: 0; background: var(--bg); color: var(--text); font-family: var(--
|
||||
.siteNav { position: sticky; top: 0; z-index: 50; height: var(--nav-h); background: rgba(0,0,0,.10); border-bottom: 1px solid var(--line); backdrop-filter: blur(8px); margin-bottom: 25px; }
|
||||
.navInner { height: 90px; max-width: 1400px; margin: 0 auto; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; gap: 24px; }
|
||||
.brand { display: flex; align-items: center; gap: 12px; text-decoration: none; color: var(--text); }
|
||||
.brandLogo { width: 2.5em; height: 2.5em; image-rendering: pixelated; }
|
||||
.brandLogo { width: 75px; height: 75px; image-rendering: pixelated; }
|
||||
.brandName { letter-spacing: .12em; font-weight: 900; font-size: 18px; }
|
||||
.navLinks { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
|
||||
.navLinks a { color: var(--muted); text-decoration: none; font-weight: 800; letter-spacing: .12em; font-size: 16px; }
|
||||
@@ -89,7 +89,7 @@ body { margin: 0; background: var(--bg); color: var(--text); font-family: var(--
|
||||
.switch input { display: none; }
|
||||
.slider { position: absolute; inset: 0; background: rgba(255,255,255,.14); border: 1px solid rgba(255,255,255,.14); border-radius: 999px; transition: .2s ease; }
|
||||
.slider::before { content: ""; position: absolute; width: 22px; height: 22px; left: 3px; top: 2px; background: rgba(255,255,255,.92); border-radius: 999px; transition: .2s ease; pointer-events: none; }
|
||||
.switch input:checked + .slider { background: rgba(40,240,122,.25); border-color: rgba(40,240,122,.30); }
|
||||
.switch input:checked + .slider { background: rgba(40,240,122,.30); border-color: rgba(40,240,122,.30); }
|
||||
.switch input:checked + .slider::before { transform: translateX(28px); }
|
||||
|
||||
/* --- HEXADECIMAL --- */
|
||||
@@ -226,3 +226,147 @@ body { margin: 0; background: var(--bg); color: var(--text); font-family: var(--
|
||||
line-height: 150%;
|
||||
margin: 0 0 2em 2em;
|
||||
}
|
||||
|
||||
.btnRandomRunning {
|
||||
background: rgba(40,240,122,.18) !important;
|
||||
border-color: rgba(40,240,122,.35) !important;
|
||||
color: rgba(232,232,238,.95) !important; /* Added this so the text pops like the reset button */
|
||||
animation: randomPulse 750ms ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes randomPulse {
|
||||
0% { box-shadow: 0 0 0 rgba(40,240,122,0); }
|
||||
50% { box-shadow: 0 0 22px rgba(40,240,122,.38); } /* Matched the .38 opacity peak */
|
||||
100% { box-shadow: 0 0 0 rgba(40,240,122,0); }
|
||||
}
|
||||
|
||||
.btnReset { color: rgba(232,232,238,.95); }
|
||||
.btnReset:hover {
|
||||
background: rgba(255,80,80,.18);
|
||||
border-color: rgba(255,80,80,.35);
|
||||
animation: resetPulse 750ms ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes resetPulse {
|
||||
0% { box-shadow: 0 0 0 rgba(255,80,80,0); }
|
||||
50% { box-shadow: 0 0 22px rgba(255,80,80,.38); }
|
||||
100% { box-shadow: 0 0 0 rgba(255,80,80,0); }
|
||||
}
|
||||
|
||||
/* --- ACCORDION ANIMATIONS FOR ALL TOOLBOXES --- */
|
||||
.panelCol .cardTitle,
|
||||
.pb-toolbox .cardTitle,
|
||||
.lg-toolbox .cardTitle {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.panelCol .cardTitle::after,
|
||||
.pb-toolbox .cardTitle::after,
|
||||
.lg-toolbox .cardTitle::after {
|
||||
content: '▼';
|
||||
font-size: 0.7em;
|
||||
opacity: 0.6;
|
||||
transition: transform 0.25s ease;
|
||||
}
|
||||
|
||||
.panelCol .card.collapsed .cardTitle::after,
|
||||
.pb-toolbox .card.collapsed .cardTitle::after,
|
||||
.lg-toolbox .card.collapsed .cardTitle::after {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.panelCol .cardBody,
|
||||
.pb-toolbox .cardBody,
|
||||
.lg-toolbox .cardBody {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr;
|
||||
transition: grid-template-rows 350ms cubic-bezier(0.2, 0.9, 0.2, 1);
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.panelCol .card.collapsed .cardBody,
|
||||
.pb-toolbox .card.collapsed .cardBody,
|
||||
.lg-toolbox .card.collapsed .cardBody {
|
||||
grid-template-rows: 0fr;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.panelCol .cardBodyInner,
|
||||
.pb-toolbox .cardBodyInner,
|
||||
.lg-toolbox .cardBodyInner {
|
||||
overflow: hidden;
|
||||
opacity: 1;
|
||||
transition: opacity 250ms ease 100ms, visibility 0s 0s;
|
||||
}
|
||||
|
||||
.panelCol .card.collapsed .cardBodyInner,
|
||||
.pb-toolbox .card.collapsed .cardBodyInner,
|
||||
.lg-toolbox .card.collapsed .cardBodyInner {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 200ms ease 0s, visibility 0s 200ms;
|
||||
}
|
||||
|
||||
/* --- BASE STATE: Lock the scale so it never stretches --- */
|
||||
.btnRandom, #btnRandom,
|
||||
.btnReset, #btnReset {
|
||||
transform: scale(1) !important;
|
||||
transition: transform 0.1s ease-out, border-color 0.2s ease-out, color 0.2s ease-out !important;
|
||||
}
|
||||
|
||||
/* --- THE PULSE ANIMATIONS --- */
|
||||
@keyframes neonPulseGreen {
|
||||
0%, 100% {
|
||||
background-color: rgba(40, 240, 122, 0.05);
|
||||
box-shadow: inset 0 0 10px rgba(40, 240, 122, 0.4),
|
||||
0 0 5px rgba(40, 240, 122, 0.2);
|
||||
}
|
||||
50% {
|
||||
background-color: rgba(40, 240, 122, 0.15);
|
||||
box-shadow: inset 0 0 22px rgba(40, 240, 122, 0.8),
|
||||
0 0 12px rgba(40, 240, 122, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes neonPulseRed {
|
||||
0%, 100% {
|
||||
background-color: rgba(255, 85, 85, 0.05);
|
||||
box-shadow: inset 0 0 10px rgba(255, 85, 85, 0.4),
|
||||
0 0 5px rgba(255, 85, 85, 0.2);
|
||||
}
|
||||
50% {
|
||||
background-color: rgba(255, 85, 85, 0.15);
|
||||
box-shadow: inset 0 0 22px rgba(255, 85, 85, 0.8),
|
||||
0 0 12px rgba(255, 85, 85, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- HOVER STATES: Trigger the infinite pulse --- */
|
||||
.btnRandom:hover, .btnRandom:focus, .btnRandom.active,
|
||||
#btnRandom:hover, #btnRandom:focus, #btnRandom.active {
|
||||
border-color: #28f07a !important;
|
||||
color: #28f07a !important;
|
||||
/* 1.5s cycle, infinite loop, smooth easing */
|
||||
animation: neonPulseGreen 1.5s infinite ease-in-out !important;
|
||||
}
|
||||
|
||||
.btnReset:hover, .btnReset:focus, .btnReset.active,
|
||||
#btnReset:hover, #btnReset:focus, #btnReset.active {
|
||||
border-color: #ff5555 !important;
|
||||
color: #ff5555 !important;
|
||||
/* 1.5s cycle, infinite loop, smooth easing */
|
||||
animation: neonPulseRed 1.5s infinite ease-in-out !important;
|
||||
}
|
||||
|
||||
/* --- CLICK STATE: Interrupt the pulse and shrink --- */
|
||||
.btnRandom:active, #btnRandom:active,
|
||||
.btnReset:active, #btnReset:active {
|
||||
transform: scale(0.96) !important;
|
||||
animation: none !important; /* Immediately kill the pulse while clicked */
|
||||
background-color: transparent !important;
|
||||
box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.8) !important;
|
||||
}
|
||||
@@ -68,32 +68,65 @@ body:has(#logicPage) .pageWrap {
|
||||
}
|
||||
.lg-zoom-btn:hover { background: rgba(255,255,255,0.1); border-color: #28f07a; color: #28f07a; }
|
||||
|
||||
/* Update the SVG layer to sit ON TOP of nodes */
|
||||
/* Move the wire layer behind the nodes */
|
||||
.lg-svg-layer {
|
||||
position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1; /* Lower than nodes */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Wires */
|
||||
/* Wires - allow them to be clickable even behind the 'hit area' of ports */
|
||||
.lg-wire {
|
||||
stroke: rgba(255,255,255,0.25); stroke-width: 6; fill: none; stroke-linecap: round;
|
||||
transition: stroke 0.1s ease, filter 0.1s ease, stroke-width 0.1s ease;
|
||||
pointer-events: stroke; cursor: pointer;
|
||||
stroke: #ffffff40;
|
||||
stroke-width: 6;
|
||||
fill: none;
|
||||
stroke-linecap: round;
|
||||
transition: stroke .1s ease,filter .1s ease,stroke-width .1s ease;
|
||||
pointer-events: stroke;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
.lg-wire:hover {
|
||||
stroke: #fff9;
|
||||
stroke-width: 10
|
||||
}
|
||||
.lg-wire.active {
|
||||
stroke: #28f07a;
|
||||
filter: drop-shadow(0 0 6px rgba(40,240,122,.6))
|
||||
}
|
||||
.lg-wire.active:hover {
|
||||
stroke: #5dff9e
|
||||
}
|
||||
.lg-wire:hover { stroke: rgba(255,255,255,0.6); stroke-width: 10; }
|
||||
.lg-wire.active { stroke: #28f07a; filter: drop-shadow(0 0 6px rgba(40,240,122,0.6)); }
|
||||
.lg-wire.active:hover { stroke: #5dff9e; }
|
||||
.lg-wire.selected {
|
||||
stroke: #ff5555 !important; stroke-width: 8 !important; stroke-dasharray: 8 8;
|
||||
filter: drop-shadow(0 0 8px rgba(255,85,85,0.8)) !important; animation: wireDash 1s linear infinite;
|
||||
stroke: #f55!important;
|
||||
stroke-width: 8!important;
|
||||
stroke-dasharray: 8 8;
|
||||
filter: drop-shadow(0 0 8px rgba(255,85,85,.8))!important;
|
||||
animation: wireDash 1s linear infinite
|
||||
}
|
||||
@keyframes wireDash {
|
||||
to {
|
||||
stroke-dashoffset: -16
|
||||
}
|
||||
}
|
||||
.lg-wire-temp {
|
||||
stroke: #fff6;
|
||||
stroke-dasharray: 8 8;
|
||||
pointer-events: none
|
||||
}
|
||||
@keyframes wireDash { to { stroke-dashoffset: -16; } }
|
||||
.lg-wire-temp { stroke: rgba(255,255,255,0.4); stroke-dasharray: 8 8; pointer-events: none; }
|
||||
|
||||
/* Nodes */
|
||||
/* Nodes - move them in front of wires */
|
||||
.lg-node {
|
||||
position: absolute; background: transparent; border: none; border-radius: 0; padding: 4px;
|
||||
display: flex; flex-direction: column; align-items: center; cursor: grab;
|
||||
z-index: 10; user-select: none; transition: filter 0.2s;
|
||||
pointer-events: auto; /* Re-enables interaction inside the viewport */
|
||||
position: absolute;
|
||||
z-index: 10; /* Higher than wires */
|
||||
pointer-events: auto;
|
||||
user-select: none;
|
||||
}
|
||||
.lg-node:active { cursor: grabbing; z-index: 20; }
|
||||
.lg-node.selected { filter: drop-shadow(0 0 10px rgba(255,85,85,0.8)); }
|
||||
@@ -108,13 +141,24 @@ body:has(#logicPage) .pageWrap {
|
||||
.lg-line-svg { width: 30px; height: 50px; display: block; }
|
||||
|
||||
/* Connection Ports */
|
||||
/* Update ports to sit even higher so they stay clickable */
|
||||
/* Ports - Ensure the dots are the top-most layer */
|
||||
.lg-port {
|
||||
width: 16px; height: 16px; background: #a9acb8; border-radius: 50%; cursor: crosshair;
|
||||
border: 3px solid var(--bg); box-shadow: 0 0 0 1px rgba(255,255,255,0.2); transition: all 0.2s;
|
||||
position: absolute; z-index: 5; transform: translate(-50%, -50%);
|
||||
width: 12px; /* Balanced size */
|
||||
height: 12px;
|
||||
background: #a9acb8;
|
||||
border: 2px solid #1f2027; /* Dark border helps it 'pop' over glowing wires */
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
z-index: 100; /* Absolute top */
|
||||
transform: translate(-50%, -50%);
|
||||
cursor: crosshair;
|
||||
}
|
||||
.lg-port:hover { transform: translate(-50%, -50%) scale(1.3); background: #fff; }
|
||||
.lg-port.active { background: #28f07a; box-shadow: 0 0 12px rgba(40,240,122,0.8); border-color: #1f2027; }
|
||||
.lg-port.active {
|
||||
background: #28f07a;
|
||||
box-shadow: 0 0 8px rgba(40,240,122,.45);
|
||||
}
|
||||
|
||||
/* === FLOATING TOOLBOX === */
|
||||
.toolboxToggle {
|
||||
@@ -163,3 +207,14 @@ body:has(#logicPage) .pageWrap {
|
||||
.tt-table th { position: sticky; top: 0; background: rgba(31,32,39,0.95); padding: 8px 12px; border-bottom: 1px solid rgba(255,255,255,0.15); color: var(--muted); font-family: var(--bit-font); font-weight: normal; }
|
||||
.tt-table td { padding: 8px 12px; border-bottom: 1px solid rgba(255,255,255,0.05); }
|
||||
.tt-table .tt-on { color: #28f07a; text-shadow: 0 0 8px rgba(40,240,122,0.5); }
|
||||
|
||||
|
||||
/* Ensure the active-sim class actually changes the color */
|
||||
.active-sim .slider {
|
||||
background-color: rgba(40,240,122,.25) !important; /* Bright Green */
|
||||
box-shadow: 0 0 15px rgba(40, 240, 122, 0.5);
|
||||
}
|
||||
|
||||
.active-sim .slider::before {
|
||||
transform: translateX(28px) !important;
|
||||
}
|
||||
@@ -63,7 +63,38 @@
|
||||
.hexColWeight { font-family: var(--bit-font); font-size: 40px; color: rgba(232,232,238,.6); margin-top: 14px; }
|
||||
|
||||
/* --- TOOLBOX COMPONENTS FOR NUMBERS --- */
|
||||
.panelCol { position: fixed; top: var(--toolbox-top); right: 22px; width: var(--toolbox-w); z-index: 80; display: flex; flex-direction: column; gap: 16px; transform: translateX(0); opacity: 1; transition: transform 420ms cubic-bezier(.2,.9,.2,1), opacity 220ms ease; }
|
||||
.panelCol {
|
||||
/* Your original layout and animations */
|
||||
position: fixed;
|
||||
top: var(--toolbox-top);
|
||||
right: 22px;
|
||||
width: var(--toolbox-w);
|
||||
z-index: 80;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
transition: transform 420ms cubic-bezier(.2,.9,.2,1), opacity 220ms ease;
|
||||
|
||||
/* THE FIX: Push the bottom edge higher up the screen */
|
||||
/* If your footer is ~140px tall, 170px gives you a perfect 30px safe gap */
|
||||
bottom: 110px;
|
||||
|
||||
/* Let the inside scroll smoothly */
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
/* Hide the scrollbar in Chrome/Safari */
|
||||
.panelCol::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.panelCol .card {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.binaryPage.toolboxCollapsed .panelCol { transform: translateX(calc(var(--toolbox-w) + 32px)); opacity: 0; pointer-events: none; }
|
||||
|
||||
.toggleRow { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
||||
|
||||
@@ -39,13 +39,15 @@ body:has(#pcPage) .pageWrap {
|
||||
.pb-zoom-btn:hover { background: rgba(255,255,255,0.1); border-color: #55aaff; color: #55aaff; }
|
||||
|
||||
/* Wires sit at the VERY FRONT so they are never hidden in the case */
|
||||
.pb-svg-layer { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 100; pointer-events: none; }
|
||||
/* Wire layer - sits above case but below ports */
|
||||
/* Wire layer - Physically on TOP of components */
|
||||
.pb-svg-layer { z-index: 100 !important; pointer-events: none; position: absolute; inset: 0; width: 100%; height: 100%; }
|
||||
|
||||
/* Cables */
|
||||
.pb-wire {
|
||||
stroke: rgba(255,255,255,0.25); stroke-width: 6; fill: none; stroke-linecap: round;
|
||||
transition: stroke 0.1s ease, filter 0.1s ease, stroke-width 0.1s ease; pointer-events: stroke; cursor: pointer;
|
||||
}
|
||||
/* Cables - make sure they have a width and color */
|
||||
/* Cables - Styled for visibility */
|
||||
.pb-wire { stroke: #55aaff; stroke-width: 6; fill: none; pointer-events: stroke; }
|
||||
|
||||
.pb-wire:hover { stroke: rgba(255,255,255,0.6); stroke-width: 10; }
|
||||
.pb-wire.active { stroke: #55aaff; filter: drop-shadow(0 0 6px rgba(85,170,255,0.6)); }
|
||||
.pb-wire.active:hover { stroke: #88ccff; }
|
||||
@@ -58,6 +60,7 @@ body:has(#pcPage) .pageWrap {
|
||||
position: absolute; background: transparent; border: none; border-radius: 0; padding: 0;
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: grab;
|
||||
user-select: none; transition: filter 0.2s; pointer-events: auto;
|
||||
z-index: 10;
|
||||
}
|
||||
.pb-node:active { cursor: grabbing; }
|
||||
.pb-node.selected { filter: drop-shadow(0 0 10px rgba(255,85,85,0.8)); }
|
||||
@@ -67,7 +70,7 @@ body:has(#pcPage) .pageWrap {
|
||||
.pb-port {
|
||||
width: 14px; height: 14px; background: #222; border-radius: 50%; cursor: crosshair;
|
||||
border: 2px solid #55aaff; box-shadow: 0 0 0 1px rgba(255,255,255,0.2); transition: all 0.2s;
|
||||
position: absolute; z-index: 200; transform: translate(-50%, -50%); pointer-events: auto;
|
||||
position: absolute; z-index: 200 !important; transform: translate(-50%, -50%); pointer-events: auto;
|
||||
}
|
||||
.pb-port:hover { transform: translate(-50%, -50%) scale(1.4); background: #fff; }
|
||||
.pb-port.active { background: #55aaff; box-shadow: 0 0 12px rgba(85,170,255,0.8); }
|
||||
|
||||