Path: blob/main/replay/frontend/src/pages/output/index.vue
1030 views
<template lang="pug"> .Page(:style="cssVars") .Drag(@mousedown="onDragdown" @mouseup="onDragup") .Handle | .OutputPanel .Json(v-if="output.length") .JsonNode(v-for="node of output" :key="node.id" :ref="node.id" :id="node.path" :class="{highlighted: node.highlighted}") .indent(v-for="i in node.level") {{' '}} span.key(v-if="node.key") {{node.key}}: span span.brackets(v-if="node.isContent === false") {{node.content}} span.value(v-else :class="'value-' + node.type") {{formatValue(node)}} span.comma(v-if="node.showComma") , .Explainer(v-else) h4 Agent Output p This panel shows output set using "agent.output". p You can use the "agent.output" object as an array: pre | agent.output.push({ | text, | href, | }) p Or as an object: pre | agent.output.text = text; p As you set each data-entry, it will stream into this panel as the same json you'll get if you print agent.output: pre console.log(agent.output); .Datasize(v-if="dataSize") Output size: {{dataSize}} </template> <script lang="ts"> import Vue from 'vue'; import Component from 'vue-class-component'; import { ipcRenderer } from 'electron'; import { getTheme } from '~shared/utils/themes'; import settings from '~frontend/lib/settings'; import NoCache from '~frontend/lib/NoCache'; import flattenJson, { FlatJson } from '~frontend/pages/output/flattenJson'; @Component export default class OutputPanel extends Vue { private output: FlatJson[] = []; private isResizing = false; private lastResizeScreenX: number; private dataSize: string = null; private get theme() { return getTheme(settings.theme); } private formattedSize(bytes: number): string { if (!bytes) return null; const kb = bytes / 1024; if (kb > 1024) { const mb = kb / 1024; return `${Math.round(mb * 10) / 10}mb`; } return `${Math.round(kb * 10) / 10}kb`; } private formatValue(node: FlatJson): string { let text = node.content + ''; if (node.type === 'string') text = `"${text}"`; return text; } @NoCache private get cssVars() { return { '--toolbarBackgroundColor': this.theme.toolbarBackgroundColor, '--toolbarBorderColor': this.theme.toolbarBottomLineBackgroundColor, }; } mounted() { ipcRenderer.on( 'set:output', (_, output: any, bytes: number, changes: { path: string; type: string }[]) => { if (output === null || bytes === 0) { this.dataSize = null; this.output = []; return; } const json = flattenJson(output); let counter = 0; let recordToScroll: FlatJson; for (const record of json) { record.id = counter += 1; if (changes && changes.some(x => x.path && record.path.startsWith(x.path))) { record.highlighted = true; recordToScroll = record; } } this.dataSize = this.formattedSize(bytes); this.output = json; if (recordToScroll) { this.$nextTick(() => { const refs = this.$refs[recordToScroll.id] as HTMLElement[]; if (!refs) return; if (refs.length) { refs[refs.length - 1].scrollIntoView({ block: 'center' }); } }); } }, ); window.addEventListener('mouseup', this.onDragup); } private onDragdown() { window.addEventListener('mousemove', this.onDragmove); this.isResizing = true; } private onDragup() { window.removeEventListener('mousemove', this.onDragmove); this.isResizing = false; } private onDragmove(event) { const start = this.lastResizeScreenX; this.lastResizeScreenX = event.screenX; if (this.isResizing && start !== undefined) { const moveX = start - this.lastResizeScreenX; if (moveX !== 0) { ipcRenderer.send('output-drag', moveX); } } } } </script> <style lang="scss"> @import '../../assets/style/resets'; body { height: 100vh; margin: 0; border-top: 0 none; width: 100%; } .Page { box-sizing: border-box; background: white; border-left: 1px solid var(--toolbarBorderColor); box-shadow: inset 1px 0 rgba(0, 0, 0, 0.2); margin: 0; } .vjs-tree { font-size: 12px; } .Drag { position: absolute; z-index: 1; user-select: none; top: 50%; .Handle { position: relative; left: -8px; height: 30px; background: #aaaaaa; width: 16px; border-radius: 5px; text-align: center; color: white; text-indent: 5px; vertical-align: middle; line-height: 30px; } } .OutputPanel { box-sizing: border-box; padding: 20px 10px; overflow-y: auto; overflow-x: hidden; position: relative; height: 100%; width: 100%; h4 { text-align: center; text-decoration: underline; margin-top: 0; margin-bottom: 5px; } .Json { font-family: 'Monaco', 'Menlo', 'Consolas', 'Bitstream Vera Sans Mono', monospace; font-size: 12px; text-align: left; } } .Explainer { margin: 5px; border-radius: 5px; border: 1px solid #e2ecec; background: #eeeeee; padding: 10px; } .Datasize { text-align: center; font-style: italic; color: #3c3c3c; } .JsonNode { display: flex; position: relative; &.highlighted { background-color: #f3fbff; } .key { padding-right: 5px; } .brackets, .comma { color: #949494; } .indent { flex: 0 0 1em; border-left: 1px dashed #d9d9d9; } .comment { color: #bfcbd9; } .value-null { color: #ff4949; } .value-number { color: #1d8ce0; } .value-boolean { color: #1d8ce0; } .value-string { color: #13ce66; } } </style>