Compare commits
9 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2291ed61fc | |||
| f9d6cdc9dd | |||
| 61d3b44c40 | |||
| 1279317a0e | |||
| 6e5db3f2db | |||
| fdc0066cab | |||
| 4e57a4d730 | |||
| fc8e382be9 | |||
| 417c0a2c6d |
6 changed files with 74 additions and 28 deletions
|
|
@ -9,7 +9,8 @@ AI assistant plugin for [DankMaterialShell](https://github.com/AvengeMedia/DankM
|
|||
|-|-|-|
|
||||
| |  | 
|
||||
|
||||
|
||||
## Usage
|
||||
- Toggle the plugin: `dms ipc call plugins toggle nexusAi`
|
||||
## Features
|
||||
- **Multi-Provider Ready:** Connect to standard cloud APIs or local providers.
|
||||
- **Vision Support:** Models with vision can be used with image attachments, even from clipboard
|
||||
|
|
@ -19,7 +20,6 @@ AI assistant plugin for [DankMaterialShell](https://github.com/AvengeMedia/DankM
|
|||
|
||||
## Requirements
|
||||
- `curl`
|
||||
- `wl-copy`
|
||||
- That's it, you probably already have them
|
||||
|
||||
## Roadmap
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"id": "nexusAI",
|
||||
"id": "nexusAi",
|
||||
"name": "Nexus AI",
|
||||
"description": "AI assistant, made to be useful",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.7",
|
||||
"author": "myNicole98",
|
||||
"type": "daemon",
|
||||
"capabilities": ["slideout"],
|
||||
|
|
|
|||
|
|
@ -24,10 +24,13 @@ Item {
|
|||
+ (dark ? "dark" : "light") + "/" + stem + ".svg").toString()
|
||||
: ""
|
||||
readonly property string _alt: stem.length > 0
|
||||
&& primaryImg.status === Image.Error
|
||||
? Qt.resolvedUrl("../../resources/icons/"
|
||||
+ (dark ? "light" : "dark") + "/" + stem + ".svg").toString()
|
||||
: ""
|
||||
readonly property string _model: stem.length > 0
|
||||
&& primaryImg.status === Image.Error
|
||||
&& altImg.status === Image.Error
|
||||
? Qt.resolvedUrl("../../resources/icons/models/" + stem + ".svg").toString()
|
||||
: ""
|
||||
|
||||
|
|
|
|||
|
|
@ -558,28 +558,39 @@ Rectangle {
|
|||
}
|
||||
|
||||
|
||||
// Streaming indicator before first content: the tumbling hex
|
||||
// sphere (same small config as the footer status row) with a
|
||||
// plain status label.
|
||||
Row {
|
||||
visible: isStreaming && content.length === 0 && thinking.length === 0
|
||||
spacing: 8
|
||||
|
||||
HexSphereLogo {
|
||||
radius: 11
|
||||
strokeWidth: 1.5
|
||||
subdivisions: 1
|
||||
yawRate: 20
|
||||
pitchRate: 8
|
||||
interactive: false
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
// Streaming indicator before first content: a plain status
|
||||
// label breathing toward the accent — same 750 ms cadence as
|
||||
// the tool chip's in-flight pulse.
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
id: promptRow
|
||||
visible: isStreaming && content.length === 0 && thinking.length === 0
|
||||
text: "Processing prompt"
|
||||
color: Theme.surfaceVariantText
|
||||
font.pixelSize: 11
|
||||
|
||||
// 0 → 1 → 0 while the label shows; lerps the color toward
|
||||
// the accent (k = 0.55, matching breatheColor on the chips).
|
||||
property real breathe: 0
|
||||
SequentialAnimation on breathe {
|
||||
running: promptRow.visible
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation {
|
||||
from: 0; to: 1; duration: 750
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
NumberAnimation {
|
||||
from: 1; to: 0; duration: 750
|
||||
easing.type: Easing.InOutSine
|
||||
}
|
||||
}
|
||||
color: {
|
||||
var k = breathe * 0.55;
|
||||
var base = Theme.surfaceVariantText;
|
||||
var accent = Theme.primary;
|
||||
return Qt.rgba(
|
||||
base.r + (accent.r - base.r) * k,
|
||||
base.g + (accent.g - base.g) * k,
|
||||
base.b + (accent.b - base.b) * k,
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -703,7 +714,7 @@ Rectangle {
|
|||
visible: isUser && bubble.content.length > 0
|
||||
iconName: "content_copy"
|
||||
onClicked: Quickshell.execDetached(
|
||||
["wl-copy", "--", bubble.content])
|
||||
["dms", "cl", "copy", bubble.content])
|
||||
}
|
||||
HoverIcon {
|
||||
visible: isUser
|
||||
|
|
@ -724,7 +735,7 @@ Rectangle {
|
|||
visible: !isUser && bubble.content.length > 0
|
||||
iconName: "content_copy"
|
||||
onClicked: Quickshell.execDetached(
|
||||
["wl-copy", "--", bubble.content])
|
||||
["dms", "cl", "copy", bubble.content])
|
||||
}
|
||||
|
||||
Text {
|
||||
|
|
@ -858,7 +869,7 @@ Rectangle {
|
|||
iconName: "content_copy"
|
||||
iconColor: Theme.surfaceVariantText
|
||||
onClicked: Quickshell.execDetached(
|
||||
["wl-copy", "--", codeRect.rawCode])
|
||||
["dms", "cl", "copy", codeRect.rawCode])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,6 +107,34 @@ Rectangle {
|
|||
return pruned;
|
||||
}
|
||||
|
||||
// Generate a dynamic cache during model search that returns
|
||||
// the same array instead of rebuild the list at every keystroke
|
||||
property var _rowsCache: []
|
||||
function rowsFor(filter) {
|
||||
var rows = buildRows(filter);
|
||||
var old = _rowsCache;
|
||||
if (old.length === rows.length) {
|
||||
var same = true;
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var a = old[i], b = rows[i];
|
||||
if (!a || !b
|
||||
|| a.kind !== b.kind
|
||||
|| a.model !== b.model
|
||||
|| a.providerId !== b.providerId
|
||||
|| a.type !== b.type
|
||||
|| a.name !== b.name
|
||||
|| a.pid !== b.pid
|
||||
|| a.collapsed !== b.collapsed) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (same) return old;
|
||||
}
|
||||
_rowsCache = rows;
|
||||
return rows;
|
||||
}
|
||||
|
||||
// Model zone (left): label + chevron. Hover highlights the zone
|
||||
// (not the tune section) and shows the pointing cursor.
|
||||
Item {
|
||||
|
|
@ -252,7 +280,10 @@ Rectangle {
|
|||
clip: true
|
||||
spacing: 2
|
||||
model: selector.service
|
||||
? selector.buildRows(manualEntry.text) : []
|
||||
? selector.rowsFor(manualEntry.text) : []
|
||||
// Recycle delegates on scroll instead of destroying
|
||||
// and recreating them (each row is ~30 objects).
|
||||
reuseItems: true
|
||||
|
||||
delegate: Item {
|
||||
id: rowDelegate
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ Item {
|
|||
anchors.fill: parent
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g,
|
||||
Theme.surfaceContainer.b, SettingsData.popupTransparency)
|
||||
Theme.surfaceContainer.b)
|
||||
}
|
||||
|
||||
Flickable {
|
||||
|
|
@ -54,6 +54,7 @@ Item {
|
|||
// ── Header row ────────────────────────────────────────────
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Text {
|
||||
text: "Settings"
|
||||
color: Theme.surfaceText
|
||||
|
|
|
|||
Loading…
Reference in a new issue