pinokiod 7.5.13 → 7.5.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/kernel/gpu/amd.js +30 -43
- package/kernel/gpu/amd_gfx_targets.json +67 -0
- package/kernel/gpu/nvidia.js +90 -4
- package/kernel/index.js +4 -3
- package/kernel/sysinfo.js +16 -25
- package/package.json +2 -1
- package/script/update-amd-gfx-targets.js +119 -0
- package/server/lib/app_log_report.js +1 -2
- package/server/public/logs.js +230 -30
- package/server/public/style.css +28 -12
- package/server/views/index.ejs +7 -0
- package/test/amd-gpu-target.test.js +169 -0
- package/test/gpu-target-template-variable.test.js +69 -0
- package/test/logs-ask-ai.test.js +65 -10
- package/test/nvidia-gpu-target.test.js +98 -0
- package/test/sysinfo-gpu-driver.test.js +134 -1
- package/kernel/gpu/apple.js +0 -8
- package/kernel/gpu/intel.js +0 -47
- package/release_notes/8.0.0/assets/app-ask-ai-drawer.png +0 -0
- package/release_notes/8.0.0/assets/app-detail-header-actions.png +0 -0
- package/release_notes/8.0.0/assets/autolaunch-dependencies.png +0 -0
- package/release_notes/8.0.0/assets/connect-huggingface-login.png +0 -0
- package/release_notes/8.0.0/assets/home-app-actions.png +0 -0
- package/release_notes/8.0.0/assets/home-sidebar-apps.png +0 -0
- package/release_notes/8.0.0/assets/logs-terminal-reporting.png +0 -0
- package/release_notes/8.0.0/assets/mobile-home-footer-nav.png +0 -0
- package/release_notes/8.0.0/assets/network-phone-access.png +0 -0
- package/release_notes/8.0.0/assets/plugins-agent-tools.png +0 -0
- package/release_notes/8.0.0/assets/skills-management.png +0 -0
- package/release_notes/8.0.0/assets/tools-runtime-management.png +0 -0
- package/release_notes/8.0.0/ui.md +0 -91
package/server/public/logs.js
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
const DRAFT_TITLE_NOISE_PATTERN = /^\s*(?:<<PINOKIO_SHELL>>|={6,}|-{6,}|\[api\s+local\.set\]|The default interactive shell is now|To update your account|For more details, please visit)/i
|
|
23
23
|
const ASK_AI_DEFAULT_PROMPT = 'Investigate what went wrong. Inspect the app logs and explain the likely root cause and next fix.'
|
|
24
24
|
const TOOL_PREFERENCE_KEY = 'pinokio.universalLauncher.tool'
|
|
25
|
+
const ASK_AI_TOOL_CATEGORY_ORDER = ['Terminal', 'Desktop']
|
|
25
26
|
const textEncoder = typeof TextEncoder === 'function' ? new TextEncoder() : null
|
|
26
27
|
|
|
27
28
|
const safeJsonParse = (value) => {
|
|
@@ -152,8 +153,10 @@
|
|
|
152
153
|
installed: typeof plugin.installed === 'boolean' ? plugin.installed : null
|
|
153
154
|
}
|
|
154
155
|
}).filter(Boolean).sort((a, b) => {
|
|
155
|
-
const categoryDelta =
|
|
156
|
+
const categoryDelta = askAiToolCategoryRank(a.category) - askAiToolCategoryRank(b.category)
|
|
156
157
|
if (categoryDelta !== 0) return categoryDelta
|
|
158
|
+
const categoryNameDelta = String(a.category || '').localeCompare(String(b.category || ''), undefined, { sensitivity: 'base' })
|
|
159
|
+
if (categoryNameDelta !== 0) return categoryNameDelta
|
|
157
160
|
return String(a.label || '').localeCompare(String(b.label || ''), undefined, { sensitivity: 'base' })
|
|
158
161
|
})
|
|
159
162
|
}
|
|
@@ -170,6 +173,12 @@
|
|
|
170
173
|
return `${label} (${category})`
|
|
171
174
|
}
|
|
172
175
|
|
|
176
|
+
const askAiToolCategoryRank = (category) => {
|
|
177
|
+
const normalized = String(category || '').trim().toLowerCase()
|
|
178
|
+
const index = ASK_AI_TOOL_CATEGORY_ORDER.findIndex((item) => item.toLowerCase() === normalized)
|
|
179
|
+
return index >= 0 ? index : ASK_AI_TOOL_CATEGORY_ORDER.length
|
|
180
|
+
}
|
|
181
|
+
|
|
173
182
|
class PrivacyFilterClient {
|
|
174
183
|
constructor() {
|
|
175
184
|
this.worker = null
|
|
@@ -695,9 +704,11 @@
|
|
|
695
704
|
toolPicker.className = 'universal-launcher-tool-picker logs-ask-ai-tool-picker'
|
|
696
705
|
toolSection.appendChild(toolPicker)
|
|
697
706
|
|
|
698
|
-
const toolTrigger = document.createElement('
|
|
707
|
+
const toolTrigger = document.createElement('button')
|
|
708
|
+
toolTrigger.type = 'button'
|
|
699
709
|
toolTrigger.className = 'universal-launcher-tool-trigger has-value logs-ask-ai-tool-trigger'
|
|
700
|
-
toolTrigger.setAttribute('aria-
|
|
710
|
+
toolTrigger.setAttribute('aria-haspopup', 'listbox')
|
|
711
|
+
toolTrigger.setAttribute('aria-expanded', 'false')
|
|
701
712
|
toolPicker.appendChild(toolTrigger)
|
|
702
713
|
|
|
703
714
|
const toolIcon = document.createElement('span')
|
|
@@ -721,10 +732,51 @@
|
|
|
721
732
|
toolCaret.setAttribute('aria-hidden', 'true')
|
|
722
733
|
toolTrigger.appendChild(toolCaret)
|
|
723
734
|
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
735
|
+
const toolSheetLayer = document.createElement('div')
|
|
736
|
+
toolSheetLayer.className = 'universal-launcher-tool-sheet-layer logs-ask-ai-tool-sheet-layer'
|
|
737
|
+
toolSheetLayer.hidden = true
|
|
738
|
+
panel.appendChild(toolSheetLayer)
|
|
739
|
+
|
|
740
|
+
const toolSheetBackdrop = document.createElement('button')
|
|
741
|
+
toolSheetBackdrop.type = 'button'
|
|
742
|
+
toolSheetBackdrop.className = 'universal-launcher-tool-sheet-backdrop'
|
|
743
|
+
toolSheetBackdrop.setAttribute('aria-label', 'Close agent selection')
|
|
744
|
+
toolSheetLayer.appendChild(toolSheetBackdrop)
|
|
745
|
+
|
|
746
|
+
const toolSheet = document.createElement('section')
|
|
747
|
+
toolSheet.className = 'universal-launcher-tool-sheet logs-ask-ai-tool-sheet'
|
|
748
|
+
toolSheet.setAttribute('aria-label', 'Choose agent')
|
|
749
|
+
toolSheetLayer.appendChild(toolSheet)
|
|
750
|
+
|
|
751
|
+
const toolSheetHeader = document.createElement('div')
|
|
752
|
+
toolSheetHeader.className = 'universal-launcher-tool-sheet-header'
|
|
753
|
+
toolSheet.appendChild(toolSheetHeader)
|
|
754
|
+
|
|
755
|
+
const toolSheetHeading = document.createElement('div')
|
|
756
|
+
toolSheetHeading.className = 'universal-launcher-tool-sheet-heading'
|
|
757
|
+
toolSheetHeader.appendChild(toolSheetHeading)
|
|
758
|
+
|
|
759
|
+
const toolSheetTitle = document.createElement('div')
|
|
760
|
+
toolSheetTitle.className = 'universal-launcher-tool-sheet-title'
|
|
761
|
+
toolSheetTitle.textContent = 'Choose agent'
|
|
762
|
+
toolSheetHeading.appendChild(toolSheetTitle)
|
|
763
|
+
|
|
764
|
+
const toolSheetDescription = document.createElement('div')
|
|
765
|
+
toolSheetDescription.className = 'universal-launcher-tool-sheet-description'
|
|
766
|
+
toolSheetDescription.textContent = 'Launch this report with a local agent.'
|
|
767
|
+
toolSheetHeading.appendChild(toolSheetDescription)
|
|
768
|
+
|
|
769
|
+
const toolSheetClose = document.createElement('button')
|
|
770
|
+
toolSheetClose.type = 'button'
|
|
771
|
+
toolSheetClose.className = 'universal-launcher-tool-sheet-close'
|
|
772
|
+
toolSheetClose.setAttribute('aria-label', 'Close agent selection')
|
|
773
|
+
toolSheetClose.innerHTML = '<i class="fa-solid fa-xmark" aria-hidden="true"></i>'
|
|
774
|
+
toolSheetHeader.appendChild(toolSheetClose)
|
|
775
|
+
|
|
776
|
+
const toolSheetBody = document.createElement('div')
|
|
777
|
+
toolSheetBody.className = 'universal-launcher-tool-sheet-body logs-ask-ai-tool-sheet-body'
|
|
778
|
+
toolSheetBody.setAttribute('role', 'listbox')
|
|
779
|
+
toolSheet.appendChild(toolSheetBody)
|
|
728
780
|
|
|
729
781
|
const footerActions = document.createElement('div')
|
|
730
782
|
footerActions.className = 'universal-launcher-footer-actions'
|
|
@@ -737,14 +789,45 @@
|
|
|
737
789
|
footerActions.appendChild(runButton)
|
|
738
790
|
|
|
739
791
|
let returnFocusEl = null
|
|
792
|
+
let toolSheetOpen = false
|
|
793
|
+
const closeToolSheet = (options = {}) => {
|
|
794
|
+
toolSheetOpen = false
|
|
795
|
+
toolSheetLayer.hidden = true
|
|
796
|
+
toolPicker.classList.remove('open')
|
|
797
|
+
toolTrigger.setAttribute('aria-expanded', 'false')
|
|
798
|
+
if (options.focusTrigger !== false) {
|
|
799
|
+
toolTrigger.focus()
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
const openToolSheet = () => {
|
|
803
|
+
if (!Array.isArray(this.askAiTools) || this.askAiTools.length === 0) {
|
|
804
|
+
return
|
|
805
|
+
}
|
|
806
|
+
toolSheetOpen = true
|
|
807
|
+
toolSheetLayer.hidden = false
|
|
808
|
+
toolPicker.classList.add('open')
|
|
809
|
+
toolTrigger.setAttribute('aria-expanded', 'true')
|
|
810
|
+
window.requestAnimationFrame(() => {
|
|
811
|
+
const selected = toolSheetBody.querySelector('.universal-launcher-tool.selected')
|
|
812
|
+
const first = toolSheetBody.querySelector('.universal-launcher-tool')
|
|
813
|
+
const target = selected || first || toolSheetClose
|
|
814
|
+
if (target && typeof target.focus === 'function') {
|
|
815
|
+
target.focus()
|
|
816
|
+
}
|
|
817
|
+
})
|
|
818
|
+
}
|
|
740
819
|
const syncRunState = () => {
|
|
741
820
|
runButton.disabled = !String(promptTextarea.value || '').trim() || !this.selectedAskAiTool()
|
|
742
821
|
}
|
|
743
822
|
const getFocusable = () => {
|
|
744
|
-
|
|
823
|
+
const scope = toolSheetOpen ? toolSheetLayer : panel
|
|
824
|
+
return Array.from(scope.querySelectorAll('a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'))
|
|
745
825
|
.filter((node) => node && !node.hidden && node.offsetParent !== null)
|
|
746
826
|
}
|
|
747
827
|
const setOpen = (isOpen) => {
|
|
828
|
+
if (!isOpen) {
|
|
829
|
+
closeToolSheet({ focusTrigger: false })
|
|
830
|
+
}
|
|
748
831
|
overlay.hidden = !isOpen
|
|
749
832
|
document.documentElement.classList.toggle('universal-launcher-open', isOpen)
|
|
750
833
|
document.body.classList.toggle('universal-launcher-open', isOpen)
|
|
@@ -771,6 +854,8 @@
|
|
|
771
854
|
const tool = this.selectedAskAiTool()
|
|
772
855
|
toolLabel.textContent = tool ? tool.label : 'Choose agent'
|
|
773
856
|
toolMeta.textContent = tool ? (tool.category || 'Plugin') : ''
|
|
857
|
+
toolTrigger.classList.toggle('has-value', Boolean(tool))
|
|
858
|
+
toolTrigger.setAttribute('aria-label', tool ? `Agent: ${askAiToolOptionLabel(tool)}` : 'Choose agent')
|
|
774
859
|
toolIcon.textContent = ''
|
|
775
860
|
while (toolIcon.firstChild) {
|
|
776
861
|
toolIcon.removeChild(toolIcon.firstChild)
|
|
@@ -780,6 +865,15 @@
|
|
|
780
865
|
icon.src = tool.iconSrc
|
|
781
866
|
icon.alt = ''
|
|
782
867
|
icon.className = 'logs-ask-ai-tool-trigger-image'
|
|
868
|
+
icon.onerror = () => {
|
|
869
|
+
while (toolIcon.firstChild) {
|
|
870
|
+
toolIcon.removeChild(toolIcon.firstChild)
|
|
871
|
+
}
|
|
872
|
+
const fallbackIcon = document.createElement('i')
|
|
873
|
+
fallbackIcon.className = 'fa-solid fa-robot'
|
|
874
|
+
fallbackIcon.setAttribute('aria-hidden', 'true')
|
|
875
|
+
toolIcon.appendChild(fallbackIcon)
|
|
876
|
+
}
|
|
783
877
|
toolIcon.appendChild(icon)
|
|
784
878
|
} else {
|
|
785
879
|
const icon = document.createElement('i')
|
|
@@ -787,6 +881,11 @@
|
|
|
787
881
|
icon.setAttribute('aria-hidden', 'true')
|
|
788
882
|
toolIcon.appendChild(icon)
|
|
789
883
|
}
|
|
884
|
+
;(this.askAiLauncher && this.askAiLauncher.toolEntries ? this.askAiLauncher.toolEntries : []).forEach((entry) => {
|
|
885
|
+
const selected = Boolean(tool && entry.tool === tool)
|
|
886
|
+
entry.button.classList.toggle('selected', selected)
|
|
887
|
+
entry.button.setAttribute('aria-selected', selected ? 'true' : 'false')
|
|
888
|
+
})
|
|
790
889
|
syncRunState()
|
|
791
890
|
}
|
|
792
891
|
const run = async () => {
|
|
@@ -799,7 +898,7 @@
|
|
|
799
898
|
}
|
|
800
899
|
if (!tool) {
|
|
801
900
|
error.textContent = 'Choose an agent.'
|
|
802
|
-
|
|
901
|
+
toolTrigger.focus()
|
|
803
902
|
return
|
|
804
903
|
}
|
|
805
904
|
error.textContent = ''
|
|
@@ -813,23 +912,31 @@
|
|
|
813
912
|
}
|
|
814
913
|
|
|
815
914
|
closeButton.addEventListener('click', close)
|
|
915
|
+
toolTrigger.addEventListener('click', () => {
|
|
916
|
+
if (toolSheetOpen) {
|
|
917
|
+
closeToolSheet()
|
|
918
|
+
} else {
|
|
919
|
+
openToolSheet()
|
|
920
|
+
}
|
|
921
|
+
})
|
|
922
|
+
toolSheetBackdrop.addEventListener('click', () => closeToolSheet())
|
|
923
|
+
toolSheetClose.addEventListener('click', () => closeToolSheet())
|
|
816
924
|
overlay.addEventListener('click', (event) => {
|
|
817
925
|
if (event.target === overlay) {
|
|
818
926
|
close()
|
|
819
927
|
}
|
|
820
928
|
})
|
|
821
|
-
toolSelect.addEventListener('change', syncTool)
|
|
822
|
-
toolSelect.addEventListener('change', () => {
|
|
823
|
-
const tool = this.selectedAskAiTool()
|
|
824
|
-
setStoredToolPreference(tool && tool.value ? tool.value : '')
|
|
825
|
-
})
|
|
826
929
|
promptTextarea.addEventListener('input', syncRunState)
|
|
827
930
|
runButton.addEventListener('click', run)
|
|
828
931
|
overlay.addEventListener('keydown', (event) => {
|
|
829
932
|
event.stopPropagation()
|
|
830
933
|
if (event.key === 'Escape') {
|
|
831
934
|
event.preventDefault()
|
|
832
|
-
|
|
935
|
+
if (toolSheetOpen) {
|
|
936
|
+
closeToolSheet()
|
|
937
|
+
} else {
|
|
938
|
+
close()
|
|
939
|
+
}
|
|
833
940
|
} else if (event.key === 'Tab') {
|
|
834
941
|
const focusable = getFocusable()
|
|
835
942
|
if (focusable.length === 0) {
|
|
@@ -858,38 +965,131 @@
|
|
|
858
965
|
overlay,
|
|
859
966
|
panel,
|
|
860
967
|
promptTextarea,
|
|
861
|
-
|
|
968
|
+
toolTrigger,
|
|
969
|
+
toolSheetBody,
|
|
970
|
+
toolSheetLayer,
|
|
862
971
|
toolLabel,
|
|
863
972
|
toolMeta,
|
|
864
973
|
toolIcon,
|
|
974
|
+
selectedToolIndex: -1,
|
|
975
|
+
toolEntries: [],
|
|
976
|
+
setToolIndex(index) {
|
|
977
|
+
const nextIndex = Number.isFinite(index) ? index : -1
|
|
978
|
+
this.selectedToolIndex = nextIndex
|
|
979
|
+
const tool = Array.isArray(this.owner.askAiTools) ? this.owner.askAiTools[nextIndex] : null
|
|
980
|
+
setStoredToolPreference(tool && tool.value ? tool.value : '')
|
|
981
|
+
this.syncTool()
|
|
982
|
+
},
|
|
865
983
|
error,
|
|
866
984
|
runButton,
|
|
867
985
|
setOpen,
|
|
986
|
+
closeToolSheet,
|
|
868
987
|
syncTool,
|
|
869
|
-
syncRunState
|
|
988
|
+
syncRunState,
|
|
989
|
+
owner: this
|
|
870
990
|
}
|
|
871
991
|
return this.askAiLauncher
|
|
872
992
|
}
|
|
873
993
|
populateAskAiLauncherTools(tools) {
|
|
874
994
|
const launcher = this.createAskAiLauncher()
|
|
875
|
-
while (launcher.
|
|
876
|
-
launcher.
|
|
995
|
+
while (launcher.toolSheetBody.firstChild) {
|
|
996
|
+
launcher.toolSheetBody.removeChild(launcher.toolSheetBody.firstChild)
|
|
997
|
+
}
|
|
998
|
+
launcher.toolEntries = []
|
|
999
|
+
const createFallbackToolIcon = () => {
|
|
1000
|
+
const icon = document.createElement('span')
|
|
1001
|
+
icon.className = 'universal-launcher-tool-icon logs-ask-ai-tool-fallback-icon'
|
|
1002
|
+
icon.innerHTML = '<i class="fa-solid fa-robot" aria-hidden="true"></i>'
|
|
1003
|
+
return icon
|
|
877
1004
|
}
|
|
878
|
-
const
|
|
879
|
-
placeholder.value = ''
|
|
880
|
-
placeholder.textContent = 'Choose agent'
|
|
881
|
-
launcher.toolSelect.appendChild(placeholder)
|
|
1005
|
+
const groups = new Map()
|
|
882
1006
|
tools.forEach((tool, index) => {
|
|
883
|
-
const
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1007
|
+
const category = tool && tool.category ? tool.category : 'Plugin'
|
|
1008
|
+
if (!groups.has(category)) {
|
|
1009
|
+
groups.set(category, [])
|
|
1010
|
+
}
|
|
1011
|
+
groups.get(category).push({ tool, index })
|
|
1012
|
+
})
|
|
1013
|
+
const orderedGroupCategories = []
|
|
1014
|
+
ASK_AI_TOOL_CATEGORY_ORDER.forEach((preferredCategory) => {
|
|
1015
|
+
const match = Array.from(groups.keys()).find((category) => String(category).toLowerCase() === preferredCategory.toLowerCase())
|
|
1016
|
+
if (match && !orderedGroupCategories.includes(match)) {
|
|
1017
|
+
orderedGroupCategories.push(match)
|
|
1018
|
+
}
|
|
1019
|
+
})
|
|
1020
|
+
Array.from(groups.keys()).sort((a, b) => {
|
|
1021
|
+
const rankDelta = askAiToolCategoryRank(a) - askAiToolCategoryRank(b)
|
|
1022
|
+
if (rankDelta !== 0) return rankDelta
|
|
1023
|
+
return String(a || '').localeCompare(String(b || ''), undefined, { sensitivity: 'base' })
|
|
1024
|
+
}).forEach((category) => {
|
|
1025
|
+
if (!orderedGroupCategories.includes(category)) {
|
|
1026
|
+
orderedGroupCategories.push(category)
|
|
1027
|
+
}
|
|
1028
|
+
})
|
|
1029
|
+
orderedGroupCategories.forEach((category) => {
|
|
1030
|
+
const entries = groups.get(category) || []
|
|
1031
|
+
const group = document.createElement('div')
|
|
1032
|
+
group.className = 'universal-launcher-tool-group'
|
|
1033
|
+
const heading = document.createElement('div')
|
|
1034
|
+
heading.className = 'universal-launcher-tool-group-title'
|
|
1035
|
+
heading.textContent = category
|
|
1036
|
+
group.appendChild(heading)
|
|
1037
|
+
const list = document.createElement('div')
|
|
1038
|
+
list.className = 'universal-launcher-tool-list logs-ask-ai-tool-list'
|
|
1039
|
+
group.appendChild(list)
|
|
1040
|
+
entries.forEach(({ tool, index }) => {
|
|
1041
|
+
const option = document.createElement('button')
|
|
1042
|
+
option.type = 'button'
|
|
1043
|
+
option.className = 'universal-launcher-tool'
|
|
1044
|
+
option.setAttribute('role', 'option')
|
|
1045
|
+
option.setAttribute('aria-selected', 'false')
|
|
1046
|
+
option.dataset.toolIndex = String(index)
|
|
1047
|
+
|
|
1048
|
+
const indicator = document.createElement('span')
|
|
1049
|
+
indicator.className = 'universal-launcher-tool-indicator'
|
|
1050
|
+
indicator.setAttribute('aria-hidden', 'true')
|
|
1051
|
+
option.appendChild(indicator)
|
|
1052
|
+
|
|
1053
|
+
if (tool && tool.iconSrc) {
|
|
1054
|
+
const icon = document.createElement('img')
|
|
1055
|
+
icon.className = 'universal-launcher-tool-icon'
|
|
1056
|
+
icon.src = tool.iconSrc
|
|
1057
|
+
icon.alt = ''
|
|
1058
|
+
icon.onerror = () => {
|
|
1059
|
+
icon.replaceWith(createFallbackToolIcon())
|
|
1060
|
+
}
|
|
1061
|
+
option.appendChild(icon)
|
|
1062
|
+
} else {
|
|
1063
|
+
option.appendChild(createFallbackToolIcon())
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
const text = document.createElement('span')
|
|
1067
|
+
text.className = 'universal-launcher-tool-copy'
|
|
1068
|
+
const label = document.createElement('span')
|
|
1069
|
+
label.className = 'universal-launcher-tool-label'
|
|
1070
|
+
label.textContent = tool ? tool.label : 'Agent'
|
|
1071
|
+
text.appendChild(label)
|
|
1072
|
+
const meta = document.createElement('span')
|
|
1073
|
+
meta.className = 'universal-launcher-tool-meta'
|
|
1074
|
+
meta.textContent = tool && tool.category ? tool.category : 'Plugin'
|
|
1075
|
+
text.appendChild(meta)
|
|
1076
|
+
option.appendChild(text)
|
|
1077
|
+
|
|
1078
|
+
option.addEventListener('click', () => {
|
|
1079
|
+
launcher.setToolIndex(index)
|
|
1080
|
+
launcher.closeToolSheet()
|
|
1081
|
+
launcher.toolTrigger.focus()
|
|
1082
|
+
})
|
|
1083
|
+
list.appendChild(option)
|
|
1084
|
+
launcher.toolEntries.push({ button: option, tool })
|
|
1085
|
+
})
|
|
1086
|
+
launcher.toolSheetBody.appendChild(group)
|
|
887
1087
|
})
|
|
888
1088
|
const preferredTool = getStoredToolPreference()
|
|
889
1089
|
const preferredIndex = preferredTool
|
|
890
1090
|
? tools.findIndex((tool) => tool && tool.value === preferredTool)
|
|
891
1091
|
: -1
|
|
892
|
-
launcher.
|
|
1092
|
+
launcher.selectedToolIndex = preferredIndex >= 0 ? preferredIndex : -1
|
|
893
1093
|
if (preferredTool && preferredIndex < 0) {
|
|
894
1094
|
setStoredToolPreference('')
|
|
895
1095
|
}
|
|
@@ -900,8 +1100,8 @@
|
|
|
900
1100
|
if (!launcher || !Array.isArray(this.askAiTools)) {
|
|
901
1101
|
return null
|
|
902
1102
|
}
|
|
903
|
-
const index =
|
|
904
|
-
return Number.isFinite(index) ? this.askAiTools[index] || null : null
|
|
1103
|
+
const index = launcher.selectedToolIndex
|
|
1104
|
+
return Number.isFinite(index) && index >= 0 ? this.askAiTools[index] || null : null
|
|
905
1105
|
}
|
|
906
1106
|
async openAskAiModal() {
|
|
907
1107
|
if (!this.currentMarkdown && !this.reviewMarkdown) {
|
package/server/public/style.css
CHANGED
|
@@ -5298,25 +5298,33 @@ body.dark .logs-help-action--ai:focus-visible {
|
|
|
5298
5298
|
width: 100%;
|
|
5299
5299
|
}
|
|
5300
5300
|
.logs-ask-ai-tool-trigger {
|
|
5301
|
-
pointer-events:
|
|
5301
|
+
pointer-events: auto;
|
|
5302
|
+
font: inherit;
|
|
5302
5303
|
}
|
|
5303
5304
|
.logs-ask-ai-tool-picker:focus-within .logs-ask-ai-tool-trigger {
|
|
5304
5305
|
border-color: var(--universal-launcher-border-strong);
|
|
5305
5306
|
background: var(--universal-launcher-control-surface);
|
|
5306
5307
|
outline: none;
|
|
5307
5308
|
}
|
|
5308
|
-
.logs-ask-ai-tool-
|
|
5309
|
-
|
|
5310
|
-
inset: 0;
|
|
5311
|
-
width: 100%;
|
|
5312
|
-
height: 100%;
|
|
5313
|
-
appearance: none;
|
|
5314
|
-
border: 0;
|
|
5315
|
-
opacity: 0;
|
|
5316
|
-
cursor: pointer;
|
|
5309
|
+
.logs-ask-ai-tool-sheet-layer {
|
|
5310
|
+
padding: 12px;
|
|
5317
5311
|
}
|
|
5318
|
-
.logs-ask-ai-tool-
|
|
5319
|
-
|
|
5312
|
+
.logs-ask-ai-tool-sheet {
|
|
5313
|
+
width: min(100%, 520px);
|
|
5314
|
+
max-height: 100%;
|
|
5315
|
+
}
|
|
5316
|
+
.logs-ask-ai-tool-sheet.logs-ask-ai-tool-sheet .universal-launcher-tool-meta {
|
|
5317
|
+
display: block;
|
|
5318
|
+
}
|
|
5319
|
+
.logs-ask-ai-tool-list {
|
|
5320
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
5321
|
+
}
|
|
5322
|
+
.logs-ask-ai-tool-fallback-icon {
|
|
5323
|
+
display: inline-flex;
|
|
5324
|
+
align-items: center;
|
|
5325
|
+
justify-content: center;
|
|
5326
|
+
color: var(--universal-launcher-text-muted);
|
|
5327
|
+
font-size: 13px;
|
|
5320
5328
|
}
|
|
5321
5329
|
.logs-ask-ai-tool-trigger-icon {
|
|
5322
5330
|
display: inline-flex;
|
|
@@ -5332,6 +5340,14 @@ body.dark .logs-help-action--ai:focus-visible {
|
|
|
5332
5340
|
object-fit: contain;
|
|
5333
5341
|
border-radius: 4px;
|
|
5334
5342
|
}
|
|
5343
|
+
@media (max-width: 560px), (max-height: 520px) {
|
|
5344
|
+
.logs-ask-ai-tool-list {
|
|
5345
|
+
grid-template-columns: minmax(0, 1fr);
|
|
5346
|
+
}
|
|
5347
|
+
.logs-ask-ai-tool-sheet-layer {
|
|
5348
|
+
padding: 8px;
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5335
5351
|
.logs-report-pane,
|
|
5336
5352
|
.logs-redaction-pane {
|
|
5337
5353
|
min-width: 0;
|
package/server/views/index.ejs
CHANGED
|
@@ -820,6 +820,13 @@ body.dark .home-app-line {
|
|
|
820
820
|
border-radius: 6px;
|
|
821
821
|
object-fit: cover;
|
|
822
822
|
}
|
|
823
|
+
.home-app-line .description {
|
|
824
|
+
margin-bottom: 0;
|
|
825
|
+
line-height: 1.32;
|
|
826
|
+
}
|
|
827
|
+
.home-app-line .menu-btns {
|
|
828
|
+
margin-top: 4px;
|
|
829
|
+
}
|
|
823
830
|
.menu-btns .home-icon-btn {
|
|
824
831
|
width: 28px;
|
|
825
832
|
height: 22px;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const fs = require('node:fs')
|
|
3
|
+
const path = require('node:path')
|
|
4
|
+
const test = require('node:test')
|
|
5
|
+
|
|
6
|
+
const system = require('systeminformation')
|
|
7
|
+
|
|
8
|
+
const amd = require('../kernel/gpu/amd')
|
|
9
|
+
const gfxTargets = require('../kernel/gpu/amd_gfx_targets.json')
|
|
10
|
+
const packageJson = require('../package.json')
|
|
11
|
+
const Sysinfo = require('../kernel/sysinfo')
|
|
12
|
+
|
|
13
|
+
const root = path.join(__dirname, '..')
|
|
14
|
+
|
|
15
|
+
test('AMD gfx generated data is checked in and self describing', () => {
|
|
16
|
+
assert.equal(
|
|
17
|
+
gfxTargets.source,
|
|
18
|
+
'https://raw.githubusercontent.com/ROCm/ROCm/develop/docs/reference/gpu-arch-specs.rst'
|
|
19
|
+
)
|
|
20
|
+
assert.equal(gfxTargets.generated_by, 'script/update-amd-gfx-targets.js')
|
|
21
|
+
|
|
22
|
+
assert.equal(gfxTargets.entries['radeon rx 6800 xt'], 'gfx1030')
|
|
23
|
+
assert.equal(gfxTargets.entries['radeon rx 7900 xtx'], 'gfx1100')
|
|
24
|
+
assert.equal(gfxTargets.entries.mi210, 'gfx90a')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('AMD gfx target refresh is an explicit maintainer command', () => {
|
|
28
|
+
assert.equal(packageJson.scripts['update:amd-gfx-targets'], 'node script/update-amd-gfx-targets.js')
|
|
29
|
+
assert.equal(Object.prototype.hasOwnProperty.call(packageJson.scripts, 'update:amd-rocm-targets'), false)
|
|
30
|
+
|
|
31
|
+
for (const script of ['preinstall', 'install', 'postinstall', 'prestart', 'start', 'poststart']) {
|
|
32
|
+
assert.doesNotMatch(packageJson.scripts[script] || '', /update-amd-gfx-targets/)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('AMD gfx resolver maps common product names to exact gfx targets', () => {
|
|
37
|
+
const cases = [
|
|
38
|
+
['AMD Radeon RX 6800 XT', 'gfx1030'],
|
|
39
|
+
['AMD Radeon RX 7600', 'gfx1102'],
|
|
40
|
+
['Advanced Micro Devices Radeon RX 7900 XTX', 'gfx1100'],
|
|
41
|
+
['AMD Radeon RX 9070 XT', 'gfx1201'],
|
|
42
|
+
['AMD Radeon AI PRO R9700', 'gfx1201'],
|
|
43
|
+
['AMD Instinct MI210', 'gfx90a'],
|
|
44
|
+
['AMD Radeon 780M', 'gfx1103'],
|
|
45
|
+
['AMD Radeon 890M', 'gfx1150'],
|
|
46
|
+
['gfx1030', 'gfx1030']
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
for (const [model, target] of cases) {
|
|
50
|
+
assert.equal(amd.resolve_rocm_gfx_target(model), target, model)
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('AMD gfx resolver rejects unknown products and preserves raw gfx targets', () => {
|
|
55
|
+
const unknownModels = [
|
|
56
|
+
'AMD Radeon RX 480',
|
|
57
|
+
'AMD Radeon RX 5700 XT',
|
|
58
|
+
'AMD Radeon RX 9999',
|
|
59
|
+
'AMD Radeon RX 9999M XT'
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
for (const model of unknownModels) {
|
|
63
|
+
assert.equal(amd.resolve_rocm_gfx_target(model), null, model)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const target of ['gfx803', 'gfx1013', 'gfx1104', 'gfx1209', 'GFX90A']) {
|
|
67
|
+
assert.equal(amd.resolve_rocm_gfx_target(target), target.toLowerCase(), target)
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('AMD gpu_target resolution uses CPU brand only for generic Radeon Graphics models', async () => {
|
|
72
|
+
let cpuBrandCalls = 0
|
|
73
|
+
const cpuBrand = async () => {
|
|
74
|
+
cpuBrandCalls += 1
|
|
75
|
+
return 'AMD Ryzen AI 9 HX 375'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
assert.equal(await amd.resolve_gpu_target('AMD Radeon RX 6800 XT', cpuBrand), 'gfx1030')
|
|
79
|
+
assert.equal(cpuBrandCalls, 0)
|
|
80
|
+
|
|
81
|
+
assert.equal(await amd.resolve_gpu_target('AMD Radeon RX 480', cpuBrand), null)
|
|
82
|
+
assert.equal(cpuBrandCalls, 0)
|
|
83
|
+
|
|
84
|
+
assert.equal(await amd.resolve_gpu_target('AMD Radeon Graphics', cpuBrand), 'gfx1150')
|
|
85
|
+
assert.equal(cpuBrandCalls, 1)
|
|
86
|
+
|
|
87
|
+
assert.equal(await amd.resolve_gpu_target('AMD Radeon Graphics', 'Unknown AMD CPU'), null)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('Sysinfo exposes AMD gpu_target for resolved discrete GPUs', async (t) => {
|
|
91
|
+
t.mock.method(system, 'graphics', async () => ({
|
|
92
|
+
controllers: [
|
|
93
|
+
{
|
|
94
|
+
vendor: 'AMD',
|
|
95
|
+
model: 'AMD Radeon RX 6800 XT',
|
|
96
|
+
vram: 16384,
|
|
97
|
+
driverVersion: '31.0.1'
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
displays: []
|
|
101
|
+
}))
|
|
102
|
+
|
|
103
|
+
const sys = new Sysinfo()
|
|
104
|
+
sys.info = {}
|
|
105
|
+
|
|
106
|
+
await sys.gpus()
|
|
107
|
+
|
|
108
|
+
assert.equal(sys.info.gpu, 'amd')
|
|
109
|
+
assert.equal(sys.info.gpu_target, 'gfx1030')
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test('Sysinfo leaves unresolved AMD gpu_target null', async (t) => {
|
|
113
|
+
t.mock.method(system, 'graphics', async () => ({
|
|
114
|
+
controllers: [
|
|
115
|
+
{
|
|
116
|
+
vendor: 'AMD',
|
|
117
|
+
model: 'AMD Radeon RX 480',
|
|
118
|
+
vram: 8192,
|
|
119
|
+
driverVersion: '31.0.1'
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
displays: []
|
|
123
|
+
}))
|
|
124
|
+
|
|
125
|
+
const sys = new Sysinfo()
|
|
126
|
+
sys.info = {}
|
|
127
|
+
|
|
128
|
+
await sys.gpus()
|
|
129
|
+
|
|
130
|
+
assert.equal(sys.info.gpu, 'amd')
|
|
131
|
+
assert.equal(sys.info.gpu_target, null)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('Sysinfo exposes AMD gpu_target through generic APU CPU fallback', async (t) => {
|
|
135
|
+
let cpuBrandCalls = 0
|
|
136
|
+
t.mock.method(system, 'graphics', async () => ({
|
|
137
|
+
controllers: [
|
|
138
|
+
{
|
|
139
|
+
vendor: 'AMD',
|
|
140
|
+
model: 'AMD Radeon Graphics',
|
|
141
|
+
vram: 16384,
|
|
142
|
+
driverVersion: '31.0.1'
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
displays: []
|
|
146
|
+
}))
|
|
147
|
+
t.mock.method(system, 'cpu', async () => {
|
|
148
|
+
cpuBrandCalls += 1
|
|
149
|
+
return { brand: 'AMD Ryzen AI 9 HX 375' }
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const sys = new Sysinfo()
|
|
153
|
+
sys.info = {}
|
|
154
|
+
|
|
155
|
+
await sys.gpus()
|
|
156
|
+
|
|
157
|
+
assert.equal(sys.info.gpu, 'amd')
|
|
158
|
+
assert.equal(sys.info.gpu_target, 'gfx1150')
|
|
159
|
+
assert.equal(cpuBrandCalls, 1)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test('AMD gfx runtime resolver stays offline and data-only', () => {
|
|
163
|
+
const source = fs.readFileSync(path.join(root, 'kernel', 'gpu', 'amd.js'), 'utf8')
|
|
164
|
+
|
|
165
|
+
assert.doesNotMatch(source, /require\(["'](?:node:)?https?["']\)/)
|
|
166
|
+
assert.doesNotMatch(source, /require\(["'](?:node:)?child_process["']\)/)
|
|
167
|
+
assert.doesNotMatch(source, /\b(fetch|axios|rocminfo|hipInfo|ze_info)\b/)
|
|
168
|
+
assert.doesNotMatch(source, /\b(kfd|topology)\b/i)
|
|
169
|
+
})
|