Skip to content

Commit eb73bbd

Browse files
committed
Allow to configure multiple Part-DB instances one can chose between.
1 parent 00be648 commit eb73bbd

13 files changed

Lines changed: 466 additions & 160 deletions

File tree

PRIVACY.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ The extension stores the following data in your browser using the standard WebEx
1515

1616
| Storage | Key | Contents |
1717
|---|---|---|
18-
| `storage.sync` | `partdbUrl` | The URL of your Part-DB server |
19-
| `storage.sync` | `partdbLocale` | Your preferred Part-DB interface language |
20-
| `storage.local` | `infoCache` | A short-lived cache (5 minutes) of the provider list fetched from your Part-DB server |
21-
| `storage.local` | `lastProvider` | The last info-provider you selected in the popup |
18+
| `storage.sync` | `hosts` | The list of Part-DB instances you configured, each with a name, URL, and interface language |
19+
| `storage.local` | `lastHostId` | The instance you last selected in the popup, so it is remembered next time |
20+
| `storage.local` | `infoCacheByHost` | A short-lived cache (5 minutes), per instance, of the provider list fetched from your Part-DB server |
21+
| `storage.local` | `lastProviderByHost` | The last info-provider you selected in the popup, per instance |
2222

2323
`storage.sync` data is synced across your browser profile by your browser vendor (e.g., Google or Mozilla) if you have browser sync enabled. This is subject to your browser vendor's own privacy policy. The extension itself has no control over or visibility into this sync.
2424

@@ -49,12 +49,12 @@ The extension does not:
4949

5050
| Permission | Type | Why it is needed |
5151
|---|---|---|
52-
| `storage` | Required | Save your server URL and locale preference |
52+
| `storage` | Required | Save your configured server URL(s) and locale preference(s) |
5353
| `activeTab` | Required | Read the page HTML on demand when you click Submit |
5454
| `scripting` | Required | Execute the page-reading code in the active tab when you click Submit |
55-
| Host access to your Part-DB URL | Optional — granted when you save your server URL | Allows the extension to make requests to your Part-DB server; scoped to exactly the URL you configure |
55+
| Host access to your Part-DB URL(s) | Optional — granted when you save a server URL | Allows the extension to make requests to your Part-DB server; scoped to exactly the URL(s) you configure |
5656

57-
The extension requests no host permissions at install time. When you save your Part-DB server URL in the settings, the browser will ask you to grant access to that specific address only.
57+
The extension requests no host permissions at install time. When you save a Part-DB server URL in the settings, the browser will ask you to grant access to that specific address only.
5858

5959
## Contact
6060

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,26 @@ Then load the `src/` directory as an unpacked extension (see above).
5555

5656
## Configuration
5757

58-
After installing, click the extension icon and then **Open Settings** (or right-click the icon → *Options*):
58+
After installing, click the extension icon and then **Open Settings** (or right-click the icon → *Options*). You can configure one or more Part-DB instances; click **+ Add instance** to add another.
5959

6060
| Setting | Description |
6161
|---------|-------------|
62+
| **Name** | Optional label to tell instances apart, e.g. `Production`. Defaults to the hostname if left blank. |
6263
| **Part-DB base URL** | Root URL of your Part-DB instance, e.g. `https://partdb.example.com` |
6364
| **Interface locale** | Language code used in Part-DB URLs, e.g. `en`, `de`, `fr`. Check your browser's address bar when using Part-DB. |
6465

65-
Use the **Test Connection** button to verify the URL is reachable and you are logged in.
66+
Use the **Test Connection** button on an instance to verify its URL is reachable and you are logged in.
67+
68+
If you configure more than one instance, the popup shows an **Instance** dropdown to pick which one to submit to — it remembers your last choice for next time.
6669

6770
## Permissions
6871

6972
| Permission | Type | Reason |
7073
|------------|------|--------|
71-
| `storage` | Required | Saves your Part-DB URL and locale setting |
74+
| `storage` | Required | Saves your configured Part-DB instances and settings |
7275
| `activeTab` | Required | Reads the page HTML on demand when you click Submit |
7376
| `scripting` | Required | Executes the page-reading code in the active tab |
74-
| Host access to your Part-DB URL | Optional — granted when you save your server URL | Required to reach your Part-DB instance; scoped to exactly the URL you configure |
77+
| Host access to your Part-DB URL(s) | Optional — granted when you save a server URL | Required to reach your Part-DB instance(s); scoped to exactly the URLs you configure |
7578

7679
No host permissions are requested at install time. The extension has no passive presence on web pages — it reads page content only at the moment you click Submit.
7780

src/_locales/de/messages.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
"popup_connected_to": {
2222
"message": "Verbunden mit"
2323
},
24+
"popup_host_label": {
25+
"message": "Instanz"
26+
},
2427
"popup_loading": {
2528
"message": "Wird geladen …"
2629
},
@@ -119,7 +122,19 @@
119122
"message": "Einstellungen"
120123
},
121124
"options_connection_title": {
122-
"message": "Verbindung"
125+
"message": "Part-DB-Instanzen"
126+
},
127+
"options_add_host_btn": {
128+
"message": "+ Instanz hinzufügen"
129+
},
130+
"options_no_hosts_hint": {
131+
"message": "Noch keine Instanzen konfiguriert. Klicken Sie auf „Instanz hinzufügen“, um zu starten."
132+
},
133+
"options_host_name_placeholder": {
134+
"message": "z. B. Produktiv"
135+
},
136+
"options_remove_host": {
137+
"message": "Instanz entfernen"
123138
},
124139
"options_url_label": {
125140
"message": "Part-DB Basis-URL"

src/_locales/en/messages.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
"popup_connected_to": {
2222
"message": "Connected to"
2323
},
24+
"popup_host_label": {
25+
"message": "Instance"
26+
},
2427
"popup_loading": {
2528
"message": "Loading…"
2629
},
@@ -119,7 +122,19 @@
119122
"message": "Settings"
120123
},
121124
"options_connection_title": {
122-
"message": "Connection"
125+
"message": "Part-DB Instances"
126+
},
127+
"options_add_host_btn": {
128+
"message": "+ Add instance"
129+
},
130+
"options_no_hosts_hint": {
131+
"message": "No instances configured yet. Click \"Add instance\" to get started."
132+
},
133+
"options_host_name_placeholder": {
134+
"message": "e.g. Production"
135+
},
136+
"options_remove_host": {
137+
"message": "Remove instance"
123138
},
124139
"options_url_label": {
125140
"message": "Part-DB base URL"

src/background/service-worker.js

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,31 @@
1+
if (typeof importScripts === 'function') {
2+
importScripts('../shared/host-storage.js');
3+
}
4+
15
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
26
if (message.action === 'submit') {
3-
handleSubmit(message.html, message.url, message.title, message.provider).then(sendResponse);
7+
handleSubmit(message.hostId, message.html, message.url, message.title, message.provider).then(sendResponse);
48
return true;
59
}
610
if (message.action === 'getInfo') {
7-
handleGetInfo().then(sendResponse);
11+
handleGetInfo(message.hostId).then(sendResponse);
812
return true;
913
}
1014
});
1115

12-
async function getConfig() {
13-
const { partdbUrl, partdbLocale } = await chrome.storage.sync.get({
14-
partdbUrl: '',
15-
partdbLocale: 'en',
16-
});
17-
const baseUrl = (partdbUrl || '').replace(/\/$/, '');
18-
const locale = partdbLocale || 'en';
19-
return { baseUrl, locale };
20-
}
21-
2216
const INFO_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
2317

24-
async function handleGetInfo() {
25-
const { baseUrl, locale } = await getConfig();
26-
if (!baseUrl) return { success: false, error: 'no_config' };
18+
async function handleGetInfo(hostId) {
19+
const host = await getHostById(hostId);
20+
if (!host) return { success: false, error: 'no_config' };
2721

28-
const { infoCache } = await chrome.storage.local.get({ infoCache: null });
29-
if (infoCache && infoCache.baseUrl === baseUrl && Date.now() - infoCache.ts < INFO_CACHE_TTL) {
30-
return { success: true, data: infoCache.data };
22+
const baseUrl = host.url;
23+
const locale = host.locale || 'en';
24+
25+
const { infoCacheByHost } = await chrome.storage.local.get({ infoCacheByHost: {} });
26+
const cached = infoCacheByHost[hostId];
27+
if (cached && cached.baseUrl === baseUrl && Date.now() - cached.ts < INFO_CACHE_TTL) {
28+
return { success: true, data: cached.data };
3129
}
3230

3331
const endpoint = `${baseUrl}/${locale}/tools/info_providers/browser_info`;
@@ -49,20 +47,23 @@ async function handleGetInfo() {
4947
return { success: false, error: 'not_logged_in', loginUrl: `${baseUrl}/${locale}/login` };
5048
}
5149
const data = await response.json();
52-
await chrome.storage.local.set({ infoCache: { baseUrl, data, ts: Date.now() } });
50+
infoCacheByHost[hostId] = { baseUrl, data, ts: Date.now() };
51+
await chrome.storage.local.set({ infoCacheByHost });
5352
return { success: true, data };
5453
} catch (err) {
5554
return { success: false, error: 'network_error', message: err.message };
5655
}
5756
}
5857

59-
async function handleSubmit(html, url, title, provider) {
60-
const { baseUrl, locale } = await getConfig();
61-
62-
if (!baseUrl) {
58+
async function handleSubmit(hostId, html, url, title, provider) {
59+
const host = await getHostById(hostId);
60+
if (!host) {
6361
return { success: false, error: 'no_config' };
6462
}
6563

64+
const baseUrl = host.url;
65+
const locale = host.locale || 'en';
66+
6667
const endpoint = `${baseUrl}/${locale}/tools/info_providers/browser_html`;
6768

6869
try {

src/i18n.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
function applyI18n() {
2-
for (const el of document.querySelectorAll('[data-i18n]')) {
1+
function applyI18n(root = document) {
2+
for (const el of root.querySelectorAll('[data-i18n]')) {
33
const msg = chrome.i18n.getMessage(el.dataset.i18n);
44
if (msg) el.textContent = msg;
55
}
6-
for (const el of document.querySelectorAll('[data-i18n-html]')) {
6+
for (const el of root.querySelectorAll('[data-i18n-html]')) {
77
const msg = chrome.i18n.getMessage(el.dataset.i18nHtml);
88
if (msg) {
99
const doc = new DOMParser().parseFromString(msg, 'text/html');
1010
el.replaceChildren(...doc.body.childNodes);
1111
}
1212
}
13-
for (const el of document.querySelectorAll('[data-i18n-placeholder]')) {
13+
for (const el of root.querySelectorAll('[data-i18n-placeholder]')) {
1414
const msg = chrome.i18n.getMessage(el.dataset.i18nPlaceholder);
1515
if (msg) el.placeholder = msg;
1616
}
17+
for (const el of root.querySelectorAll('[data-i18n-title]')) {
18+
const msg = chrome.i18n.getMessage(el.dataset.i18nTitle);
19+
if (msg) el.title = msg;
20+
}
1721
}

src/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "__MSG_ext_name__",
4-
"version": "1.1.1",
4+
"version": "1.2.0",
55
"description": "__MSG_ext_description__",
66
"default_locale": "en",
77
"permissions": ["storage", "activeTab", "scripting"],
@@ -18,7 +18,7 @@
1818
},
1919
"background": {
2020
"service_worker": "background/service-worker.js",
21-
"scripts": ["background/service-worker.js"]
21+
"scripts": ["shared/host-storage.js", "background/service-worker.js"]
2222
},
2323
"options_ui": {
2424
"page": "options/options.html",

src/options/options.css

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,71 @@ body {
5858
padding-bottom: 10px;
5959
}
6060

61+
.card-title-row {
62+
display: flex;
63+
align-items: center;
64+
justify-content: space-between;
65+
gap: 12px;
66+
border-bottom: 1px solid #e9ecef;
67+
padding-bottom: 10px;
68+
}
69+
70+
.card-title-row .card-title {
71+
border-bottom: none;
72+
padding-bottom: 0;
73+
}
74+
75+
/* Host list */
76+
#hosts-list {
77+
display: flex;
78+
flex-direction: column;
79+
gap: 16px;
80+
}
81+
82+
.host-row {
83+
border: 1px solid #e9ecef;
84+
border-radius: 8px;
85+
padding: 16px;
86+
display: flex;
87+
flex-direction: column;
88+
gap: 14px;
89+
}
90+
91+
.host-row-header {
92+
display: flex;
93+
align-items: center;
94+
gap: 8px;
95+
}
96+
97+
.host-name {
98+
flex: 1;
99+
font-weight: 600;
100+
}
101+
102+
.host-row-actions {
103+
display: flex;
104+
}
105+
106+
.icon-btn {
107+
flex-shrink: 0;
108+
width: 30px;
109+
height: 30px;
110+
border: 1px solid #ced4da;
111+
border-radius: 6px;
112+
background: #fff;
113+
color: #6c757d;
114+
cursor: pointer;
115+
font-size: 13px;
116+
line-height: 1;
117+
transition: background 0.15s, border-color 0.15s, color 0.15s;
118+
}
119+
120+
.icon-btn:hover {
121+
background: #f8d7da;
122+
border-color: #f5c6cb;
123+
color: #721c24;
124+
}
125+
61126
/* Form */
62127
.form-group {
63128
display: flex;
@@ -154,6 +219,11 @@ input:invalid:not(:placeholder-shown) {
154219
background: #dee2e6;
155220
}
156221

222+
.btn-sm {
223+
padding: 6px 12px;
224+
font-size: 13px;
225+
}
226+
157227
.save-status {
158228
font-size: 13px;
159229
font-weight: 500;

0 commit comments

Comments
 (0)