diff --git a/VERSION b/VERSION
index 9a52cbd..c8aa910 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.0.69
+0.0.70
diff --git a/install_local.sh b/install_local.sh
new file mode 100755
index 0000000..7e9ac40
--- /dev/null
+++ b/install_local.sh
@@ -0,0 +1,111 @@
+#!/bin/bash
+# install_local.sh — Build sovereign_browser from local source and install to
+# /usr/local/bin (or $INSTALL_PREFIX/bin).
+#
+# This is a lightweight alternative to install.sh for developers who already
+# have the build environment set up. It does NOT install apt dependencies,
+# FIPS, or Tor — it just runs `make` and copies the binary.
+#
+# Usage:
+# ./install_local.sh # build + install to /usr/local
+# INSTALL_PREFIX=/opt ./install_local.sh # install elsewhere
+# ./install_local.sh --prefix /opt # same
+# ./install_local.sh --help # show help
+
+set -euo pipefail
+
+INSTALL_PREFIX="${INSTALL_PREFIX:-/usr/local}"
+
+# --- Output helpers ------------------------------------------------------
+
+if [[ -t 2 && -z "${NO_COLOR:-}" ]]; then
+ RED='\033[0;31m'
+ GREEN='\033[0;32m'
+ YELLOW='\033[1;33m'
+ BLUE='\033[0;34m'
+ NC='\033[0m'
+else
+ RED='' GREEN='' YELLOW='' BLUE='' NC=''
+fi
+
+print_info() { echo -e "${BLUE}[INFO]${NC} $*" >&2; }
+print_success() { echo -e "${GREEN}[SUCCESS]${NC} $*" >&2; }
+print_warning() { echo -e "${YELLOW}[WARNING]${NC} $*" >&2; }
+print_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
+die() { print_error "$*"; exit 1; }
+
+# --- Privilege helper ----------------------------------------------------
+# Returns "sudo" if the target path is not user-writable, else "".
+sudo_for() {
+ local target="$1"
+ if [[ -w "$target" ]]; then
+ echo ""
+ else
+ command -v sudo >/dev/null 2>&1 || die "sudo is required to write to $target but is not available."
+ echo "sudo"
+ fi
+}
+
+# --- Usage ---------------------------------------------------------------
+
+show_usage() {
+ cat <<'EOF'
+install_local.sh — Build sovereign_browser from local source and install
+
+Usage: ./install_local.sh [options]
+
+Options:
+ --prefix
Install prefix (default: /usr/local)
+ -h, --help Show this help
+
+Environment:
+ INSTALL_PREFIX Alternative to --prefix (default: /usr/local)
+EOF
+}
+
+# --- Args ----------------------------------------------------------------
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --prefix) INSTALL_PREFIX="${2:-}"; shift 2 ;;
+ -h|--help) show_usage; exit 0 ;;
+ *) die "Unknown option: $1 (try --help)" ;;
+ esac
+done
+
+# --- Build ---------------------------------------------------------------
+
+print_info "Building sovereign_browser from local source..."
+if ! make; then
+ die "Build failed. Ensure build dependencies are installed (see Makefile)."
+fi
+
+[[ -x ./sovereign_browser ]] || die "Build finished but ./sovereign_browser was not produced."
+
+print_success "Build complete."
+
+# --- Install binary ------------------------------------------------------
+
+prefix_bin="$INSTALL_PREFIX/bin"
+SUDO="$(sudo_for "$INSTALL_PREFIX")"
+
+print_info "Installing binary to $prefix_bin/sovereign_browser"
+$SUDO mkdir -p "$prefix_bin"
+$SUDO cp -f ./sovereign_browser "$prefix_bin/sovereign_browser"
+$SUDO chmod +x "$prefix_bin/sovereign_browser"
+print_success "Binary installed: $prefix_bin/sovereign_browser"
+
+# --- Done ----------------------------------------------------------------
+
+print_success "sovereign_browser installed to $prefix_bin"
+if [[ ":${PATH}:" != *":$prefix_bin:"* ]]; then
+ print_warning "$prefix_bin is not in your PATH. Add it: export PATH=\"$prefix_bin:\$PATH\""
+fi
+
+cat >&2 <inspector_w > 0 && bs->inspector_h > 0) {
- gtk_window_resize(GTK_WINDOW(toplevel),
- bs->inspector_w, bs->inspector_h);
- }
- if (bs->inspector_x >= 0 && bs->inspector_y >= 0) {
- gtk_window_move(GTK_WINDOW(toplevel),
- bs->inspector_x, bs->inspector_y);
- }
- /* Track future changes. */
- g_signal_connect(toplevel, "configure-event",
- G_CALLBACK(on_inspector_window_configure), NULL);
- }
-}
-static void on_inspector_detach(WebKitWebInspector *inspector,
- gpointer user_data) {
- (void)user_data;
WebKitWebViewBase *insp_view = webkit_web_inspector_get_web_view(inspector);
- if (insp_view) {
- GtkWidget *w = GTK_WIDGET(insp_view);
- /* Hook realize to catch the detached window. */
- g_signal_connect(w, "realize",
- G_CALLBACK(on_inspector_webview_realize), NULL);
+ if (!insp_view) {
+ return FALSE; /* let WebKit handle it */
}
-}
-/* Track whether the inspector is currently shown for the active tab.
- * We use a per-tab flag stored in a static hash (inspector state is
- * per-webview, but we track it globally for the toggle). */
-static gboolean g_inspector_visible = FALSE;
+ /* Create a new top-level window for the inspector. */
+ GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(window), "Web Inspector");
+
+ /* Restore saved geometry if we have it. */
+ const browser_settings_t *bs = settings_get();
+ if (bs->inspector_w > 0 && bs->inspector_h > 0) {
+ gtk_window_set_default_size(GTK_WINDOW(window),
+ bs->inspector_w, bs->inspector_h);
+ } else {
+ gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
+ }
+
+ /* Add the inspector's WebView to our window. */
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(insp_view));
+
+ /* Position the window if we have saved coordinates. */
+ if (bs->inspector_x >= 0 && bs->inspector_y >= 0) {
+ gtk_window_move(GTK_WINDOW(window), bs->inspector_x, bs->inspector_y);
+ }
+
+ /* Connect signals:
+ * - delete-event: route the X button through the inspector API
+ * - configure-event: persist window position/size
+ * - destroy: reset visibility flag as a fallback */
+ g_signal_connect(window, "delete-event",
+ G_CALLBACK(on_inspector_window_delete), inspector);
+ g_signal_connect(window, "configure-event",
+ G_CALLBACK(on_inspector_window_configure), NULL);
+
+ /* Show the window and all its children. */
+ gtk_widget_show_all(window);
+
+ /* Raise the window to the top. */
+ gtk_window_present(GTK_WINDOW(window));
+
+ return TRUE; /* prevent WebKit from creating its own window */
+}
void tab_manager_toggle_inspector(void) {
tab_info_t *tab = tab_manager_get_active();
@@ -2104,8 +2149,10 @@ void tab_manager_toggle_inspector(void) {
* and persists for the webview's lifetime). We check a g_object data
* flag to avoid connecting multiple times. */
if (g_object_get_data(G_OBJECT(insp), "sovereign-inspector-hooked") == NULL) {
- g_signal_connect(insp, "detach",
- G_CALLBACK(on_inspector_detach), NULL);
+ g_signal_connect(insp, "open-window",
+ G_CALLBACK(on_inspector_open_window), NULL);
+ g_signal_connect(insp, "closed",
+ G_CALLBACK(on_inspector_closed), NULL);
g_object_set_data(G_OBJECT(insp), "sovereign-inspector-hooked",
GINT_TO_POINTER(1));
}
@@ -2114,6 +2161,11 @@ void tab_manager_toggle_inspector(void) {
webkit_web_inspector_close(insp);
g_inspector_visible = FALSE;
} else {
+ /* Force detach so the inspector opens in a separate window
+ * (triggers ::detach then ::open-window). */
+ if (webkit_web_inspector_get_can_attach(insp)) {
+ webkit_web_inspector_detach(insp);
+ }
webkit_web_inspector_show(insp);
g_inspector_visible = TRUE;
}
diff --git a/src/version.h b/src/version.h
index 1cf6c0c..8338e0b 100644
--- a/src/version.h
+++ b/src/version.h
@@ -11,9 +11,9 @@
#ifndef SOVEREIGN_BROWSER_VERSION_H
#define SOVEREIGN_BROWSER_VERSION_H
-#define SB_VERSION "v0.0.69"
+#define SB_VERSION "v0.0.70"
#define SB_VERSION_MAJOR 0
#define SB_VERSION_MINOR 0
-#define SB_VERSION_PATCH 69
+#define SB_VERSION_PATCH 70
#endif /* SOVEREIGN_BROWSER_VERSION_H */