Advanced Configuration
This guide covers advanced configuration patterns including file organization, modular config systems, and complex automation.
Config File Organization
Include System
Split your config into multiple files using glide.include():
// glide.ts (main config)
glide.include("keymaps.glide.ts");
glide.include("options.glide.ts");
glide.include("autocmds.glide.ts");
// keymaps.glide.ts
glide.keymaps.set("normal", "<leader>r", "config_reload");
glide.keymaps.set("normal", "<leader>e", "config_edit");
// options.glide.ts
glide.g.mapleader = "<Space>";
glide.o.which_key_delay = 500;
glide.o.hint_chars = "asdfghjkl";
Path Resolution
Relative paths are resolved relative to the current file:
// glide.ts
glide.include("plugins/github/init.ts");
// plugins/github/init.ts
glide.include("keymaps.ts"); // Resolves to plugins/github/keymaps.ts
Absolute paths:
glide.include(glide.path.join(
glide.path.profile_dir,
"glide",
"shared.glide.ts"
));
File System API
Read and write files relative to your config:
// Read config data
const data = await glide.fs.read("data.json", "utf8");
const config = JSON.parse(data);
// Write config data
await glide.fs.write("cache.json", JSON.stringify({ ... }));
// Check file existence
if (await glide.fs.exists("custom.css")) {
const css = await glide.fs.read("custom.css", "utf8");
glide.styles.add(css);
}
const stat = await glide.fs.stat("userChrome.css");
console.log(stat.last_modified); // 1758835015092
console.log(stat.type); // "file"
Directory Operations
// Create directory
await glide.fs.mkdir("plugins/custom");
// Create without parents
await glide.fs.mkdir("plugins/custom", { parents: false });
// Error if exists
await glide.fs.mkdir("plugins", { exists_ok: false });
Modular Plugin System
Plugin Structure
Organize features as plugins:
glide/
├── glide.ts
└── plugins/
├── github/
│ ├── init.ts
│ ├── keymaps.ts
│ └── autocmds.ts
├── reddit/
│ ├── init.ts
│ └── styles.css
└── tabs/
└── init.ts
Plugin Loader
// glide.ts
const PLUGINS = [
"github",
"reddit",
"tabs"
];
for (const plugin of PLUGINS) {
try {
glide.include(`plugins/${plugin}/init.ts`);
} catch (err) {
console.error(`Failed to load plugin ${plugin}:`, err);
}
}
Plugin Example
// plugins/github/init.ts
glide.autocmds.create("UrlEnter", { hostname: "github.com" }, () => {
// Load GitHub-specific keymaps
glide.include("keymaps.ts");
// Load GitHub-specific autocmds
glide.include("autocmds.ts");
});
// plugins/github/keymaps.ts
glide.buf.keymaps.set("normal", "<leader>gi", async () => {
const url = glide.ctx.url;
const parts = url.pathname.split("/").filter(Boolean);
url.pathname = `/${parts[0]}/${parts[1]}/issues`;
await browser.tabs.update({ url: url.toString() });
}, { description: "Go to issues" });
glide.buf.keymaps.set("normal", "<leader>gp", async () => {
const url = glide.ctx.url;
const parts = url.pathname.split("/").filter(Boolean);
url.pathname = `/${parts[0]}/${parts[1]}/pulls`;
await browser.tabs.update({ url: url.toString() });
}, { description: "Go to pull requests" });
Loading External Styles
// plugins/reddit/init.ts
glide.autocmds.create("UrlEnter", { hostname: "reddit.com" }, async () => {
try {
const css = await glide.fs.read("styles.css", "utf8");
glide.styles.add(css, { id: 'reddit-custom' });
return () => {
glide.styles.remove('reddit-custom');
};
} catch (err) {
console.warn("Could not load reddit styles:", err);
}
});
Environment Variables
Manage environment variables from your config:
// Get environment variable
const editor = glide.env.get("EDITOR");
if (!editor) {
console.warn("EDITOR not set");
}
// Set environment variable
glide.env.set("PATH", "/usr/local/bin:/usr/bin:/bin");
// Delete environment variable
glide.env.delete("DEBUG");
macOS PATH Configuration
On macOS, applications don’t inherit your shell environment. Set the PATH manually:
if (glide.ctx.os === "macosx") {
glide.env.set("PATH", "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin");
}
Process Spawning
Basic Process Spawning
glide.keymaps.set("normal", "<leader>t", async () => {
await glide.process.spawn("kitty", ["nvim", "glide.ts"], {
cwd: glide.path.join(glide.path.home_dir, ".config/glide")
});
});
Execute and Wait
glide.keymaps.set("normal", "<leader>c", async () => {
const result = await glide.process.execute("git", ["status"], {
cwd: glide.path.home_dir
});
console.log(`Exit code: ${result.exit_code}`);
console.log(`Output: ${result.stdout}`);
});
Process Options
interface SpawnOptions {
/** Working directory */
cwd?: string;
/** Environment variables */
env?: Record<string, string>;
/** Check exit code (throw on non-zero) */
check_exit_code?: boolean; // default: true
}
Example:
try {
await glide.process.execute("npm", ["test"], {
cwd: "/path/to/project",
check_exit_code: true
});
} catch (err) {
console.error("Tests failed:", err);
}
Path Utilities
console.log(glide.path.cwd); // Current working directory
console.log(glide.path.home_dir); // Home directory
console.log(glide.path.temp_dir); // Temporary directory
console.log(glide.path.profile_dir); // Firefox profile directory
Path Joining
const config_path = glide.path.join(
glide.path.home_dir,
".config",
"glide",
"glide.ts"
);
glide.path.join() throws an error on absolute paths. Use relative paths only.
Type Extensions
Custom Options
Define your own typed options:
declare global {
interface GlideOptions {
// Simple types
auto_reload?: boolean;
theme_name?: string;
tab_limit?: number;
// Complex types
custom_engines?: Array<{
name: string;
url: string;
}>;
// Union types
panel_position?: "left" | "right" | "bottom";
}
}
glide.o.auto_reload = true;
glide.o.theme_name = "nord";
glide.o.custom_engines = [
{ name: "DuckDuckGo", url: "https://duckduckgo.com/?q={searchTerms}" }
];
Custom Globals
declare global {
interface GlideGlobals {
// State
session_start?: number;
page_visits?: Map<string, number>;
// Configuration
plugin_config?: Record<string, any>;
// Utilities
utils?: {
format_date(ts: number): string;
};
}
}
glide.g.session_start = Date.now();
glide.g.page_visits = new Map();
glide.g.utils = {
format_date(ts: number) {
return new Date(ts).toISOString();
}
};
Custom Modes
Register custom modes:
declare global {
interface GlideModes {
leap: "leap";
peek: "peek";
}
}
glide.modes.register("leap", { caret: "block" });
glide.modes.register("peek", { caret: "underline" });
// Now you can use them
glide.keymaps.set("leap", "<Esc>", "mode_change normal");
Advanced Autocmd Patterns
Multi-Pattern Matching
const DEV_HOSTS = ["localhost", "127.0.0.1", "0.0.0.0"];
glide.autocmds.create("UrlEnter", /./, ({ url }) => {
const hostname = new URL(url).hostname;
if (DEV_HOSTS.includes(hostname)) {
glide.buf.keymaps.set("normal", "<leader>r", async () => {
const tab = await glide.tabs.active();
await browser.tabs.reload(tab.id);
});
}
});
Autocmd Chains
glide.autocmds.create("UrlEnter", { hostname: "github.com" }, () => {
console.log("Entered GitHub");
});
glide.autocmds.create("UrlEnter", { hostname: "github.com" }, () => {
console.log("Still GitHub");
});
glide.autocmds.create("UrlEnter", { hostname: "github.com" }, () => {
console.log("More GitHub setup");
});
// All three fire in order
State Cleanup
declare global {
interface GlideGlobals {
github_state?: { repo: string };
}
}
glide.autocmds.create("UrlEnter", { hostname: "github.com" }, () => {
const parts = glide.ctx.url.pathname.split("/").filter(Boolean);
if (parts.length >= 2) {
glide.g.github_state = { repo: `${parts[0]}/${parts[1]}` };
}
return () => {
delete glide.g.github_state;
};
});
Messengers (IPC)
Communicate between main and content processes:
type Messages = {
focus_detected: { element_id: string };
scroll_position: { x: number; y: number };
};
const messenger = glide.messengers.create<Messages>((message) => {
switch (message.name) {
case "focus_detected":
console.log(`Element focused: ${message.data.element_id}`);
break;
case "scroll_position":
console.log(`Scroll: x=${message.data.x}, y=${message.data.y}`);
break;
}
});
glide.keymaps.set("normal", "<leader>m", async ({ tab_id }) => {
await messenger.content.execute((messenger) => {
document.addEventListener('focusin', (event) => {
if (event.target.id) {
messenger.send('focus_detected', { element_id: event.target.id });
}
});
window.addEventListener('scroll', () => {
messenger.send('scroll_position', {
x: window.scrollX,
y: window.scrollY
});
});
}, { tab_id });
});
Search Engine Management
Add custom search engines:
glide.search_engines.add({
name: "Glide Docs",
keyword: "glide",
search_url: "https://glide-browser.app/search?q={searchTerms}",
favicon_url: "https://glide-browser.app/favicon.ico"
});
glide.search_engines.add({
name: "Hacker News",
keyword: "hn",
search_url: "https://hn.algolia.com/?q={searchTerms}"
});
Search engines are not automatically removed when deleted from config. Remove them manually via about:preferences#search.
Lazy Loading
let github_loaded = false;
glide.autocmds.create("UrlEnter", { hostname: "github.com" }, () => {
if (!github_loaded) {
glide.include("plugins/github/init.ts");
github_loaded = true;
}
});
Conditional Features
const ENABLE_ADVANCED_FEATURES = true;
if (ENABLE_ADVANCED_FEATURES) {
glide.include("advanced/init.ts");
}
Debouncing
let timeout: number | null = null;
glide.autocmds.create("KeyStateChanged", ({ sequence }) => {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
console.log("User stopped typing:", sequence);
}, 500);
});
Configuration Reload Handling
glide.autocmds.create("ConfigLoaded", () => {
const is_reload = glide.g.session_start !== undefined;
if (is_reload) {
console.log("Config reloaded");
} else {
console.log("Initial load");
glide.g.session_start = Date.now();
}
});
See Also