From 82f749a9cefb564f5ed0fde98a235756a7465b1a Mon Sep 17 00:00:00 2001
From: jamesread
Date: Mon, 25 May 2026 20:47:39 +0100
Subject: [PATCH] feat: Default icon is now a CLI HugeIcon instead of a smiley
face
---
.../pages/action_customization/icons.adoc | 33 ++++++---
docs/modules/ROOT/pages/config.adoc | 10 +--
frontend/resources/vue/ActionButton.vue | 23 ++-----
.../vue/components/ActionIconGlyph.vue | 68 +++++++++++++++++++
.../resources/vue/views/ActionDetailsView.vue | 4 +-
.../resources/vue/views/ExecutionView.vue | 3 +-
frontend/resources/vue/views/LogsListView.vue | 7 +-
service/internal/config/config.go | 2 +-
service/internal/config/sanitize_test.go | 2 +-
9 files changed, 113 insertions(+), 39 deletions(-)
create mode 100644 frontend/resources/vue/components/ActionIconGlyph.vue
diff --git a/docs/modules/ROOT/pages/action_customization/icons.adoc b/docs/modules/ROOT/pages/action_customization/icons.adoc
index 78ffff9..ed120b7 100644
--- a/docs/modules/ROOT/pages/action_customization/icons.adoc
+++ b/docs/modules/ROOT/pages/action_customization/icons.adoc
@@ -2,7 +2,7 @@
= Icons
You can specify any HTML for an icon. It's a popular choice to use Unicode
-icons because they are extremely fast to load and there are a lot of them,
+icons because they are extremely fast to load and there are a lot of them,
but OliveTin also support Iconify, and simple PNG, JPG, WEBP and similar images.
.Examples of icons in OliveTin
@@ -39,14 +39,31 @@ And you should get something that looks like this;
image::../action-button-iconify.png[]
+== HugeIcons icons (bundled)
+
+The OliveTin web UI ships with curated https://www.hugeicons.com/[HugeIcons] symbols.
+Set `icon:` to `hugeicons:` followed by the icon export name, for example `hugeicons:NeutralIcon`.
+
+This is the neutral glyph OliveTin uses when no icon is configured for an action.
+
+.`config.yaml`
+----
+actions:
+ - title: Action with the bundled CLI HugeIcon
+ icon: hugeicons:CommandLineIcon
+ shell: echo hello
+----
+
+Known `hugeicons:` names are registered in the web UI (`ActionIconGlyph` Vue component).
+
== Unicode icons ("emoji")
-Using simple emoji (unicode) icons from your browser's font is extremely fast, and can look good on some platforms. However, the icons are platform specific, which mean's they'll look different between browsers and between operating systems.
+Using simple emoji (unicode) icons from your browser's font is extremely fast, and can look good on some platforms. However, the icons are platform specific, which mean's they'll look different between browsers and between operating systems.
There are great sites like link:https://symbl.cc/en/emoji/[symbl.cc - a list of
-"Emoji" in unicode].
+"Emoji" in unicode].
-For example, if you find "link:https://symbl.cc/en/1F60E/[Smiling face with sunglasses]" you can click
+For example, if you find "link:https://symbl.cc/en/1F60E/[Smiling face with sunglasses]" you can click
on it to see it's "HTML-code". In OliveTin, you'd setup the icon like this;
----
@@ -56,16 +73,16 @@ actions:
shell: echo "You are awesome"
----
-=== Unicode alises
+=== Unicode alises
OliveTin has hard-coded aliases for a few commonly used icons, so you don't have to type out the full unicode codes. A list of those hard coded icons is;
.Alias'd unicode reference table
[%header]
|===
-| Alias | Rendered as
+| Alias | Rendered as
-| `poop` | 💩
+| `poop` | 💩
| `smile` | 😀
| `ping` | 📡
| `backup` | 💾
@@ -140,5 +157,3 @@ examples;
shell: echo "I like purple"
----
////
-
-
diff --git a/docs/modules/ROOT/pages/config.adoc b/docs/modules/ROOT/pages/config.adoc
index cd5de64..299426f 100644
--- a/docs/modules/ROOT/pages/config.adoc
+++ b/docs/modules/ROOT/pages/config.adoc
@@ -2,11 +2,11 @@
= Configuration
OliveTin is controlled by a `config.yaml` file. On startup, it looks for this
-file in the following locations;
+file in the following locations;
1. The value specified by the `--configdir` argument, which defaults to the current working directory (`./`)
2. `/config/` - Mostly used for containers
-3. `/etc/OliveTin/` - this is the recommended directory on Linux for your `config.yaml`.
+3. `/etc/OliveTin/` - this is the recommended directory on Linux for your `config.yaml`.
The most simple `config.yaml` would be something like this;
@@ -18,9 +18,9 @@ actions:
shell: echo 'Hello World!'
----
-The configuration does not really get more complicated than that. You can of course add more actions, and customize more, but the syntax otherwise extremely simple.
+The configuration does not really get more complicated than that. You can of course add more actions, and customize more, but the syntax otherwise extremely simple.
-For building up from here, look at the following resources;
+For building up from here, look at the following resources;
* See the xref:action_examples/intro.adoc[action examples] section for extra examples of what OliveTin could be configured to do.
@@ -54,7 +54,7 @@ All configuration options are covered in the solution sections
| `showNavigateOnStartIcons` | Show (or hide) the small icons on action buttons that indicate popup/argument/background behavior on start. | `true` | Live reloadable | xref:advanced_configuration/webui.adoc[Customize the web UI].
| `sectionNavigationStyle` | The style of the section navigation. `sidebar`, `topbar` | `sidebar` | Live reloadable | xref:advanced_configuration/webui.adoc[Customize the web UI].
| `defaultPopupOnStart` | The default popup to show on start. | `none` | Live reloadable | xref:action_customization/popuponstart.adoc[Popup On Start].
-| `defaultIconForActions` | The default icon to use for actions. | `smile` | Requires Restart | -
+| `defaultIconForActions` | The default icon string for actions (Unicode aliases such as `smile`, `hugeicons:NeutralIcon`, HTML, Iconify snippets, images, etc.). See xref:action_customization/icons.adoc[Icons]. | `hugeicons:CommandLineIcon` | Requires Restart | -
| `defaultIconForDirectories` | The default icon to use for directories. | `directory` | Requires Restart | -
| `defaultIconForBack` | The default icon to use for back (from directories). | `«` | Requires Restart | -
| `enableCustomJs` | Enable custom JavaScript. | `false` | Live Reloadable, but refreshing the web browser is required. | xref:advanced_configuration/webui.adoc[Custom JS].
diff --git a/frontend/resources/vue/ActionButton.vue b/frontend/resources/vue/ActionButton.vue
index 8f81521..bf3ef51 100644
--- a/frontend/resources/vue/ActionButton.vue
+++ b/frontend/resources/vue/ActionButton.vue
@@ -15,7 +15,7 @@
-
+ {{ displayTitle }}
{{ rateLimitMessage }}
@@ -30,7 +30,9 @@ import { useRouter } from 'vue-router'
import { HugeiconsIcon } from '@hugeicons/vue'
import { WorkoutRunIcon, TypeCursorIcon, ComputerTerminal01Icon } from '@hugeicons/core-free-icons'
-import { ref, watch, onMounted, onUnmounted, inject, computed } from 'vue'
+import ActionIconGlyph from './components/ActionIconGlyph.vue'
+
+import { ref, watch, onMounted, onUnmounted, computed } from 'vue'
const router = useRouter()
const navigateOnStart = ref('')
@@ -53,7 +55,6 @@ const canExec = ref(true)
const popupOnStart = ref('')
// Display properties
-const unicodeIcon = ref('💩')
const displayTitle = ref('')
// State
@@ -74,6 +75,8 @@ const showNavigateOnStartIcons = computed(() => {
return window.initResponse?.showNavigateOnStartIcons ?? true
})
+const actionGlyph = computed(() => props.actionData?.icon ?? '')
+
// Combined classes including custom cssClass
const combinedClasses = computed(() => {
const classes = [...buttonClasses.value]
@@ -86,16 +89,6 @@ const combinedClasses = computed(() => {
// Timestamps
const updateIterationTimestamp = ref(0)
-function getUnicodeIcon(icon) {
- if (icon === '') {
- console.log('icon not found ', icon)
-
- return '💩'
- } else {
- return unescape(icon)
- }
-}
-
function constructFromJson(json) {
updateIterationTimestamp.value = 0
@@ -114,8 +107,6 @@ function constructFromJson(json) {
isDisabled.value = !json.canExec
displayTitle.value = title.value
- unicodeIcon.value = getUnicodeIcon(json.icon)
-
// Initialize rate limit from action data (parse datetime string)
if (json.datetimeRateLimitExpires) {
const date = new Date(json.datetimeRateLimitExpires.replace(' ', 'T'))
@@ -134,8 +125,6 @@ function updateFromJson(json) {
// Fields that should not be updated
// title - as the callback URL relies on it
- unicodeIcon.value = getUnicodeIcon(json.icon)
-
// Update rate limiting if changed (parse datetime string)
if (json.datetimeRateLimitExpires) {
const date = new Date(json.datetimeRateLimitExpires.replace(' ', 'T'))
diff --git a/frontend/resources/vue/components/ActionIconGlyph.vue b/frontend/resources/vue/components/ActionIconGlyph.vue
new file mode 100644
index 0000000..e7290e6
--- /dev/null
+++ b/frontend/resources/vue/components/ActionIconGlyph.vue
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/resources/vue/views/ActionDetailsView.vue b/frontend/resources/vue/views/ActionDetailsView.vue
index 9d8b31e..59df188 100644
--- a/frontend/resources/vue/views/ActionDetailsView.vue
+++ b/frontend/resources/vue/views/ActionDetailsView.vue
@@ -22,7 +22,7 @@