forked from Shinonome/dots-hyprland
ai: gemini: annotation sources
This commit is contained in:
@@ -9,6 +9,11 @@ function getDomain(url) {
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
function getBaseUrl(url) {
|
||||
const match = url.match(/^(https?:\/\/[^\/]+)(\/.*)?$/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
function shellSingleQuoteEscape(str) {
|
||||
// escape single quotes
|
||||
return String(str)
|
||||
|
||||
@@ -20,10 +20,15 @@ Item {
|
||||
property var inputField: messageInputField
|
||||
readonly property var messages: Ai.messages
|
||||
property string commandPrefix: "/"
|
||||
property string faviconDownloadPath: StringUtils.trimFileProtocol(`${StandardPaths.standardLocations(StandardPaths.CacheLocation)[0]}/media/favicons`)
|
||||
|
||||
property var suggestionQuery: ""
|
||||
property var suggestionList: []
|
||||
|
||||
Component.onCompleted: {
|
||||
Hyprland.dispatch(`exec mkdir -p ${faviconDownloadPath}`)
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: panelWindow
|
||||
function onVisibleChanged(visible) {
|
||||
@@ -205,6 +210,7 @@ int main(int argc, char* argv[]) {
|
||||
messageIndex: index
|
||||
messageData: modelData
|
||||
messageInputField: root.inputField
|
||||
faviconDownloadPath: root.faviconDownloadPath
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ Rectangle {
|
||||
property int messageIndex
|
||||
property var messageData
|
||||
property var messageInputField
|
||||
property string faviconDownloadPath
|
||||
|
||||
property real messagePadding: 7
|
||||
property real contentSpacing: 3
|
||||
@@ -74,7 +75,7 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
ColumnLayout { // Main layout of the whole thing
|
||||
id: columnLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
@@ -228,7 +229,7 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
ColumnLayout { // Message content
|
||||
id: messageContentColumnLayout
|
||||
|
||||
spacing: 0
|
||||
@@ -257,6 +258,25 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Flow { // Annotations
|
||||
id: annotationFlowLayout
|
||||
visible: root.messageData?.annotationSources?.length > 0
|
||||
spacing: 5
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
Repeater {
|
||||
model: root.messageData.annotationSources
|
||||
delegate: AnnotationSourceButton {
|
||||
id: annotationButton
|
||||
faviconDownloadPath: root.faviconDownloadPath
|
||||
displayText: modelData.text
|
||||
url: modelData.url
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import Qt.labs.platform
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Button {
|
||||
id: root
|
||||
property string displayText
|
||||
property string url
|
||||
|
||||
implicitHeight: 30
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
|
||||
property string downloadUserAgent: ConfigOptions.networking.userAgent
|
||||
property string faviconDownloadPath
|
||||
property string domainName: url.includes("vertexaisearch") ? displayText : StringUtils.getBaseUrl(url)
|
||||
// property string faviconUrl: `https://${domainName}/favicon.ico`
|
||||
property string faviconUrl: `https://www.google.com/s2/favicons?domain=${domainName}&sz=32`
|
||||
property string fileName: `${domainName}.ico`
|
||||
property string faviconFilePath: `${faviconDownloadPath}/${fileName}`
|
||||
|
||||
Process {
|
||||
id: faviconDownloadProcess
|
||||
running: false
|
||||
command: ["bash", "-c", `[ -f ${faviconFilePath} ] || curl -s '${root.faviconUrl}' -o '${faviconFilePath}' -L -H 'User-Agent: ${downloadUserAgent}'`]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.faviconUrl = root.faviconFilePath
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("Favicon download:", faviconDownloadProcess.command.join(" "))
|
||||
faviconDownloadProcess.running = true
|
||||
}
|
||||
|
||||
PointingHandInteraction {}
|
||||
onClicked: {
|
||||
if (url) {
|
||||
Qt.openUrlExternally(url)
|
||||
Hyprland.dispatch("global quickshell:sidebarLeftClose")
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
radius: Appearance.rounding.full
|
||||
color: (root.down ? Appearance.colors.colSurfaceContainerHighestActive :
|
||||
root.hovered ? Appearance.colors.colSurfaceContainerHighestHover :
|
||||
Appearance.m3colors.m3surfaceContainerHighest)
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 5
|
||||
IconImage {
|
||||
id: iconImage
|
||||
source: Qt.resolvedUrl(root.faviconUrl)
|
||||
implicitSize: text.implicitHeight
|
||||
}
|
||||
StyledText {
|
||||
id: text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: displayText
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,11 +310,33 @@ Singleton {
|
||||
}
|
||||
|
||||
function parseGeminiBuffer() {
|
||||
// console.log("BUFFER DATA: ", requester.geminiBuffer);
|
||||
try {
|
||||
const dataJson = JSON.parse(requester.geminiBuffer);
|
||||
const responseContent = dataJson.candidates[0]?.content?.parts[0]?.text
|
||||
requester.message.content += responseContent;
|
||||
const annotationSources = dataJson.candidates[0]?.groundingMetadata.groundingChunks?.map(chunk => {
|
||||
return {
|
||||
"type": "url_citation",
|
||||
"text": chunk?.web?.title,
|
||||
"url": chunk?.web?.uri,
|
||||
}
|
||||
});
|
||||
const annotations = dataJson.candidates[0]?.groundingMetadata.groundingSupports?.map(citation => {
|
||||
return {
|
||||
"type": "url_citation",
|
||||
"start_index": citation.segment?.startIndex,
|
||||
"end_index": citation.segment?.endIndex,
|
||||
"text": citation?.segment.text,
|
||||
"url": annotationSources[citation.groundingChunkIndices[0]]?.url,
|
||||
"sources": citation.groundingChunkIndices
|
||||
}
|
||||
});
|
||||
requester.message.annotationSources = annotationSources;
|
||||
requester.message.annotations = annotations;
|
||||
// console.log(JSON.stringify(requester.message, null, 2));
|
||||
} catch (e) {
|
||||
console.log("[AI] Could not parse response from stream: ", e);
|
||||
requester.message.content += requester.geminiBuffer
|
||||
} finally {
|
||||
requester.geminiBuffer = "";
|
||||
|
||||
@@ -7,4 +7,6 @@ QtObject {
|
||||
property string model
|
||||
property bool thinking: true
|
||||
property bool done: false
|
||||
property var annotations: []
|
||||
property var annotationSources: []
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user