diff --git a/cmd/qrunproxy/static/index.html b/cmd/qrunproxy/static/index.html
index 2ae4eb3..bc14fb8 100644
--- a/cmd/qrunproxy/static/index.html
+++ b/cmd/qrunproxy/static/index.html
@@ -177,28 +177,28 @@ function render(data) {
for (let r = 0; r < numRows; r++) {
const cells = data.tracks.map(t => t.cells[r] || {});
- const hasCue = cells.some(c => c.block_id && c.event && (data.blocks[c.block_id] || {}).type === 'cue');
- const hasSignal = !hasCue && cells.some(c => c.event && c.is_signal);
+ const hasCue = cells.some(c => (c.type === 'event' || c.type === 'signal') && c.block_id && (data.blocks[c.block_id] || {}).type === 'cue');
+ const hasSignal = !hasCue && cells.some(c => c.type === 'signal');
const rowCls = hasCue ? ' cue-row' : (hasSignal ? ' sig-row' : '');
cells.forEach((c, ti) => {
const div = document.createElement('div');
div.className = 'cell' + rowCls;
- if (c.is_title) {
+ if (c.type === 'title') {
const block = data.blocks[c.block_id] || {};
const loop = block.loop ? ' \u21A9' : '';
div.innerHTML = `
${block.name || ''}${loop}
`;
- } else if (c.is_chain) {
+ } else if (c.type === 'chain') {
const nextCell = data.tracks[ti]?.cells[r+1] || {};
- const sym = nextCell.is_start ? '\u2193' : '\u2502';
+ const sym = nextCell.event === 'START' ? '\u2193' : '\u2502';
div.innerHTML = `${sym}
`;
- } else if (c.block_id) {
+ } else if (c.type === 'event' || c.type === 'signal') {
const block = data.blocks[c.block_id] || {};
- const isInfinity = r === numRows - 1 && !c.is_end && !c.is_title;
+ const isInfinity = r === numRows - 1 && c.event !== 'END' && c.event !== 'GO';
let seg = 'mid';
- if (c.is_start && c.is_end) seg = 'single';
- else if (c.is_start) seg = 'start';
- else if (c.is_end) seg = 'end';
+ if (c.event === 'GO') seg = 'single';
+ else if (c.event === 'START') seg = 'start';
+ else if (c.event === 'END') seg = 'end';
div.className += isInfinity ? ' infinity-cell' : '';
let inner = ``;
@@ -206,12 +206,15 @@ function render(data) {
inner += `
${block.name || ''}
`;
} else if (c.event) {
let hCls = 'hook';
- if (c.is_signal) hCls += ' sig';
+ if (c.type === 'signal') hCls += ' sig';
inner += `
${c.event.replace('_', ' ')}
`;
}
inner += `
`;
if (isInfinity) inner += `∿∿∿
`;
div.innerHTML = inner;
+ } else if (c.type === 'continuation') {
+ const block = data.blocks[c.block_id] || {};
+ div.innerHTML = ``;
}
timeline.appendChild(div);
});
diff --git a/cmd/qrunproxy/timeline.go b/cmd/qrunproxy/timeline.go
index 97947e9..d79e43c 100644
--- a/cmd/qrunproxy/timeline.go
+++ b/cmd/qrunproxy/timeline.go
@@ -20,15 +20,21 @@ type Timeline struct {
exclusives []exclusiveGroup `json:"-"`
}
+type CellType string
+
+const (
+ CellEvent CellType = "event"
+ CellTitle CellType = "title"
+ CellContinuation CellType = "continuation"
+ CellGap CellType = "gap"
+ CellChain CellType = "chain"
+ CellSignal CellType = "signal"
+)
+
type TimelineCell struct {
+ Type CellType `json:"type"`
BlockID string `json:"block_id,omitempty"`
- IsStart bool `json:"is_start,omitempty"`
- IsEnd bool `json:"is_end,omitempty"`
Event string `json:"event,omitempty"`
- IsTitle bool `json:"is_title,omitempty"`
- IsSignal bool `json:"is_signal,omitempty"`
- IsGap bool `json:"-"`
- IsChain bool `json:"is_chain,omitempty"`
row int `json:"-"`
track *TimelineTrack `json:"-"`
}
@@ -84,7 +90,7 @@ func (g exclusiveGroup) satisfied(tracks []*TimelineTrack) bool {
continue
}
c := t.Cells[row]
- if c.IsGap || c.IsChain || c.BlockID == "" {
+ if c.Type != CellEvent && c.Type != CellTitle && c.Type != CellSignal {
continue
}
return false
@@ -167,19 +173,18 @@ func (track *TimelineTrack) appendCells(cells ...*TimelineCell) {
func getCueCells(block *Block) []*TimelineCell {
return []*TimelineCell{{
+ Type: CellEvent,
BlockID: block.ID,
- IsStart: true,
- IsEnd: true,
Event: "GO",
}}
}
func getBlockCells(block *Block) []*TimelineCell {
return []*TimelineCell{
- {BlockID: block.ID, IsStart: true, Event: "START"},
- {BlockID: block.ID, IsTitle: true},
- {BlockID: block.ID, Event: "FADE_OUT"},
- {BlockID: block.ID, IsEnd: true, Event: "END"},
+ {Type: CellEvent, BlockID: block.ID, Event: "START"},
+ {Type: CellTitle, BlockID: block.ID},
+ {Type: CellEvent, BlockID: block.ID, Event: "FADE_OUT"},
+ {Type: CellEvent, BlockID: block.ID, Event: "END"},
}
}
@@ -214,9 +219,9 @@ func (tl *Timeline) buildCells(endChains map[string]bool) {
}
if block.Type != "cue" && lastOnTrack[block.Track] != block {
if endChains[block.ID] {
- track.appendCells(&TimelineCell{IsChain: true})
+ track.appendCells(&TimelineCell{Type: CellChain})
} else {
- track.appendCells(&TimelineCell{IsGap: true})
+ track.appendCells(&TimelineCell{Type: CellGap})
}
}
}
@@ -232,7 +237,7 @@ func (tl *Timeline) buildConstraints() {
t := tl.findCell(target.Block, target.Hook)
if source.track != t.track {
tl.addConstraint("same_row", source, t)
- source.IsSignal = true
+ source.Type = CellSignal
}
group.members = append(group.members, t)
}
@@ -301,7 +306,7 @@ func (tl *Timeline) enforceExclusives() bool {
continue
}
c := t.Cells[row]
- if c.IsGap || c.IsChain || c.BlockID == "" {
+ if c.Type != CellEvent && c.Type != CellTitle && c.Type != CellSignal {
continue
}
tl.insertGap(t, row)
@@ -320,11 +325,11 @@ func (tl *Timeline) isAllRemovableGapRow(row int, except *TimelineTrack) bool {
continue
}
c := t.Cells[row]
- if !c.IsGap && !c.IsChain {
+ if c.Type != CellGap && c.Type != CellChain && c.Type != CellContinuation {
return false
}
- hasBefore := row > 0 && t.Cells[row-1].BlockID != "" && !t.Cells[row-1].IsGap && !t.Cells[row-1].IsChain
- hasAfter := row+1 < len(t.Cells) && t.Cells[row+1].BlockID != "" && !t.Cells[row+1].IsGap && !t.Cells[row+1].IsChain
+ hasBefore := row > 0 && (t.Cells[row-1].Type == CellEvent || t.Cells[row-1].Type == CellTitle || t.Cells[row-1].Type == CellSignal)
+ hasAfter := row+1 < len(t.Cells) && (t.Cells[row+1].Type == CellEvent || t.Cells[row+1].Type == CellTitle || t.Cells[row+1].Type == CellSignal)
if hasBefore && hasAfter {
return false
}
@@ -358,10 +363,11 @@ func (tl *Timeline) insertGap(track *TimelineTrack, beforeIndex int) {
return
}
- gap := &TimelineCell{IsGap: true, row: beforeIndex, track: track}
+ gap := &TimelineCell{Type: CellGap, row: beforeIndex, track: track}
if beforeIndex > 0 {
prev := track.Cells[beforeIndex-1]
- if prev.BlockID != "" && !prev.IsEnd {
+ if prev.BlockID != "" && prev.Event != "END" && prev.Event != "GO" {
+ gap.Type = CellContinuation
gap.BlockID = prev.BlockID
}
}