Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 21 additions & 24 deletions bin/claude-preview-diff.sh
Original file line number Diff line number Diff line change
Expand Up @@ -138,43 +138,40 @@ if [[ "$HAS_NVIM" == "true" ]]; then
DISPLAY_ESC="$(escape_lua "$DISPLAY_NAME")"
FILE_PATH_ESC="$(escape_lua "$FILE_PATH")"

# Query config + file visibility from nvim in a single RPC call
HOOK_CTX=$(nvim --server "$NVIM_SOCKET" --remote-expr "luaeval(\"require('claude-preview').hook_context('${FILE_PATH_ESC}')\")" 2>/dev/null || echo '{}')
VISIBLE_ONLY=$(echo "$HOOK_CTX" | jq -r '.visible_only // false')
FILE_VISIBLE=$(echo "$HOOK_CTX" | jq -r '.file_visible // false')

# Decide whether to show the diff
SHOULD_SHOW="1"
if [[ "$VISIBLE_ONLY" == "true" && "$FILE_VISIBLE" != "true" ]]; then
SHOULD_SHOW="0"
fi

# Determine change status for neo-tree indicator
# Check if the actual file exists on disk (not the temp copy, which is always created)
if [[ -f "$FILE_PATH" ]]; then
CHANGE_STATUS="modified"
else
CHANGE_STATUS="created"
fi

nvim_send "require('claude-preview.changes').set('$FILE_PATH_ESC', '$CHANGE_STATUS')" || true
nvim_send "pcall(function() require('claude-preview.neo_tree').refresh() end)" || true
# Reveal the file in neo-tree: for modified files reveal the file itself,
# for created files reveal the nearest existing parent directory
if [[ "$CHANGE_STATUS" == "modified" ]]; then
if [[ "$SHOULD_SHOW" == "1" ]]; then
nvim_send "require('claude-preview.changes').set('$FILE_PATH_ESC', '$CHANGE_STATUS')" || true
nvim_send "pcall(function() require('claude-preview.neo_tree').refresh() end)" || true
nvim_send "vim.defer_fn(function() pcall(function() require('claude-preview.neo_tree').reveal('$FILE_PATH_ESC') end) end, 300)" || true
else
# Walk up to find the nearest existing parent directory
REVEAL_DIR="$(dirname "$FILE_PATH")"
while [[ ! -d "$REVEAL_DIR" && "$REVEAL_DIR" != "/" ]]; do
REVEAL_DIR="$(dirname "$REVEAL_DIR")"
done
# Reveal a file inside the parent dir to force neo-tree to expand it
REVEAL_TARGET="$(find "$REVEAL_DIR" -maxdepth 1 -type f | head -1)"
if [[ -z "$REVEAL_TARGET" ]]; then
REVEAL_TARGET="$REVEAL_DIR"
fi
REVEAL_TARGET_ESC="$(escape_lua "$REVEAL_TARGET")"
nvim_send "vim.defer_fn(function() pcall(function() require('claude-preview.neo_tree').reveal('$REVEAL_TARGET_ESC') end) end, 300)" || true
nvim_send "require('claude-preview.diff').show_diff('$ORIG_ESC', '$PROP_ESC', '$DISPLAY_ESC')" || true
fi
nvim_send "require('claude-preview.diff').show_diff('$ORIG_ESC', '$PROP_ESC', '$DISPLAY_ESC')" || true
fi

# --- Always ask for user confirmation ---
# --- Permission decision ---

if [[ "$HAS_NVIM" == "true" ]]; then
if [[ "$HAS_NVIM" == "true" && "$SHOULD_SHOW" == "0" ]]; then
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"File not visible in Neovim, auto-approved"}}\n'
elif [[ "$HAS_NVIM" == "true" ]]; then
REASON="Diff preview sent to Neovim. Review before accepting."
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"%s"}}\n' "$REASON"
else
REASON="Neovim not running. Review the diff in CLI before accepting."
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"%s"}}\n' "$REASON"
fi

printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"%s"}}\n' "$REASON"
42 changes: 42 additions & 0 deletions lua/claude-preview/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ local default_config = {
auto_close = true,
equalize = true,
full_file = true,
visible_only = false, -- only show diffs for files open in a visible nvim window
},
neo_tree = {
enabled = true,
Expand Down Expand Up @@ -87,6 +88,15 @@ function M.setup(user_config)
M.status()
end, { desc = "Show claude-preview status" })

vim.api.nvim_create_user_command("ClaudePreviewToggleVisibleOnly", function()
M.config.diff.visible_only = not M.config.diff.visible_only
vim.notify(
"claude-preview: visible_only = " .. tostring(M.config.diff.visible_only),
vim.log.levels.INFO,
{ title = "claude-preview" }
)
end, { desc = "Toggle visible_only — show diffs only for open buffers vs all files" })

-- Neo-tree integration (soft dependency)
if M.config.neo_tree.enabled then
require("claude-preview.neo_tree").setup(M.config)
Expand All @@ -97,6 +107,38 @@ function M.setup(user_config)
end, { desc = "Close claude-preview diff" })
end

--- Query hook context for the PreToolUse shell script.
--- Returns a JSON string with config + file visibility in a single RPC call.
--- @param file_path string absolute path of the file being edited
--- @return string JSON: { visible_only, file_visible }
function M.hook_context(file_path)
local cfg = M.config
local visible_only = cfg.diff.visible_only and true or false

local file_visible = false
if visible_only and file_path ~= "" then
local is_mac = vim.fn.has("mac") == 1
local target = vim.uv.fs_realpath(file_path) or vim.fn.fnamemodify(file_path, ":p")
if is_mac then target = target:lower() end

for _, w in ipairs(vim.api.nvim_list_wins()) do
local b = vim.api.nvim_win_get_buf(w)
local name = vim.uv.fs_realpath(vim.api.nvim_buf_get_name(b))
or vim.fn.fnamemodify(vim.api.nvim_buf_get_name(b), ":p")
if is_mac then name = name:lower() end
if name == target then
file_visible = true
break
end
end
end

return vim.json.encode({
visible_only = visible_only,
file_visible = file_visible,
})
end

function M.status()
local lines = { "claude-preview.nvim status", string.rep("─", 40) }

Expand Down
Loading