From 1712844313d255409c4eaf8f08f964edfe76e591 Mon Sep 17 00:00:00 2001 From: Kernel Builder Date: Thu, 1 Jan 2026 18:25:54 +0100 Subject: [PATCH 1/6] fix release file --- .github/workflows/release.yml | 64 ++++++++++++++--------------------- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 14a6768..8023de5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,15 +5,6 @@ on: branches: - main workflow_dispatch: - inputs: - release_name: - description: 'Custom Release Name (e.g., "Jolly", "Christmas")' - required: true - type: string - release_tag: - description: 'Release Tag (e.g., "v1.0.0")' - required: true - type: string jobs: release: @@ -38,38 +29,33 @@ jobs: run: bun install shell: bash - - name: Determine Release Info - id: release_info + - name: Extract version + id: extract run: | - # For workflow_dispatch, use the provided inputs - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - release_name="${{ github.event.inputs.release_name }}" - release_tag="${{ github.event.inputs.release_tag }}" - else - # For push events, extract version and generate a name - if [ ! -f scripts/build.mjs ]; then - echo "scripts/build.mjs not found" >&2 - exit 1 - fi + # Extract major.minor.patch version from scripts/build.mjs + # This locates the first quoted three-segment version string in the build script (e.g. "1.1.7") + if [ ! -f scripts/build.mjs ]; then + echo "scripts/build.mjs not found" >&2 + exit 1 + fi + # Print a snippet of the file for debugging in workflow logs + echo '--- scripts/build.mjs (snippet) ---' >&2 + sed -n '1,200p' scripts/build.mjs >&2 || true - # Extract version - raw=$(grep -m1 -oE '\"[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?\"' scripts/build.mjs | tr -d '\"' | head -n1 || true) - version=$(printf '%s' "$raw" | sed -E 's/^([0-9]+\.[0-9]+\.[0-9]+).*/\1/') + # Primary extraction: find the first quoted semver (may include prerelease suffix, but we strip it) + raw=$(grep -m1 -oE '\"[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?\"' scripts/build.mjs | tr -d '\"' | head -n1 || true) - if ! printf '%s' "$version" | grep -xE '^[0-9]+\.[0-9]+\.[0-9]+$' >/dev/null; then - echo "Failed to extract a valid base version from scripts/build.mjs" >&2 - exit 1 - fi + # Strip any prerelease suffix, keep only major.minor.patch + version=$(printf '%s' "$raw" | sed -E 's/^([0-9]+\.[0-9]+\.[0-9]+).*/\1/') - # Use version-based naming for push events - release_name="Release v${version}" - release_tag="v${version}" + # Validate final result is exactly major.minor.patch + if ! printf '%s' "$version" | grep -xE '^[0-9]+\.[0-9]+\.[0-9]+$' >/dev/null; then + echo "Failed to extract a valid base version from scripts/build.mjs" >&2 + sed -n '1,200p' scripts/build.mjs >&2 || true + exit 1 fi - echo "release_name=${release_name}" >> $GITHUB_OUTPUT - echo "release_tag=${release_tag}" >> $GITHUB_OUTPUT - echo "Using release name: ${release_name}" >&2 - echo "Using release tag: ${release_tag}" >&2 + echo "version=$version" >> $GITHUB_OUTPUT shell: bash - name: Prepare tag @@ -79,7 +65,9 @@ jobs: run: | set -euo pipefail - tag="${{ steps.release_info.outputs.release_tag }}" + # Use the extracted version directly as the tag for main branch releases + version="${{ steps.extract.outputs.version }}" + tag="${version}" echo "[debug] computed tag=${tag}" >&2 @@ -107,8 +95,8 @@ jobs: - name: Create Release uses: softprops/action-gh-release@v1 with: - tag_name: ${{ steps.release_info.outputs.release_tag }} - name: ${{ steps.release_info.outputs.release_name }} + tag_name: ${{ steps.prepare.outputs.tag }} + name: Release ${{ steps.prepare.outputs.tag }} files: | dist/shiggycord.js dist/shiggycord.min.js From bea629bf787a1f1efaa16f82ec2ce1b698430cde Mon Sep 17 00:00:00 2001 From: Kernel Builder Date: Thu, 1 Jan 2026 18:28:17 +0100 Subject: [PATCH 2/6] fix version --- scripts/build.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build.mjs b/scripts/build.mjs index 79745a2..76c3960 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -75,7 +75,7 @@ const config = { constModules: { globals: { "bunny-build-info": { - version: `"1.4"`, + version: `"1.4.0"`, }, }, }, @@ -146,4 +146,4 @@ if (isThisFileBeingRunViaCLI) { }); printBuildSuccess(context.hash, releaseBranch, minifiedTimeTook, true); -} +} \ No newline at end of file From 845e14869b8338448cda22b560faca243b5959aa Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Fri, 2 Jan 2026 14:44:56 +0100 Subject: [PATCH 3/6] fix: errors on community cards --- src/core/ui/settings/pages/General/index.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/core/ui/settings/pages/General/index.tsx b/src/core/ui/settings/pages/General/index.tsx index 8a5bef0..d851d39 100644 --- a/src/core/ui/settings/pages/General/index.tsx +++ b/src/core/ui/settings/pages/General/index.tsx @@ -32,7 +32,19 @@ export default function General() { const navigation = NavigationNative.useNavigation(); // Custom Community Card Button - const CommunityCardButton = ({ icon, label, subLabel, color, onPress }) => ( + const CommunityCardButton = ({ + icon, + label, + subLabel, + color, + onPress, + }: { + icon?: number | { uri: string }; + label: string; + subLabel?: string | null; + color?: string; + onPress?: () => void; + }) => ( } + icon={} trailing={} /> Date: Thu, 6 Nov 2025 23:48:54 +0100 Subject: [PATCH 4/6] Add support for spec 1 themes --- src/lib/addons/themes/colors/parser.ts | 317 +++++++++++++++---------- src/lib/addons/themes/colors/types.ts | 15 +- 2 files changed, 211 insertions(+), 121 deletions(-) diff --git a/src/lib/addons/themes/colors/parser.ts b/src/lib/addons/themes/colors/parser.ts index 4b3eff5..e2d7ced 100644 --- a/src/lib/addons/themes/colors/parser.ts +++ b/src/lib/addons/themes/colors/parser.ts @@ -11,147 +11,224 @@ const tokenRef = findByProps("SemanticColor"); export function parseColorManifest(manifest: ColorManifest): InternalColorDefinition { const resolveType = (type = "dark") => (colorsPref.type ?? type) === "dark" ? "darker" : "light"; - if (manifest.spec === 3) { - const semanticColorDefinitions: InternalColorDefinition["semantic"] = {}; + if (manifest.spec === 3) { + const semanticColorDefinitions: InternalColorDefinition["semantic"] = {}; - for (const [semanticColorKey, semanticColorValue] of Object.entries( - manifest.main.semantic ?? {}, - )) { - if (typeof semanticColorValue === "object") { - const { - type, - value, - opacity: semanticColorOpacity, - } = semanticColorValue; + for (const [semanticColorKey, semanticColorValue] of Object.entries(manifest.main.semantic ?? {})) { + if (typeof semanticColorValue === "object") { + const { type, value, opacity: semanticColorOpacity } = semanticColorValue; - if (type === "raw") { - semanticColorDefinitions[semanticColorKey] = { - value, - opacity: semanticColorOpacity ?? 1, - }; - } else { - const rawColorValue = tokenRef.RawColor[value]; - semanticColorDefinitions[semanticColorKey] = { - value: rawColorValue, - opacity: semanticColorOpacity ?? 1, - }; + if (type === "raw") { + semanticColorDefinitions[semanticColorKey] = { + value, + opacity: semanticColorOpacity ?? 1, + }; + } else { + const rawColorValue = tokenRef.RawColor[value]; + semanticColorDefinitions[semanticColorKey] = { + value: rawColorValue, + opacity: semanticColorOpacity ?? 1, + }; + } + } else if (typeof semanticColorValue === "string") { + if (semanticColorValue.startsWith("#")) { + semanticColorDefinitions[semanticColorKey] = { + value: chroma.hex(semanticColorValue).hex(), + opacity: 1, + }; + } else { + semanticColorDefinitions[semanticColorKey] = { + value: tokenRef.RawColor[semanticColorValue], + opacity: 1, + }; + } + } else { + throw new Error(`Invalid semantic definitions: ${semanticColorValue}`); + } } - } else if (typeof semanticColorValue === "string") { - if (semanticColorValue.startsWith("#")) { - semanticColorDefinitions[semanticColorKey] = { - value: chroma.hex(semanticColorValue).hex(), - opacity: 1, - }; - } else { - semanticColorDefinitions[semanticColorKey] = { - value: tokenRef.RawColor[semanticColorValue], - opacity: 1, - }; - } - } else { - throw new Error(`Invalid semantic definitions: ${semanticColorValue}`); - } - } - if (Platform.OS === "android") applyAndroidAlphaKeys(manifest.main.raw); + if (Platform.OS === "android") applyAndroidAlphaKeys(manifest.main.raw); - return { - spec: 3, - reference: resolveType(manifest.type), - semantic: semanticColorDefinitions, - raw: manifest.main.raw ?? {}, - background: manifest.main.background, - }; - } - - if (manifest.spec === 2) { - // is Vendetta theme - const semanticDefinitions: InternalColorDefinition["semantic"] = {}; - const background: InternalColorDefinition["background"] | undefined = - manifest.background - ? { - ...omit(manifest.background, ["alpha"]), - opacity: manifest.background.alpha, - } - : undefined; - - if (manifest.semanticColors) { - for (const key in manifest.semanticColors) { - const values = manifest.semanticColors[key] - .map((c) => c || undefined) - .slice(0, 2); - if (!values[0]) continue; - - semanticDefinitions[key] = { - value: normalizeToHex(values[resolveType() === "light" ? 1 : 0])!, - opacity: 1, + return { + spec: 3, + reference: resolveType(manifest.type), + semantic: semanticColorDefinitions, + raw: manifest.main.raw ?? {}, + background: manifest.main.background, }; - } } - if (manifest.rawColors) { - const draft: typeof manifest.rawColors = {}; + if (manifest.spec === 2) { // is Vendetta theme + const semanticDefinitions: InternalColorDefinition["semantic"] = {}; + const background: InternalColorDefinition["background"] | undefined = manifest.background ? { + ...omit(manifest.background, ["alpha"]), + opacity: manifest.background.alpha + } : undefined; - for (const key in manifest.rawColors) { - const value = manifest.rawColors[key]; - if (!value) continue; - draft[key] = normalizeToHex(value)!; - } + if (manifest.semanticColors) { + for (const key in manifest.semanticColors) { + const values = manifest.semanticColors[key].map(c => c || undefined).slice(0, 2); + if (!values[0]) continue; - if (Platform.OS === "android") applyAndroidAlphaKeys(draft); - manifest.rawColors = draft; + semanticDefinitions[key] = { + value: normalizeToHex(values[resolveType() === "light" ? 1 : 0])!, + opacity: 1 + }; + } + } + + if (manifest.rawColors) { + const draft: typeof manifest.rawColors = {}; + + for (const key in manifest.rawColors) { + const value = manifest.rawColors[key]; + if (!value) continue; + draft[key] = normalizeToHex(value)!; + } + + if (Platform.OS === "android") applyAndroidAlphaKeys(draft); + manifest.rawColors = draft; + } + + return { + spec: 2, + reference: resolveType(), + semantic: semanticDefinitions, + raw: manifest.rawColors ?? {}, + background + }; } - return { - spec: 2, - reference: resolveType(), - semantic: semanticDefinitions, - raw: manifest.rawColors ?? {}, - background, - }; - } + if (manifest.spec === 1 || (manifest.theme_color_map && !manifest.spec)) { - throw new Error("Invalid theme spec"); + const semanticDefinitions: InternalColorDefinition["semantic"] = {}; + const rawDefinitions: Record = {}; + + const themeIndex = resolveType() === "light" ? 1 : 0; + + if (manifest.theme_color_map) { + for (const [key, colorArray] of Object.entries(manifest.theme_color_map)) { + if (!Array.isArray(colorArray) || colorArray.length < 2) { + console.warn(`[Parser] Invalid color array for ${key}:`, colorArray); + continue; + } + + const colorValue = colorArray[themeIndex]; + + if (!colorValue) { + console.warn(`[Parser] Missing color value for ${key} at index ${themeIndex}`); + continue; + } + + const normalized = normalizeToHex(colorValue); + if (normalized) { + semanticDefinitions[key] = { + value: normalized, + opacity: 1 + }; + } + } + } + + const rawColors = manifest.colours || manifest.colors; + if (rawColors) { + for (const [key, colorValue] of Object.entries(rawColors)) { + if (typeof colorValue !== "string") continue; + + const normalized = normalizeToHex(colorValue); + if (normalized) { + const discordKey = convertEnmityToDiscordRawKey(key); + rawDefinitions[discordKey] = normalized; + + if (discordKey !== key) { + rawDefinitions[key] = normalized; + } + } + } + } + + if (manifest.unsafe_colors) { + for (const [key, colorValue] of Object.entries(manifest.unsafe_colors)) { + if (typeof colorValue !== "string") continue; + + const normalized = normalizeToHex(colorValue); + if (normalized) { + rawDefinitions[key] = normalized; + } + } + } + + if (Platform.OS === "android") applyAndroidAlphaKeys(rawDefinitions); + + return { + spec: 2, + reference: resolveType(), + semantic: semanticDefinitions, + raw: rawDefinitions, + background: manifest.background + }; + } + throw new Error("Invalid theme spec"); } + function convertEnmityToDiscordRawKey(enmityKey: string): string { + const conversions: Record = { + "PRIMARY_DARK": "PRIMARY_100", + "PRIMARY_DARK_100": "PRIMARY_100", + "PRIMARY_DARK_200": "PRIMARY_200", + "PRIMARY_DARK_300": "PRIMARY_300", + "PRIMARY_DARK_360": "PRIMARY_360", + "PRIMARY_DARK_400": "PRIMARY_400", + "PRIMARY_DARK_500": "PRIMARY_500", + "PRIMARY_DARK_600": "PRIMARY_600", + "PRIMARY_DARK_630": "PRIMARY_630", + "PRIMARY_DARK_700": "PRIMARY_700", + "PRIMARY_DARK_800": "PRIMARY_800", + "PRIMARY_DARK_900": "PRIMARY_900", + }; + + return conversions[enmityKey] || enmityKey; + } + export function applyAndroidAlphaKeys(rawColors?: Record) { - if (!rawColors) return; + if (!rawColors) return; - // these are native Discord Android keys - const alphaMap: Record = { - BLACK_ALPHA_60: ["BLACK", 0.6], - BRAND_NEW_360_ALPHA_20: ["BRAND_360", 0.2], - BRAND_NEW_360_ALPHA_25: ["BRAND_360", 0.25], - BRAND_NEW_500_ALPHA_20: ["BRAND_500", 0.2], - PRIMARY_DARK_500_ALPHA_20: ["PRIMARY_500", 0.2], - PRIMARY_DARK_700_ALPHA_60: ["PRIMARY_700", 0.6], - STATUS_GREEN_500_ALPHA_20: ["GREEN_500", 0.2], - STATUS_RED_500_ALPHA_20: ["RED_500", 0.2], - }; + // these are native Discord Android keys + const alphaMap: Record = { + "BLACK_ALPHA_60": ["BLACK", 0.6], + "BRAND_NEW_360_ALPHA_20": ["BRAND_360", 0.2], + "BRAND_NEW_360_ALPHA_25": ["BRAND_360", 0.25], + "BRAND_NEW_500_ALPHA_20": ["BRAND_500", 0.2], + "PRIMARY_DARK_500_ALPHA_20": ["PRIMARY_500", 0.2], + "PRIMARY_DARK_700_ALPHA_60": ["PRIMARY_700", 0.6], + "STATUS_GREEN_500_ALPHA_20": ["GREEN_500", 0.2], + "STATUS_RED_500_ALPHA_20": ["RED_500", 0.2], + }; - for (const key in alphaMap) { - const [colorKey, alpha] = alphaMap[key]; - if (!rawColors[colorKey]) continue; - rawColors[key] = chroma(rawColors[colorKey]).alpha(alpha).hex(); - } + for (const key in alphaMap) { + const [colorKey, alpha] = alphaMap[key]; + if (!rawColors[colorKey]) continue; + rawColors[key] = chroma(rawColors[colorKey]).alpha(alpha).hex(); + } - return rawColors; + return rawColors; } -export function normalizeToHex( - colorString: string | undefined, -): string | undefined { - if (colorString === undefined) return undefined; - if (chroma.valid(colorString)) return chroma(colorString).hex(); +export function normalizeToHex(colorString: string | undefined): string | undefined { + if (colorString === undefined) return undefined; - const color = Number(processColor(colorString)); + if (colorString.toLowerCase() === "transparent") { + return "#00000000"; + } - return chroma - .rgb( - (color >> 16) & 0xff, // red - (color >> 8) & 0xff, // green - color & 0xff, // blue - (color >> 24) & 0xff, // alpha - ) - .hex(); + if (chroma.valid(colorString)) return chroma(colorString).hex(); + + const color = Number(processColor(colorString)); + + return chroma.rgb( + color >> 16 & 0xff, // red + color >> 8 & 0xff, // green + color & 0xff, // blue + color >> 24 & 0xff // alpha + ).hex(); } diff --git a/src/lib/addons/themes/colors/types.ts b/src/lib/addons/themes/colors/types.ts index 203f3c0..b9a6039 100644 --- a/src/lib/addons/themes/colors/types.ts +++ b/src/lib/addons/themes/colors/types.ts @@ -37,6 +37,19 @@ export interface VendettaThemeManifest { }; } +// enmity themes +export interface EnmityColorManifest { + spec?: 1; + name?: string; + description?: string; + authors?: Author[]; + theme_color_map?: Record; + colours?: Record; + colors?: Record; + unsafe_colors?: Record; + background?: BackgroundDefinition; +} + /** @internal */ export interface InternalColorDefinition { spec: 1 | 2 | 3; @@ -49,4 +62,4 @@ export interface InternalColorDefinition { background?: BackgroundDefinition; } -export type ColorManifest = BunnyColorManifest | VendettaThemeManifest; +export type ColorManifest = BunnyColorManifest | VendettaThemeManifest | EnmityColorManifest; From 937144b45ff162e237859cfc0906a72b6f7c8932 Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Mon, 5 Jan 2026 01:01:27 +0100 Subject: [PATCH 5/6] fix: Property hex does not exist on type ChromaStatic --- src/lib/addons/themes/colors/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/addons/themes/colors/parser.ts b/src/lib/addons/themes/colors/parser.ts index e2d7ced..2bafafd 100644 --- a/src/lib/addons/themes/colors/parser.ts +++ b/src/lib/addons/themes/colors/parser.ts @@ -33,7 +33,7 @@ export function parseColorManifest(manifest: ColorManifest): InternalColorDefini } else if (typeof semanticColorValue === "string") { if (semanticColorValue.startsWith("#")) { semanticColorDefinitions[semanticColorKey] = { - value: chroma.hex(semanticColorValue).hex(), + value: chroma(semanticColorValue).hex(), opacity: 1, }; } else { From 0e7283aececcb1e8a17ba54efca0cfd44e4e4ba2 Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Mon, 5 Jan 2026 01:04:59 +0100 Subject: [PATCH 6/6] fix: url.parse is deprecate --- scripts/serve.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/serve.mjs b/scripts/serve.mjs index 6458fa6..e901e14 100644 --- a/scripts/serve.mjs +++ b/scripts/serve.mjs @@ -4,7 +4,7 @@ import { readFile } from "fs/promises"; import http from "http"; import os from "os"; import readline from "readline"; -import url from "url"; +import { URL } from "url"; import yargs from "yargs-parser"; import { buildBundle } from "./build.mjs"; @@ -15,7 +15,7 @@ const args = yargs(process.argv.slice(2)); export function serve(options) { // @ts-ignore const server = http.createServer(async (req, res) => { - const { pathname } = url.parse(req.url || "", true); + const { pathname } = new URL(req.url || "", `http://localhost:${args.port ?? 4040}`); if (pathname?.endsWith(".js")) { try { let fileToServe;