sshler 0.7.0__tar.gz → 0.7.2__tar.gz

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.
Files changed (129) hide show
  1. {sshler-0.7.0 → sshler-0.7.2}/MANIFEST.in +1 -0
  2. {sshler-0.7.0/sshler.egg-info → sshler-0.7.2}/PKG-INFO +1 -1
  3. {sshler-0.7.0 → sshler-0.7.2}/pyproject.toml +2 -1
  4. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/files.py +28 -0
  5. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/models.py +1 -0
  6. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/recovery.py +7 -2
  7. sshler-0.7.2/sshler/static/dist/assets/Alert-C0caXY3O.js +48 -0
  8. sshler-0.7.2/sshler/static/dist/assets/BoxesView-Cw5NiFP6.css +1 -0
  9. sshler-0.7.2/sshler/static/dist/assets/BoxesView-DCsH8F6Q.js +1 -0
  10. sshler-0.7.2/sshler/static/dist/assets/DirectoryPickerModal-CVB_AH0M.css +1 -0
  11. sshler-0.7.2/sshler/static/dist/assets/DirectoryPickerModal-DlIkiyp1.js +27 -0
  12. sshler-0.7.2/sshler/static/dist/assets/FilesView-BFEnr1Va.css +1 -0
  13. sshler-0.7.2/sshler/static/dist/assets/FilesView-jrF0GwSm.js +799 -0
  14. sshler-0.7.2/sshler/static/dist/assets/Grid-B_OzHEym.js +1 -0
  15. sshler-0.7.2/sshler/static/dist/assets/InputNumber-XDnEotgL.js +13 -0
  16. sshler-0.7.2/sshler/static/dist/assets/LoginView-BLrnakeI.css +1 -0
  17. sshler-0.7.2/sshler/static/dist/assets/LoginView-C2biy_5H.js +110 -0
  18. sshler-0.7.2/sshler/static/dist/assets/MultiTerminalView-C8x4yNn3.js +1 -0
  19. sshler-0.7.2/sshler/static/dist/assets/MultiTerminalView-UnOQ5utL.css +1 -0
  20. sshler-0.7.2/sshler/static/dist/assets/NotFoundView-DYaoqkDb.css +1 -0
  21. sshler-0.7.2/sshler/static/dist/assets/NotFoundView-D_GMBtkq.js +1 -0
  22. sshler-0.7.2/sshler/static/dist/assets/OverviewView-BSwVX2Hs.js +1 -0
  23. sshler-0.7.2/sshler/static/dist/assets/OverviewView-SxVVVBkg.css +1 -0
  24. sshler-0.7.2/sshler/static/dist/assets/PhCircle.vue-BD-yU_HB.js +1 -0
  25. sshler-0.7.2/sshler/static/dist/assets/PhHouse.vue-BwwabvLx.js +1 -0
  26. sshler-0.7.2/sshler/static/dist/assets/PhPlus.vue-ChJ-UG0M.js +1 -0
  27. sshler-0.7.2/sshler/static/dist/assets/PhPushPinSimple.vue-DBP-0k94.js +1 -0
  28. sshler-0.7.2/sshler/static/dist/assets/PhTrash.vue-N4dJGebi.js +153 -0
  29. sshler-0.7.2/sshler/static/dist/assets/SettingsView-DCCByJuu.js +74 -0
  30. sshler-0.7.2/sshler/static/dist/assets/SettingsView-xrtebMdI.css +1 -0
  31. sshler-0.7.2/sshler/static/dist/assets/Spin-13rKpfWM.js +43 -0
  32. sshler-0.7.2/sshler/static/dist/assets/Switch-DsrNJCg_.js +102 -0
  33. sshler-0.7.2/sshler/static/dist/assets/TerminalView--aIl3nME.js +2 -0
  34. sshler-0.7.2/sshler/static/dist/assets/TerminalView-DKOHtH_z.css +1 -0
  35. sshler-0.7.2/sshler/static/dist/assets/favorites-CJMYgFyA.js +1 -0
  36. sshler-0.7.2/sshler/static/dist/assets/index-BCipBsn9.js +1813 -0
  37. sshler-0.7.2/sshler/static/dist/assets/index-Cwi9_lHU.css +1 -0
  38. sshler-0.7.2/sshler/static/dist/fonts/MonaspaceNeon-Bold.woff2 +0 -0
  39. sshler-0.7.2/sshler/static/dist/fonts/MonaspaceNeon-Regular.woff2 +0 -0
  40. sshler-0.7.2/sshler/static/dist/fonts/MonaspaceNeon-Variable.woff2 +0 -0
  41. sshler-0.7.2/sshler/static/dist/icon-192.png +0 -0
  42. sshler-0.7.2/sshler/static/dist/icon-512-maskable.png +0 -0
  43. sshler-0.7.2/sshler/static/dist/icon-512.png +0 -0
  44. sshler-0.7.2/sshler/static/dist/index.html +22 -0
  45. sshler-0.7.2/sshler/static/dist/manifest.webmanifest +29 -0
  46. sshler-0.7.2/sshler/static/favicon.png +0 -0
  47. sshler-0.7.2/sshler/static/logo.png +0 -0
  48. {sshler-0.7.0 → sshler-0.7.2/sshler.egg-info}/PKG-INFO +1 -1
  49. {sshler-0.7.0 → sshler-0.7.2}/sshler.egg-info/SOURCES.txt +41 -0
  50. {sshler-0.7.0 → sshler-0.7.2}/LICENSE +0 -0
  51. {sshler-0.7.0 → sshler-0.7.2}/README.md +0 -0
  52. {sshler-0.7.0 → sshler-0.7.2}/scripts/check_frontend_dist.py +0 -0
  53. {sshler-0.7.0 → sshler-0.7.2}/setup.cfg +0 -0
  54. {sshler-0.7.0 → sshler-0.7.2}/sshler/__init__.py +0 -0
  55. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/__init__.py +0 -0
  56. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/archive.py +0 -0
  57. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/auth.py +0 -0
  58. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/batch.py +0 -0
  59. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/boxes.py +0 -0
  60. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/config.py +0 -0
  61. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/dependencies.py +0 -0
  62. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/grep.py +0 -0
  63. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/helpers.py +0 -0
  64. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/rate_limiting.py +0 -0
  65. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/search.py +0 -0
  66. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/sessions.py +0 -0
  67. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/snippets.py +0 -0
  68. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/terminal.py +0 -0
  69. {sshler-0.7.0 → sshler-0.7.2}/sshler/api/tunnels.py +0 -0
  70. {sshler-0.7.0 → sshler-0.7.2}/sshler/auth.py +0 -0
  71. {sshler-0.7.0 → sshler-0.7.2}/sshler/cli.py +0 -0
  72. {sshler-0.7.0 → sshler-0.7.2}/sshler/config.py +0 -0
  73. {sshler-0.7.0 → sshler-0.7.2}/sshler/config_cache.py +0 -0
  74. {sshler-0.7.0 → sshler-0.7.2}/sshler/rate_limit.py +0 -0
  75. {sshler-0.7.0 → sshler-0.7.2}/sshler/scripts/install-sshler-task.ps1 +0 -0
  76. {sshler-0.7.0 → sshler-0.7.2}/sshler/scripts/remove-sshler-task.ps1 +0 -0
  77. {sshler-0.7.0 → sshler-0.7.2}/sshler/scripts/run-sshler.ps1 +0 -0
  78. {sshler-0.7.0 → sshler-0.7.2}/sshler/session.py +0 -0
  79. {sshler-0.7.0 → sshler-0.7.2}/sshler/settings.py +0 -0
  80. {sshler-0.7.0 → sshler-0.7.2}/sshler/snapshot.py +0 -0
  81. {sshler-0.7.0 → sshler-0.7.2}/sshler/spa.py +0 -0
  82. {sshler-0.7.0 → sshler-0.7.2}/sshler/ssh.py +0 -0
  83. {sshler-0.7.0 → sshler-0.7.2}/sshler/ssh_config.py +0 -0
  84. {sshler-0.7.0 → sshler-0.7.2}/sshler/ssh_pool.py +0 -0
  85. {sshler-0.7.0 → sshler-0.7.2}/sshler/state.py +0 -0
  86. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/base.js +0 -0
  87. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/command-palette.js +0 -0
  88. {sshler-0.7.0/sshler/static → sshler-0.7.2/sshler/static/dist}/favicon.png +0 -0
  89. {sshler-0.7.0/sshler/static → sshler-0.7.2/sshler/static/dist}/logo.png +0 -0
  90. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/favicon-terminal-local.svg +0 -0
  91. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/favicon-terminal.svg +0 -0
  92. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/favicon.svg +0 -0
  93. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/file-browser.js +0 -0
  94. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/file-edit.js +0 -0
  95. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/file-view.js +0 -0
  96. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/manifest.json +0 -0
  97. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/multi-session.js +0 -0
  98. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/style.css +0 -0
  99. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/sw.js +0 -0
  100. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/term.js +0 -0
  101. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/vendor/xterm/xterm-addon-fit.js +0 -0
  102. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/vendor/xterm/xterm-addon-search.js +0 -0
  103. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/vendor/xterm/xterm.css +0 -0
  104. {sshler-0.7.0 → sshler-0.7.2}/sshler/static/vendor/xterm/xterm.js +0 -0
  105. {sshler-0.7.0 → sshler-0.7.2}/sshler/validation.py +0 -0
  106. {sshler-0.7.0 → sshler-0.7.2}/sshler/webapp.py +0 -0
  107. {sshler-0.7.0 → sshler-0.7.2}/sshler.egg-info/dependency_links.txt +0 -0
  108. {sshler-0.7.0 → sshler-0.7.2}/sshler.egg-info/entry_points.txt +0 -0
  109. {sshler-0.7.0 → sshler-0.7.2}/sshler.egg-info/requires.txt +0 -0
  110. {sshler-0.7.0 → sshler-0.7.2}/sshler.egg-info/top_level.txt +0 -0
  111. {sshler-0.7.0 → sshler-0.7.2}/tests/test_api_v1.py +0 -0
  112. {sshler-0.7.0 → sshler-0.7.2}/tests/test_archive.py +0 -0
  113. {sshler-0.7.0 → sshler-0.7.2}/tests/test_basic.py +0 -0
  114. {sshler-0.7.0 → sshler-0.7.2}/tests/test_batch_ops.py +0 -0
  115. {sshler-0.7.0 → sshler-0.7.2}/tests/test_command_injection.py +0 -0
  116. {sshler-0.7.0 → sshler-0.7.2}/tests/test_config.py +0 -0
  117. {sshler-0.7.0 → sshler-0.7.2}/tests/test_database_indexes.py +0 -0
  118. {sshler-0.7.0 → sshler-0.7.2}/tests/test_dev_workflow.py +0 -0
  119. {sshler-0.7.0 → sshler-0.7.2}/tests/test_grep.py +0 -0
  120. {sshler-0.7.0 → sshler-0.7.2}/tests/test_handshake_status.py +0 -0
  121. {sshler-0.7.0 → sshler-0.7.2}/tests/test_httpx_ws.py +0 -0
  122. {sshler-0.7.0 → sshler-0.7.2}/tests/test_path_validation.py +0 -0
  123. {sshler-0.7.0 → sshler-0.7.2}/tests/test_rate_limit.py +0 -0
  124. {sshler-0.7.0 → sshler-0.7.2}/tests/test_search.py +0 -0
  125. {sshler-0.7.0 → sshler-0.7.2}/tests/test_session_auth.py +0 -0
  126. {sshler-0.7.0 → sshler-0.7.2}/tests/test_snippets.py +0 -0
  127. {sshler-0.7.0 → sshler-0.7.2}/tests/test_ssh.py +0 -0
  128. {sshler-0.7.0 → sshler-0.7.2}/tests/test_tunnels.py +0 -0
  129. {sshler-0.7.0 → sshler-0.7.2}/tests/test_websocket.py +0 -0
@@ -1,2 +1,3 @@
1
1
  recursive-include sshler/templates *.html
2
2
  recursive-include sshler/static *
3
+ recursive-include sshler/static/dist *
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sshler
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Summary: A local FastAPI-powered SSH multiplexer for tmux-in-browser — from your laptop only.
5
5
  Author-email: Gabu <gabu@gabu.quest>
6
6
  License-Expression: MIT
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "sshler"
8
- version = "0.7.0"
8
+ version = "0.7.2"
9
9
  description = "A local FastAPI-powered SSH multiplexer for tmux-in-browser — from your laptop only."
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.12"
@@ -69,6 +69,7 @@ dev = [
69
69
  "mypy>=1.19.1",
70
70
  "types-pyyaml>=6.0.12.20250915",
71
71
  "pre-commit>=4.0.0",
72
+ "build>=1.4.0",
72
73
  ]
73
74
 
74
75
  [project.scripts]
@@ -66,6 +66,25 @@ def _cleanup_temp(path: str) -> BackgroundTask:
66
66
  return BackgroundTask(_remove)
67
67
 
68
68
 
69
+ async def _get_gitignored_names(directory: str, names: list[str]) -> set[str]:
70
+ """Check which filenames in a directory are gitignored. Returns set of ignored names."""
71
+ try:
72
+ proc = await asyncio.create_subprocess_exec(
73
+ "git", "check-ignore", "--stdin",
74
+ stdin=asyncio.subprocess.PIPE,
75
+ stdout=asyncio.subprocess.PIPE,
76
+ stderr=asyncio.subprocess.PIPE,
77
+ cwd=directory,
78
+ )
79
+ input_data = "\n".join(names).encode()
80
+ stdout, _ = await proc.communicate(input=input_data)
81
+ if proc.returncode not in (0, 1): # 1 means none matched, 128 means not a git repo
82
+ return set()
83
+ return set(line.strip() for line in stdout.decode().splitlines() if line.strip())
84
+ except Exception:
85
+ return set()
86
+
87
+
69
88
  def get_router(deps: APIDependencies) -> APIRouter:
70
89
  router = APIRouter()
71
90
 
@@ -95,6 +114,14 @@ def get_router(deps: APIDependencies) -> APIRouter:
95
114
  except OSError:
96
115
  continue # broken symlink or deleted between iterdir and stat
97
116
  children.sort(key=lambda item: (not item[1], item[0].name.lower()))
117
+
118
+ # Check gitignored status for all entries in one batch
119
+ ignored_names: set[str] = set()
120
+ if children:
121
+ ignored_names = await _get_gitignored_names(
122
+ normalized, [c.name for c, _ in children]
123
+ )
124
+
98
125
  for child, is_dir in children:
99
126
  try:
100
127
  stats = child.stat()
@@ -108,6 +135,7 @@ def get_router(deps: APIDependencies) -> APIRouter:
108
135
  size=stats.st_size if child.is_file() else None,
109
136
  modified=stats.st_mtime,
110
137
  mode=stats.st_mode & 0o7777,
138
+ gitignored=child.name in ignored_names,
111
139
  )
112
140
  )
113
141
  return APIDirectoryListing(box=box.name, directory=normalized, entries=entries)
@@ -26,6 +26,7 @@ class APIDirectoryEntry(BaseModel):
26
26
  size: int | None = None
27
27
  modified: float | None = None
28
28
  mode: int | None = None
29
+ gitignored: bool = False
29
30
 
30
31
 
31
32
  class APIDirectoryListing(BaseModel):
@@ -61,15 +61,20 @@ def get_router(deps: APIDependencies) -> APIRouter:
61
61
 
62
62
  @router.post("/recovery/{session_id}/dismiss", response_model=APISimpleMessage)
63
63
  async def api_dismiss_single(session_id: str) -> APISimpleMessage:
64
- """Dismiss a single recovery session."""
64
+ """Dismiss a single recovery session permanently."""
65
65
  lost = get_recovery_sessions()
66
66
  set_recovery_sessions([s for s in lost if s["id"] != session_id])
67
+ # Mark inactive in DB so the snapshot loop stops re-detecting it
68
+ await state.update_session_activity_async(session_id, active=False)
67
69
  return APISimpleMessage(status="ok", message="Dismissed")
68
70
 
69
71
  @router.post("/recovery/dismiss", response_model=APISimpleMessage)
70
72
  async def api_dismiss_recovery() -> APISimpleMessage:
71
- """Dismiss all recovery notifications."""
73
+ """Dismiss all recovery notifications permanently."""
72
74
  lost = get_recovery_sessions()
75
+ # Mark all as inactive so they don't get re-detected
76
+ for s in lost:
77
+ await state.update_session_activity_async(s["id"], active=False)
73
78
  set_recovery_sessions([])
74
79
  logger.info("Dismissed %d recovery session(s)", len(lost))
75
80
  return APISimpleMessage(status="ok", message=f"Dismissed {len(lost)} session(s)")
@@ -0,0 +1,48 @@
1
+ import{ap as d,at as t,as as u,cA as H,ao as j,d as F,a1 as r,m as M,cB as N,aQ as O,av as V,cC as W,a5 as D,ax as C,az as G,c as p,cD as K,aA as i,aB as Q,g as q,cE as J,aS as U,cF as X,ce as Y,cG as Z,cH as ee}from"./index-BCipBsn9.js";const oe=d("alert",`
2
+ line-height: var(--n-line-height);
3
+ border-radius: var(--n-border-radius);
4
+ position: relative;
5
+ transition: background-color .3s var(--n-bezier);
6
+ background-color: var(--n-color);
7
+ text-align: start;
8
+ word-break: break-word;
9
+ `,[t("border",`
10
+ border-radius: inherit;
11
+ position: absolute;
12
+ left: 0;
13
+ right: 0;
14
+ top: 0;
15
+ bottom: 0;
16
+ transition: border-color .3s var(--n-bezier);
17
+ border: var(--n-border);
18
+ pointer-events: none;
19
+ `),u("closable",[d("alert-body",[t("title",`
20
+ padding-right: 24px;
21
+ `)])]),t("icon",{color:"var(--n-icon-color)"}),d("alert-body",{padding:"var(--n-padding)"},[t("title",{color:"var(--n-title-text-color)"}),t("content",{color:"var(--n-content-text-color)"})]),H({originalTransition:"transform .3s var(--n-bezier)",enterToProps:{transform:"scale(1)"},leaveToProps:{transform:"scale(0.9)"}}),t("icon",`
22
+ position: absolute;
23
+ left: 0;
24
+ top: 0;
25
+ align-items: center;
26
+ justify-content: center;
27
+ display: flex;
28
+ width: var(--n-icon-size);
29
+ height: var(--n-icon-size);
30
+ font-size: var(--n-icon-size);
31
+ margin: var(--n-icon-margin);
32
+ `),t("close",`
33
+ transition:
34
+ color .3s var(--n-bezier),
35
+ background-color .3s var(--n-bezier);
36
+ position: absolute;
37
+ right: 0;
38
+ top: 0;
39
+ margin: var(--n-close-margin);
40
+ `),u("show-icon",[d("alert-body",{paddingLeft:"calc(var(--n-icon-margin-left) + var(--n-icon-size) + var(--n-icon-margin-right))"})]),u("right-adjust",[d("alert-body",{paddingRight:"calc(var(--n-close-size) + var(--n-padding) + 2px)"})]),d("alert-body",`
41
+ border-radius: var(--n-border-radius);
42
+ transition: border-color .3s var(--n-bezier);
43
+ `,[t("title",`
44
+ transition: color .3s var(--n-bezier);
45
+ font-size: 16px;
46
+ line-height: 19px;
47
+ font-weight: var(--n-title-font-weight);
48
+ `,[j("& +",[t("content",{marginTop:"9px"})])]),t("content",{transition:"color .3s var(--n-bezier)",fontSize:"var(--n-font-size)"})]),t("icon",{transition:"color .3s var(--n-bezier)"})]),re=Object.assign(Object.assign({},C.props),{title:String,showIcon:{type:Boolean,default:!0},type:{type:String,default:"default"},bordered:{type:Boolean,default:!0},closable:Boolean,onClose:Function,onAfterLeave:Function,onAfterHide:Function}),ie=F({name:"Alert",inheritAttrs:!1,props:re,slots:Object,setup(n){const{mergedClsPrefixRef:e,mergedBorderedRef:l,inlineThemeDisabled:g,mergedRtlRef:v}=D(n),h=C("Alert","-alert",oe,J,n,e),z=G("Alert",v,e),b=p(()=>{const{common:{cubicBezierEaseInOut:a},self:o}=h.value,{fontSize:y,borderRadius:R,titleFontWeight:A,lineHeight:I,iconSize:$,iconMargin:m,iconMarginRtl:w,closeIconSize:B,closeBorderRadius:P,closeSize:T,closeMargin:_,closeMarginRtl:S,padding:k}=o,{type:s}=n,{left:E,right:L}=K(m);return{"--n-bezier":a,"--n-color":o[i("color",s)],"--n-close-icon-size":B,"--n-close-border-radius":P,"--n-close-color-hover":o[i("closeColorHover",s)],"--n-close-color-pressed":o[i("closeColorPressed",s)],"--n-close-icon-color":o[i("closeIconColor",s)],"--n-close-icon-color-hover":o[i("closeIconColorHover",s)],"--n-close-icon-color-pressed":o[i("closeIconColorPressed",s)],"--n-icon-color":o[i("iconColor",s)],"--n-border":o[i("border",s)],"--n-title-text-color":o[i("titleTextColor",s)],"--n-content-text-color":o[i("contentTextColor",s)],"--n-line-height":I,"--n-border-radius":R,"--n-font-size":y,"--n-title-font-weight":A,"--n-icon-size":$,"--n-icon-margin":m,"--n-icon-margin-rtl":w,"--n-close-size":T,"--n-close-margin":_,"--n-close-margin-rtl":S,"--n-padding":k,"--n-icon-margin-left":E,"--n-icon-margin-right":L}}),c=g?Q("alert",p(()=>n.type[0]),b,n):void 0,f=q(!0),x=()=>{const{onAfterLeave:a,onAfterHide:o}=n;a&&a(),o&&o()};return{rtlEnabled:z,mergedClsPrefix:e,mergedBordered:l,visible:f,handleCloseClick:()=>{var a;Promise.resolve((a=n.onClose)===null||a===void 0?void 0:a.call(n)).then(o=>{o!==!1&&(f.value=!1)})},handleAfterLeave:()=>{x()},mergedTheme:h,cssVars:g?void 0:b,themeClass:c?.themeClass,onRender:c?.onRender}},render(){var n;return(n=this.onRender)===null||n===void 0||n.call(this),r(W,{onAfterLeave:this.handleAfterLeave},{default:()=>{const{mergedClsPrefix:e,$slots:l}=this,g={class:[`${e}-alert`,this.themeClass,this.closable&&`${e}-alert--closable`,this.showIcon&&`${e}-alert--show-icon`,!this.title&&this.closable&&`${e}-alert--right-adjust`,this.rtlEnabled&&`${e}-alert--rtl`],style:this.cssVars,role:"alert"};return this.visible?r("div",Object.assign({},M(this.$attrs,g)),this.closable&&r(N,{clsPrefix:e,class:`${e}-alert__close`,onClick:this.handleCloseClick}),this.bordered&&r("div",{class:`${e}-alert__border`}),this.showIcon&&r("div",{class:`${e}-alert__icon`,"aria-hidden":"true"},O(l.icon,()=>[r(U,{clsPrefix:e},{default:()=>{switch(this.type){case"success":return r(ee,null);case"info":return r(Z,null);case"warning":return r(Y,null);case"error":return r(X,null);default:return null}}})])),r("div",{class:[`${e}-alert-body`,this.mergedBordered&&`${e}-alert-body--bordered`]},V(l.header,v=>{const h=v||this.title;return h?r("div",{class:`${e}-alert-body__title`},h):null}),l.default&&r("div",{class:`${e}-alert-body__content`},l))):null}})}});export{ie as N};
@@ -0,0 +1 @@
1
+ .page[data-v-60b8424c]{display:flex;flex-direction:column;gap:16px}.page-header[data-v-60b8424c]{display:flex;flex-direction:column;gap:4px}.eyebrow[data-v-60b8424c]{text-transform:uppercase;letter-spacing:.4px;font-size:12px;margin:0;color:var(--muted)}.small[data-v-60b8424c]{font-size:12px}
@@ -0,0 +1 @@
1
+ import{d as V,k as $,l as G,u as I,j as P,c as j,n as A,a as w,e as c,s,y as k,b as E,v as r,t as e,w as a,F as L,x as M,K as q,N as y,U as B,L as D,V as C,I as f,J as d,W as H,H as J,X as K,o as h,_ as T}from"./index-BCipBsn9.js";import{G as U}from"./PhPushPinSimple.vue-DBP-0k94.js";import{u as W}from"./favorites-CJMYgFyA.js";import{N as x}from"./Alert-C0caXY3O.js";import{N as X,a as O}from"./Grid-B_OzHEym.js";const Q={class:"page"},R={class:"page-header"},Y={class:"eyebrow"},Z={class:"text-muted"},ee={class:"card-title"},te={class:"text-muted small"},se=V({__name:"BoxesView",setup(ae){const m=$(),u=G(),i=W(),p=I(),{t:n}=P(),_=j(()=>m.token||m.payload?.token||null);async function z(){!m.payload&&!m.loading&&await m.bootstrap(),!u.items.length&&!u.loading&&await u.load(_.value),u.items.length&&i.hydrateFromBoxes(u.items)}function v(o,l){u.setBoxes(u.items.map(t=>t.name===o?{...t,...l}:t))}async function F(o){const l=await i.setPinned(o,_.value);if(i.error){p.error(i.error);return}v(o,{pinned:l}),p.success(n(l?"boxes.pinned":"boxes.unpinned"))}async function S(o,l){const t=!i.isFavorite(o,l),N=await i.setFavorite(o,l,t,_.value);if(i.error){p.error(i.error);return}const g=await i.loadBox(o,_.value);g?v(o,{favorites:g.favorites,pinned:g.pinned}):v(o,{favorites:Array.from(i.favoritesForBox(o).values())}),p.success(n(N?"boxes.favorited":"boxes.unfavorited"))}async function b(o){const l=await K(o,_.value);p.info(n("boxes.status_info",{status:l.status,latency:String(l.latency_ms||0)}))}return A(async()=>{await z()}),(o,l)=>(h(),w("div",Q,[c("header",R,[c("div",null,[c("p",Y,r(e(n)("boxes.title")),1),c("h1",null,r(e(n)("boxes.subtitle")),1),c("p",Z,r(e(n)("boxes.description")),1)])]),s(e(X),{"x-gap":12,"y-gap":12,cols:3},{default:a(()=>[(h(!0),w(L,null,M(e(u).items,t=>(h(),k(e(O),{key:t.name},{default:a(()=>[s(e(q),{class:"surface-card",size:"small"},{default:a(()=>[c("div",ee,[s(e(y),{size:"16"},{default:a(()=>[s(e(B),{weight:"duotone"})]),_:1}),c("span",null,r(e(D)(t.name))+" "+r(t.name),1)]),c("p",te,r(t.host)+" • "+r(t.transport),1),s(e(C),{size:"small",style:{"margin-bottom":"8px"}},{default:a(()=>[s(e(f),{size:"tiny",type:"primary",onClick:()=>o.$router.push(`/files?box=${t.name}`)},{default:a(()=>[s(e(y),{size:"14"},{default:a(()=>[s(e(B),{weight:"duotone"})]),_:1}),d(" "+r(e(n)("boxes.files")),1)]),_:1},8,["onClick"]),s(e(f),{size:"tiny",type:"primary",onClick:()=>o.$router.push(`/terminal?box=${t.name}`)},{default:a(()=>[s(e(y),{size:"14"},{default:a(()=>[s(e(H),{weight:"duotone"})]),_:1}),d(" "+r(e(n)("boxes.terminal")),1)]),_:1},8,["onClick"]),s(e(f),{size:"tiny",onClick:()=>o.$router.push(`/multi-terminal?box=${t.name}`)},{default:a(()=>[d(r(e(n)("boxes.multi")),1)]),_:1},8,["onClick"])]),_:2},1024),s(e(C),{size:"small"},{default:a(()=>[s(e(f),{size:"tiny",secondary:"",onClick:()=>F(t.name)},{default:a(()=>[s(e(y),{size:"14"},{default:a(()=>[s(e(U),{weight:"duotone"})]),_:1}),d(" "+r(e(i).isPinned(t.name)?e(n)("boxes.unpin"):e(n)("boxes.pin")),1)]),_:2},1032,["onClick"]),s(e(f),{size:"tiny",secondary:"",onClick:()=>S(t.name,t.default_dir||"/")},{default:a(()=>[s(e(y),{size:"14"},{default:a(()=>[s(e(J),{weight:"duotone"})]),_:1}),d(" "+r(e(i).isFavorite(t.name,t.default_dir||"/")?e(n)("boxes.unfavorite"):e(n)("boxes.favorite")),1)]),_:2},1032,["onClick"]),s(e(f),{size:"tiny",quaternary:"",onClick:()=>b(t.name)},{default:a(()=>[d(r(e(n)("common.status")),1)]),_:1},8,["onClick"])]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),e(u).error?(h(),k(e(x),{key:0,type:"error",closable:""},{default:a(()=>[d(r(e(u).error),1)]),_:1})):e(i).error?(h(),k(e(x),{key:1,type:"error",closable:""},{default:a(()=>[d(r(e(i).error),1)]),_:1})):E("",!0)]))}}),ue=T(se,[["__scopeId","data-v-60b8424c"]]);export{ue as default};
@@ -0,0 +1 @@
1
+ .terminal-wrapper[data-v-24a9756b]{position:relative;width:100%;height:100%;min-width:0;min-height:0;display:flex;flex-direction:column;border-radius:14px;overflow:hidden;background:var(--surface);border:1px solid var(--stroke);box-shadow:0 2px 12px var(--shadow),0 1px 4px var(--shadow);transition:box-shadow .3s ease,border-color .3s ease}.terminal-wrapper[data-v-24a9756b]:focus-within{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-bg),0 2px 12px var(--shadow)}.terminal-wrapper.has-activity[data-v-24a9756b]{border-color:var(--warning);box-shadow:0 0 0 2px var(--warning-bg),0 2px 12px var(--shadow)}.terminal-titlebar[data-v-24a9756b]{display:flex;align-items:center;justify-content:space-between;height:36px;padding:0 12px;background:var(--surface-variant);border-bottom:1px solid var(--stroke);-webkit-user-select:none;user-select:none;flex-shrink:0}.titlebar-left[data-v-24a9756b],.titlebar-right[data-v-24a9756b]{display:flex;align-items:center;gap:6px;min-width:80px}.titlebar-right[data-v-24a9756b]{justify-content:flex-end}.titlebar-center[data-v-24a9756b]{display:flex;align-items:center;gap:8px;flex:1;justify-content:center}.titlebar-text[data-v-24a9756b]{font-size:12px;font-weight:500;color:var(--muted);letter-spacing:.02em}.toolbar-group[data-v-24a9756b]{display:flex;align-items:center;gap:1px;border-radius:8px;overflow:hidden;padding:2px}.toolbar-yellow[data-v-24a9756b]{background:#fac8501a}.toolbar-purple[data-v-24a9756b]{background:#b482fa1a}.toolbar-red[data-v-24a9756b]{background:#fa64641a}.toolbar-btn[data-v-24a9756b]{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;border-radius:6px;background:transparent;cursor:pointer;transition:all .12s ease;color:inherit}.toolbar-yellow .toolbar-btn[data-v-24a9756b]{color:#fac850b3}.toolbar-yellow .toolbar-btn[data-v-24a9756b]:hover{background:#fac85033;color:#fac850}.toolbar-purple .toolbar-btn[data-v-24a9756b]{color:#b482fab3}.toolbar-purple .toolbar-btn[data-v-24a9756b]:hover{background:#b482fa33;color:#b482fa}.toolbar-red .toolbar-btn[data-v-24a9756b]{color:#fa6464b3}.toolbar-red .toolbar-btn[data-v-24a9756b]:hover{background:#fa646433;color:#fa6464}.toolbar-btn-danger[data-v-24a9756b]{opacity:.85}.toolbar-btn-danger[data-v-24a9756b]:hover{opacity:1}.toolbar-btn[data-v-24a9756b]:disabled{opacity:.3;cursor:default;pointer-events:none}.connection-indicator[data-v-24a9756b]{width:6px;height:6px;border-radius:50%;background:#ff5f56;margin-left:8px;transition:all .3s ease}.connection-indicator.connected[data-v-24a9756b]{background:#27c93f;animation:breathe-24a9756b 2s ease-in-out infinite}.connection-indicator.connecting[data-v-24a9756b]{background:#ffbd2e;animation:pulse-24a9756b 1s ease-in-out infinite}.snapshot-indicator[data-v-24a9756b]{width:6px;height:6px;border-radius:50%;margin-left:4px;transition:background 1s ease,box-shadow 1s ease}.terminal-titlebar-collapsed[data-v-24a9756b]{display:flex;align-items:center;gap:8px;height:20px;padding:0 12px;background:var(--surface-variant);border-bottom:1px solid var(--stroke);cursor:pointer;-webkit-user-select:none;user-select:none;transition:background .15s ease}.terminal-titlebar-collapsed[data-v-24a9756b]:hover{background:var(--surface-hover)}.terminal-titlebar-collapsed .connection-indicator[data-v-24a9756b]{width:6px;height:6px;margin-left:0}.collapsed-text[data-v-24a9756b]{font-size:11px;color:var(--muted)}.expand-hint[data-v-24a9756b]{font-size:10px;color:var(--muted);opacity:.6;margin-left:auto}.terminal-wrapper.is-fullscreen[data-v-24a9756b],.terminal-wrapper.is-fullscreen .terminal-titlebar[data-v-24a9756b]{border-radius:0}@keyframes breathe-24a9756b{0%,to{box-shadow:0 0 #27c93f66;transform:scale(1)}50%{box-shadow:0 0 0 4px #27c93f00;transform:scale(1.05)}}@keyframes pulse-24a9756b{0%,to{opacity:1}50%{opacity:.5}}.titlebar-btn[data-v-24a9756b]{display:flex;align-items:center;justify-content:center;width:26px;height:26px;border:none;border-radius:6px;background:transparent;color:var(--muted);cursor:pointer;transition:all .15s ease}.titlebar-btn[data-v-24a9756b]:hover{background:var(--hover-overlay);color:var(--text)}.titlebar-btn[data-v-24a9756b]:disabled{opacity:.3;cursor:default;pointer-events:none}.help-btn[data-v-24a9756b]{opacity:.5;margin-left:2px;border-left:1px solid var(--hover-overlay);padding-left:4px;border-radius:0 6px 6px 0}.help-btn[data-v-24a9756b]:hover{opacity:1}.mouse-btn[data-v-24a9756b]{position:relative}.mouse-status-dot[data-v-24a9756b]{position:absolute;bottom:3px;right:3px;width:5px;height:5px;border-radius:50%;transition:background .2s ease}.mouse-btn.mouse-on .mouse-status-dot[data-v-24a9756b]{background:#27c93f;box-shadow:0 0 4px #27c93f80}.mouse-btn.mouse-off .mouse-status-dot[data-v-24a9756b]{background:#ff5f56;box-shadow:0 0 4px #ff5f5680}.mouse-btn.mouse-on[data-v-24a9756b]{color:#27c93fb3}.mouse-btn.mouse-off[data-v-24a9756b]{color:#ff5f56b3}.mouse-btn.mouse-on[data-v-24a9756b]:hover{background:#27c93f26;color:#27c93f}.mouse-btn.mouse-off[data-v-24a9756b]:hover{background:#ff5f5626;color:#ff5f56}.terminal-container[data-v-24a9756b]{position:relative;flex:1;min-width:0;min-height:0;padding:8px 12px 12px;overflow:hidden}.terminal-container.no-titlebar[data-v-24a9756b]{border-radius:14px 14px 0 0}.scanlines[data-v-24a9756b]{position:absolute;inset:0;pointer-events:none;z-index:10;background:repeating-linear-gradient(0deg,rgba(0,0,0,.02) 0px,rgba(0,0,0,.02) 1px,transparent 1px,transparent 2px);mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 60%,transparent 100%);-webkit-mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 60%,transparent 100%)}.terminal[data-v-24a9756b]{width:100%;height:100%;min-width:0;min-height:0}.terminal[data-v-24a9756b] .xterm{width:100%!important;height:100%!important}.terminal[data-v-24a9756b] .xterm-viewport,.terminal[data-v-24a9756b] .xterm-screen{width:100%!important}.terminal[data-v-24a9756b] .xterm-rows{text-shadow:0 0 1px currentColor}.terminal[data-v-24a9756b] .xterm-selection,.terminal[data-v-24a9756b] .xterm-selection div{background:#53bdfa59!important}.terminal[data-v-24a9756b] .xterm-viewport::-webkit-scrollbar{width:8px}.terminal[data-v-24a9756b] .xterm-viewport::-webkit-scrollbar-track{background:transparent;border-radius:4px}.terminal[data-v-24a9756b] .xterm-viewport::-webkit-scrollbar-thumb{background:linear-gradient(180deg,#ffffff1f,#ffffff14);border-radius:4px;border:2px solid transparent;background-clip:padding-box}.terminal[data-v-24a9756b] .xterm-viewport::-webkit-scrollbar-thumb:hover{background:linear-gradient(180deg,#fff3,#ffffff26)}.terminal[data-v-24a9756b] .xterm-viewport{scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.1) transparent}.activity-line[data-v-24a9756b]{position:absolute;bottom:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,rgba(230,180,80,.3),transparent);opacity:0;transition:opacity .15s ease}.activity-line.active[data-v-24a9756b]{opacity:1;animation:line-glow-24a9756b .3s ease}@keyframes line-glow-24a9756b{0%{background:linear-gradient(90deg,transparent,rgba(230,180,80,.8),transparent)}to{background:linear-gradient(90deg,transparent,rgba(230,180,80,.3),transparent)}}.terminal-overlay[data-v-24a9756b]{position:absolute;inset:0;background:var(--panel-bg-translucent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;color:var(--muted);font-family:"Monaspace Neon",var(--font-mono);z-index:20}.connecting-indicator[data-v-24a9756b],.disconnected-indicator[data-v-24a9756b]{display:flex;flex-direction:column;align-items:center;gap:12px}.connecting-indicator small[data-v-24a9756b]{display:block;font-size:12px;opacity:.7;margin-top:4px}.spinner[data-v-24a9756b]{width:24px;height:24px;border:2px solid rgba(230,180,80,.2);border-top:2px solid #e6b450;border-radius:50%;animation:spin-24a9756b 1s linear infinite}.reconnect-btn[data-v-24a9756b]{padding:8px 16px;background:#53bdfa26;color:#53bdfa;border:1px solid rgba(83,189,250,.3);border-radius:6px;cursor:pointer;font-size:14px;font-family:inherit;transition:all .2s ease}.reconnect-btn[data-v-24a9756b]:hover{background:#53bdfa40;border-color:#53bdfa80;box-shadow:0 0 20px #53bdfa26}@keyframes spin-24a9756b{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media(max-width:768px){.terminal-wrapper[data-v-24a9756b]{border-radius:0;border:none;box-shadow:none;background:var(--panel-bg)}.terminal-wrapper[data-v-24a9756b]:focus-within{border-color:transparent;box-shadow:none}.terminal-titlebar[data-v-24a9756b]{height:28px;padding:0 6px}.titlebar-text[data-v-24a9756b]{font-size:10px}.toolbar-group[data-v-24a9756b]{display:none}.titlebar-btn[data-v-24a9756b]{width:28px;height:28px}.titlebar-right .titlebar-btn[data-v-24a9756b]:nth-child(2),.titlebar-right .titlebar-btn[data-v-24a9756b]:nth-child(3){display:none}.terminal-container[data-v-24a9756b]{padding:2px 2px 0}.terminal[data-v-24a9756b] .xterm-rows{text-shadow:none}.scanlines[data-v-24a9756b]{display:none}.terminal[data-v-24a9756b] .xterm-viewport{touch-action:pan-y pinch-zoom}.terminal[data-v-24a9756b] .xterm-screen{touch-action:pan-y}.terminal[data-v-24a9756b] .xterm-viewport::-webkit-scrollbar{width:0}.terminal[data-v-24a9756b] .xterm-viewport{scrollbar-width:none}}@media(prefers-contrast:high){.terminal-wrapper[data-v-24a9756b]{border:2px solid var(--stroke)}.scanlines[data-v-24a9756b]{display:none}}@media(prefers-reduced-motion:reduce){.spinner[data-v-24a9756b],.connection-indicator.connected[data-v-24a9756b],.connection-indicator.connecting[data-v-24a9756b],.activity-line.active[data-v-24a9756b]{animation:none}.terminal-wrapper[data-v-24a9756b],.terminal-wrapper[data-v-24a9756b]:focus-within,.terminal-wrapper.has-activity[data-v-24a9756b]{transition:none}}.terminal-search-bar[data-v-24a9756b]{position:absolute;top:0;right:8px;display:flex;align-items:center;gap:4px;padding:4px 8px;background:var(--surface, #1e1e2e);border:1px solid var(--stroke, rgba(255, 255, 255, .1));border-top:none;border-radius:0 0 6px 6px;z-index:20;box-shadow:0 2px 8px #0000004d}.search-input[data-v-24a9756b]{flex:1;padding:4px 8px;font-size:13px;font-family:var(--font-mono);background:var(--surface-variant, rgba(255, 255, 255, .05));border:1px solid var(--stroke, rgba(255, 255, 255, .15));border-radius:4px;color:var(--text, #cdd6f4);outline:none}.search-input[data-v-24a9756b]:focus{border-color:var(--accent, #7c5dff)}.search-input.no-match[data-v-24a9756b]{border-color:#e88080;background:#e880801a}.search-no-match[data-v-24a9756b]{font-size:11px;color:#e88080;white-space:nowrap}.search-btn[data-v-24a9756b]{display:flex;align-items:center;justify-content:center;width:28px;height:28px;background:none;border:none;border-radius:4px;color:var(--muted, #888);cursor:pointer}.search-btn[data-v-24a9756b]:hover{background:var(--hover-overlay, rgba(255, 255, 255, .08));color:var(--text, #cdd6f4)}.titlebar-btn.active[data-v-24a9756b]{color:var(--accent, #7c5dff);background:var(--accent-bg, rgba(124, 93, 255, .15))}[data-theme=light] .scanlines{display:none}[data-theme=light] .terminal .xterm-rows{text-shadow:none}[data-theme=light] .terminal-wrapper{box-shadow:0 2px 12px #0000001a,0 1px 4px #0000000f}[data-theme=light] .terminal .xterm-viewport{scrollbar-color:rgba(0,0,0,.15) transparent}[data-theme=light] .terminal .xterm-viewport::-webkit-scrollbar-thumb{background:linear-gradient(180deg,#0000001f,#00000014)}[data-theme=light] .terminal .xterm-viewport::-webkit-scrollbar-thumb:hover{background:linear-gradient(180deg,#0003,#00000026)}.directory-picker[data-v-8a2864f1]{display:flex;flex-direction:column;gap:16px}.nav-bar[data-v-8a2864f1]{display:flex;align-items:center;gap:8px;padding:8px;background:var(--surface);border-radius:6px}.current-path[data-v-8a2864f1]{flex:1;font-family:var(--font-mono);font-size:13px;padding:4px 8px;background:var(--bg);border-radius:4px;cursor:text;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.favorites-section[data-v-8a2864f1]{padding:8px;background:#faad141a;border-radius:6px;border:1px solid rgba(250,173,20,.2)}.section-label[data-v-8a2864f1]{font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin-bottom:8px}.favorites-list[data-v-8a2864f1]{display:flex;flex-direction:column;gap:4px}.favorite-item[data-v-8a2864f1]{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;cursor:pointer;transition:background .15s ease}.favorite-item[data-v-8a2864f1]:hover{background:#faad1426}.fav-path[data-v-8a2864f1]{font-family:var(--font-mono);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.directory-list[data-v-8a2864f1]{max-height:300px;overflow-y:auto;border:1px solid var(--stroke);border-radius:6px}.loading-state[data-v-8a2864f1]{display:flex;justify-content:center;padding:32px}.directory-item[data-v-8a2864f1]{display:flex;align-items:center;gap:8px;padding:10px 12px;cursor:pointer;transition:background .15s ease;border-bottom:1px solid var(--stroke)}.directory-item[data-v-8a2864f1]:last-child{border-bottom:none}.directory-item[data-v-8a2864f1]:hover{background:var(--surface)}.folder-icon[data-v-8a2864f1]{color:var(--accent);flex-shrink:0}.dir-name[data-v-8a2864f1]{flex:1;font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.star-btn[data-v-8a2864f1]{opacity:0;transition:opacity .15s ease}.directory-item:hover .star-btn[data-v-8a2864f1]{opacity:1}@media(hover:none)and (pointer:coarse){.star-btn[data-v-8a2864f1]{opacity:1}.directory-item[data-v-8a2864f1]{padding:14px 12px;min-height:48px}.favorite-item[data-v-8a2864f1]{padding:10px 8px;min-height:48px}}.footer-actions[data-v-8a2864f1]{display:flex;justify-content:flex-end;gap:8px;padding-top:8px;border-top:1px solid var(--stroke)}