Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
ollama
Commits
e88dd25b
Unverified
Commit
e88dd25b
authored
Jul 14, 2023
by
hoyyeva
Committed by
GitHub
Jul 14, 2023
Browse files
ollama app welcome screen for first time run (#80)
parent
567e74e7
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
2576 additions
and
343 deletions
+2576
-343
app/forge.config.ts
app/forge.config.ts
+1
-1
app/package-lock.json
app/package-lock.json
+2391
-147
app/package.json
app/package.json
+5
-0
app/src/app.tsx
app/src/app.tsx
+113
-146
app/src/declarations.d.ts
app/src/declarations.d.ts
+4
-0
app/src/index.ts
app/src/index.ts
+49
-49
app/src/ollama.svg
app/src/ollama.svg
+9
-0
app/webpack.rules.ts
app/webpack.rules.ts
+4
-0
No files found.
app/forge.config.ts
View file @
e88dd25b
...
@@ -58,7 +58,7 @@ const config: ForgeConfig = {
...
@@ -58,7 +58,7 @@ const config: ForgeConfig = {
new
AutoUnpackNativesPlugin
({}),
new
AutoUnpackNativesPlugin
({}),
new
WebpackPlugin
({
new
WebpackPlugin
({
mainConfig
,
mainConfig
,
devContentSecurityPolicy
:
`default-src * 'unsafe-eval' 'unsafe-inline'`
,
devContentSecurityPolicy
:
`default-src * 'unsafe-eval' 'unsafe-inline'
; img-src data: 'self'
`
,
renderer
:
{
renderer
:
{
config
:
rendererConfig
,
config
:
rendererConfig
,
nodeIntegration
:
true
,
nodeIntegration
:
true
,
...
...
app/package-lock.json
View file @
e88dd25b
This diff is collapsed.
Click to expand it.
app/package.json
View file @
e88dd25b
...
@@ -30,6 +30,7 @@
...
@@ -30,6 +30,7 @@
"@electron-forge/plugin-auto-unpack-natives"
:
"^6.2.1"
,
"@electron-forge/plugin-auto-unpack-natives"
:
"^6.2.1"
,
"@electron-forge/plugin-webpack"
:
"^6.2.1"
,
"@electron-forge/plugin-webpack"
:
"^6.2.1"
,
"@electron-forge/publisher-github"
:
"^6.2.1"
,
"@electron-forge/publisher-github"
:
"^6.2.1"
,
"@svgr/webpack"
:
"^8.0.1"
,
"@types/chmodr"
:
"^1.0.0"
,
"@types/chmodr"
:
"^1.0.0"
,
"@types/node"
:
"^20.4.0"
,
"@types/node"
:
"^20.4.0"
,
"@types/react"
:
"^18.2.14"
,
"@types/react"
:
"^18.2.14"
,
...
@@ -54,17 +55,21 @@
...
@@ -54,17 +55,21 @@
"prettier"
:
"^2.8.8"
,
"prettier"
:
"^2.8.8"
,
"prettier-plugin-tailwindcss"
:
"^0.3.0"
,
"prettier-plugin-tailwindcss"
:
"^0.3.0"
,
"style-loader"
:
"^3.3.3"
,
"style-loader"
:
"^3.3.3"
,
"svg-inline-loader"
:
"^0.8.2"
,
"tailwindcss"
:
"^3.3.2"
,
"tailwindcss"
:
"^3.3.2"
,
"ts-loader"
:
"^9.4.3"
,
"ts-loader"
:
"^9.4.3"
,
"ts-node"
:
"^10.9.1"
,
"ts-node"
:
"^10.9.1"
,
"typescript"
:
"~4.5.4"
,
"typescript"
:
"~4.5.4"
,
"url-loader"
:
"^4.1.1"
,
"webpack"
:
"^5.88.0"
,
"webpack"
:
"^5.88.0"
,
"webpack-cli"
:
"^5.1.4"
,
"webpack-cli"
:
"^5.1.4"
,
"webpack-dev-server"
:
"^4.15.1"
"webpack-dev-server"
:
"^4.15.1"
},
},
"dependencies"
:
{
"dependencies"
:
{
"@electron/remote"
:
"^2.0.10"
,
"@electron/remote"
:
"^2.0.10"
,
"@heroicons/react"
:
"^2.0.18"
,
"@segment/analytics-node"
:
"^1.0.0"
,
"@segment/analytics-node"
:
"^1.0.0"
,
"copy-to-clipboard"
:
"^3.3.3"
,
"electron-squirrel-startup"
:
"^1.0.0"
,
"electron-squirrel-startup"
:
"^1.0.0"
,
"electron-store"
:
"^8.1.0"
,
"electron-store"
:
"^8.1.0"
,
"react"
:
"^18.2.0"
,
"react"
:
"^18.2.0"
,
...
...
app/src/app.tsx
View file @
e88dd25b
import
{
useState
}
from
'
react
'
import
{
useState
}
from
"
react
"
import
path
from
'
path
'
import
copy
from
'
copy-to-clipboard
'
import
os
from
'
os
'
import
{
exec
}
from
'
child_process
'
import
{
dialog
,
getCurrentWindow
}
from
'
@electron/remote
'
import
*
as
path
from
'
path
'
import
*
as
fs
from
'
fs
'
const
API_URL
=
'
http://127.0.0.1:7734
'
import
{
DocumentDuplicateIcon
}
from
'
@heroicons/react/24/outline
'
import
{
app
}
from
'
@electron/remote
'
type
Message
=
{
import
OllamaIcon
from
'
./ollama.svg
'
sender
:
'
bot
'
|
'
human
'
content
:
string
const
ollama
=
app
.
isPackaged
}
?
path
.
join
(
process
.
resourcesPath
,
'
ollama
'
)
:
path
.
resolve
(
process
.
cwd
(),
'
..
'
,
'
ollama
'
)
const
userInfo
=
os
.
userInfo
()
function
installCLI
(
callback
:
()
=>
void
)
{
async
function
generate
(
prompt
:
string
,
model
:
string
,
callback
:
(
res
:
string
)
=>
void
)
{
const
symlinkPath
=
'
/usr/local/bin/ollama
'
const
result
=
await
fetch
(
`
${
API_URL
}
/generate`
,
{
method
:
'
POST
'
,
if
(
fs
.
existsSync
(
symlinkPath
)
&&
fs
.
readlinkSync
(
symlinkPath
)
===
ollama
)
{
headers
:
{
callback
&&
callback
()
'
Content-Type
'
:
'
application/json
'
,
},
body
:
JSON
.
stringify
({
prompt
,
model
,
}),
})
if
(
!
result
.
ok
)
{
return
return
}
}
let
reader
=
result
.
body
.
getReader
()
const
command
=
`
do shell script "ln -F -s
${
ollama
}
/usr/local/bin/ollama" with administrator privileges
while
(
true
)
{
`
const
{
done
,
value
}
=
await
reader
.
read
()
exec
(
`osascript -e '
${
command
}
'`
,
(
error
:
Error
|
null
,
stdout
:
string
,
stderr
:
string
)
=>
{
if
(
error
)
{
if
(
done
)
{
console
.
error
(
`cli: failed to install cli:
${
error
.
message
}
`
)
break
callback
&&
callback
()
return
}
}
let
decoder
=
new
TextDecoder
()
callback
&&
callback
()
let
str
=
decoder
.
decode
(
value
)
})
let
re
=
/}
\s
*{/g
str
=
'
[
'
+
str
.
replace
(
re
,
'
},{
'
)
+
'
]
'
let
messages
=
JSON
.
parse
(
str
)
for
(
const
message
of
messages
)
{
const
choice
=
message
.
choices
[
0
]
callback
(
choice
.
text
)
if
(
choice
.
finish_reason
===
'
stop
'
)
{
break
}
}
}
return
}
}
export
default
function
()
{
export
default
function
()
{
const
[
prompt
,
setPrompt
]
=
useState
(
''
)
const
[
step
,
setStep
]
=
useState
(
0
)
const
[
messages
,
setMessages
]
=
useState
<
Message
[]
>
([])
const
[
model
,
setModel
]
=
useState
(
''
)
const
command
=
'
ollama run orca
'
const
[
generating
,
setGenerating
]
=
useState
(
false
)
return
(
return
(
<
div
className
=
'flex min-h-screen flex-1 flex-col justify-between bg-white'
>
<
div
className
=
'flex flex-col justify-between mx-auto w-full pt-16 px-4 min-h-screen bg-white'
>
<
header
className
=
'drag sticky top-0 z-50 flex h-14 w-full flex-row items-center border-b border-black/10 bg-white/75 backdrop-blur-md'
>
{
step
===
0
&&
(
<
div
className
=
'mx-auto w-full max-w-xl leading-none'
>
<>
<
h1
className
=
'text-sm font-medium'
>
{
path
.
basename
(
model
).
replace
(
'
.bin
'
,
''
)
}
</
h1
>
<
div
className
=
"mx-auto text-center"
>
</
div
>
<
h1
className
=
"mt-4 mb-6 text-2xl tracking-tight text-gray-900"
>
Welcome to Ollama
</
h1
>
</
header
>
<
p
className
=
"mx-auto w-[65%] text-sm text-gray-400"
>
{
model
?
(
Let’s get you up and running with your own large language models.
<
section
className
=
'mx-auto mb-10 w-full max-w-xl flex-1 break-words'
>
</
p
>
{
messages
.
map
((
m
,
i
)
=>
(
<
button
<
div
className
=
'my-4 flex gap-4'
key
=
{
i
}
>
onClick
=
{
()
=>
{
<
div
className
=
'flex-none pr-1 text-lg'
>
setStep
(
1
)
{
m
.
sender
===
'
human
'
?
(
}
}
<
div
className
=
'mt-px flex h-6 w-6 items-center justify-center rounded-md bg-neutral-200 text-sm text-neutral-700'
>
className
=
'mx-auto w-[40%] rounded-dm my-8 rounded-md bg-black px-4 py-2 text-sm text-white hover:brightness-110'
{
userInfo
.
username
[
0
].
toUpperCase
()
}
>
</
div
>
Next
)
:
(
</
button
>
<
div
className
=
'mt-0.5 flex h-6 w-6 items-center justify-center rounded-md bg-blue-600 text-sm text-white'
>
</
div
>
{
path
.
basename
(
model
)[
0
].
toUpperCase
()
}
<
div
className
=
"mx-auto"
>
</
div
>
<
OllamaIcon
/>
)
}
</
div
>
</
div
>
</>
<
div
className
=
'flex-1 text-gray-800'
>
)
}
{
m
.
content
}
{
step
===
1
&&
(
{
m
.
sender
===
'
bot
'
&&
generating
&&
i
===
messages
.
length
-
1
&&
(
<>
<
span
className
=
'blink relative -top-[3px] left-1 text-[10px]'
>
█
</
span
>
<
div
className
=
"flex flex-col space-y-28 mx-auto text-center"
>
)
}
<
h1
className
=
"mt-4 text-2xl tracking-tight text-gray-900"
>
Install the command line
</
h1
>
<
pre
className
=
"mx-auto text-4xl text-gray-400"
>
>
ollama
</
pre
>
<
div
className
=
"mx-auto"
>
<
button
onClick
=
{
()
=>
{
// install the command line
installCLI
(()
=>
{
window
.
focus
()
setStep
(
2
)
})
}
}
className
=
'mx-auto w-[60%] rounded-dm rounded-md bg-black px-4 py-2 text-sm text-white hover:brightness-110'
>
Install
</
button
>
<
p
className
=
"mx-auto w-[70%] text-xs text-gray-400 my-4"
>
You will be prompted for administrator access
</
p
>
</
div
>
</
div
>
</>
)
}
{
step
===
2
&&
(
<>
<
div
className
=
"flex flex-col space-y-20 mx-auto text-center"
>
<
h1
className
=
"mt-4 text-2xl tracking-tight text-gray-900"
>
Run your first model
</
h1
>
<
div
className
=
"flex flex-col"
>
<
div
className
=
"group relative flex items-center"
>
<
pre
className
=
"text-start w-full language-none rounded-md bg-gray-100 px-4 py-3 text-2xs leading-normal"
>
{
command
}
</
pre
>
<
button
className
=
'absolute right-[5px] rounded-md border bg-white/90 px-2 py-2 text-gray-400 opacity-0 backdrop-blur-xl hover:text-gray-600 group-hover:opacity-100'
onClick
=
{
()
=>
{
copy
(
command
)
}
}
>
<
DocumentDuplicateIcon
className
=
"h-4 w-4 text-gray-500"
/>
</
button
>
</
div
>
</
div
>
<
p
className
=
"mx-auto w-[70%] text-xs text-gray-400 my-4"
>
Run this command in your favorite terminal.
</
p
>
</
div
>
</
div
>
))
}
<
button
</
section
>
onClick
=
{
()
=>
{
)
:
(
window
.
close
()
<
section
className
=
'flex flex-1 select-none flex-col items-center justify-center pb-20'
>
}
}
<
h2
className
=
'text-3xl font-light text-neutral-400'
>
No model selected
</
h2
>
className
=
'mx-auto w-[60%] rounded-dm rounded-md bg-black px-4 py-2 text-sm text-white hover:brightness-110'
<
button
>
onClick
=
{
async
()
=>
{
Finish
const
res
=
await
dialog
.
showOpenDialog
(
getCurrentWindow
(),
{
</
button
>
properties
:
[
'
openFile
'
,
'
multiSelections
'
],
</
div
>
})
</>
if
(
res
.
canceled
)
{
return
}
setModel
(
res
.
filePaths
[
0
])
}
}
className
=
'rounded-dm my-8 rounded-md bg-blue-600 px-4 py-2 text-sm text-white hover:brightness-110'
>
Open file...
</
button
>
</
section
>
)
}
)
}
<
div
className
=
'sticky bottom-0 bg-gradient-to-b from-transparent to-white'
>
{
model
&&
(
<
textarea
autoFocus
rows
=
{
1
}
value
=
{
prompt
}
placeholder
=
'Send a message...'
onChange
=
{
e
=>
setPrompt
(
e
.
target
.
value
)
}
className
=
'mx-auto my-4 block w-full max-w-xl resize-none rounded-xl border border-gray-200 px-5 py-3.5 text-[15px] shadow-lg shadow-black/5 focus:outline-none'
onKeyDownCapture
=
{
async
e
=>
{
if
(
e
.
key
===
'
Enter
'
&&
!
e
.
shiftKey
)
{
e
.
preventDefault
()
if
(
generating
)
{
return
}
if
(
!
prompt
)
{
return
}
await
setMessages
(
messages
=>
{
return
[...
messages
,
{
sender
:
'
human
'
,
content
:
prompt
},
{
sender
:
'
bot
'
,
content
:
''
}]
})
setPrompt
(
''
)
setGenerating
(
true
)
await
generate
(
prompt
,
model
,
res
=>
{
setMessages
(
messages
=>
{
let
last
=
messages
[
messages
.
length
-
1
]
return
[...
messages
.
slice
(
0
,
messages
.
length
-
1
),
{
...
last
,
content
:
last
.
content
+
res
}]
})
})
setGenerating
(
false
)
}
}
}
></
textarea
>
)
}
</
div
>
</
div
>
</
div
>
)
)
}
}
\ No newline at end of file
app/src/declarations.d.ts
0 → 100644
View file @
e88dd25b
declare
module
'
*.svg
'
{
const
content
:
string
;
export
default
content
;
}
\ No newline at end of file
app/src/index.ts
View file @
e88dd25b
import
{
spawn
,
exec
}
from
'
child_process
'
import
{
spawn
}
from
'
child_process
'
import
{
app
,
autoUpdater
,
dialog
,
Tray
,
Menu
}
from
'
electron
'
import
{
app
,
autoUpdater
,
dialog
,
Tray
,
Menu
,
BrowserWindow
}
from
'
electron
'
import
Store
from
'
electron-store
'
import
Store
from
'
electron-store
'
import
winston
from
'
winston
'
import
winston
from
'
winston
'
import
'
winston-daily-rotate-file
'
import
'
winston-daily-rotate-file
'
import
*
as
path
from
'
path
'
import
*
as
path
from
'
path
'
import
*
as
fs
from
'
fs
'
import
{
analytics
,
id
}
from
'
./telemetry
'
import
{
analytics
,
id
}
from
'
./telemetry
'
require
(
'
@electron/remote/main
'
).
initialize
()
require
(
'
@electron/remote/main
'
).
initialize
()
const
store
=
new
Store
()
const
store
=
new
Store
()
let
tray
:
Tray
|
null
=
null
let
tray
:
Tray
|
null
=
null
let
welcomeWindow
:
BrowserWindow
|
null
=
null
declare
const
MAIN_WINDOW_WEBPACK_ENTRY
:
string
const
logger
=
winston
.
createLogger
({
const
logger
=
winston
.
createLogger
({
transports
:
[
transports
:
[
...
@@ -30,7 +33,37 @@ if (!SingleInstanceLock) {
...
@@ -30,7 +33,37 @@ if (!SingleInstanceLock) {
app
.
quit
()
app
.
quit
()
}
}
const
createSystemtray
=
()
=>
{
function
firstRunWindow
()
{
// Create the browser window.
welcomeWindow
=
new
BrowserWindow
({
width
:
400
,
height
:
500
,
frame
:
false
,
fullscreenable
:
false
,
resizable
:
false
,
movable
:
false
,
transparent
:
true
,
webPreferences
:
{
nodeIntegration
:
true
,
contextIsolation
:
false
,
},
})
require
(
'
@electron/remote/main
'
).
enable
(
welcomeWindow
.
webContents
)
// and load the index.html of the app.
welcomeWindow
.
loadURL
(
MAIN_WINDOW_WEBPACK_ENTRY
)
// for debugging
// welcomeWindow.webContents.openDevTools()
if
(
process
.
platform
===
'
darwin
'
)
{
app
.
dock
.
hide
()
}
}
function
createSystemtray
()
{
let
iconPath
=
path
.
join
(
__dirname
,
'
..
'
,
'
..
'
,
'
assets
'
,
'
ollama_icon_16x16Template.png
'
)
let
iconPath
=
path
.
join
(
__dirname
,
'
..
'
,
'
..
'
,
'
assets
'
,
'
ollama_icon_16x16Template.png
'
)
if
(
app
.
isPackaged
)
{
if
(
app
.
isPackaged
)
{
...
@@ -49,8 +82,6 @@ if (require('electron-squirrel-startup')) {
...
@@ -49,8 +82,6 @@ if (require('electron-squirrel-startup')) {
app
.
quit
()
app
.
quit
()
}
}
const
ollama
=
path
.
join
(
process
.
resourcesPath
,
'
ollama
'
)
function
server
()
{
function
server
()
{
const
binary
=
app
.
isPackaged
const
binary
=
app
.
isPackaged
?
path
.
join
(
process
.
resourcesPath
,
'
ollama
'
)
?
path
.
join
(
process
.
resourcesPath
,
'
ollama
'
)
...
@@ -81,51 +112,12 @@ function server() {
...
@@ -81,51 +112,12 @@ function server() {
})
})
}
}
function
installCLI
()
{
if
(
process
.
platform
===
'
darwin
'
)
{
const
symlinkPath
=
'
/usr/local/bin/ollama
'
app
.
dock
.
hide
()
if
(
fs
.
existsSync
(
symlinkPath
)
&&
fs
.
readlinkSync
(
symlinkPath
)
===
ollama
)
{
return
}
dialog
.
showMessageBox
({
type
:
'
info
'
,
title
:
'
Ollama CLI installation
'
,
message
:
'
To make the Ollama command work in your terminal, it needs administrator privileges.
'
,
buttons
:
[
'
OK
'
],
})
.
then
(
result
=>
{
if
(
result
.
response
===
0
)
{
const
command
=
`
do shell script "ln -F -s
${
ollama
}
/usr/local/bin/ollama" with administrator privileges
`
exec
(
`osascript -e '
${
command
}
'`
,
(
error
:
Error
|
null
,
stdout
:
string
,
stderr
:
string
)
=>
{
if
(
error
)
{
logger
.
error
(
`cli: failed to install cli:
${
error
.
message
}
`
)
return
}
logger
.
info
(
stdout
)
logger
.
error
(
stderr
)
})
}
})
}
}
app
.
on
(
'
ready
'
,
()
=>
{
app
.
on
(
'
ready
'
,
()
=>
{
if
(
process
.
platform
===
'
darwin
'
)
{
if
(
process
.
platform
===
'
darwin
'
)
{
app
.
dock
.
hide
()
if
(
!
store
.
has
(
'
first-time-run
'
))
{
// This is the first run
app
.
setLoginItemSettings
({
openAtLogin
:
true
})
store
.
set
(
'
first-time-run
'
,
false
)
}
else
{
// The app has been run before
app
.
setLoginItemSettings
({
openAtLogin
:
app
.
getLoginItemSettings
().
openAtLogin
})
}
if
(
app
.
isPackaged
)
{
if
(
app
.
isPackaged
)
{
if
(
!
app
.
isInApplicationsFolder
())
{
if
(
!
app
.
isInApplicationsFolder
())
{
const
chosen
=
dialog
.
showMessageBoxSync
({
const
chosen
=
dialog
.
showMessageBoxSync
({
...
@@ -157,13 +149,21 @@ app.on('ready', () => {
...
@@ -157,13 +149,21 @@ app.on('ready', () => {
}
}
}
}
}
}
installCLI
()
}
}
}
}
createSystemtray
()
createSystemtray
()
server
()
server
()
if
(
!
store
.
has
(
'
first-time-run
'
))
{
// This is the first run
app
.
setLoginItemSettings
({
openAtLogin
:
true
})
firstRunWindow
()
store
.
set
(
'
first-time-run
'
,
false
)
}
else
{
// The app has been run before
app
.
setLoginItemSettings
({
openAtLogin
:
app
.
getLoginItemSettings
().
openAtLogin
})
}
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// Quit when all windows are closed, except on macOS. There, it's common
...
...
app/src/ollama.svg
0 → 100644
View file @
e88dd25b
<svg
width=
"133"
height=
"185"
viewBox=
"0 0 133 185"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<rect
width=
"133"
height=
"185"
fill=
"url(#pattern0)"
/>
<defs>
<pattern
id=
"pattern0"
patternContentUnits=
"objectBoundingBox"
width=
"1"
height=
"1"
>
<use
xlink:href=
"#image0_406_1657"
transform=
"matrix(0.00552486 0 0 0.00397193 0 -0.00840675)"
/>
</pattern>
<image
id=
"image0_406_1657"
width=
"181"
height=
"256"
xlink:href=
"data:image/png;base64,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"
/>
</defs>
</svg>
app/webpack.rules.ts
View file @
e88dd25b
...
@@ -28,4 +28,8 @@ export const rules: Required<ModuleOptions>['rules'] = [
...
@@ -28,4 +28,8 @@ export const rules: Required<ModuleOptions>['rules'] = [
},
},
},
},
},
},
{
test
:
/
\.
svg$/
,
use
:
[
'
@svgr/webpack
'
],
},
]
]
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment