removed hardcoded variables and created setup script

This commit is contained in:
Nicholai Vogel 2026-02-05 01:04:54 -07:00
parent 2312859450
commit 748c7ec40f
11 changed files with 401 additions and 29 deletions

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
lazy-lock.json
tmp/
config.json
config.json.backup.*

23
config.example.json Normal file
View File

@ -0,0 +1,23 @@
{
"paths": {
"obsidianVault": "~/Documents/obsidian-vault/",
"srcDirectory": "~/.local/src/",
"scriptsDirectory": "~/scripts/",
"wallpaperScript": "~/scripts/pywal/wallpapermenu.sh"
},
"editor": {
"tabSize": 4,
"scrollOffset": 8,
"theme": "wave"
},
"ai": {
"model": "claude-sonnet-4-5",
"openCodeModel": "anthropic/claude-sonnet-4-5"
},
"lsp": {
"servers": ["ts_ls", "eslint", "jsonls", "html", "cssls", "tailwindcss"]
},
"treesitter": {
"languages": ["lua", "vim", "bash", "javascript", "typescript", "tsx", "json", "yaml", "html", "css"]
}
}

156
lua/core/config.lua Normal file
View File

@ -0,0 +1,156 @@
-- Config loader module
-- Loads user configuration from config.json with defaults and validation
local M = {}
-- Default configuration values
M.defaults = {
paths = {
obsidianVault = "~/Documents/obsidian-vault/",
srcDirectory = "~/.local/src/",
scriptsDirectory = "~/scripts/",
wallpaperScript = "~/scripts/pywal/wallpapermenu.sh",
},
editor = {
tabSize = 4,
scrollOffset = 8,
theme = "wave",
},
ai = {
model = "claude-sonnet-4-5",
openCodeModel = "anthropic/claude-sonnet-4-5",
},
lsp = {
servers = { "ts_ls", "eslint", "jsonls", "html", "cssls", "tailwindcss" },
},
treesitter = {
languages = { "lua", "vim", "bash", "javascript", "typescript", "tsx", "json", "yaml", "html", "css" },
},
}
-- Cache for loaded config
local config_cache = nil
-- Deep merge two tables (b overrides a)
local function deep_merge(a, b)
local result = vim.deepcopy(a)
for k, v in pairs(b) do
if type(v) == "table" and type(result[k]) == "table" then
result[k] = deep_merge(result[k], v)
else
result[k] = v
end
end
return result
end
-- Expand ~ to home directory in paths
local function expand_path(path)
if type(path) ~= "string" then
return path
end
return path:gsub("^~", vim.fn.expand("$HOME"))
end
-- Recursively expand paths in a table
local function expand_paths(tbl)
-- Check if this is an array (sequential numeric keys)
if #tbl > 0 then
local result = {}
for i, v in ipairs(tbl) do
result[i] = v
end
return result
end
local result = {}
for k, v in pairs(tbl) do
if type(v) == "table" then
result[k] = expand_paths(v)
elseif type(v) == "string" and type(k) == "string" and (k:lower():match("path") or k:lower():match("vault") or k:lower():match("directory") or k:lower():match("script")) then
result[k] = expand_path(v)
else
result[k] = v
end
end
return result
end
-- Load configuration from config.json
function M.load()
if config_cache then
return config_cache
end
local config_path = vim.fn.stdpath("config") .. "/config.json"
local user_config = {}
-- Check if config file exists
if vim.fn.filereadable(config_path) == 1 then
local file = io.open(config_path, "r")
if file then
local content = file:read("*a")
file:close()
-- Try to parse JSON
local ok, parsed = pcall(vim.json.decode, content)
if ok and type(parsed) == "table" then
user_config = parsed
else
vim.notify("config.json: Parse error, using defaults", vim.log.levels.WARN)
end
end
end
-- Merge defaults with user config
local merged = deep_merge(M.defaults, user_config)
-- Expand paths
config_cache = expand_paths(merged)
return config_cache
end
-- Get a config value by dot-notation path (e.g., "paths.obsidianVault")
function M.get(path)
local config = M.load()
local keys = vim.split(path, ".", { plain = true })
local value = config
for _, key in ipairs(keys) do
if type(value) ~= "table" then
return nil
end
value = value[key]
end
return value
end
-- Convenience getters for common sections
function M.paths()
return M.load().paths
end
function M.editor()
return M.load().editor
end
function M.ai()
return M.load().ai
end
function M.lsp()
return M.load().lsp
end
function M.treesitter()
return M.load().treesitter
end
-- Reset cache (useful for testing or config reload)
function M.reset()
config_cache = nil
end
return M

View File

@ -1,4 +1,7 @@
-- Basic Settings
local config = require("core.config")
local editor = config.editor()
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.mouse = 'a'
@ -6,15 +9,15 @@ vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.hlsearch = false
vim.opt.wrap = false
vim.opt.tabstop = 4
vim.opt.shiftwidth = 4
vim.opt.tabstop = editor.tabSize
vim.opt.shiftwidth = editor.tabSize
vim.opt.expandtab = true
vim.opt.termguicolors = true
vim.opt.cursorline = true
vim.opt.signcolumn = 'yes'
vim.opt.clipboard = "unnamedplus"
vim.opt.scrolloff = 8
vim.opt.sidescrolloff = 8
vim.opt.scrolloff = editor.scrollOffset
vim.opt.sidescrolloff = editor.scrollOffset
-- Leader key
vim.g.mapleader = ' '

View File

@ -1,3 +1,6 @@
local config = require("core.config")
local ai = config.ai()
return {
-- AI agent (99)
{
@ -52,10 +55,10 @@ return {
local result = handle:read("*a")
handle:close()
if result:match("^[24]%d%d") then
return "anthropic/claude-sonnet-4-5"
return ai.openCodeModel
end
end
return "claude-sonnet-4-5"
return ai.model
end)(),
logger = {

View File

@ -1,3 +1,7 @@
local config = require("core.config")
local editor = config.editor()
local paths = config.paths()
return {
-- Kanagawa colorscheme
{
@ -24,9 +28,9 @@ return {
overrides = function(colors)
return {}
end,
theme = "wave",
theme = editor.theme,
background = {
dark = "wave",
dark = editor.theme,
light = "lotus"
},
})
@ -93,7 +97,7 @@ return {
if selection.value == "_wallpaper_picker" then
-- launch wallpaper picker, then reload pywal
vim.fn.jobstart(
{ "bash", "-c", "~/scripts/pywal/wallpapermenu.sh && sleep 1" },
{ "bash", "-c", paths.wallpaperScript .. " && sleep 1" },
{
on_exit = function()
vim.schedule(function()

View File

@ -1,3 +1,5 @@
local config = require("core.config")
return {
-- Mason (LSP installer)
{
@ -12,7 +14,7 @@ return {
dependencies = { "williamboman/mason.nvim" },
config = function()
require("mason-lspconfig").setup({
ensure_installed = { "ts_ls", "eslint", "jsonls", "html", "cssls", "tailwindcss" },
ensure_installed = config.get("lsp.servers"),
})
end,
},

View File

@ -1,3 +1,5 @@
local config = require("core.config")
return {
-- Obsidian integration
{
@ -14,7 +16,7 @@ return {
workspaces = {
{
name = "vault",
path = "/mnt/work/obsidian-vault/",
path = config.get("paths.obsidianVault"),
},
},
completion = {

View File

@ -1,3 +1,5 @@
local config = require("core.config")
return {
-- Syntax highlighting
{
@ -5,10 +7,7 @@ return {
build = ":TSUpdate",
config = function()
require("nvim-treesitter.configs").setup({
ensure_installed = {
"lua", "vim", "bash", "javascript", "typescript", "tsx", "json", "yaml", "html", "css", "prisma",
"graphql"
},
ensure_installed = config.get("treesitter.languages"),
highlight = { enable = true },
indent = { enable = true },
})

View File

@ -1,3 +1,6 @@
local config = require("core.config")
local paths = config.paths()
return {
-- Status line
{
@ -31,8 +34,8 @@ return {
dashboard.button("e", " New file", ":ene <BAR> startinsert <CR>"),
dashboard.button("f", "󰍉 Find file", ":lua require('fzf-lua').files() <CR>"),
dashboard.button("t", " Browse cwd", ":NvimTreeOpen<CR>"),
dashboard.button("r", " Browse src", ":e ~/.local/src/<CR>"),
dashboard.button("s", "󰯂 Browse scripts", ":e ~/scripts/<CR>"),
dashboard.button("r", " Browse src", ":e " .. paths.srcDirectory .. "<CR>"),
dashboard.button("s", "󰯂 Browse scripts", ":e " .. paths.scriptsDirectory .. "<CR>"),
dashboard.button("c", " Config", ":e ~/.config/nvim/<CR>"),
dashboard.button("m", " Mappings", ":e ~/.config/nvim/lua/core/keymaps.lua<CR>"),
dashboard.button("p", " Plugins", ":PlugInstall<CR>"),

195
setup.sh
View File

@ -1,43 +1,206 @@
#!/bin/bash
set -e # exit on error
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Helper function for colored output
print_green() { echo -e "${GREEN}$1${NC}"; }
print_yellow() { echo -e "${YELLOW}$1${NC}"; }
print_blue() { echo -e "${BLUE}$1${NC}"; }
# Helper function for prompts with defaults
prompt_with_default() {
local prompt="$1"
local default="$2"
local result
read -p "$(echo -e "${BLUE}$prompt${NC} [${GREEN}$default${NC}]: ")" result
echo "${result:-$default}"
}
# This script sets up dotfiles and installs opencode:
# - Backs up and symlinks .tmux.conf from dotfiles/ to ~/
# - Backs up and symlinks starship.toml from dotfiles/ to ~/.config/
# - Installs opencode CLI if not already present
# - Generates config.json with interactive prompts
NVIM_CONFIG_DIR="$HOME/.config/nvim"
# ============================================
# Section 1: Interactive Config Generation
# ============================================
print_green "╔════════════════════════════════════════════╗"
print_green "║ Neovim Configuration Setup Wizard ║"
print_green "╚════════════════════════════════════════════╝"
echo ""
# Check if config.json already exists
if [ -f "$NVIM_CONFIG_DIR/config.json" ]; then
print_yellow "Existing config.json found."
read -p "$(echo -e "${YELLOW}Overwrite? (y/N): ${NC}")" overwrite
if [[ ! "$overwrite" =~ ^[Yy]$ ]]; then
print_blue "Keeping existing config.json"
SKIP_CONFIG=true
else
# Backup existing config
cp "$NVIM_CONFIG_DIR/config.json" "$NVIM_CONFIG_DIR/config.json.backup.$(date +%Y%m%d%H%M%S)"
print_green "Backed up existing config.json"
SKIP_CONFIG=false
fi
else
SKIP_CONFIG=false
fi
if [ "$SKIP_CONFIG" = false ]; then
echo ""
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_blue " Section 1: Paths"
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
OBSIDIAN_VAULT=$(prompt_with_default "Obsidian vault path" "~/Documents/obsidian-vault/")
SRC_DIR=$(prompt_with_default "Source code directory" "~/.local/src/")
SCRIPTS_DIR=$(prompt_with_default "Scripts directory" "~/scripts/")
WALLPAPER_SCRIPT=$(prompt_with_default "Wallpaper script path" "~/scripts/pywal/wallpapermenu.sh")
echo ""
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_blue " Section 2: Editor Settings"
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
TAB_SIZE=$(prompt_with_default "Tab size" "4")
SCROLL_OFFSET=$(prompt_with_default "Scroll offset" "8")
echo ""
print_yellow "Theme variants: wave (default), dragon (dark), lotus (light)"
THEME=$(prompt_with_default "Kanagawa theme variant" "wave")
echo ""
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_blue " Section 3: Languages"
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
print_yellow "Enter comma-separated lists"
echo ""
LSP_SERVERS=$(prompt_with_default "LSP servers" "ts_ls, eslint, jsonls, html, cssls, tailwindcss")
TS_LANGUAGES=$(prompt_with_default "Treesitter languages" "lua, vim, bash, javascript, typescript, tsx, json, yaml, html, css")
echo ""
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_blue " Section 4: AI Settings"
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
print_yellow "Claude models: claude-sonnet-4-5, claude-opus-4, haiku"
AI_MODEL=$(prompt_with_default "Claude Code model" "claude-sonnet-4-5")
OPENCODE_MODEL=$(prompt_with_default "OpenCode model (with provider prefix)" "anthropic/claude-sonnet-4-5")
# Convert comma-separated strings to JSON arrays
format_json_array() {
echo "$1" | sed 's/,/\n/g' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | \
awk 'BEGIN{printf "["} NR>1{printf ", "} {printf "\"%s\"", $0} END{printf "]"}'
}
LSP_SERVERS_JSON=$(format_json_array "$LSP_SERVERS")
TS_LANGUAGES_JSON=$(format_json_array "$TS_LANGUAGES")
# Generate config.json
cat > "$NVIM_CONFIG_DIR/config.json" << EOF
{
"paths": {
"obsidianVault": "$OBSIDIAN_VAULT",
"srcDirectory": "$SRC_DIR",
"scriptsDirectory": "$SCRIPTS_DIR",
"wallpaperScript": "$WALLPAPER_SCRIPT"
},
"editor": {
"tabSize": $TAB_SIZE,
"scrollOffset": $SCROLL_OFFSET,
"theme": "$THEME"
},
"ai": {
"model": "$AI_MODEL",
"openCodeModel": "$OPENCODE_MODEL"
},
"lsp": {
"servers": $LSP_SERVERS_JSON
},
"treesitter": {
"languages": $TS_LANGUAGES_JSON
}
}
EOF
echo ""
print_green "✓ config.json generated successfully!"
echo ""
fi
# ============================================
# Section 2: Dotfile Symlinks
# ============================================
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_blue " Setting up dotfile symlinks..."
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Check and backup/symlink .tmux.conf
TMUX_TARGET="$HOME/.config/nvim/dotfiles/.tmux.conf"
if [ -L ~/.tmux.conf ] && [ "$(readlink ~/.tmux.conf)" = "$TMUX_TARGET" ]; then
echo ".tmux.conf already linked correctly"
print_green "✓ .tmux.conf already linked correctly"
elif [ -f ~/.tmux.conf ] || [ -L ~/.tmux.conf ]; then
cp ~/.tmux.conf ~/.tmux.conf.backup.$(date +%Y%m%d%H%M%S)
ln -sf "$TMUX_TARGET" ~/.tmux.conf
echo ".tmux.conf backed up and linked"
print_green "✓ .tmux.conf backed up and linked"
elif [ -f "$TMUX_TARGET" ]; then
ln -sf "$TMUX_TARGET" ~/.tmux.conf
echo ".tmux.conf linked"
print_green "✓ .tmux.conf linked"
fi
# Check and backup/symlink starship.toml
STARSHIP_TARGET="$HOME/.config/nvim/dotfiles/starship.toml"
if [ -L ~/.config/starship.toml ] && [ "$(readlink ~/.config/starship.toml)" = "$STARSHIP_TARGET" ]; then
echo "starship.toml already linked correctly"
print_green "✓ starship.toml already linked correctly"
elif [ -f ~/.config/starship.toml ] || [ -L ~/.config/starship.toml ]; then
cp ~/.config/starship.toml ~/.config/starship.toml.backup.$(date +%Y%m%d%H%M%S)
ln -sf "$STARSHIP_TARGET" ~/.config/starship.toml
echo "starship.toml backed up and linked"
print_green "✓ starship.toml backed up and linked"
elif [ -f "$STARSHIP_TARGET" ]; then
mkdir -p ~/.config
ln -sf "$STARSHIP_TARGET" ~/.config/starship.toml
echo "starship.toml linked"
print_green "✓ starship.toml linked"
fi
# Check if opencode is installed, if not, install opencode
# ============================================
# Section 3: OpenCode Installation
# ============================================
echo ""
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_blue " Checking OpenCode installation..."
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
if ! command -v opencode &> /dev/null; then
print_yellow "Installing OpenCode..."
curl -fsSL https://opencode.ai/install | bash
print_green "✓ OpenCode installed"
else
print_green "✓ OpenCode already installed"
fi
# ============================================
# Section 4: Shell Aliases
# ============================================
echo ""
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_blue " Setting up shell aliases..."
print_blue "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Detect shell config file
SHELL_RC=""
if [ -f ~/.zshrc ]; then
@ -162,13 +325,25 @@ EOF
echo "" >> "$SHELL_RC"
echo "# Added by nvim dotfiles setup" >> "$SHELL_RC"
cat "$TEMP_ALIASES" >> "$SHELL_RC"
echo "Shell aliases and functions added to $SHELL_RC"
print_green "Shell aliases and functions added to $SHELL_RC"
else
echo "Shell aliases already exist in $SHELL_RC, skipping..."
print_green "✓ Shell aliases already exist in $SHELL_RC"
fi
# Clean up
rm "$TEMP_ALIASES"
else
echo "No .bashrc or .zshrc found, skipping shell config setup"
print_yellow "⚠ No .bashrc or .zshrc found, skipping shell config setup"
fi
# ============================================
# Done!
# ============================================
echo ""
print_green "╔════════════════════════════════════════════╗"
print_green "║ Setup Complete! ║"
print_green "╚════════════════════════════════════════════╝"
echo ""
print_blue "Run 'nvim' to start using your configuration."
print_blue "Your config is stored in: $NVIM_CONFIG_DIR/config.json"
echo ""