diff --git a/examples/raytrace.dx b/examples/raytrace.dx index c4aa278e5..4e9ca85d8 100644 --- a/examples/raytrace.dx +++ b/examples/raytrace.dx @@ -8,6 +8,7 @@ described import png import plot + '## Generic Helper Functions Some of these should probably go in prelude. diff --git a/src/lib/RenderHtml.hs b/src/lib/RenderHtml.hs index 3923453c0..eebc99812 100644 --- a/src/lib/RenderHtml.hs +++ b/src/lib/RenderHtml.hs @@ -44,7 +44,8 @@ data RenderedSourceBlock = RenderedSourceBlock , rsbNumLines :: Int , rsbBlockId :: BlockId , rsbLexemeList :: [SrcId] - , rsbHtml :: String } + , rsbText :: String + , rsbHtml :: String} deriving (Generic) type RenderedOutputs = [RenderedOutput] @@ -79,6 +80,7 @@ renderSourceBlock n b = RenderedSourceBlock , rsbNumLines = length $ lines $ T.unpack $ sbText b , rsbBlockId = n , rsbLexemeList = unsnoc $ lexemeList $ sbLexemeInfo b + , rsbText = T.unpack $ sbText b , rsbHtml = renderHtml case sbContents b of Misc (ProseBlock s) -> cdiv "prose-block" $ mdToHtml s _ -> renderSpans n (sbLexemeInfo b) (sbText b) diff --git a/static/dynamic.html b/static/dynamic.html index bc8e0b487..cb4da817f 100644 --- a/static/dynamic.html +++ b/static/dynamic.html @@ -21,10 +21,10 @@ -
- (hover over code for more information) -
+
+
(hover over code for more information)
+ diff --git a/static/index.ts b/static/index.ts index 422478990..72ee1c7d1 100644 --- a/static/index.ts +++ b/static/index.ts @@ -9,6 +9,7 @@ function oops() : T {throw new Error("oops")} const body : Element = document.getElementById("main-output") ?? oops() const hoverInfoDiv : Element = document.getElementById("hover-info") ?? oops() +const minimap : Element = document.getElementById("minimap") ?? oops() type CellId = number type SrcId = number @@ -24,10 +25,11 @@ type HoverMap = Map type Cell = { cellId : CellId; root : Div; - status : Div; lineNums : Div; source : Div; results : Div; + status : Div; + sourceText : string; spanMap : SpanMap; highlightMap : HighlightMap; hoverMap : HoverMap} @@ -39,6 +41,7 @@ type HsRenderedSourceBlock = { rsbNumLines : number; rsbBlockId : CellId; rsbLexemeList : SrcId[]; + rsbText : string; rsbHtml : HTMLString } type HsRenderedOutput = {tag: "RenderedTextOut" ; contents: string } | @@ -74,12 +77,10 @@ type HsMsg = { const cells = new Map() const curHighlights : Div[] = [] // HTML elements currently highlighted -let frozenHover:boolean = false; function processUpdates(msg:HsMsg) { - for (let i = 0; i < msg.orderedNodesUpdate.numDropped; i++) { - let cell : Element = body.lastElementChild ?? oops() - cell.remove();} + dropElements(body , msg.orderedNodesUpdate.numDropped) + dropElements(minimap, msg.orderedNodesUpdate.numDropped) msg.nodeMapUpdate.forEach(function (elt:[CellId, HsCellMapUpdate]) { const [cellId, update] = elt switch (update.tag) { @@ -92,44 +93,55 @@ function processUpdates(msg:HsMsg) { updateCellContents(lookupCell(cellId), update.contents);}}) msg.orderedNodesUpdate.newTail.forEach(function (cellId) { const cell : Cell = lookupCell(cellId); - body.appendChild(cell.root);}) + body.appendChild(cell.root) + minimap.appendChild(cell.status);}) msg.nodeMapUpdate.forEach(function (elt:[CellId, HsCellMapUpdate]) { const [cellId, update] = elt switch (update.tag) { case "Create": const cell : Cell = lookupCell(cellId) const lexemes : SrcId[] = update.contents[0].rsbLexemeList + attachStatusHovertip(cell) lexemes.map((lexemeId) => attachHovertip(cell, lexemeId)) break case "Update": // nothing }}) } +function dropElements(div:Div, n:number) { + for (let i = 0; i < n; i++) { + const cell : Element = div.lastElementChild ?? oops() + cell.remove()} +} function lookupCell(cellId: CellId) : Cell { return cells.get(cellId) ?? oops() } // Structure of each cell // [cell] -// [status] [line-nums] [contents] -// [source] -// [results] -// [result] -// [result] +// [line-nums] [contents] +// [source] +// [results] +// [result] +// [result] function createCell(cellId: CellId) : Cell { const root : Div = document.createElement("div") + const cellHtmlId : string = "cell_".concat(cellId.toString()) + root.id = cellHtmlId root.className = "cell" - const status : Div = addChild(root, "status") const lineNums : Div = addChild(root, "line-nums") const contents : Div = addChild(root, "contents") const source : Div = addChild(contents, "source") const results : Div = addChild(contents, "results") - source.innerHTML + const status : Div = document.createElement("a") + status.setAttribute("href", "#".concat(cellHtmlId)) + status.className = "status" const cell : Cell = { cellId : cellId, root : root, - status : status, lineNums : lineNums, source : source, results : results, + status : status, + sourceText : "", spanMap : new Map(), highlightMap : new Map(), hoverMap : new Map()} @@ -143,6 +155,7 @@ function initializeCell(cell: Cell, state: HsCellState) { const lineNum : number = i + source.rsbLine const s : string = lineNum.toString().concat("\n") appendText(cell.lineNums, s)} + cell.sourceText = source.rsbText setCellStatus(cell, status) extendCellOutput(cell, outs) } @@ -153,24 +166,24 @@ function updateCellContents(cell:Cell, update:HsCellUpdate) { extendCellOutput(cell, outputs) } function removeHover() { - if (frozenHover) return; hoverInfoDiv.innerHTML = "" curHighlights.map(function (element) { element.classList.remove("highlighted", "highlighted-leaf")}) curHighlights.length = 0 } -function toggleFrozenHover() { - if (frozenHover) { - frozenHover = false - removeHover() - } else { - frozenHover = true} +function attachStatusHovertip(cell:Cell) { + cell.status.addEventListener("mouseover", function (event:Event) { + event.stopPropagation() + setHoverInfo(cell.sourceText)}) + cell.status.addEventListener("mouseout", function (event:Event) { + event.stopPropagation() + removeHover()}) } function attachHovertip(cell:Cell, srcId:SrcId) { let span = selectSpan(cell, srcId) span.addEventListener("mouseover", function (event:Event) { event.stopPropagation() - applyHover(cell, srcId)}) + applyCellHover(cell, srcId)}) span.addEventListener("mouseout" , function (event:Event) { event.stopPropagation() removeHover()}) @@ -217,9 +230,13 @@ function cellStatusClass(status: Status) : string { default: return oops()} } +function setDivStatus(div: Div, status: Status) { + div.classList.remove("status-waiting", "status-running", "status-success") + div.classList.add(cellStatusClass(status)) +} function setCellStatus(cell: Cell, status: Status) { - cell.status.className = "status" - cell.status.classList.add(cellStatusClass(status)) + setDivStatus(cell.lineNums, status) + setDivStatus(cell.status , status) } function addTextResult(cell:Cell, contents:string) { const child = addChild(cell.results, "text-result") @@ -273,15 +290,15 @@ function extendSourceInfo(cell: Cell, info: HsSourceInfo) { // nothing } } -function applyHover(cell:Cell, srcId:SrcId) { - if (frozenHover) return; - applyHoverInfo(cell, srcId) +function applyCellHover(cell:Cell, srcId:SrcId) { applyHoverHighlights(cell, srcId) -} -function applyHoverInfo(cell:Cell, srcId:SrcId) { const hoverInfo : undefined | HTMLString = cell.hoverMap.get(srcId) if (hoverInfo !== undefined) { - hoverInfoDiv.innerHTML = hoverInfo} + setHoverInfo(hoverInfo)} +} +function setHoverInfo(s:string) { + hoverInfoDiv.innerHTML = "" + appendText(hoverInfoDiv, s) } function applyHoverHighlights(cell:Cell, srcId:SrcId) { // TODO @@ -299,9 +316,6 @@ function render(renderMode:RenderMode) { const msg = JSON.parse(event.data); if (msg == "start") { body.innerHTML = "" - body.addEventListener("click", function (event) { - event.stopPropagation() - toggleFrozenHover()}) cells.clear() return } else { diff --git a/static/style.css b/static/style.css index 12ae502f7..178032916 100644 --- a/static/style.css +++ b/static/style.css @@ -12,9 +12,29 @@ body { padding-bottom:50vw; } +#main-output { + margin-left: 20px; +} +#minimap { + display: flex; + flex-direction: column; + position: fixed; + top: 0em; + left: 0em; + height: 90vh; + width: 32px; + overflow: hidden; +} +.status { + flex: 1; + width : 30px; + border-top: 1px solid; + border-color: gray; + margin-left: 1px; +} #hover-info { position: fixed; - height: 5rem; + height: 10vh; bottom: 0em; width: 100vw; overflow: hidden; @@ -28,15 +48,10 @@ body { .cell { display: flex; } -.status, .line-nums, .contents { +.line-nums, .contents { font-family: monospace; white-space: pre; } -.status { - flex: 0 0 1em; - height: 1em; - margin-left: 1em; -} .line-nums { flex: 0 0 3em; height: 100%; @@ -56,7 +71,7 @@ body { .status-waiting {background-color: gray;} .status-running {background-color: lightblue;} .status-err {background-color: red;} -.status-success {} /* i.e. white */ +.status-success {background-color: white;} /* error highlighting */ .err-span { @@ -65,7 +80,7 @@ body { } /* Lexeme colors */ -.comment {color: #808080;} +.comment {color: gray;} .keyword {color: #0000DD;} .command {color: #A80000;} .symbol {color: #E07000;}