Improve KDE GUI file and command handling
This commit is contained in:
parent
483981a9cd
commit
9e0e5cb0f4
18
Makefile
18
Makefile
|
|
@ -26,6 +26,8 @@ kf5:
|
|||
|
||||
install: build
|
||||
@echo "=== Installing ollie-kde (KF6) ==="
|
||||
# Remove stale copies from old install path
|
||||
rm -f /usr/local/bin/ollie-ask /usr/local/bin/ollie-session-here
|
||||
# Binaries
|
||||
install -m755 $(BUILD_DIR)/ollie-tray /usr/bin/
|
||||
install -m755 $(BUILD_DIR)/ollie-gui /usr/bin/
|
||||
|
|
@ -58,8 +60,8 @@ install: build
|
|||
mkdir -p ~/.local/share/kio/servicemenus
|
||||
rm -f ~/.local/share/kio/servicemenus/ollie-actions.desktop
|
||||
install -m755 dolphin/ollie-actions.desktop ~/.local/share/kio/servicemenus/
|
||||
install -m755 dolphin/ollie-ask /usr/local/bin/
|
||||
install -m755 dolphin/ollie-session-here /usr/local/bin/
|
||||
install -m755 dolphin/ollie-ask /usr/bin/
|
||||
install -m755 dolphin/ollie-session-here /usr/bin/
|
||||
# Finalize
|
||||
ldconfig
|
||||
kquitapp6 krunner 2>/dev/null; true
|
||||
|
|
@ -78,7 +80,7 @@ uninstall:
|
|||
rm -rf /usr/lib64/qt6/qml/org/ollie
|
||||
kpackagetool6 -g -t Plasma/Applet -r org.ollie.plasmoid 2>/dev/null; true
|
||||
rm -f ~/.local/share/kio/servicemenus/ollie-actions.desktop
|
||||
rm -f /usr/local/bin/ollie-ask /usr/local/bin/ollie-session-here
|
||||
rm -f /usr/bin/ollie-ask /usr/bin/ollie-session-here
|
||||
ldconfig
|
||||
kquitapp6 krunner 2>/dev/null; true
|
||||
@echo "=== Uninstall complete ==="
|
||||
|
|
@ -96,8 +98,8 @@ dolphin-install:
|
|||
mkdir -p ~/.local/share/kio/servicemenus
|
||||
rm -f ~/.local/share/kio/servicemenus/ollie-actions.desktop
|
||||
install -m755 dolphin/ollie-actions.desktop ~/.local/share/kio/servicemenus/
|
||||
install -m755 dolphin/ollie-ask /usr/local/bin/
|
||||
install -m755 dolphin/ollie-session-here /usr/local/bin/
|
||||
install -m755 dolphin/ollie-ask /usr/bin/
|
||||
install -m755 dolphin/ollie-session-here /usr/bin/
|
||||
@echo "Dolphin menus installed. Restart Dolphin or re-open context menu."
|
||||
|
||||
plasmoid-install:
|
||||
|
|
@ -156,8 +158,8 @@ kf5-install:
|
|||
mkdir -p ~/.local/share/kio/servicemenus
|
||||
rm -f ~/.local/share/kio/servicemenus/ollie-actions.desktop
|
||||
install -m755 dolphin/ollie-actions-kf5.desktop ~/.local/share/kio/servicemenus/ollie-actions.desktop
|
||||
install -m755 dolphin/ollie-ask /usr/local/bin/
|
||||
install -m755 dolphin/ollie-session-here /usr/local/bin/
|
||||
install -m755 dolphin/ollie-ask /usr/bin/
|
||||
install -m755 dolphin/ollie-session-here /usr/bin/
|
||||
# Finalize
|
||||
ldconfig
|
||||
kquitapp5 krunner 2>/dev/null; true
|
||||
|
|
@ -173,7 +175,7 @@ kf5-uninstall:
|
|||
rm -rf /usr/lib/x86_64-linux-gnu/qt5/qml/org/ollie
|
||||
kpackagetool5 -t Plasma/Applet -r org.ollie.plasmoid 2>/dev/null; true
|
||||
rm -f ~/.local/share/kio/servicemenus/ollie-actions.desktop
|
||||
rm -f /usr/local/bin/ollie-ask /usr/local/bin/ollie-session-here
|
||||
rm -f /usr/bin/ollie-ask /usr/bin/ollie-session-here
|
||||
ldconfig
|
||||
kquitapp5 krunner 2>/dev/null; true
|
||||
@echo "=== Uninstall complete (KF5) ==="
|
||||
|
|
|
|||
|
|
@ -1,11 +1,21 @@
|
|||
#!/bin/sh
|
||||
# ollie-session-here: start a session in the given directory
|
||||
# Works from both Dolphin (passes %f) and terminal (uses $PWD).
|
||||
TARGET="$1"
|
||||
[ -f "$TARGET" ] && TARGET="$(dirname "$TARGET")"
|
||||
[ -z "$TARGET" ] && TARGET="$HOME"
|
||||
[ -z "$TARGET" ] && TARGET="$PWD"
|
||||
TARGET="$(cd "$TARGET" 2>/dev/null && pwd)" || TARGET="$PWD"
|
||||
|
||||
dbus-send --session --dest=org.ollie.SessionManager --type=method_call \
|
||||
RESULT=$(dbus-send --session --dest=org.ollie.SessionManager --type=method_call --print-reply \
|
||||
/org/ollie/SessionManager org.ollie.SessionManager.CreateSession \
|
||||
string:"$TARGET" string:"" string:"" string:"default"
|
||||
string:"$TARGET" string:"" string:"" string:"default" 2>&1)
|
||||
|
||||
notify-send -t 3000 "Ollie" "Session started in $TARGET"
|
||||
SESSION_ID=$(echo "$RESULT" | grep 'string "' | head -1 | sed 's/.*string "\(.*\)"/\1/')
|
||||
|
||||
if [ -t 1 ]; then
|
||||
# Terminal: print session ID for further use
|
||||
echo "$SESSION_ID"
|
||||
else
|
||||
# Dolphin/non-interactive: desktop notification
|
||||
notify-send -t 3000 "Ollie" "Session started in $TARGET"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
#include <KSyntaxHighlighting/Format>
|
||||
#include <QGuiApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QUrl>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QPalette>
|
||||
|
|
@ -469,7 +472,97 @@ void ChatBlockModel::copyToClipboard(const QString &text) const
|
|||
QGuiApplication::clipboard()->setText(text);
|
||||
}
|
||||
|
||||
void ChatBlockModel::executeInTerminal(const QString &code, const QString &language) const
|
||||
QString ChatBlockModel::linkifyFilePaths(const QString &markdown) const
|
||||
{
|
||||
// Agent output normally uses absolute paths, optionally followed by :line:column.
|
||||
// Restrict matching to absolute paths so ordinary prose and relative URLs remain intact.
|
||||
static const QRegularExpression pathRe(
|
||||
R"((?<![A-Za-z0-9_:])(`?)(/[^\s`<>\[\]\(\),;!?\:]+)(?::(\d+))?(?::(\d+))?\1)");
|
||||
|
||||
// Preserve complete Markdown links; rewriting a path inside their label would
|
||||
// produce nested links that Qt renders as literal Markdown.
|
||||
static const QRegularExpression markdownLinkRe(R"(\[[^\]]*\]\([^\)]*\))");
|
||||
QList<QPair<int, int>> linkRanges;
|
||||
auto links = markdownLinkRe.globalMatch(markdown);
|
||||
while (links.hasNext()) {
|
||||
const QRegularExpressionMatch link = links.next();
|
||||
linkRanges.append({link.capturedStart(), link.capturedEnd()});
|
||||
}
|
||||
|
||||
QString result;
|
||||
result.reserve(markdown.size() + 64);
|
||||
int lastEnd = 0;
|
||||
auto matches = pathRe.globalMatch(markdown);
|
||||
while (matches.hasNext()) {
|
||||
const QRegularExpressionMatch match = matches.next();
|
||||
const QString path = match.captured(2);
|
||||
const int start = match.capturedStart();
|
||||
|
||||
bool insideMarkdownLink = false;
|
||||
for (const auto &range : linkRanges) {
|
||||
if (start >= range.first && start < range.second) {
|
||||
insideMarkdownLink = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (insideMarkdownLink)
|
||||
continue;
|
||||
|
||||
result += markdown.mid(lastEnd, start - lastEnd);
|
||||
QUrl url = QUrl::fromLocalFile(path);
|
||||
QStringList query;
|
||||
if (!match.captured(3).isEmpty())
|
||||
query << QStringLiteral("line=") + match.captured(3);
|
||||
if (!match.captured(4).isEmpty())
|
||||
query << QStringLiteral("column=") + match.captured(4);
|
||||
if (!query.isEmpty())
|
||||
url.setQuery(query.join(QLatin1Char('&')));
|
||||
|
||||
QString label = match.captured(0);
|
||||
if (!match.captured(1).isEmpty())
|
||||
label = label.mid(1, label.size() - 2);
|
||||
result += QStringLiteral("[") + label + QStringLiteral("](")
|
||||
+ url.toString(QUrl::FullyEncoded) + QStringLiteral(")");
|
||||
lastEnd = match.capturedEnd();
|
||||
}
|
||||
result += markdown.mid(lastEnd);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ChatBlockModel::openLink(const QString &link) const
|
||||
{
|
||||
const QUrl url(link);
|
||||
if (!url.isLocalFile()) {
|
||||
QDesktopServices::openUrl(url);
|
||||
return;
|
||||
}
|
||||
|
||||
const QString path = url.toLocalFile();
|
||||
int line = 0;
|
||||
int column = 0;
|
||||
const QStringList queryItems = url.query().split(QLatin1Char('&'), Qt::SkipEmptyParts);
|
||||
for (const QString &item : queryItems) {
|
||||
if (item.startsWith(QLatin1String("line=")))
|
||||
line = item.mid(5).toInt();
|
||||
else if (item.startsWith(QLatin1String("column=")))
|
||||
column = item.mid(7).toInt();
|
||||
}
|
||||
|
||||
const QString kate = QStandardPaths::findExecutable(QStringLiteral("kate"));
|
||||
if (!kate.isEmpty() && line > 0) {
|
||||
QStringList args;
|
||||
args << QStringLiteral("--line") << QString::number(line);
|
||||
if (column > 0)
|
||||
args << QStringLiteral("--column") << QString::number(column);
|
||||
args << path;
|
||||
if (QProcess::startDetached(kate, args))
|
||||
return;
|
||||
}
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
}
|
||||
|
||||
bool ChatBlockModel::executeInTerminal(const QString &code, const QString &language,
|
||||
const QString &workingDirectory) const
|
||||
{
|
||||
// Determine interpreter/shebang
|
||||
QString shebang = QStringLiteral("#!/bin/bash");
|
||||
|
|
@ -507,7 +600,7 @@ void ChatBlockModel::executeInTerminal(const QString &code, const QString &langu
|
|||
tmp.setFileTemplate(QStringLiteral("/tmp/ollie-exec-XXXXXX") + ext);
|
||||
tmp.setAutoRemove(false); // keep it so terminal can read it
|
||||
if (!tmp.open())
|
||||
return;
|
||||
return false;
|
||||
|
||||
QString script = shebang + QStringLiteral("\n") + code + QStringLiteral("\n");
|
||||
tmp.write(script.toUtf8());
|
||||
|
|
@ -536,7 +629,9 @@ void ChatBlockModel::executeInTerminal(const QString &code, const QString &langu
|
|||
args << QStringLiteral("-e") << scriptPath;
|
||||
}
|
||||
|
||||
QProcess::startDetached(exe, args);
|
||||
return;
|
||||
const QString cwd = QDir(workingDirectory).exists() ? workingDirectory : QDir::homePath();
|
||||
if (QProcess::startDetached(exe, args, cwd))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,10 @@ public:
|
|||
Q_INVOKABLE QString highlightedHtml(const QString &code, const QString &language) const;
|
||||
Q_INVOKABLE QString prettyJson(const QString &text) const;
|
||||
Q_INVOKABLE void copyToClipboard(const QString &text) const;
|
||||
Q_INVOKABLE void executeInTerminal(const QString &code, const QString &language) const;
|
||||
Q_INVOKABLE QString linkifyFilePaths(const QString &markdown) const;
|
||||
Q_INVOKABLE void openLink(const QString &link) const;
|
||||
Q_INVOKABLE bool executeInTerminal(const QString &code, const QString &language,
|
||||
const QString &workingDirectory = QString()) const;
|
||||
Q_INVOKABLE bool loadEarlier(); // returns true if more blocks were loaded
|
||||
|
||||
signals:
|
||||
|
|
|
|||
|
|
@ -1081,14 +1081,14 @@ ApplicationWindow {
|
|||
id: proseSubDelegate
|
||||
Text {
|
||||
width: parent ? parent.width : implicitWidth
|
||||
text: subBlock.content || ""
|
||||
text: chatModel.linkifyFilePaths(subBlock.content || "")
|
||||
wrapMode: Text.Wrap
|
||||
textFormat: Text.MarkdownText
|
||||
color: palette.text
|
||||
font.pointSize: root.font.pointSize
|
||||
font.family: root.font.family
|
||||
lineHeight: 1.3
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
onLinkActivated: chatModel.openLink(link)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1186,7 +1186,8 @@ ApplicationWindow {
|
|||
"py", "perl", "ruby", "rb", "lua", "node",
|
||||
"javascript", "js", "php"].indexOf(lang) >= 0
|
||||
}
|
||||
onClicked: chatModel.executeInTerminal(subBlock.content, subBlock.language)
|
||||
onClicked: chatModel.executeInTerminal(subBlock.content, subBlock.language,
|
||||
dbus.activeCwd)
|
||||
ToolTip.text: "Execute in terminal"
|
||||
ToolTip.visible: hovered
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1086,7 +1086,7 @@ ApplicationWindow {
|
|||
id: proseSubDelegate
|
||||
Text {
|
||||
width: parent ? parent.width : implicitWidth
|
||||
text: subBlock.content || ""
|
||||
text: chatModel.linkifyFilePaths(subBlock.content || "")
|
||||
wrapMode: Text.Wrap
|
||||
textFormat: Text.MarkdownText
|
||||
color: palette.text
|
||||
|
|
@ -1094,7 +1094,7 @@ ApplicationWindow {
|
|||
font.family: root.font.family
|
||||
lineHeight: 1.3
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
chatModel.openLink(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1193,7 +1193,8 @@ ApplicationWindow {
|
|||
"py", "perl", "ruby", "rb", "lua", "node",
|
||||
"javascript", "js", "php"].indexOf(lang) >= 0
|
||||
}
|
||||
onClicked: chatModel.executeInTerminal(subBlock.content, subBlock.language)
|
||||
onClicked: chatModel.executeInTerminal(subBlock.content, subBlock.language,
|
||||
dbus.activeCwd)
|
||||
ToolTip.text: "Execute in terminal"
|
||||
ToolTip.visible: hovered
|
||||
}
|
||||
|
|
|
|||
Reference in New Issue