Skip to content

Commit c7782e7

Browse files
authored
Release 0.21.0
Merge pull request #805 from dreamteamprod/dev
2 parents 7e64776 + 78fb8bd commit c7782e7

File tree

63 files changed

+3462
-718
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+3462
-718
lines changed

client/package-lock.json

Lines changed: 36 additions & 31 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "client",
3-
"version": "0.20.2",
3+
"version": "0.21.0",
44
"private": true,
55
"scripts": {
66
"prebuild": "scripts/copy-docs.sh && node scripts/generate-doc-manifest.js",
@@ -55,9 +55,9 @@
5555
"eslint-plugin-import": "2.32.0",
5656
"eslint-plugin-vue": "^9.27.0",
5757
"eslint-plugin-vuejs-accessibility": "1.2.0",
58-
"jsdom": "^27.3.0",
58+
"jsdom": "^27.4.0",
5959
"node-sass": "7.0.3",
60-
"sass": "1.97.0",
60+
"sass": "1.97.1",
6161
"sass-loader": "13.3.3",
6262
"vite": "4.5.5",
6363
"vitest": "^4.0.16"

client/src/constants/lineTypes.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Script Line Type Constants
3+
*
4+
* These constants match the ScriptLineType enum values from the backend
5+
* (models/script.py). Use these instead of magic numbers throughout the codebase.
6+
*/
7+
8+
export const LINE_TYPES = {
9+
DIALOGUE: 1,
10+
STAGE_DIRECTION: 2,
11+
CUE_LINE: 3,
12+
SPACING: 4,
13+
};
14+
15+
export default LINE_TYPES;

client/src/js/scriptUtils.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { LINE_TYPES } from '@/constants/lineTypes';
2+
3+
/**
4+
* Determines if a script line is completely cut (all parts cut or empty).
5+
*
6+
* @param {Object} line - The script line object
7+
* @param {Array} cuts - Array of cut line part IDs
8+
* @returns {boolean} - True if the entire line is cut, false otherwise
9+
*/
10+
export function isWholeLineCut(line, cuts) {
11+
// CUE_LINE can never be completely cut
12+
if (line.line_type === LINE_TYPES.CUE_LINE) {
13+
return false;
14+
}
15+
16+
// SPACING lines are always considered cut
17+
if (line.line_type === LINE_TYPES.SPACING) {
18+
return true;
19+
}
20+
21+
// For other line types, check if all line parts are cut or empty
22+
return line.line_parts.every((linePart) => (
23+
cuts.includes(linePart.id)
24+
|| linePart.line_text == null
25+
|| linePart.line_text.trim().length === 0
26+
));
27+
}
28+
29+
export default { isWholeLineCut };
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { mapGetters } from 'vuex';
2+
import { contrastColor } from 'contrast-color';
3+
4+
/**
5+
* Shared mixin for cue display logic across live view components.
6+
* Provides methods for cue color handling with user override support.
7+
*/
8+
export default {
9+
computed: {
10+
...mapGetters(['CUE_COLOUR_OVERRIDES']),
11+
},
12+
methods: {
13+
contrastColor,
14+
cuePrefix(cue) {
15+
const cueType = this.cueTypes.find((cT) => (cT.id === cue.cue_type_id));
16+
return cueType.prefix;
17+
},
18+
cueLabel(cue) {
19+
const cueType = this.cueTypes.find((cT) => cT.id === cue.cue_type_id);
20+
return `${cueType.prefix} ${cue.ident}`;
21+
},
22+
cueBackgroundColour(cue) {
23+
const cueType = this.cueTypes.find((ct) => ct.id === cue.cue_type_id);
24+
if (!cueType) return '#000000'; // Fallback
25+
26+
// Check if user has an override for this cue type
27+
const override = this.CUE_COLOUR_OVERRIDES.find((o) => o.settings.id === cueType.id);
28+
if (override) {
29+
return override.settings.colour;
30+
}
31+
32+
return cueType.colour;
33+
},
34+
},
35+
};
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { LINE_TYPES } from '@/constants/lineTypes';
2+
3+
/**
4+
* Shared mixin for script display presentation logic.
5+
* Handles stage directions, act/scene labels, intervals, and viewport tracking.
6+
*
7+
* This mixin assumes the component has:
8+
* - Props: line, lineIndex, previousLine, previousLineIndex, acts, scenes,
9+
* stageDirectionStyles, stageDirectionStyleOverrides
10+
* - Refs: lineContainer
11+
* - Methods: isWholeLineCut, getPreviousLineForIndex (from scriptNavigationMixin)
12+
*/
13+
export default {
14+
data() {
15+
return {
16+
observer: null,
17+
};
18+
},
19+
computed: {
20+
needsActSceneLabel() {
21+
let { previousLine } = this;
22+
let lineIndex = this.previousLineIndex;
23+
while (previousLine != null && this.isWholeLineCut(previousLine)) {
24+
[lineIndex, previousLine] = this.getPreviousLineForIndex(previousLine.page, lineIndex);
25+
}
26+
if (previousLine == null) {
27+
return true;
28+
}
29+
return !(previousLine.act_id === this.line.act_id
30+
&& previousLine.scene_id === this.line.scene_id);
31+
},
32+
needsIntervalBanner() {
33+
let { previousLine, lineIndex } = this;
34+
while (previousLine != null && this.isWholeLineCut(previousLine)) {
35+
[lineIndex, previousLine] = this.getPreviousLineForIndex(previousLine.page, lineIndex);
36+
}
37+
if (previousLine == null) {
38+
return false;
39+
}
40+
return previousLine.act_id !== this.line.act_id;
41+
},
42+
previousActLabel() {
43+
return this.acts.find((act) => (act.id === this.previousLine.act_id)).name;
44+
},
45+
actLabel() {
46+
return this.acts.find((act) => (act.id === this.line.act_id)).name;
47+
},
48+
sceneLabel() {
49+
return this.scenes.find((scene) => (scene.id === this.line.scene_id)).name;
50+
},
51+
stageDirectionStyle() {
52+
const sdStyle = this.stageDirectionStyles.find(
53+
(style) => (style.id === this.line.stage_direction_style_id),
54+
);
55+
const override = this.stageDirectionStyleOverrides
56+
.find((elem) => elem.settings.id === sdStyle.id);
57+
if (this.line.line_type === LINE_TYPES.STAGE_DIRECTION) {
58+
return override ? override.settings : sdStyle;
59+
}
60+
return null;
61+
},
62+
stageDirectionStyling() {
63+
if (this.line.stage_direction_style_id == null || this.stageDirectionStyle == null) {
64+
return {
65+
'background-color': 'darkslateblue',
66+
'font-style': 'italic',
67+
};
68+
}
69+
const style = {
70+
'font-weight': this.stageDirectionStyle.bold ? 'bold' : 'normal',
71+
'font-style': this.stageDirectionStyle.italic ? 'italic' : 'normal',
72+
'text-decoration-line': this.stageDirectionStyle.underline ? 'underline' : 'none',
73+
color: this.stageDirectionStyle.text_colour,
74+
};
75+
if (this.stageDirectionStyle.enable_background_colour) {
76+
style['background-color'] = this.stageDirectionStyle.background_colour;
77+
}
78+
return style;
79+
},
80+
},
81+
mounted() {
82+
/* eslint-disable no-restricted-syntax */
83+
this.observer = new MutationObserver((mutations) => {
84+
for (const m of mutations) {
85+
const newValue = m.target.getAttribute(m.attributeName);
86+
this.$nextTick(() => {
87+
this.onClassChange(newValue, m.oldValue);
88+
});
89+
}
90+
});
91+
/* eslint-enable no-restricted-syntax */
92+
93+
this.observer.observe(this.$refs.lineContainer, {
94+
attributes: true,
95+
attributeOldValue: true,
96+
attributeFilter: ['class'],
97+
});
98+
},
99+
destroyed() {
100+
this.observer.disconnect();
101+
},
102+
methods: {
103+
onClassChange(classAttrValue, oldClassAttrValue) {
104+
const classList = classAttrValue.split(' ');
105+
const oldClassList = oldClassAttrValue.split(' ');
106+
if (classList.includes('last-script-element') && !oldClassList.includes('last-script-element')) {
107+
this.$emit('last-line-change', this.line.page, this.lineIndex);
108+
}
109+
if (classList.includes('first-script-element') && !oldClassList.includes('first-script-element')) {
110+
let previousLine = null;
111+
if (this.previousLine != null) {
112+
previousLine = `page_${this.previousLine.page}_line_${this.previousLineIndex}`;
113+
}
114+
this.$emit('first-line-change', this.line.page, this.lineIndex, previousLine);
115+
}
116+
},
117+
startInterval() {
118+
this.$emit('start-interval', this.acts.find((act) => (act.id === this.previousLine.act_id)).id);
119+
},
120+
},
121+
};

0 commit comments

Comments
 (0)