Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better log(aka. message) formatting #2891

Draft
wants to merge 8 commits into
base: development
Choose a base branch
from
85 changes: 85 additions & 0 deletions packages/playground/src/components/formatted_log.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<template>
<template v-if="Array.isArray(msg)">
<FormattedLog v-for="item in msg" :depth="depth + 1" :key="item.id" :msg="item" />
</template>

<template v-else-if="msg.type === 'Literal'">
<span style="word-break: break-all" v-text="normalizeText(msg.value)" />
</template>

<template v-else-if="msg.type === 'Object' || msg.type === 'Array'">
<div
:class="{
'd-inline': collapsed,
'd-flex': !collapsed && root,
'd-inline-flex': !collapsed && !root,
'align-start': !collapsed,
}"
>
<v-btn class="mr-1" variant="tonal" icon @click.stop="collapsed = !collapsed">
<VIcon icon="mdi-chevron-right" class="open-arrow" :class="{ 'is-active': !collapsed }" />
</v-btn>
<span v-show="collapsed">
<span v-html="msg.type === 'Array' ? '&#91; ' : '&#123; '" />
<span v-text="'...'" />
<span v-html="msg.type === 'Array' ? ' &#93;' : ' &#125;'" /><span v-if="!root" v-text="','" />
</span>

<div v-show="!collapsed">
<span v-html="msg.type === 'Array' ? '&#91; ' : '&#123; '" />
<div class="ml-4 d-flex" v-for="([key, item], index) in Object.entries(msg.value)" :key="item.id">
"{{ key }}":
<FormattedLog :depth="depth + 1" :msg="item" :coma="index !== Object.keys(msg.value).length - 1" is-prop />
</div>
<span v-html="msg.type === 'Array' ? ' &#93;' : ' &#125;'" /><span v-if="coma" v-text="','" />
</div>
</div>
</template>

<template v-else> here? </template>
</template>

<script lang="ts">
import { toRef } from "vue";
import { computed } from "vue";
import { type PropType, ref } from "vue";

export type MsgToken =
| { id: number; type: "Literal"; value: string }
| { id: number; type: "Array"; value: MsgToken[] }
| { id: number; type: "Object"; value: { [key: string]: MsgToken } };

export default {
name: "FormattedLog",
props: {
msg: { type: Object as PropType<MsgToken | MsgToken[]>, required: true },
depth: { type: Number, default: () => 0 },
coma: { type: Boolean, default: () => false },
isProp: { type: Boolean, default: () => false },
},
setup(_props) {
const props = toRef(_props);
const root = computed(() => props.value.depth === 1);
const collapsed = ref(true);

function normalizeText(text: string) {
if (props.value.isProp) text = `"${text}"`;
if (props.value.coma) text += ",";
return text;
}

return { root, collapsed, normalizeText };
},
};
</script>

<style scoped lang="scss">
button {
--v-btn-height: 0;
padding: 0 !important;
}

.open-arrow.is-active {
transform: rotate(90deg);
}
</style>
92 changes: 92 additions & 0 deletions packages/playground/src/components/tf_log_formatter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<template>
<FormattedLog :msg="parsedMsg" />
</template>

<script lang="ts">
import { computed, type PropType, toRef } from "vue";

import FormattedLog, { type MsgToken } from "./formatted_log.vue";

function parseMsg(msg?: any): MsgToken[] {
const _msg = msg || "";
const parts: any[] = [];

let open = 0;
let part = "";
for (const char of _msg) {
if (char === "{" || char === "[") {
if (open === 0 && part.length > 0) {
parts.push(part);
part = "";
}
part += char;
open++;
} else if (char === "}" || char === "]") {
part += char;
if (open === 1 && part.length > 0) {
try {
parts.push(JSON.parse(part));
} catch {
parts.push(part);
}
part = "";
}
open--;
} else {
part += char;
}
}

if (part.length > 0) {
parts.push(part);
}

const tokens: MsgToken[] = [];

const idRef = { id: 0 };
for (const part of parts) {
if (typeof part === "string") {
tokens.push({ id: idRef.id++, type: "Literal", value: part });
}

if (typeof part === "object") {
tokens.push(parseObject(idRef, part));
}
}

return tokens;
}

function parseObject(idRef: { id: number }, obj: any): MsgToken {
if (Array.isArray(obj)) {
return { id: idRef.id++, type: "Array", value: obj.map(x => parseObject(idRef, x)) };
}

if (typeof obj === "object" && obj) {
return {
id: idRef.id++,
type: "Object",
value: Object.keys(obj).reduce((res, key) => {
res[key] = parseObject(idRef, obj[key]);
return res;
}, {} as any),
};
}

return { id: idRef.id++, type: "Literal", value: String(obj) };
}

export default {
name: "TfLogFormatter",
components: { FormattedLog },
props: {
msg: { type: String as PropType<string | undefined | null>, required: true },
},
setup(_props) {
const props = toRef(_props);
const parsedMsg = computed(() => parseMsg(props.value.msg));

return { parsedMsg };
},
};
</script>
2 changes: 1 addition & 1 deletion packages/playground/src/components/weblet_layout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

<div ref="msgAlert">
<v-alert variant="tonal" v-show="profileManager.profile && status" :type="alertType">
{{ message }}
<TfLogFormatter :msg="message" />
</v-alert>
</div>

Expand Down
2 changes: 2 additions & 0 deletions packages/playground/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import InputValidator from "./components/input_validator.vue";
import TfSelectCountry from "./components/node_selector/select_location_internals/TfSelectCountry.vue";
import TfSelectRegion from "./components/node_selector/select_location_internals/TfSelectRegion.vue";
import PasswordInputWrapper from "./components/password_input_wrapper.vue";
import TfLogFormatter from "./components/tf_log_formatter.vue";
import ViewLayout from "./components/view_layout.vue";
import * as validators from "./utils/validators";

Expand All @@ -34,6 +35,7 @@ const GLOBAL_COMPONENTS: { [key: string]: Component } = {
TfSelectionDetails: defineAsyncComponent(() => import("./components/node_selector/TfSelectionDetails.vue")),
TfSelectRegion,
TfSelectCountry,
TfLogFormatter,
};

export function defineGlobals(app: App<Element>): void {
Expand Down
3 changes: 3 additions & 0 deletions packages/playground/src/global-components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import TfSelectCountry from "./components/node_selector/select_location_internal
import TfSelectRegion from "./components/node_selector/select_location_internals/TfSelectRegion.vue";
import TfSelectionDetails from "./components/node_selector/TfSelectionDetails.vue";
import PasswordInputWrapper from "./components/password_input_wrapper.vue";
import TfErrorFormater from "./components/tf_error_formater.vue";
import ViewLayout from "./components/view_layout.vue";
import WebletLayout from "./components/weblet_layout.vue";
import type * as validators from "./utils/validators";

declare module "@vue/runtime-core" {
export interface GlobalComponents {
PasswordInputWrapper: typeof PasswordInputWrapper;
Expand All @@ -27,6 +29,7 @@ declare module "@vue/runtime-core" {
TfSelectionDetails: typeof TfSelectionDetails;
TfSelectRegion: typeof TfSelectRegion;
TfSelectCountry: typeof TfSelectCountry;
TfErrorFormater: typeof TfErrorFormater;
}

interface ComponentCustomProperties {
Expand Down
Loading