pi-sdk-web 0.4.0 → 0.4.2

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.
@@ -28,7 +28,23 @@ from rpc_client import RpcClient, RpcCommands
28
28
  from websocket import WebSocketConnection, WebSocketServer
29
29
 
30
30
  DEFAULT_PORT = 4080
31
- STATIC_DIR = Path(__file__).resolve().parent.parent / "static"
31
+
32
+
33
+ def _resolve_static_dir() -> Path:
34
+ """Find the static/ directory: the repo layout keeps it next to the
35
+ project root (server.py's parent.parent), while the npm package layout
36
+ ships it at dist/static (server.py lives at dist/pi-bin/server/). Search
37
+ upward so both layouts resolve."""
38
+ d = Path(__file__).resolve().parent
39
+ for _ in range(4):
40
+ candidate = d / "static"
41
+ if candidate.is_dir():
42
+ return candidate
43
+ d = d.parent
44
+ return Path(__file__).resolve().parent / "static"
45
+
46
+
47
+ STATIC_DIR = _resolve_static_dir()
32
48
 
33
49
 
34
50
  class Peripheral:
package/dist/server.js CHANGED
@@ -530,11 +530,13 @@ export class PiWebServer {
530
530
  case "set_scoped_models": {
531
531
  const models = Array.isArray(data.models) ? data.models : [];
532
532
  const resolved = [];
533
+ const enabledIds = [];
533
534
  for (const m of models) {
534
535
  const provider = m.provider;
535
536
  const modelId = m.modelId;
536
537
  if (!provider || !modelId)
537
538
  continue;
539
+ enabledIds.push(`${provider}/${modelId}`);
538
540
  const model = this.session.modelRuntime
539
541
  .getAvailableSnapshot()
540
542
  .find((x) => x.provider === provider && x.id === modelId);
@@ -542,6 +544,11 @@ export class PiWebServer {
542
544
  resolved.push({ model, thinkingLevel: m.thinkingLevel });
543
545
  }
544
546
  this.session.setScopedModels(resolved);
547
+ // Persist only on explicit save (TUI Ctrl+S onPersist); toggling is
548
+ // session-only (onChange) and must not rewrite settings.json.
549
+ if (data.persist === true) {
550
+ this.session.settingsManager.setEnabledModels(enabledIds.length > 0 ? enabledIds : undefined);
551
+ }
545
552
  this.broadcastState();
546
553
  break;
547
554
  }
@@ -1872,6 +1872,7 @@ class PiWebClient {
1872
1872
  this.scopedModelsAll = [];
1873
1873
  this.scopedModelsSelected = new Set();
1874
1874
  this.scopedModelsData = null;
1875
+ this.scopedModelsSaved = true;
1875
1876
  this.modalList.innerHTML = '<div class="modal-message">Loading models...</div>';
1876
1877
  this.send({ type: 'get_scoped_models' });
1877
1878
  }
@@ -1910,11 +1911,13 @@ class PiWebClient {
1910
1911
  })
1911
1912
  .join('');
1912
1913
  this.modalList.innerHTML =
1913
- rows +
1914
1914
  `<div class="modal-actions">
1915
- <button class="modal-btn ok-btn">Apply</button>
1915
+ <button class="modal-btn ok-btn">Save</button>
1916
1916
  <button class="modal-btn cancel-btn">Cancel</button>
1917
- </div>`;
1917
+ <span class="modal-hint"></span>
1918
+ </div>` +
1919
+ rows;
1920
+ this.updateScopedUnsavedHint();
1918
1921
  this.modalList.querySelectorAll('.scoped-model').forEach((el) => {
1919
1922
  el.addEventListener('click', () => {
1920
1923
  const key = el.dataset.key;
@@ -1923,26 +1926,14 @@ class PiWebClient {
1923
1926
  const checked = this.scopedModelsSelected.has(key);
1924
1927
  el.classList.toggle('selected', checked);
1925
1928
  el.querySelector('.modal-check').textContent = checked ? '☑' : '☐';
1929
+ // TUI onChange: apply to session (memory, persist=false); Save persists.
1930
+ this.applyScopedModels(false);
1926
1931
  });
1927
1932
  });
1928
1933
  const okBtn = this.modalList.querySelector('.ok-btn');
1929
1934
  if (okBtn) {
1930
1935
  okBtn.addEventListener('click', () => {
1931
- const models = [];
1932
- for (const m of this.scopedModelsAll || []) {
1933
- const key = `${m.provider}/${m.id}`;
1934
- if (this.scopedModelsSelected.has(key)) {
1935
- const scopedInfo = (this.scopedModelsData?.scoped || []).find(
1936
- (s) => `${s.provider}/${s.id}` === key,
1937
- );
1938
- models.push({
1939
- provider: m.provider,
1940
- modelId: m.id,
1941
- thinkingLevel: scopedInfo?.thinkingLevel,
1942
- });
1943
- }
1944
- }
1945
- this.send({ type: 'set_scoped_models', models });
1936
+ this.applyScopedModels(true);
1946
1937
  this.closeModal();
1947
1938
  });
1948
1939
  }
@@ -1950,6 +1941,34 @@ class PiWebClient {
1950
1941
  if (cancelBtn) cancelBtn.addEventListener('click', () => this.closeModal());
1951
1942
  }
1952
1943
 
1944
+ /** Send the current selection to the server (persist=false = memory only). */
1945
+ applyScopedModels(persist) {
1946
+ const models = [];
1947
+ for (const m of this.scopedModelsAll || []) {
1948
+ const key = `${m.provider}/${m.id}`;
1949
+ if (this.scopedModelsSelected.has(key)) {
1950
+ const scopedInfo = (this.scopedModelsData?.scoped || []).find(
1951
+ (s) => `${s.provider}/${s.id}` === key,
1952
+ );
1953
+ models.push({
1954
+ provider: m.provider,
1955
+ modelId: m.id,
1956
+ thinkingLevel: scopedInfo?.thinkingLevel,
1957
+ });
1958
+ }
1959
+ }
1960
+ this.send({ type: 'set_scoped_models', models, persist: persist });
1961
+ this.scopedModelsSaved = persist;
1962
+ this.updateScopedUnsavedHint();
1963
+ }
1964
+
1965
+ updateScopedUnsavedHint() {
1966
+ const hint = this.modalList.querySelector('.modal-hint');
1967
+ if (!hint) return;
1968
+ hint.textContent = this.scopedModelsSaved ? '' : ' (unsaved)';
1969
+ hint.className = this.scopedModelsSaved ? 'modal-hint' : 'modal-hint unsaved';
1970
+ }
1971
+
1953
1972
  // ------------------------------------------------------------------
1954
1973
  // Slash command menu
1955
1974
  // ------------------------------------------------------------------
@@ -859,6 +859,19 @@ body {
859
859
  gap: 8px;
860
860
  justify-content: flex-end;
861
861
  margin-top: 10px;
862
+ margin-bottom: 10px;
863
+ align-items: center;
864
+ }
865
+
866
+ .modal-hint {
867
+ font-size: 12px;
868
+ color: var(--dim);
869
+ margin-left: auto;
870
+ }
871
+
872
+ .modal-hint.unsaved {
873
+ color: var(--warning);
874
+ font-weight: 600;
862
875
  }
863
876
 
864
877
  .modal-btn {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-sdk-web",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Browser Web access for Pi (AI coding agent) via the Pi SDK - standalone module, zero modification to Pi itself",
5
5
  "type": "module",
6
6
  "bin": {