Przeglądaj źródła

Merge pull request #79 from nanobrowser/enhance

Enhance
Ashu 4 miesięcy temu
rodzic
commit
2ae7e6b075

+ 1 - 1
.github/ISSUE_TEMPLATE/bug_report.yml

@@ -1,7 +1,7 @@
 name: Bug Report
 description: Create a report to help us improve NanoBrowser
 title: "[Bug]: "
-labels: ["bug"]
+labels: ["bug", "needs-triage"]
 body:
   - type: markdown
     attributes:

+ 7 - 0
.github/ISSUE_TEMPLATE/config.yml

@@ -0,0 +1,7 @@
+## To encourage contributors to use issue templates, we don't allow blank issues
+blank_issues_enabled: false
+
+contact_links:
+  - name: GitHub Discussions
+    url: https://github.com/nanobrowser/nanobrowser/discussions
+    about: Please ask and answer questions here to keep the issue tracker clean.

+ 19 - 0
.github/ISSUE_TEMPLATE/docs.yml

@@ -0,0 +1,19 @@
+name: Documentation
+description: Suggest a change to our documentation
+labels: ["docs", "needs-triage"]
+body:
+  - type: markdown
+    attributes:
+      value: |
+        If you are unsure if the docs are relevant or needed, please open up a discussion first.
+  - type: textarea
+    attributes:
+      label: Describe the change
+      description: |
+        Please describe the documentation you want to change or add, and if it is for end-users or contributors.
+    validations:
+      required: true
+  - type: textarea
+    attributes:
+      label: Additional context
+      description: Add any other context to the feature (like screenshots, resources)

+ 1 - 1
.github/ISSUE_TEMPLATE/feature_request.yml

@@ -1,7 +1,7 @@
 name: Feature
 description: Suggest an idea for Nanobrowser
 title: "[Feature]: "
-labels: ["enhancement"]
+labels: ["enhancement", "needs-triage"]
 body:
   - type: markdown
     attributes:

+ 1 - 0
README.md

@@ -217,6 +217,7 @@ Nanobrowser builds on top of other awesome open-source projects:
 - [Browser Use](https://github.com/browser-use/browser-use)
 - [Puppeteer](https://github.com/EmergenceAI/Agent-E)
 - [Chrome Extension Boilerplate](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite)
+- [LangChain](https://github.com/langchain-ai/langchainjs)
 
 Huge thanks to their creators and contributors!
 

+ 11 - 2
pages/options/src/Options.tsx

@@ -5,11 +5,12 @@ import { withErrorBoundary, withSuspense } from '@extension/shared';
 import { GeneralSettings } from './components/GeneralSettings';
 import { ModelSettings } from './components/ModelSettings';
 
-type TabTypes = 'general' | 'models';
+type TabTypes = 'general' | 'models' | 'help';
 
 const TABS: { id: TabTypes; icon: string; label: string }[] = [
   { id: 'general', icon: '⚙️', label: 'General' },
   { id: 'models', icon: '📊', label: 'Models' },
+  { id: 'help', icon: '📚', label: 'Help' },
 ];
 
 const Options = () => {
@@ -29,6 +30,14 @@ const Options = () => {
     return () => darkModeMediaQuery.removeEventListener('change', handleChange);
   }, []);
 
+  const handleTabClick = (tabId: TabTypes) => {
+    if (tabId === 'help') {
+      window.location.href = 'https://nanobrowser.ai/docs';
+    } else {
+      setActiveTab(tabId);
+    }
+  };
+
   const renderTabContent = () => {
     switch (activeTab) {
       case 'general':
@@ -52,7 +61,7 @@ const Options = () => {
             {TABS.map(item => (
               <li key={item.id}>
                 <Button
-                  onClick={() => setActiveTab(item.id)}
+                  onClick={() => handleTabClick(item.id)}
                   className={`flex w-full items-center space-x-2 rounded-lg px-4 py-2 text-left text-base 
                     ${
                       activeTab !== item.id

+ 61 - 13
pages/options/src/components/ModelSettings.tsx

@@ -46,6 +46,8 @@ export const ModelSettings = ({ isDarkMode = false }: ModelSettingsProps) => {
   const [isProviderSelectorOpen, setIsProviderSelectorOpen] = useState(false);
   const newlyAddedProviderRef = useRef<string | null>(null);
   const [nameErrors, setNameErrors] = useState<Record<string, string>>({});
+  // Add state for tracking API key visibility
+  const [visibleApiKeys, setVisibleApiKeys] = useState<Record<string, boolean>>({});
   // Create a non-async wrapper for use in render functions
   const [availableModels, setAvailableModels] = useState<
     Array<{ provider: string; providerName: string; model: string }>
@@ -199,6 +201,14 @@ export const ModelSettings = ({ isDarkMode = false }: ModelSettingsProps) => {
     }));
   };
 
+  // Add a toggle handler for API key visibility
+  const toggleApiKeyVisibility = (provider: string) => {
+    setVisibleApiKeys(prev => ({
+      ...prev,
+      [provider]: !prev[provider],
+    }));
+  };
+
   const handleNameChange = (provider: string, name: string) => {
     setModifiedProviders(prev => new Set(prev).add(provider));
     setProviders(prev => {
@@ -899,23 +909,61 @@ export const ModelSettings = ({ isDarkMode = false }: ModelSettingsProps) => {
                       className={`w-20 text-sm font-medium ${isDarkMode ? 'text-gray-300' : 'text-gray-700'}`}>
                       Key{providerConfig.type !== ProviderTypeEnum.CustomOpenAI ? '*' : ''}
                     </label>
-                    <input
-                      id={`${providerId}-api-key`}
-                      type="password"
-                      placeholder={
-                        providerConfig.type === ProviderTypeEnum.CustomOpenAI
-                          ? `${providerConfig.name || providerId} API key (optional)`
-                          : `${providerConfig.name || providerId} API key (required)`
-                      }
-                      value={providerConfig.apiKey || ''}
-                      onChange={e => handleApiKeyChange(providerId, e.target.value, providerConfig.baseUrl)}
-                      className={`flex-1 rounded-md border text-sm ${isDarkMode ? 'border-slate-600 bg-slate-700 text-gray-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-800' : 'border-gray-300 bg-white text-gray-700 focus:border-blue-400 focus:ring-2 focus:ring-blue-200'} p-2 outline-none`}
-                    />
+                    <div className="relative flex-1">
+                      <input
+                        id={`${providerId}-api-key`}
+                        type="password"
+                        placeholder={
+                          providerConfig.type === ProviderTypeEnum.CustomOpenAI
+                            ? `${providerConfig.name || providerId} API key (optional)`
+                            : `${providerConfig.name || providerId} API key (required)`
+                        }
+                        value={providerConfig.apiKey || ''}
+                        onChange={e => handleApiKeyChange(providerId, e.target.value, providerConfig.baseUrl)}
+                        className={`w-full rounded-md border text-sm ${isDarkMode ? 'border-slate-600 bg-slate-700 text-gray-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-800' : 'border-gray-300 bg-white text-gray-700 focus:border-blue-400 focus:ring-2 focus:ring-blue-200'} p-2 outline-none`}
+                      />
+                      {/* Show eye button only for newly added providers */}
+                      {modifiedProviders.has(providerId) && !providersFromStorage.has(providerId) && (
+                        <button
+                          type="button"
+                          className={`absolute right-2 top-1/2 -translate-y-1/2 ${
+                            isDarkMode ? 'text-gray-400 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'
+                          }`}
+                          onClick={() => toggleApiKeyVisibility(providerId)}
+                          aria-label={visibleApiKeys[providerId] ? 'Hide API key' : 'Show API key'}>
+                          <svg
+                            xmlns="http://www.w3.org/2000/svg"
+                            viewBox="0 0 24 24"
+                            fill="none"
+                            stroke="currentColor"
+                            strokeWidth="2"
+                            strokeLinecap="round"
+                            strokeLinejoin="round"
+                            className="h-5 w-5"
+                            aria-hidden="true">
+                            <title>{visibleApiKeys[providerId] ? 'Hide API key' : 'Show API key'}</title>
+                            {visibleApiKeys[providerId] ? (
+                              <>
+                                <path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" />
+                                <circle cx="12" cy="12" r="3" />
+                                <line x1="2" y1="22" x2="22" y2="2" />
+                              </>
+                            ) : (
+                              <>
+                                <path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" />
+                                <circle cx="12" cy="12" r="3" />
+                              </>
+                            )}
+                          </svg>
+                        </button>
+                      )}
+                    </div>
                   </div>
 
-                  {/* Display API key for newly added providers */}
+                  {/* Display API key for newly added providers only when visible */}
                   {modifiedProviders.has(providerId) &&
                     !providersFromStorage.has(providerId) &&
+                    visibleApiKeys[providerId] &&
                     providerConfig.apiKey && (
                       <div className="ml-20 mt-1">
                         <p