Repository: ollama/ollama
Pull request: welcome screen improvements

- make window draggable
- improve copy command experience on the finish page

Changed files:
filename: app/src/app.css
patch: @@ -11,6 +11,10 @@ body {
   -webkit-app-region: drag;
 }
 
+.no-drag {
+  -webkit-app-region: no-drag;
+}
+
 .blink {
   -webkit-animation: 1s blink step-end infinite;
   -moz-animation: 1s blink step-end infinite;

filename: app/src/app.tsx
patch: @@ -1,6 +1,6 @@
 import { useState } from 'react'
 import copy from 'copy-to-clipboard'
-import { DocumentDuplicateIcon } from '@heroicons/react/24/outline'
+import { CheckIcon, DocumentDuplicateIcon } from '@heroicons/react/24/outline'
 import Store from 'electron-store'
 import { getCurrentWindow } from '@electron/remote'
 
@@ -17,86 +17,95 @@ enum Step {
 
 export default function () {
   const [step, setStep] = useState<Step>(Step.WELCOME)
+  const [commandCopied, setCommandCopied] = useState<boolean>(false)
 
   const command = 'ollama run orca'
 
   return (
-    <div className='mx-auto flex min-h-screen w-full flex-col justify-between bg-white px-4 pt-16'>
-      {step === Step.WELCOME && (
-        <>
-          <div className='mx-auto text-center'>
-            <h1 className='mb-6 mt-4 text-2xl tracking-tight text-gray-900'>Welcome to Ollama</h1>
-            <p className='mx-auto w-[65%] text-sm text-gray-400'>
-              Let's get you up and running with your own large language models.
-            </p>
-            <button
-              onClick={() => setStep(Step.CLI)}
-              className='rounded-dm mx-auto my-8 w-[40%] rounded-md bg-black px-4 py-2 text-sm text-white hover:brightness-110'
-            >
-              Next
-            </button>
-          </div>
-          <div className='mx-auto'>
-            <OllamaIcon />
-          </div>
-        </>
-      )}
-      {step === Step.CLI && (
-        <>
-          <div className='mx-auto flex flex-col space-y-28 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'>&gt; ollama</pre>
-            <div className='mx-auto'>
+    <div className='drag'>
+      <div className='mx-auto flex min-h-screen w-full flex-col justify-between bg-white px-4 pt-16'>
+        {step === Step.WELCOME && (
+          <>
+            <div className='mx-auto text-center'>
+              <h1 className='mb-6 mt-4 text-2xl tracking-tight text-gray-900'>Welcome to Ollama</h1>
+              <p className='mx-auto w-[65%] text-sm text-gray-400'>
+                Let's get you up and running with your own large language models.
+              </p>
               <button
-                onClick={async () => {
-                  await install()
-                  getCurrentWindow().show()
-                  getCurrentWindow().focus()
-                  setStep(Step.FINISH)
-                }}
-                className='rounded-dm mx-auto w-[60%] rounded-md bg-black px-4 py-2 text-sm text-white hover:brightness-110'
+                onClick={() => setStep(Step.CLI)}
+                className='no-drag rounded-dm mx-auto my-8 w-[40%] rounded-md bg-black px-4 py-2 text-sm text-white hover:brightness-110'
               >
-                Install
+                Next
               </button>
-              <p className='mx-auto my-4 w-[70%] text-xs text-gray-400'>
-                You will be prompted for administrator access
-              </p>
             </div>
-          </div>
-        </>
-      )}
-      {step === Step.FINISH && (
-        <>
-          <div className='mx-auto flex flex-col space-y-20 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='language-none text-2xs w-full rounded-md bg-gray-100 px-4 py-3 text-start leading-normal'>
-                  {command}
-                </pre>
+            <div className='mx-auto'>
+              <OllamaIcon />
+            </div>
+          </>
+        )}
+        {step === Step.CLI && (
+          <>
+            <div className='mx-auto flex flex-col space-y-28 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'>&gt; ollama</pre>
+              <div className='mx-auto'>
                 <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)
+                  onClick={async () => {
+                    await install()
+                    getCurrentWindow().show()
+                    getCurrentWindow().focus()
+                    setStep(Step.FINISH)
                   }}
+                  className='no-drag rounded-dm mx-auto w-[60%] rounded-md bg-black px-4 py-2 text-sm text-white hover:brightness-110'
                 >
-                  <DocumentDuplicateIcon className='h-4 w-4 text-gray-500' />
+                  Install
                 </button>
+                <p className='mx-auto my-4 w-[70%] text-xs text-gray-400'>
+                  You will be prompted for administrator access
+                </p>
               </div>
-              <p className='mx-auto my-4 w-[70%] text-xs text-gray-400'>Run this command in your favorite terminal.</p>
             </div>
-            <button
-              onClick={() => {
-                store.set('first-time-run', true)
-                window.close()
-              }}
-              className='rounded-dm mx-auto w-[60%] rounded-md bg-black px-4 py-2 text-sm text-white hover:brightness-110'
-            >
-              Finish
-            </button>
-          </div>
-        </>
-      )}
+          </>
+        )}
+        {step === Step.FINISH && (
+          <>
+            <div className='mx-auto flex flex-col space-y-20 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='language-none text-2xs w-full rounded-md bg-gray-100 px-4 py-3 text-start leading-normal'>
+                    {command}
+                  </pre>
+                  <button
+                    className={`no-drag absolute right-[5px] px-2 py-2 ${commandCopied ? 'text-gray-900 opacity-100 hover:cursor-auto' : 'text-gray-200 opacity-50 hover:cursor-pointer'} hover:text-gray-900 hover:font-bold group-hover:opacity-100`}
+                    onClick={() => {
+                      copy(command)
+                      setCommandCopied(true)
+                      setTimeout(() => setCommandCopied(false), 3000)
+                    }}
+                  >
+                    {commandCopied ? (
+                      <CheckIcon className='h-4 w-4 text-gray-500 font-bold' />
+                    ) : (
+                      <DocumentDuplicateIcon className='h-4 w-4 text-gray-500' />
+                    )}
+                  </button>
+                </div>
+                <p className='mx-auto my-4 w-[70%] text-xs text-gray-400'>Run this command in your favorite terminal.</p>
+              </div>
+              <button
+                onClick={() => {
+                  store.set('first-time-run', true)
+                  window.close()
+                }}
+                className='no-drag rounded-dm mx-auto w-[60%] rounded-md bg-black px-4 py-2 text-sm text-white hover:brightness-110'
+              >
+                Finish
+              </button>
+            </div>
+          </>
+        )}
+      </div>
     </div>
   )
 }