simvyn 2.6.0 → 2.6.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.
package/README.md CHANGED
@@ -50,9 +50,9 @@ npx simvyn
50
50
 
51
51
  ## Features
52
52
 
53
- - **Device Management** — Discover, boot, shutdown, erase, create, and clone simulators, emulators, and physical devices
53
+ - **Device Management** — Discover, boot, shutdown, erase, create, and clone simulators, emulators, and physical devices with favourite pinning
54
54
  - **App Management** — Install, launch, terminate, uninstall apps via drag-and-drop or CLI
55
- - **Log Viewer** — Real-time streaming with level filtering, regex search, pagination, and export
55
+ - **Log Viewer** — Real-time streaming with level filtering, find-in-page search (Cmd+F), regex search, pagination, and export
56
56
  - **Location Simulation** — Set GPS coordinates, play GPX/KML routes with speed control, save favorites
57
57
  - **Device Settings** — Dark mode, locale, permissions, status bar overrides, accessibility presets
58
58
  - **Screenshots and Recording** — Capture screenshots and record screen video with history
@@ -81,6 +81,7 @@ Discover all connected iOS Simulators, Android Emulators, and USB-connected phys
81
81
  - Boot, shutdown, and erase devices with one click or CLI command
82
82
  - Create new iOS simulators with device type and runtime selection
83
83
  - Clone and rename existing simulators
84
+ - Pin favourite devices to the top of the device selector
84
85
  - Real-time device state updates via WebSocket
85
86
 
86
87
  ### App Management
@@ -97,15 +98,17 @@ Install, launch, terminate, and uninstall apps on any connected device. Drag and
97
98
 
98
99
  ### Log Viewer
99
100
 
100
- Stream device logs in real time with powerful filtering. Filter by log level, search with regex patterns, and filter by process name. Export filtered logs for sharing or archival. Paginated history lets you scroll back through thousands of entries without performance issues.
101
+ Stream device logs in real time with powerful filtering. Filter by log level, search with regex patterns, and filter by process name. Use find-in-page search (Cmd+F) to highlight specific entries without hiding surrounding context. Export filtered logs for sharing or archival. Paginated history lets you scroll back through thousands of entries without performance issues.
101
102
 
102
103
  <p align="center"><img src="https://raw.githubusercontent.com/pranshuchittora/simvyn/main/docs/assets/log-viewer.png" alt="Log Viewer" width="700" /></p>
103
104
 
104
105
  - Real-time log streaming via WebSocket
105
106
  - Filter by level: debug, info, warning, error, fatal
107
+ - Find-in-page search (Cmd+F) with match highlighting, navigation, and counter
106
108
  - Regex search and process name filtering
107
109
  - Paginated history with virtual scrolling
108
110
  - Export filtered logs to file
111
+ - Pause/resume log streaming while preserving state across clears
109
112
 
110
113
  ### Location
111
114
 
@@ -44020,7 +44020,7 @@ function AppActions({ app, deviceId, onRefresh }) {
44020
44020
  children: loading === "uninstall" ? "..." : "Uninstall"
44021
44021
  }
44022
44022
  ),
44023
- app.type === "user" && /* @__PURE__ */ jsxRuntimeExports.jsx(
44023
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
44024
44024
  "button",
44025
44025
  {
44026
44026
  type: "button",
@@ -44063,7 +44063,15 @@ function AppList({ deviceId, onRefresh }) {
44063
44063
  const error = useAppStore((s) => s.error);
44064
44064
  const filter2 = useAppStore((s) => s.filter);
44065
44065
  const setFilter = useAppStore((s) => s.setFilter);
44066
- const filtered = filter2 === "all" ? apps : apps.filter((a) => a.type === filter2);
44066
+ const [search, setSearch] = reactExports.useState("");
44067
+ const searchLower = search.toLowerCase();
44068
+ const filtered = apps.filter((a) => {
44069
+ if (filter2 !== "all" && a.type !== filter2) return false;
44070
+ if (searchLower) {
44071
+ return a.name.toLowerCase().includes(searchLower) || a.bundleId.toLowerCase().includes(searchLower) || a.version.toLowerCase().includes(searchLower);
44072
+ }
44073
+ return true;
44074
+ });
44067
44075
  const filters = [
44068
44076
  { label: "All", value: "all" },
44069
44077
  { label: "User", value: "user" },
@@ -44081,7 +44089,17 @@ function AppList({ deviceId, onRefresh }) {
44081
44089
  },
44082
44090
  f.value
44083
44091
  )),
44084
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-text-muted ml-auto", children: [
44092
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
44093
+ "input",
44094
+ {
44095
+ type: "text",
44096
+ value: search,
44097
+ onChange: (e) => setSearch(e.target.value),
44098
+ placeholder: "Search apps...",
44099
+ className: "glass-input text-xs ml-auto w-48"
44100
+ }
44101
+ ),
44102
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-text-muted", children: [
44085
44103
  filtered.length,
44086
44104
  " app",
44087
44105
  filtered.length !== 1 ? "s" : ""
@@ -44092,8 +44110,8 @@ function AppList({ deviceId, onRefresh }) {
44092
44110
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-block h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent mr-2" }),
44093
44111
  "Loading apps..."
44094
44112
  ] }),
44095
- !loading && !error && filtered.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "glass-empty-state", children: "No apps found" }),
44096
- !loading && filtered.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "glass-panel overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "glass-table", children: [
44113
+ !loading && !error && filtered.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "glass-empty-state", children: search ? "No apps match your search" : "No apps found" }),
44114
+ !loading && filtered.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "glass-panel overflow-x-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "glass-table", children: [
44097
44115
  /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
44098
44116
  /* @__PURE__ */ jsxRuntimeExports.jsx("th", { children: "Name" }),
44099
44117
  /* @__PURE__ */ jsxRuntimeExports.jsx("th", { children: "Bundle ID" }),
@@ -44102,9 +44120,16 @@ function AppList({ deviceId, onRefresh }) {
44102
44120
  /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "text-right", children: "Actions" })
44103
44121
  ] }) }),
44104
44122
  /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { children: filtered.map((app) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
44105
- /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "text-text-primary truncate max-w-[180px]", children: app.name }),
44106
- /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "text-text-secondary truncate max-w-[220px] font-mono text-xs", children: app.bundleId }),
44107
- /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "text-text-secondary", children: app.version }),
44123
+ /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "text-text-primary truncate max-w-[180px]", title: app.name, children: app.name }),
44124
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
44125
+ "td",
44126
+ {
44127
+ className: "text-text-secondary truncate max-w-[220px] font-mono text-xs",
44128
+ title: app.bundleId,
44129
+ children: app.bundleId
44130
+ }
44131
+ ),
44132
+ /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "text-text-secondary", title: app.version, children: app.version }),
44108
44133
  /* @__PURE__ */ jsxRuntimeExports.jsx("td", { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
44109
44134
  "span",
44110
44135
  {
@@ -52355,7 +52380,7 @@ function ToolSettingsPanel() {
52355
52380
  const [copied, setCopied] = reactExports.useState(false);
52356
52381
  const devices = useDeviceStore((s) => s.devices);
52357
52382
  const modules = useModuleStore((s) => s.modules);
52358
- const version = "2.6.0";
52383
+ const version = "2.6.2";
52359
52384
  const fetchStorage = reactExports.useCallback(() => {
52360
52385
  fetch("/api/tool-settings/storage").then((r) => r.json()).then((data) => setStorage(data)).catch(() => {
52361
52386
  });
@@ -53664,7 +53689,7 @@ function AppContent() {
53664
53689
  /* @__PURE__ */ jsxRuntimeExports.jsx(TopBar, {}),
53665
53690
  /* @__PURE__ */ jsxRuntimeExports.jsx(UpdateBanner, {}),
53666
53691
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
53667
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Sidebar, {}) }),
53692
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative flex-shrink-0 w-[76px]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Sidebar, {}) }),
53668
53693
  /* @__PURE__ */ jsxRuntimeExports.jsx(ModuleShell, {})
53669
53694
  ] })
53670
53695
  ] })
@@ -53704,4 +53729,4 @@ function App() {
53704
53729
  clientExports.createRoot(document.getElementById("root")).render(
53705
53730
  /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(App, {}) })
53706
53731
  );
53707
- //# sourceMappingURL=index-y3nYH57L.js.map
53732
+ //# sourceMappingURL=index-CV-ugcyG.js.map