From 3ee36c03df3ed1ff6b71d4ba02b147968329dd70 Mon Sep 17 00:00:00 2001 From: Pandipipas <62224708+Pandipipas@users.noreply.github.com> Date: Thu, 12 Feb 2026 00:13:18 +0100 Subject: [PATCH] Add commentary panel and Tekken-inspired OBS overlay --- package.json | 5 + schemas/commentary.json | 23 +++ src/browser_shared/replicants.ts | 2 + .../example/components/CommentaryPanel.vue | 77 ++++++++ src/dashboard/example/stores/commentary.ts | 84 ++++++++ src/dashboard/example/views/Dashboard.vue | 28 ++- src/extension/util/replicants.ts | 8 + src/graphics/commentary/main.ts | 8 + src/graphics/commentary/main.vue | 179 ++++++++++++++++++ src/types/schemas.d.ts | 1 + src/types/schemas/commentary.d.ts | 12 ++ 11 files changed, 419 insertions(+), 8 deletions(-) create mode 100644 schemas/commentary.json create mode 100644 src/dashboard/example/components/CommentaryPanel.vue create mode 100644 src/dashboard/example/stores/commentary.ts create mode 100644 src/graphics/commentary/main.ts create mode 100644 src/graphics/commentary/main.vue create mode 100644 src/types/schemas/commentary.d.ts diff --git a/package.json b/package.json index adcdd43..32751b3 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,11 @@ "file": "scoreboard/main.html", "width": 1920, "height": 1080 + }, + { + "file": "commentary/main.html", + "width": 1920, + "height": 1080 } ] }, diff --git a/schemas/commentary.json b/schemas/commentary.json new file mode 100644 index 0000000..455a223 --- /dev/null +++ b/schemas/commentary.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "leftCommentator": { + "type": "string", + "default": "" + }, + "rightCommentator": { + "type": "string", + "default": "" + } + }, + "required": [ + "leftCommentator", + "rightCommentator" + ], + "default": { + "leftCommentator": "", + "rightCommentator": "" + } +} diff --git a/src/browser_shared/replicants.ts b/src/browser_shared/replicants.ts index 667914d..d986f9c 100644 --- a/src/browser_shared/replicants.ts +++ b/src/browser_shared/replicants.ts @@ -12,3 +12,5 @@ const thisBundle = 'scoreko-dev'; export const exampleReplicant = useReplicant('exampleReplicant', thisBundle); export const playersReplicant = useReplicant('players', thisBundle); export const scoreboardReplicant = useReplicant('scoreboard', thisBundle); + +export const commentaryReplicant = useReplicant('commentary', thisBundle); diff --git a/src/dashboard/example/components/CommentaryPanel.vue b/src/dashboard/example/components/CommentaryPanel.vue new file mode 100644 index 0000000..dc0454c --- /dev/null +++ b/src/dashboard/example/components/CommentaryPanel.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/dashboard/example/stores/commentary.ts b/src/dashboard/example/stores/commentary.ts new file mode 100644 index 0000000..9ae5b4d --- /dev/null +++ b/src/dashboard/example/stores/commentary.ts @@ -0,0 +1,84 @@ +import { defineStore } from 'pinia'; +import { computed, ref, watch } from 'vue'; +import { commentaryReplicant } from '../../../browser_shared/replicants'; +import type { Schemas } from '../../../types'; + +type Commentary = Schemas.Commentary; + +const defaultCommentary: Commentary = { + leftCommentator: '', + rightCommentator: '', +}; + +const normalizeCommentary = (input: unknown): Commentary => { + const candidate = typeof input === 'object' && input !== null ? (input as Record) : {}; + return { + leftCommentator: typeof candidate.leftCommentator === 'string' ? candidate.leftCommentator : '', + rightCommentator: typeof candidate.rightCommentator === 'string' ? candidate.rightCommentator : '', + }; +}; + +export const useCommentaryStore = defineStore('commentary', () => { + const commentary = ref({ ...defaultCommentary }); + const replicant = commentaryReplicant; + const isApplyingReplicant = ref(false); + + watch( + () => replicant?.data, + (value) => { + if (!value) { + return; + } + isApplyingReplicant.value = true; + commentary.value = normalizeCommentary(value); + isApplyingReplicant.value = false; + }, + { deep: true, immediate: true }, + ); + + watch( + commentary, + (value) => { + if (isApplyingReplicant.value || !replicant) { + return; + } + replicant.data = normalizeCommentary(value); + replicant.save(); + }, + { deep: true, flush: 'sync' }, + ); + + const leftCommentator = computed({ + get: () => commentary.value.leftCommentator, + set: (value: string) => { + commentary.value = { + ...commentary.value, + leftCommentator: value, + }; + }, + }); + + const rightCommentator = computed({ + get: () => commentary.value.rightCommentator, + set: (value: string) => { + commentary.value = { + ...commentary.value, + rightCommentator: value, + }; + }, + }); + + const swapCommentators = () => { + commentary.value = { + leftCommentator: commentary.value.rightCommentator, + rightCommentator: commentary.value.leftCommentator, + }; + }; + + return { + commentary, + leftCommentator, + rightCommentator, + swapCommentators, + }; +}); diff --git a/src/dashboard/example/views/Dashboard.vue b/src/dashboard/example/views/Dashboard.vue index bc84282..60dbdeb 100644 --- a/src/dashboard/example/views/Dashboard.vue +++ b/src/dashboard/example/views/Dashboard.vue @@ -1,6 +1,7 @@ + + + + diff --git a/src/types/schemas.d.ts b/src/types/schemas.d.ts index e1dc2ec..0ec0ae0 100644 --- a/src/types/schemas.d.ts +++ b/src/types/schemas.d.ts @@ -4,6 +4,7 @@ * Also see index.d.ts for a "grouped" re-export of this as well. */ +export type { Commentary } from './schemas/commentary.d.ts'; export type { Configschema } from './schemas/configschema.d.ts'; export type { ExampleReplicant } from './schemas/exampleReplicant.d.ts'; export type { Players } from './schemas/players.d.ts'; diff --git a/src/types/schemas/commentary.d.ts b/src/types/schemas/commentary.d.ts new file mode 100644 index 0000000..4515a7f --- /dev/null +++ b/src/types/schemas/commentary.d.ts @@ -0,0 +1,12 @@ +/* prettier-ignore */ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +export interface Commentary { + leftCommentator: string; + rightCommentator: string; +}