Skip to content

Commit

Permalink
Add expandos with details, auto focus text box by default
Browse files Browse the repository at this point in the history
  • Loading branch information
Vorlias committed Dec 29, 2021
1 parent 09f729c commit 45e1b88
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 54 deletions.
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@rbxts/message-templates": "^0.3.2",
"@rbxts/net": "^3.0.0-alpha.0",
"@rbxts/roact": "1.4.0-ts.2",
"@rbxts/roact-hooks": "^0.3.0-ts.2",
"@rbxts/roact-rodux": "0.2.2-ts.5",
"@rbxts/rodux": "3.0.0-ts.3",
"@rbxts/rust-classes": "0.11.0-beta.1",
Expand Down
15 changes: 13 additions & 2 deletions src/Client/BuiltInConsole/Store/_reducers/ConsoleReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export interface ActionSetConsoleVisible extends Action<ConsoleActionName.SetCon
export interface ActionSetConsoleConfiguration extends Action<ConsoleActionName.SetConfiguration> {
executionEnabled: boolean;
hotkeyEnabled: boolean;
autoFocusTextBox: boolean;
showTagsInOutput: boolean;
logDetailsPaneEnabled: boolean;
}

export interface ActionAddOutput extends Action<ConsoleActionName.AddOutput> {
Expand Down Expand Up @@ -60,6 +62,8 @@ export interface ConsoleReducer {
visible: boolean;
executionEnabled: boolean;
hotkeyEnabled: boolean;
autoFocusTextBox: boolean;
logDetailsPaneEnabled: boolean;
showTagsInOutput: boolean;
output: ConsoleMessage[];
history: string[];
Expand All @@ -75,9 +79,11 @@ export const DEFAULT_FILTER = new Set([

const INITIAL_STATE: ConsoleReducer = {
visible: false,
autoFocusTextBox: true,
executionEnabled: false,
hotkeyEnabled: false,
showTagsInOutput: false,
logDetailsPaneEnabled: false,
showTagsInOutput: true,
output: [],
history: [],
filter: {
Expand All @@ -87,11 +93,16 @@ const INITIAL_STATE: ConsoleReducer = {

const actions: Rodux.ActionHandlers<ConsoleReducer, ConsoleActions> = {
[ConsoleActionName.SetConsoleVisible]: (state, { visible }) => ({ ...state, visible }),
[ConsoleActionName.SetConfiguration]: (state, { executionEnabled, hotkeyEnabled, showTagsInOutput }) => ({
[ConsoleActionName.SetConfiguration]: (
state,
{ executionEnabled, hotkeyEnabled, showTagsInOutput, logDetailsPaneEnabled, autoFocusTextBox },
) => ({
...state,
executionEnabled,
hotkeyEnabled,
showTagsInOutput,
logDetailsPaneEnabled,
autoFocusTextBox,
}),
[ConsoleActionName.AddOutput]: (state, { message }) => {
return $dbg({
Expand Down
3 changes: 3 additions & 0 deletions src/Client/BuiltInConsole/UI/DockedConsole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ class ZirconConsoleComponent extends Roact.Component<DockedConsoleProps, DockedC
OnClick={() => {}}
/>
<ZirconSyntaxTextBox
AutoFocus={this.props.autoFocus}
Size={new UDim2(1, -16 - 32 - 100, 1, 0)}
Position={new UDim2(0, 16, 0, 0)}
Focused={this.state.isVisible}
Expand Down Expand Up @@ -348,11 +349,13 @@ interface MappedProps {
executionEnabled: boolean;
history: string[];
searchQuery: string;
autoFocus: boolean;
levelFilter: Set<ZirconLogLevel>;
}
const mapStateToProps = (state: ConsoleReducer): MappedProps => {
return {
isVisible: state.visible,
autoFocus: state.autoFocusTextBox,
levelFilter: state.filter.Levels ?? DEFAULT_FILTER,
executionEnabled: state.executionEnabled,
searchQuery: state.filter.SearchQuery ?? "",
Expand Down
55 changes: 5 additions & 50 deletions src/Client/Components/OutputMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { formatParse, formatTokens } from "Client/Format";
import { LogLevel } from "@rbxts/log";
import { MessageTemplateParser } from "@rbxts/message-templates";
import { ZirconStructuredMessageTemplateRenderer } from "Client/Format/ZirconStructuredMessageTemplate";
import { StructuredLogMessage } from "./StructuredLogMessage";

function sanitise(input: string) {
return input.gsub("[<>]", {
Expand All @@ -39,6 +40,10 @@ interface OutputMessageProps {
function OutputMessage(props: OutputMessageProps) {
const output = props.Message;

if (output.type === ZirconMessageType.StructuredLog) {
return <StructuredLogMessage LogEvent={output.data} Context={output.context} DetailedView />;
}

return (
<ThemeContext.Consumer
render={(theme) => {
Expand All @@ -57,56 +62,6 @@ function OutputMessage(props: OutputMessageProps) {
),
);
messages.push(message.message);
} else if (output.type === ZirconMessageType.StructuredLog) {
const {
data: { Template, Timestamp, Level },
data,
} = output;

const tokens = MessageTemplateParser.GetTokens(sanitise(Template));
const renderer = new ZirconStructuredMessageTemplateRenderer(tokens, theme);
const text = renderer.Render(output.data);

messages.push(
getRichTextColor3(
theme,
"Grey",
`[${
DateTime.fromIsoDate(Timestamp)?.FormatLocalTime(
"LT",
LocalizationService.SystemLocaleId,
) ?? "?"
}]`,
),
);

if (Level === LogLevel.Information) {
messages.push(getRichTextColor3(theme, "Cyan", "INFO "));
messages.push(getRichTextColor3(theme, "White", text));
} else if (Level === LogLevel.Debugging) {
messages.push(getRichTextColor3(theme, "Green", "DEBUG"));
messages.push(getRichTextColor3(theme, "White", text));
} else if (Level === LogLevel.Verbose) {
messages.push(getRichTextColor3(theme, "Grey", "VERBOSE"));
messages.push(getRichTextColor3(theme, "White", text));
} else if (Level === LogLevel.Warning) {
messages.push(getRichTextColor3(theme, "Yellow", "WARN "));
messages.push(getRichTextColor3(theme, "White", text));
} else if (Level === LogLevel.Error) {
messages.push(getRichTextColor3(theme, "Red", "ERROR "));
messages.push(getRichTextColor3(theme, "Yellow", text));
} else if (Level === LogLevel.Fatal) {
messages.push(getRichTextColor3(theme, "Red", "FATAL "));
messages.push(getRichTextColor3(theme, "Red", text));
}

if (props.ShowTags) {
if (data.SourceContext) {
messages.push(
"- " + italicize(getRichTextColor3(theme, "Grey", tostring(data.SourceContext))),
);
}
}
} else if (output.type === ZirconMessageType.ZirconLogOutputMesage) {
const { message } = output;
messages.push(
Expand Down
203 changes: 203 additions & 0 deletions src/Client/Components/StructuredLogMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { LogEvent, LogLevel } from "@rbxts/log";
import { MessageTemplateParser } from "@rbxts/message-templates";
import Roact from "@rbxts/roact";
import RoactHooks from "@rbxts/roact-hooks";
import { ZirconStructuredMessageTemplateRenderer } from "Client/Format/ZirconStructuredMessageTemplate";
import { ZirconContext } from "Client/Types";
import ThemeContext, { getRichTextColor3, italicize } from "Client/UIKit/ThemeContext";
import Flipper from "@rbxts/flipper";
import Padding from "./Padding";
import { formatRichText } from "Client/Format";
import RoactRodux, { connect } from "@rbxts/roact-rodux";
import ZirconClientStore from "Client/BuiltInConsole/Store";
import { ConsoleReducer } from "Client/BuiltInConsole/Store/_reducers/ConsoleReducer";

function sanitise(input: string) {
return input.gsub("[<>]", {
">": "&gt;",
"<": "&lt;",
})[0];
}

export interface StructuredLogMessageProps extends MappedProps {
readonly LogEvent: LogEvent;
readonly Context: ZirconContext;
readonly DetailedView?: boolean;
}
export interface StructuredLogMessageState {
viewDetails: boolean;
}

const keys: (keyof LogEvent)[] = ["Template", "Level", "Timestamp"];

export function getNonEventProps(logEvent: LogEvent) {
const props = new Array<[string, unknown]>();
for (const [key, value] of pairs(logEvent)) {
if (!keys.includes(key)) {
props.push([key as string, value]);
}
}
return props;
}

class StructuredLogMessageComponent extends Roact.Component<StructuredLogMessageProps, StructuredLogMessageState> {
private height: Roact.Binding<number>;
private setHeight: Roact.BindingFunction<number>;
private heightMotor: Flipper.SingleMotor;

public constructor(props: StructuredLogMessageProps) {
super(props);
[this.height, this.setHeight] = Roact.createBinding(25);
this.heightMotor = new Flipper.SingleMotor(this.height.getValue());
this.heightMotor.onStep((value) => this.setHeight(value));
}

public willUnmount() {
this.heightMotor.destroy();
}

public render() {
const { LogEvent, Context } = this.props;
const { Template, Timestamp, Level, SourceContext } = LogEvent;
const messages = new Array<string>();

const tokens = MessageTemplateParser.GetTokens(sanitise(Template));
const evtProps = getNonEventProps(this.props.LogEvent);

return (
<ThemeContext.Consumer
render={(theme) => {
const renderer = new ZirconStructuredMessageTemplateRenderer(tokens, theme);
const text = renderer.Render(LogEvent);

if (Level === LogLevel.Information) {
messages.push(getRichTextColor3(theme, "Cyan", "INFO "));
messages.push(getRichTextColor3(theme, "White", text));
} else if (Level === LogLevel.Debugging) {
messages.push(getRichTextColor3(theme, "Green", "DEBUG"));
messages.push(getRichTextColor3(theme, "White", text));
} else if (Level === LogLevel.Verbose) {
messages.push(getRichTextColor3(theme, "Grey", "VERBOSE"));
messages.push(getRichTextColor3(theme, "White", text));
} else if (Level === LogLevel.Warning) {
messages.push(getRichTextColor3(theme, "Yellow", "WARN "));
messages.push(getRichTextColor3(theme, "White", text));
} else if (Level === LogLevel.Error) {
messages.push(getRichTextColor3(theme, "Red", "ERROR "));
messages.push(getRichTextColor3(theme, "Yellow", text));
} else if (Level === LogLevel.Fatal) {
messages.push(getRichTextColor3(theme, "Red", "FATAL "));
messages.push(getRichTextColor3(theme, "Red", text));
}

if (SourceContext !== undefined) {
messages.push(
"- " + italicize(getRichTextColor3(theme, "Grey", tostring(LogEvent.SourceContext))),
);
}

return (
<imagebutton
AutoButtonColor={this.props.logDetailsPaneEnabled}
Size={this.height.map((v) => new UDim2(1, 0, 0, v))}
BackgroundTransparency={0.5}
BackgroundColor3={theme.SecondaryBackgroundColor3}
BorderSizePixel={0}
Event={{
MouseButton1Click: () => {
if (!this.props.logDetailsPaneEnabled) return;

if (this.state.viewDetails) {
this.heightMotor.setGoal(new Flipper.Spring(25));
} else {
this.heightMotor.setGoal(new Flipper.Spring(25 + evtProps.size() * 30 + 5));
}

this.setState({ viewDetails: !this.state.viewDetails });
},
}}
>
<frame
Size={new UDim2(0, 5, 1, 0)}
BackgroundColor3={
Context === ZirconContext.Server
? theme.ServerContextColor
: theme.ClientContextColor
}
BorderSizePixel={0}
/>
<textlabel
RichText
Position={new UDim2(0, 10, 0, 0)}
Size={new UDim2(1, -15, 0, 25)}
Text={messages.join(" ")}
BackgroundTransparency={1}
Font={theme.ConsoleFont}
TextColor3={theme.PrimaryTextColor3}
TextXAlignment="Left"
TextSize={20}
/>
<frame
Position={new UDim2(0, 30, 0, 25)}
ClipsDescendants
BorderSizePixel={0}
BackgroundTransparency={1}
Size={this.height.map((v) => new UDim2(1, -35, 0, v - 25))}
>
<uilistlayout Padding={new UDim(0, 5)} />
{/* <uigridlayout
CellPadding={new UDim2(0, 5, 0, 5)}
CellSize={new UDim2(0.5, -5, 0, 25)}
/> */}
{this.props.logDetailsPaneEnabled &&
this.state.viewDetails &&
evtProps.map(([key, value]) => {
return (
<frame
BackgroundTransparency={1}
Size={new UDim2(1, 0, 0, 25)}
BorderSizePixel={0}
>
<Padding Padding={{ Horizontal: 5 }} />
<uilistlayout FillDirection="Horizontal" Padding={new UDim(0, 10)} />
<textlabel
Text={key}
Font={theme.ConsoleFont}
TextSize={16}
BackgroundTransparency={1}
Size={new UDim2(0.25, 0, 1, 0)}
TextColor3={theme.PrimaryTextColor3}
TextXAlignment="Left"
/>
<textlabel
Text={formatRichText(value, undefined, theme)}
Font={theme.ConsoleFont}
TextSize={16}
RichText
BackgroundTransparency={1}
Size={new UDim2(0.75, 0, 1, 0)}
TextColor3={theme.PrimaryTextColor3}
TextXAlignment="Left"
/>
</frame>
);
})}
</frame>
</imagebutton>
);
}}
/>
);
}
}

export interface MappedProps {
readonly logDetailsPaneEnabled: boolean;
}
const mapStateToProps = (props: ConsoleReducer): MappedProps => {
return {
logDetailsPaneEnabled: props.logDetailsPaneEnabled,
};
};

export const StructuredLogMessage = connect(mapStateToProps)(StructuredLogMessageComponent);
Loading

0 comments on commit 45e1b88

Please sign in to comment.