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

Action-data display is now inline with the messages. #688

Merged
merged 44 commits into from
Feb 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
c77d998
fixed random request command
robgruen Dec 17, 2024
fba0a80
added devUI checkbox to settings
robgruen Dec 17, 2024
46c1be5
replace source name with action name if available.
robgruen Dec 17, 2024
2d91526
changed comma to period
robgruen Dec 17, 2024
51288ea
removed commented out code
robgruen Dec 17, 2024
e32cb5d
ran prettier
robgruen Dec 17, 2024
ddf522f
Merge branch 'main' into dev/robgruen/ui_updates
robgruen Dec 17, 2024
be30525
Merge branch 'dev/robgruen/ui_updates' of https://github.com/microsof…
robgruen Dec 17, 2024
a27c0b3
Merge branch 'main' into dev/robgruen/ui_updates
robgruen Dec 17, 2024
55ca19b
clearing actionname after completion
robgruen Dec 17, 2024
eeb8258
Merge branch 'dev/robgruen/ui_updates' of https://github.com/microsof…
robgruen Dec 17, 2024
8d350dc
added some more behaviors to the auto complete list to make it feel b…
robgruen Dec 17, 2024
eac4eeb
ran prettier
robgruen Dec 17, 2024
34d8714
Merge branch 'main' into dev/robgruen/ui_updates
robgruen Dec 17, 2024
2a939d0
added mouse wheel support
robgruen Dec 18, 2024
c5b798e
Merge branch 'dev/robgruen/ui_updates' of https://github.com/microsof…
robgruen Dec 18, 2024
ea3ef45
added rudimentary scrollbar (visual indicator only, not clickable)
robgruen Dec 18, 2024
bf961fb
ran prettier.
robgruen Dec 18, 2024
35c9b0f
fixed expaliner action command
robgruen Dec 18, 2024
4e8e23d
Merge branch 'main' into dev/robgruen/ui_updates
robgruen Dec 19, 2024
045d961
Merge branch 'dev/robgruen/ui_updates' of https://github.com/microsof…
robgruen Dec 31, 2024
ae4fa7e
removed commented out code
robgruen Feb 4, 2025
cf3eea3
in-progress chat history storing/loading
robgruen Feb 4, 2025
4f6b22d
rehydrate chat history
robgruen Feb 4, 2025
60d6d60
ran prettier
robgruen Feb 4, 2025
90c17da
removed pull request target workflow (no longer needed)
robgruen Feb 4, 2025
69af394
Merge branch 'main' into dev/robgruen/ui_updates
robgruen Feb 4, 2025
88993db
Merge remote-tracking branch 'origin' into dev/robgruen/ui_updates
robgruen Feb 5, 2025
5787d6b
Merge remote-tracking branch 'origin' into dev/robgruen/ui_updates
robgruen Feb 5, 2025
0072348
fixed drag/drop and command backstack
robgruen Feb 5, 2025
512d907
ran prettier
robgruen Feb 5, 2025
d636e99
Merge branch 'main' of https://github.com/microsoft/TypeAgent into de…
robgruen Feb 5, 2025
f2d235f
started adding open file dialog
robgruen Feb 5, 2025
06b75ff
Merge branch 'dev/robgruen/ui_updates' of https://github.com/microsof…
robgruen Feb 7, 2025
29253e3
Fixed cursor being at end of input after selecting previous command.
robgruen Feb 7, 2025
257c69b
Merge branch 'main' into dev/robgruen/ui_updates
robgruen Feb 7, 2025
0e43fb8
ran prettier
robgruen Feb 7, 2025
6af6eda
Merge branch 'dev/robgruen/ui_updates' of https://github.com/microsof…
robgruen Feb 7, 2025
e18d319
Merge remote-tracking branch 'origin' into dev/robgruen/ui_updates
robgruen Feb 7, 2025
476500a
Fixed attach image button.
robgruen Feb 7, 2025
a006f67
Added command backstack test
robgruen Feb 7, 2025
440495c
Added action data inline where the message is.
robgruen Feb 8, 2025
74ef95e
ran prettier
robgruen Feb 8, 2025
193342b
Merge branch 'main' into dev/robgruen/ui_updates
robgruen Feb 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions ts/packages/shell/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -698,18 +698,22 @@ async function initialize() {
});

ipcMain.on("open-image-file", async () => {
// TODO: imeplement
const result = await dialog.showOpenDialog(mainWindow);
const result = await dialog.showOpenDialog(mainWindow, {
filters: [
{
name: "Image files",
extensions: ["png", "jpg", "jpeg", "gif"],
},
],
});

if (result && !result.canceled) {
let paths = result.filePaths;
if (paths && paths.length > 0) {
const content = readFileSync(paths[0], "utf-8").toString();
console.log(content);
return content;
const content = readFileSync(paths[0], "base64");
chatView.webContents.send("file-selected", paths[0], content);
}
}

return null;
});

ipcMain.on(
Expand Down
7 changes: 7 additions & 0 deletions ts/packages/shell/src/preload/electronTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ export interface ClientAPI {
onChatHistory(
callback: (e: Electron.IpcRendererEvent, chatHistory: string) => void,
): void;
onFileSelected(
callback: (
e: Electron.IpcRendererEvent,
fileName: string,
fileContent: string,
) => void,
): void;
registerClientIO(clientIO: ClientIO);
}

Expand Down
3 changes: 3 additions & 0 deletions ts/packages/shell/src/preload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const api: ClientAPI = {
onChatHistory(callback) {
ipcRenderer.on("chat-history", callback);
},
onFileSelected(callback) {
ipcRenderer.on("file-selected", callback);
},
registerClientIO: (clientIO: ClientIO) => {
if (clientIORegistered) {
throw new Error("ClientIO already registered");
Expand Down
21 changes: 9 additions & 12 deletions ts/packages/shell/src/renderer/assets/styles.less
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,12 @@ table.table-message td {
margin-right: 8px;
}

.agent-name:hover .clickable {
cursor: pointer;
text-decoration: underline;
font-weight: bolder;
}

.agent-name[action-data]:hover:after {
content: attr(action-data);
position: absolute;
Expand Down Expand Up @@ -510,17 +516,8 @@ table.table-message td {
right: 0;
}

.chat-message-explained[data-expl]:hover:after {
content: attr(data-expl);
position: absolute;
top: 0;
right: 0;
.chat-message-action-data {
white-space: nowrap;
background-color: lavender;
color: gray;
height: 100%;
border-radius: 5px;
margin-right: 5px;
font-size: 80%;
}

Expand Down Expand Up @@ -555,8 +552,8 @@ table.table-message td {
}

.chat-input-image {
max-width: 320px;
max-height: 320px;
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
overflow: auto;
Expand Down
40 changes: 21 additions & 19 deletions ts/packages/shell/src/renderer/src/chatInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,18 +263,6 @@ export class ChatInput {
e.preventDefault();
};

// this.fileInput = document.createElement("input");
// this.fileInput.type = "file";
// this.fileInput.classList.add("chat-message-hidden");
// this.fileInput.id = "image_upload";
// this.inputContainer.append(this.fileInput);
// this.fileInput.accept = "image/*,.jpg,.png,.gif";
// this.fileInput.onchange = () => {
// if (this.fileInput.files && this.fileInput.files?.length > 0) {
// this.loadImageFile(this.fileInput.files[0]);
// }
// };

this.micButton = document.createElement("button");
this.micButton.appendChild(iconMicrophone());
this.micButton.id = buttonId;
Expand Down Expand Up @@ -348,15 +336,27 @@ export class ChatInput {
this.inputContainer.appendChild(this.sendButton);
}

/**
* Loads the contents of the supplied image into the input text box.
* @param file The file whose contents to load
*/
async loadImageFile(file: File) {
let buffer: ArrayBuffer = await file.arrayBuffer();

let dropImg: HTMLImageElement = document.createElement("img");
let mimeType = file.name
this.loadImageContent(file.name, _arrayBufferToBase64(buffer));
}

/**
* Creates and sets an image in the input text area.
* @param mimeType The mime type of the supplied image content
* @param content The base64 encoded image content
*/
public async loadImageContent(fileName: string, content: string) {
let mimeType = fileName
.toLowerCase()
.substring(file.name.lastIndexOf(".") + 1, file.name.length);
.substring(fileName.lastIndexOf(".") + 1, fileName.length);

if (file.name.toLowerCase().endsWith(".jpg")) {
if (fileName.toLowerCase().endsWith(".jpg")) {
mimeType = "jpeg";
}

Expand All @@ -365,13 +365,15 @@ export class ChatInput {
"jpeg",
"png",
]);

if (!supportedMimeTypes.has(mimeType)) {
console.log(`Unsupported MIME type for '${file.name}'`);
console.log(`Unsupported MIME type for '${fileName}'`);
this.textarea.getTextEntry().innerText = `Unsupported file type '${mimeType}'. Supported types: ${Array.from(supportedMimeTypes).toString()}`;
return;
}
dropImg.src =
`data:image/${mimeType};base64,` + _arrayBufferToBase64(buffer);

let dropImg: HTMLImageElement = document.createElement("img");
dropImg.src = `data:image/${mimeType};base64,` + content;

dropImg.className = "chat-input-dropImage";

Expand Down
23 changes: 23 additions & 0 deletions ts/packages/shell/src/renderer/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { createWebSocket, webapi, webdispatcher } from "./webSocketAPI";
import * as jose from "jose";
import { AppAgentEvent } from "@typeagent/agent-sdk";
import { ClientIO, Dispatcher } from "agent-dispatcher";
import { swapContent } from "./setContent";

export function getClientAPI(): ClientAPI {
if (globalThis.api !== undefined) {
Expand Down Expand Up @@ -321,10 +322,32 @@ function addEvents(
lastSeparatorText!.innerText =
getDateDifferenceDescription(new Date(), timeStamp);
}

// rewire up action-data click handler
const nameDiv = div.querySelector(".agent-name.clickable");
if (nameDiv != null) {
const messageDiv = div.querySelector(
".chat-message-content",
);

if (messageDiv) {
nameDiv.addEventListener("click", () => {
swapContent(
nameDiv as HTMLSpanElement,
messageDiv as HTMLDivElement,
);
});
}
}

// TODO: wire up any other functionality (player agent?)
}
}
}
});
api.onFileSelected((_, fileName: string, fileContent: string) => {
chatView.chatInput.loadImageContent(fileName, fileContent);
});
}

function showNotifications(
Expand Down
66 changes: 44 additions & 22 deletions ts/packages/shell/src/renderer/src/messageContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,12 @@ import {
} from "agent-dispatcher";

import { ChoicePanel, InputChoice } from "./choicePanel";
import { setContent } from "./setContent";
import { setContent, swapContent } from "./setContent";
import { ChatView } from "./chatView";
import { iconCheckMarkCircle, iconRoadrunner, iconX } from "./icon";
import { TemplateEditor } from "./templateEditor";
import { SettingsView } from "./settingsView";

function createTimestampDiv(timestamp: Date, className: string) {
const timeStampDiv = document.createElement("div");
timeStampDiv.classList.add(className);

const nameSpan = document.createElement("span");
nameSpan.className = "agent-name";
timeStampDiv.appendChild(nameSpan); // name placeholder

const dateSpan = document.createElement("span");
dateSpan.className = "timestring";
dateSpan.setAttribute("data", timestamp.toString());

timeStampDiv.appendChild(dateSpan); // time string

dateSpan.innerText = "- " + timestamp.toLocaleTimeString();

return timeStampDiv;
}

function updateMetrics(
mainMetricsDiv: HTMLDivElement,
markMetricsDiv: HTMLDivElement,
Expand Down Expand Up @@ -100,6 +81,7 @@ export class MessageContainer {
private readonly messageDiv: HTMLDivElement;
private readonly timestampDiv: HTMLDivElement;
private readonly iconDiv?: HTMLDivElement;
private nameSpan: HTMLSpanElement;

private metricsDiv?: {
mainMetricsDiv: HTMLDivElement;
Expand Down Expand Up @@ -159,14 +141,36 @@ export class MessageContainer {
if (this.action !== undefined && !Array.isArray(this.action)) {
label.setAttribute(
"action-data",
JSON.stringify(this.action, undefined, 2),
"<pre>" +
JSON.stringify(this.action, undefined, 2) +
"</pre>",
);

// mark the span as clickable
this.nameSpan.classList.add("clickable");
}

this.iconDiv.innerText = this.sourceIcon;
}
}

private createTimestampDiv(timestamp: Date, className: string) {
const timeStampDiv = document.createElement("div");
timeStampDiv.classList.add(className);

timeStampDiv.appendChild(this.nameSpan); // name placeholder

const dateSpan = document.createElement("span");
dateSpan.className = "timestring";
dateSpan.setAttribute("data", timestamp.toString());

timeStampDiv.appendChild(dateSpan); // time string

dateSpan.innerText = "- " + timestamp.toLocaleTimeString();

return timeStampDiv;
}

constructor(
private chatView: ChatView,
private settingsView: SettingsView,
Expand All @@ -181,7 +185,25 @@ export class MessageContainer {
const div = document.createElement("div");
div.className = `chat-message-container-${classNameSuffix}`;

const timestampDiv = createTimestampDiv(
// create the name placeholder
this.nameSpan = document.createElement("span");
this.nameSpan.className = "agent-name";
this.nameSpan.addEventListener("click", () => {
swapContent(this.nameSpan, this.messageDiv);
// const data: string = this.nameSpan.getAttribute("action-data") ?? "";
// const originalMessage: string = this.messageDiv.innerHTML;

// if (this.messageDiv.classList.contains("chat-message-action-data")) {
// this.messageDiv.classList.remove("chat-message-action-data");
// } else {
// this.messageDiv.classList.add("chat-message-action-data");
// }

// this.nameSpan.setAttribute("action-data", originalMessage);
// this.messageDiv.innerHTML = data;
});

const timestampDiv = this.createTimestampDiv(
new Date(),
`chat-timestamp-${classNameSuffix}`,
);
Expand Down
23 changes: 23 additions & 0 deletions ts/packages/shell/src/renderer/src/setContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,26 @@ export function setContent(
const parser = new DOMParser();
return parser.parseFromString(contentHtml, "text/html").body.innerText;
}

/**
* Takes the "action-data" attribute from the source element and places it as the html
* of the target element.
* @param sourceElement The source element.
* @param targetElement The target element.
*/
export function swapContent(
sourceElement: HTMLElement,
targetElement: HTMLElement,
) {
const data: string = sourceElement.getAttribute("action-data") ?? "";
const originalMessage: string = targetElement.innerHTML;

if (targetElement.classList.contains("chat-message-action-data")) {
targetElement.classList.remove("chat-message-action-data");
} else {
targetElement.classList.add("chat-message-action-data");
}

sourceElement.setAttribute("action-data", originalMessage);
targetElement.innerHTML = data;
}
4 changes: 4 additions & 0 deletions ts/packages/shell/src/renderer/src/webSocketAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export const webapi: ClientAPI = {
// TODO: implement proper message rehydration on mobile
fnMap.set("chat-history", callback);
},
onFileSelected(callback) {
// TODO: implement image selection on mobile device
fnMap.set("file-selected", callback);
},
registerClientIO(clientIO: ClientIO) {
if (clientIORegistered) {
throw new Error("ClientIO already registered");
Expand Down
Loading
Loading