From 030060286f27a84e15c315a30ea459a4ff982724 Mon Sep 17 00:00:00 2001 From: Aran Roig Date: Sat, 2 May 2026 23:37:17 +0200 Subject: [PATCH] Dice rollers! --- frontend/app/assets/css/colors.scss | 2 + .../app/components/viewer/content/Note.vue | 13 +- .../widgets/display/RollWidgetDisplay.vue | 177 ++++++++++++++++++ .../RollWidgetInline.vue} | 0 frontend/app/services/Marker.js | 28 +-- frontend/app/services/widgets/DiceParser.js | 30 +-- 6 files changed, 221 insertions(+), 29 deletions(-) create mode 100644 frontend/app/components/viewer/widgets/display/RollWidgetDisplay.vue rename frontend/app/components/viewer/widgets/{RollWidget.vue => inline/RollWidgetInline.vue} (100%) diff --git a/frontend/app/assets/css/colors.scss b/frontend/app/assets/css/colors.scss index 86a79c3..180d11a 100644 --- a/frontend/app/assets/css/colors.scss +++ b/frontend/app/assets/css/colors.scss @@ -31,6 +31,7 @@ $themes: ( red: #e06c75, green: #98c379, + gray: #cccccc, icon-invert: 100% ), @@ -63,6 +64,7 @@ $themes: ( red: #e06c75, green: #98c379, + gray: #cccccc, icon-invert: 0% ) diff --git a/frontend/app/components/viewer/content/Note.vue b/frontend/app/components/viewer/content/Note.vue index 4340942..b470748 100644 --- a/frontend/app/components/viewer/content/Note.vue +++ b/frontend/app/components/viewer/content/Note.vue @@ -28,11 +28,14 @@ const compiledMarkdown = computed(() => { }); function mountComponents() { - const nodes = document.querySelectorAll('.vue-component'); - - nodes.forEach(el => { - const app = createApp(GetWidget(el.dataset.component), { content: el.dataset.content }); - app.mount(el); + // Should no need more + const widget_types = ['display', 'inline']; + widget_types.forEach((widget_type) => { + const nodes = document.querySelectorAll('.vue-component-' + widget_type); + nodes.forEach(el => { + const app = createApp(GetWidget(widget_type, el.dataset.component), { content: el.dataset.content }); + app.mount(el); + }); }); } /// diff --git a/frontend/app/components/viewer/widgets/display/RollWidgetDisplay.vue b/frontend/app/components/viewer/widgets/display/RollWidgetDisplay.vue new file mode 100644 index 0000000..2511369 --- /dev/null +++ b/frontend/app/components/viewer/widgets/display/RollWidgetDisplay.vue @@ -0,0 +1,177 @@ + + + + + \ No newline at end of file diff --git a/frontend/app/components/viewer/widgets/RollWidget.vue b/frontend/app/components/viewer/widgets/inline/RollWidgetInline.vue similarity index 100% rename from frontend/app/components/viewer/widgets/RollWidget.vue rename to frontend/app/components/viewer/widgets/inline/RollWidgetInline.vue diff --git a/frontend/app/services/Marker.js b/frontend/app/services/Marker.js index 7d87c6a..574393e 100644 --- a/frontend/app/services/Marker.js +++ b/frontend/app/services/Marker.js @@ -1,20 +1,26 @@ import { Marked } from "marked"; const widget_map = { - test: () => import("~/components/viewer/widgets/TestWidget.vue"), - table: () => import("~/components/viewer/widgets/TableWidget.vue"), - roll: () => import("~/components/viewer/widgets/RollWidget.vue"), + inline: { + roll: () => import("~/components/viewer/widgets/inline/RollWidgetInline.vue"), + }, + display: { + roll: () => import("~/components/viewer/widgets/display/RollWidgetDisplay.vue"), + } }; -const componentCache = {} +const componentCache = { + inline: {}, + display: {} +} -const GetWidget = (type) => { - if (!componentCache[type]) { - componentCache[type] = defineAsyncComponent( - widget_map[type] +const GetWidget = (type, name) => { + if (!componentCache[type][name]) { + componentCache[type][name] = defineAsyncComponent( + widget_map[type][name] ) } - return componentCache[type] + return componentCache[type][name] } @@ -39,7 +45,7 @@ const extension = { }, renderer(token) { - return `
`; + return `
`; }, }; @@ -64,7 +70,7 @@ const inlineExtension = { }, renderer(token) { - return ``; + return ``; }, }; diff --git a/frontend/app/services/widgets/DiceParser.js b/frontend/app/services/widgets/DiceParser.js index 6022fd0..03f6e9d 100644 --- a/frontend/app/services/widgets/DiceParser.js +++ b/frontend/app/services/widgets/DiceParser.js @@ -1,4 +1,4 @@ -// dice-parser.js +// DiceParser.js function roll(sides) { return Math.floor(Math.random() * sides) + 1; @@ -27,10 +27,9 @@ export function parse(expr) { const tokens = tokenize(expr); if (!tokens.length) throw new Error('Empty expression'); let pos = 0; - const rolls = []; - function peek() { return tokens[pos]; } - function consume() { return tokens[pos++]; } + const peek = () => tokens[pos]; + const consume = () => tokens[pos++]; function parseExpr() { return parseAddSub(); } @@ -41,7 +40,7 @@ export function parse(expr) { const right = parseMulDiv(); left = { value: op === '+' ? left.value + right.value : left.value - right.value, - rolls: [...left.rolls, ...right.rolls], + steps: [...left.steps, { type: 'op', op }, ...right.steps], }; } return left; @@ -55,7 +54,7 @@ export function parse(expr) { if (op === '/' && right.value === 0) throw new Error('Division by zero'); left = { value: op === '*' ? left.value * right.value : Math.floor(left.value / right.value), - rolls: [...left.rolls, ...right.rolls], + steps: [...left.steps, { type: 'op', op }, ...right.steps], }; } return left; @@ -65,7 +64,7 @@ export function parse(expr) { if (peek() === '-') { consume(); const r = parsePrimary(); - return { value: -r.value, rolls: r.rolls }; + return { value: -r.value, steps: [{ type: 'op', op: '-' }, ...r.steps] }; } return parsePrimary(); } @@ -79,7 +78,10 @@ export function parse(expr) { const inner = parseExpr(); if (peek() !== ')') throw new Error('Missing closing )'); consume(); - return inner; + return { + value: inner.value, + steps: [{ type: 'op', op: '(' }, ...inner.steps, { type: 'op', op: ')' }], + }; } const diceInfo = parseDiceToken(tok); @@ -90,7 +92,8 @@ export function parse(expr) { if (/^\d+$/.test(tok)) { consume(); - return { value: parseInt(tok), rolls: [] }; + const v = parseInt(tok); + return { value: v, steps: [{ type: 'const', value: v }] }; } throw new Error(`Unexpected token: ${tok}`); @@ -119,12 +122,13 @@ export function parse(expr) { } const entry = { sides, rawRolls, kept, mod, value: kept.reduce((a, b) => a + b, 0) }; - rolls.push(entry); - return { value: entry.value, rolls: [entry] }; + return { + value: entry.value, + steps: [{ type: 'dice', entry }], + }; } const result = parseExpr(); if (pos < tokens.length) throw new Error(`Unexpected token: ${tokens[pos]}`); - - return { total: result.value, rolls: result.rolls }; + return { total: result.value, steps: result.steps }; } \ No newline at end of file