Compare commits
5 Commits
fe6c7d8c12
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
9f6e607e78
|
|||
|
3d68f08e1d
|
|||
|
cf3dba8fe4
|
|||
|
8ecb7d4382
|
|||
|
1e30e9bf9e
|
BIN
.github/assets/CommandNotch-Demo.mp4
vendored
Normal file
BIN
.github/assets/CommandNotch-Open.png
vendored
Normal file
|
After Width: | Height: | Size: 846 KiB |
BIN
.github/assets/CommandNotch-Settings.png
vendored
Normal file
|
After Width: | Height: | Size: 575 KiB |
BIN
.github/assets/bch-receiving-address.png
vendored
Normal file
|
After Width: | Height: | Size: 58 KiB |
422
.github/demo/aurora.py
vendored
Executable file
@@ -0,0 +1,422 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Looping bathtub-and-duck terminal animation for Downterm demos."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import atexit
|
||||||
|
import math
|
||||||
|
import shutil
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
|
||||||
|
CSI = "\x1b["
|
||||||
|
RESET = f"{CSI}0m"
|
||||||
|
HIDE_CURSOR = f"{CSI}?25l"
|
||||||
|
SHOW_CURSOR = f"{CSI}?25h"
|
||||||
|
ALT_SCREEN_ON = f"{CSI}?1049h"
|
||||||
|
ALT_SCREEN_OFF = f"{CSI}?1049l"
|
||||||
|
CLEAR = f"{CSI}2J"
|
||||||
|
HOME = f"{CSI}H"
|
||||||
|
|
||||||
|
TUB = "tub"
|
||||||
|
TUB_DARK = "tub_dark"
|
||||||
|
WATER = "water"
|
||||||
|
WATER_DEEP = "water_deep"
|
||||||
|
FOAM = "foam"
|
||||||
|
BUBBLE = "bubble"
|
||||||
|
DUCK = "duck"
|
||||||
|
DUCK_LIGHT = "duck_light"
|
||||||
|
BEAK = "beak"
|
||||||
|
EYE = "eye"
|
||||||
|
LABEL = "label"
|
||||||
|
LABEL_SHADOW = "label_shadow"
|
||||||
|
|
||||||
|
RGB = {
|
||||||
|
TUB: (171, 177, 191),
|
||||||
|
TUB_DARK: (112, 118, 133),
|
||||||
|
WATER: (71, 185, 214),
|
||||||
|
WATER_DEEP: (36, 121, 161),
|
||||||
|
FOAM: (196, 242, 250),
|
||||||
|
BUBBLE: (184, 233, 245),
|
||||||
|
DUCK: (255, 208, 64),
|
||||||
|
DUCK_LIGHT: (255, 232, 132),
|
||||||
|
BEAK: (255, 154, 58),
|
||||||
|
EYE: (251, 251, 253),
|
||||||
|
LABEL: (207, 212, 222),
|
||||||
|
LABEL_SHADOW: (74, 79, 92),
|
||||||
|
}
|
||||||
|
|
||||||
|
WATER_SHADE = " .:-=+*#"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args(argv: Sequence[str]) -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Render a looping bathtub-and-duck animation."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--fps",
|
||||||
|
type=float,
|
||||||
|
default=18.0,
|
||||||
|
help="Target frames per second. Default: 18",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--speed",
|
||||||
|
type=float,
|
||||||
|
default=1.0,
|
||||||
|
help="Animation speed multiplier. Default: 1.0",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--duration",
|
||||||
|
type=float,
|
||||||
|
default=0.0,
|
||||||
|
help="Optional run duration in seconds. Default: infinite",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--label",
|
||||||
|
default="",
|
||||||
|
help="Optional centered label, e.g. --label Downterm",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--mono",
|
||||||
|
action="store_true",
|
||||||
|
help="Disable color and render as monochrome ASCII.",
|
||||||
|
)
|
||||||
|
return parser.parse_args(argv)
|
||||||
|
|
||||||
|
|
||||||
|
def terminal_size() -> tuple[int, int]:
|
||||||
|
size = shutil.get_terminal_size((90, 28))
|
||||||
|
return max(40, size.columns), max(12, size.lines)
|
||||||
|
|
||||||
|
|
||||||
|
def clamp(value: float, low: float, high: float) -> float:
|
||||||
|
return max(low, min(high, value))
|
||||||
|
|
||||||
|
|
||||||
|
def scene_size(width: int, height: int) -> tuple[int, int]:
|
||||||
|
visible_height = max(8, height - 1)
|
||||||
|
scene_width = min(max(48, width - 2), width, 110)
|
||||||
|
scene_height = min(max(16, visible_height - 1), visible_height, 24)
|
||||||
|
return scene_width, scene_height
|
||||||
|
|
||||||
|
|
||||||
|
def make_canvas(width: int, height: int) -> list[list[tuple[str, str | None]]]:
|
||||||
|
return [[(" ", None) for _ in range(width)] for _ in range(height)]
|
||||||
|
|
||||||
|
|
||||||
|
def put(canvas: list[list[tuple[str, str | None]]], x: int, y: int, char: str, color: str | None) -> None:
|
||||||
|
if 0 <= y < len(canvas) and 0 <= x < len(canvas[y]) and char:
|
||||||
|
canvas[y][x] = (char[0], color)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_text(
|
||||||
|
canvas: list[list[tuple[str, str | None]]],
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
text: str,
|
||||||
|
color: str | None,
|
||||||
|
) -> None:
|
||||||
|
for index, char in enumerate(text):
|
||||||
|
if char != " ":
|
||||||
|
put(canvas, x + index, y, char, color)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_sprite_text(
|
||||||
|
canvas: list[list[tuple[str, str | None]]],
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
text: str,
|
||||||
|
color: str | None,
|
||||||
|
) -> None:
|
||||||
|
for index, char in enumerate(text):
|
||||||
|
if char == " ":
|
||||||
|
continue
|
||||||
|
if char == "~":
|
||||||
|
put(canvas, x + index, y, " ", None)
|
||||||
|
continue
|
||||||
|
put(canvas, x + index, y, char, color)
|
||||||
|
|
||||||
|
|
||||||
|
def water_char(intensity: float) -> str:
|
||||||
|
index = int(clamp(intensity, 0.0, 0.9999) * len(WATER_SHADE))
|
||||||
|
return WATER_SHADE[index]
|
||||||
|
|
||||||
|
|
||||||
|
def tub_geometry(scene_width: int, scene_height: int) -> dict[str, int]:
|
||||||
|
top_y = 1
|
||||||
|
notch_width = max(28, min(scene_width - 18, int(scene_width * 0.44)))
|
||||||
|
rim_left = max(8, (scene_width - notch_width) // 2)
|
||||||
|
rim_right = min(scene_width - 9, rim_left + notch_width)
|
||||||
|
wall_left = rim_left + 1
|
||||||
|
wall_right = rim_right - 1
|
||||||
|
body_bottom = min(scene_height - 10, top_y + max(6, scene_height // 3))
|
||||||
|
water_top = top_y + 2
|
||||||
|
return {
|
||||||
|
"rim_left": rim_left,
|
||||||
|
"rim_right": rim_right,
|
||||||
|
"top_y": top_y,
|
||||||
|
"wall_left": wall_left,
|
||||||
|
"wall_right": wall_right,
|
||||||
|
"body_bottom": body_bottom,
|
||||||
|
"water_top": water_top,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def draw_tub(canvas: list[list[tuple[str, str | None]]], geometry: dict[str, int]) -> None:
|
||||||
|
rim_left = geometry["rim_left"]
|
||||||
|
rim_right = geometry["rim_right"]
|
||||||
|
top_y = geometry["top_y"]
|
||||||
|
wall_left = geometry["wall_left"]
|
||||||
|
wall_right = geometry["wall_right"]
|
||||||
|
body_bottom = geometry["body_bottom"]
|
||||||
|
|
||||||
|
draw_text(canvas, 2, top_y, "_" * max(0, rim_left - 2), TUB)
|
||||||
|
put(canvas, rim_left, top_y, "_", TUB)
|
||||||
|
draw_text(canvas, rim_right + 1, top_y, "_" * max(0, len(canvas[0]) - rim_right - 3), TUB)
|
||||||
|
put(canvas, rim_right, top_y, "_", TUB)
|
||||||
|
|
||||||
|
for y in range(top_y + 1, body_bottom):
|
||||||
|
put(canvas, wall_left, y, "|", TUB)
|
||||||
|
put(canvas, wall_right, y, "|", TUB)
|
||||||
|
|
||||||
|
put(canvas, wall_left, body_bottom, "\\", TUB_DARK)
|
||||||
|
draw_text(canvas, wall_left + 1, body_bottom, "_" * max(0, wall_right - wall_left - 1), TUB_DARK)
|
||||||
|
put(canvas, wall_right, body_bottom, "/", TUB_DARK)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_water_fill(canvas: list[list[tuple[str, str | None]]], geometry: dict[str, int], t: float) -> None:
|
||||||
|
rim_left = geometry["wall_left"] + 1
|
||||||
|
rim_right = geometry["wall_right"] - 1
|
||||||
|
water_top = geometry["water_top"]
|
||||||
|
body_bottom = geometry["body_bottom"] - 1
|
||||||
|
|
||||||
|
depth = max(1, body_bottom - water_top)
|
||||||
|
for row in range(depth + 1):
|
||||||
|
y = water_top + row
|
||||||
|
for x in range(rim_left, rim_right + 1):
|
||||||
|
wave = math.sin((x * 0.23) + (t * 2.1) + row * 0.55)
|
||||||
|
shimmer = math.sin((x * 0.07) - (t * 1.35))
|
||||||
|
if row == 0:
|
||||||
|
char = "~" if wave > -0.22 else "-"
|
||||||
|
color = FOAM if shimmer > 0.25 else WATER
|
||||||
|
else:
|
||||||
|
density = 0.40 + row / max(1, depth)
|
||||||
|
value = 0.45 + (wave * 0.25) + (shimmer * 0.10) + density * 0.30
|
||||||
|
char = water_char(value)
|
||||||
|
color = WATER if row <= 2 else WATER_DEEP
|
||||||
|
put(canvas, x, y, char, color)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_water_surface(canvas: list[list[tuple[str, str | None]]], geometry: dict[str, int], t: float) -> None:
|
||||||
|
left = geometry["wall_left"] + 1
|
||||||
|
right = geometry["wall_right"] - 1
|
||||||
|
y = geometry["water_top"]
|
||||||
|
|
||||||
|
for x in range(left, right + 1):
|
||||||
|
wave = math.sin((x * 0.23) + (t * 2.1))
|
||||||
|
shimmer = math.sin((x * 0.11) - (t * 1.15))
|
||||||
|
char = "~" if wave > -0.18 else "-"
|
||||||
|
color = FOAM if shimmer > 0.35 else WATER
|
||||||
|
put(canvas, x, y, char, color)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_bubbles(canvas: list[list[tuple[str, str | None]]], geometry: dict[str, int], t: float) -> None:
|
||||||
|
rim_left = geometry["wall_left"]
|
||||||
|
water_top = geometry["water_top"]
|
||||||
|
|
||||||
|
seeds = (
|
||||||
|
(0.00, rim_left + 11, 0.9),
|
||||||
|
(0.29, rim_left + 19, 1.1),
|
||||||
|
(0.57, rim_left + 27, 0.8),
|
||||||
|
(0.82, rim_left + 35, 1.0),
|
||||||
|
)
|
||||||
|
|
||||||
|
for offset, base_x, speed in seeds:
|
||||||
|
progress = (t * 0.11 * speed + offset) % 1.0
|
||||||
|
y = water_top + 4 - int(progress * 9)
|
||||||
|
x = base_x + int(round(math.sin((t * 0.8 * speed) + offset * 8.0) * 2))
|
||||||
|
put(canvas, x, y, "o" if progress < 0.5 else ".", BUBBLE)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_duck(canvas: list[list[tuple[str, str | None]]], geometry: dict[str, int], t: float) -> None:
|
||||||
|
rim_left = geometry["wall_left"]
|
||||||
|
rim_right = geometry["wall_right"]
|
||||||
|
water_top = geometry["water_top"]
|
||||||
|
|
||||||
|
span = rim_right - rim_left
|
||||||
|
bob = math.sin(t * 1.35) * 0.55
|
||||||
|
drift = math.sin(t * 0.55) * 2.0
|
||||||
|
|
||||||
|
blink_phase = int(t * 2.1) % 12
|
||||||
|
eye_char = "-" if blink_phase == 7 else "."
|
||||||
|
x = int(rim_left + span * 0.34 + drift)
|
||||||
|
y = int(water_top - 1 + bob)
|
||||||
|
|
||||||
|
rows = [
|
||||||
|
" __",
|
||||||
|
f" ___(~{eye_char})>",
|
||||||
|
" \\~<_.~) ",
|
||||||
|
" `---' ",
|
||||||
|
]
|
||||||
|
colors = [DUCK_LIGHT, DUCK, DUCK, DUCK]
|
||||||
|
|
||||||
|
for index, row in enumerate(rows):
|
||||||
|
draw_sprite_text(canvas, x, y + index, row, colors[index])
|
||||||
|
|
||||||
|
put(canvas, x + 8, y + 1, ">", BEAK)
|
||||||
|
put(canvas, x + 6, y + 1, eye_char, EYE)
|
||||||
|
|
||||||
|
if 0.74 < (t * 0.18) % 1.0 < 0.90:
|
||||||
|
splash_x = x + 9
|
||||||
|
splash_y = geometry["water_top"] - 1
|
||||||
|
for dx, dy, char in ((0, 0, "."), (2, -1, "o"), (4, 0, "."), (5, -2, "o")):
|
||||||
|
put(canvas, splash_x + dx, splash_y + dy, char, BUBBLE)
|
||||||
|
|
||||||
|
|
||||||
|
def logo_rows(text: str) -> list[str]:
|
||||||
|
if text == "commandNotch":
|
||||||
|
return [
|
||||||
|
" ██████╗ ██████╗ ███╗ ███╗███╗ ███╗ █████╗ ███╗ ██╗██████╗ ",
|
||||||
|
" ██╔════╝██╔═══██╗████╗ ████║████╗ ████║██╔══██╗████╗ ██║██╔══██╗",
|
||||||
|
" ██║ ██║ ██║██╔████╔██║██╔████╔██║███████║██╔██╗ ██║██║ ██║",
|
||||||
|
" ██║ ██║ ██║██║╚██╔╝██║██║╚██╔╝██║██╔══██║██║╚██╗██║██║ ██║",
|
||||||
|
" ╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ╚═╝ ██║██║ ██║██║ ╚████║██████╔╝",
|
||||||
|
" ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝ ",
|
||||||
|
"",
|
||||||
|
" ███╗ ██╗ ██████╗ ████████╗ ██████╗██╗ ██╗",
|
||||||
|
" ████╗ ██║██╔═══██╗╚══██╔══╝██╔════╝██║ ██║",
|
||||||
|
" ██╔██╗ ██║██║ ██║ ██║ ██║ ███████║",
|
||||||
|
" ██║╚██╗██║██║ ██║ ██║ ██║ ██╔══██║",
|
||||||
|
" ██║ ╚████║╚██████╔╝ ██║ ╚██████╗██║ ██║",
|
||||||
|
" ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═════╝╚═╝ ╚═╝",
|
||||||
|
]
|
||||||
|
|
||||||
|
return [text]
|
||||||
|
|
||||||
|
|
||||||
|
def draw_label(canvas: list[list[tuple[str, str | None]]], label: str) -> None:
|
||||||
|
logo = logo_rows(label or "commandNotch")
|
||||||
|
start_y = len(canvas) - len(logo) - 1
|
||||||
|
|
||||||
|
for index, row in enumerate(logo):
|
||||||
|
start_x = max(0, (len(canvas[0]) - len(row)) // 2)
|
||||||
|
draw_text(canvas, start_x, start_y + index, row, LABEL)
|
||||||
|
|
||||||
|
|
||||||
|
def scene_canvas(scene_width: int, scene_height: int, t: float, label: str) -> list[list[tuple[str, str | None]]]:
|
||||||
|
canvas = make_canvas(scene_width, scene_height)
|
||||||
|
geometry = tub_geometry(scene_width, scene_height)
|
||||||
|
draw_tub(canvas, geometry)
|
||||||
|
draw_water_fill(canvas, geometry, t)
|
||||||
|
draw_water_surface(canvas, geometry, t)
|
||||||
|
draw_duck(canvas, geometry, t)
|
||||||
|
draw_bubbles(canvas, geometry, t)
|
||||||
|
draw_label(canvas, label)
|
||||||
|
return canvas
|
||||||
|
|
||||||
|
|
||||||
|
def ansi_rgb(color_name: str | None) -> str:
|
||||||
|
if color_name is None:
|
||||||
|
return RESET
|
||||||
|
red, green, blue = RGB[color_name]
|
||||||
|
return f"{CSI}38;2;{red};{green};{blue}m"
|
||||||
|
|
||||||
|
|
||||||
|
def render_scene_line(cells: list[tuple[str, str | None]], mono: bool) -> str:
|
||||||
|
if mono:
|
||||||
|
return "".join(char for char, _ in cells)
|
||||||
|
|
||||||
|
chunks: list[str] = []
|
||||||
|
current_color: str | None = None
|
||||||
|
for char, color in cells:
|
||||||
|
if color != current_color:
|
||||||
|
chunks.append(ansi_rgb(color))
|
||||||
|
current_color = color
|
||||||
|
chunks.append(char)
|
||||||
|
chunks.append(RESET)
|
||||||
|
return "".join(chunks)
|
||||||
|
|
||||||
|
|
||||||
|
def render_frame(width: int, height: int, t: float, label: str, mono: bool) -> str:
|
||||||
|
visible_height = max(8, height - 1)
|
||||||
|
scene_width, scene_height = scene_size(width, height)
|
||||||
|
scene = scene_canvas(scene_width, scene_height, t, label)
|
||||||
|
top_padding = max(0, (visible_height - scene_height) // 2)
|
||||||
|
left_padding = max(0, (width - scene_width) // 2)
|
||||||
|
|
||||||
|
pieces: list[str] = [HOME]
|
||||||
|
blank_line = " " * width
|
||||||
|
|
||||||
|
for row in range(visible_height):
|
||||||
|
if top_padding <= row < top_padding + scene_height:
|
||||||
|
scene_row = scene[row - top_padding]
|
||||||
|
rendered = render_scene_line(scene_row, mono)
|
||||||
|
right_padding = max(0, width - left_padding - scene_width)
|
||||||
|
line = (" " * left_padding) + rendered + (" " * right_padding)
|
||||||
|
else:
|
||||||
|
line = blank_line
|
||||||
|
|
||||||
|
pieces.append(line)
|
||||||
|
if row != visible_height - 1:
|
||||||
|
pieces.append("\n")
|
||||||
|
|
||||||
|
return "".join(pieces)
|
||||||
|
|
||||||
|
|
||||||
|
def restore_terminal() -> None:
|
||||||
|
sys.stdout.write(f"{RESET}{SHOW_CURSOR}{ALT_SCREEN_OFF}")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def install_cleanup() -> None:
|
||||||
|
def handle_signal(signum, _frame) -> None:
|
||||||
|
restore_terminal()
|
||||||
|
raise SystemExit(128 + signum)
|
||||||
|
|
||||||
|
atexit.register(restore_terminal)
|
||||||
|
signal.signal(signal.SIGINT, handle_signal)
|
||||||
|
signal.signal(signal.SIGTERM, handle_signal)
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: Sequence[str]) -> int:
|
||||||
|
args = parse_args(argv)
|
||||||
|
if args.fps <= 0:
|
||||||
|
raise SystemExit("--fps must be greater than 0")
|
||||||
|
if args.speed <= 0:
|
||||||
|
raise SystemExit("--speed must be greater than 0")
|
||||||
|
|
||||||
|
install_cleanup()
|
||||||
|
|
||||||
|
sys.stdout.write(f"{ALT_SCREEN_ON}{HIDE_CURSOR}{CLEAR}")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
frame_interval = 1.0 / args.fps
|
||||||
|
start = time.perf_counter()
|
||||||
|
frame_index = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
now = time.perf_counter()
|
||||||
|
elapsed = now - start
|
||||||
|
if args.duration > 0 and elapsed >= args.duration:
|
||||||
|
break
|
||||||
|
|
||||||
|
width, height = terminal_size()
|
||||||
|
t = elapsed * args.speed
|
||||||
|
sys.stdout.write(render_frame(width, height, t, args.label, args.mono))
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
frame_index += 1
|
||||||
|
target = start + (frame_index * frame_interval)
|
||||||
|
sleep_for = target - time.perf_counter()
|
||||||
|
if sleep_for > 0:
|
||||||
|
time.sleep(sleep_for)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
4
.gitignore
vendored
@@ -79,7 +79,5 @@ jspm_packages/
|
|||||||
dist/
|
dist/
|
||||||
build/
|
build/
|
||||||
|
|
||||||
**/Release*
|
# Mac... files
|
||||||
CommandNotch 20*
|
|
||||||
|
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
89
CONTRIBUTING.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
Thanks for contributing to CommandNotch.
|
||||||
|
|
||||||
|
## Before You Start
|
||||||
|
|
||||||
|
- Use macOS 14+.
|
||||||
|
- Install Xcode 16+.
|
||||||
|
- Install `xcodegen` with Homebrew.
|
||||||
|
|
||||||
|
## Local Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd CommandNotch
|
||||||
|
xcodegen generate --spec project.yml
|
||||||
|
open CommandNotch.xcodeproj
|
||||||
|
```
|
||||||
|
|
||||||
|
## Useful Commands
|
||||||
|
|
||||||
|
Generate the project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd CommandNotch
|
||||||
|
xcodegen generate --spec project.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd CommandNotch
|
||||||
|
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
|
||||||
|
xcodebuild build -project CommandNotch.xcodeproj -scheme CommandNotch -destination 'platform=macOS'
|
||||||
|
```
|
||||||
|
|
||||||
|
Run tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd CommandNotch
|
||||||
|
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
|
||||||
|
xcodebuild test -project CommandNotch.xcodeproj -scheme CommandNotch -destination 'platform=macOS'
|
||||||
|
```
|
||||||
|
|
||||||
|
## What Helps Most
|
||||||
|
|
||||||
|
- Bug fixes with a clear reproduction path.
|
||||||
|
- UI polish that keeps the app feeling intentional instead of generic.
|
||||||
|
- Accessibility improvements.
|
||||||
|
- Tests around workspace, screen, settings, and hotkey behavior.
|
||||||
|
- Docs and onboarding improvements.
|
||||||
|
|
||||||
|
## Code Guidelines
|
||||||
|
|
||||||
|
- Keep changes targeted.
|
||||||
|
- Preserve the existing SwiftUI + AppKit split.
|
||||||
|
- Prefer typed settings and explicit state ownership over hidden side effects.
|
||||||
|
- Add or update tests when you change behavior.
|
||||||
|
- Regenerate the Xcode project with XcodeGen if you add or remove files.
|
||||||
|
|
||||||
|
## Pull Requests
|
||||||
|
|
||||||
|
- Keep PRs small enough to review comfortably.
|
||||||
|
- Explain the user-facing impact.
|
||||||
|
- Note any follow-up work or tradeoffs.
|
||||||
|
- Include screenshots for visible UI changes when possible.
|
||||||
|
- Mention the exact build/test command you used.
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
When filing a bug, include:
|
||||||
|
|
||||||
|
- macOS version
|
||||||
|
- what you expected
|
||||||
|
- what actually happened
|
||||||
|
- reproduction steps
|
||||||
|
- screenshots or recordings if they help
|
||||||
|
|
||||||
|
## Media Updates
|
||||||
|
|
||||||
|
README screenshots live in `.github/assets/`.
|
||||||
|
|
||||||
|
If you update the UI significantly, refresh:
|
||||||
|
|
||||||
|
- `.github/assets/CommandNotch-Open.png`
|
||||||
|
- `.github/assets/CommandNotch-Settings.png`
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
By contributing, you agree that your contributions will be released under the MIT License in this repository.
|
||||||
787
CommandNotch/CommandNotch.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,787 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 77;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
088FE22B3308AAB25027E086 /* NotchShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F57837A7115DEEE11E14B40 /* NotchShape.swift */; };
|
||||||
|
0DE8E7F29FBAA7E2B613E221 /* AppearanceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B3C8C11834931A9D53BC2B6 /* AppearanceSettingsView.swift */; };
|
||||||
|
0F133E8A88D2E313D90C32AD /* WindowFrameCalculatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D288132700770C4A625A15F6 /* WindowFrameCalculatorTests.swift */; };
|
||||||
|
12F68EDA880030DAB644FF5F /* LaunchAtLoginHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8210D7783A614FD7190F5DDD /* LaunchAtLoginHelper.swift */; };
|
||||||
|
154F363D434A26105C5999B5 /* WorkspaceRegistryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 591FCE91AF83A8A8E44E1625 /* WorkspaceRegistryTests.swift */; };
|
||||||
|
187F4B521BFC3BD29ADA79E3 /* HotkeyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEB7FE2BDE5C25B7E599F340 /* HotkeyManager.swift */; };
|
||||||
|
1AB4A0F1BE668D3130EFBA93 /* TerminalTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE9E5BADB0C427903A0D874 /* TerminalTheme.swift */; };
|
||||||
|
2089566A2BBAA65EA82119B3 /* NotchOrchestratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6D136A0B3FC79DDE12A826 /* NotchOrchestratorTests.swift */; };
|
||||||
|
2375B9DA559A0777FE558A8B /* WorkspaceStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA62DFE9AD003F4C5B55F14 /* WorkspaceStoreTests.swift */; };
|
||||||
|
23E2DDCF36D0DAB2EA72C39C /* NotchState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B352301BC9CAD7C9D8B7AA9 /* NotchState.swift */; };
|
||||||
|
26AE379040149CBE05B314BB /* WorkspacesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70A31EFACF23DD9262A040E /* WorkspacesSettingsView.swift */; };
|
||||||
|
278010607B0D552DCC8996C5 /* CommandNotchUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 726B935606FD961FD7E8C2BE /* CommandNotchUITests.swift */; };
|
||||||
|
2DF22798D3A7514E2A9183FC /* WorkspaceSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2BCA543CE54DAB1DB80E43 /* WorkspaceSummary.swift */; };
|
||||||
|
34AF69BF7AF8DC78ADE3774A /* GeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E7EEEDFBD8D9CEB8FD86A5 /* GeneralSettingsView.swift */; };
|
||||||
|
3B69CB3CDEC2E5F2DCE600F9 /* NotchSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 297BA3E5B837FBDDEED9DE66 /* NotchSettings.swift */; };
|
||||||
|
4CFA0C79095ACE0A327A2469 /* WorkspaceRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E97758F68FACCFFACA895B7 /* WorkspaceRegistry.swift */; };
|
||||||
|
4D335F67B71F7DD977B6AEF9 /* AppSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6C98C406899F4B242075AF /* AppSettingsStore.swift */; };
|
||||||
|
4E0AD14B7427532271E485AA /* NotchWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFAC70814C72BAF76D90B9DF /* NotchWindow.swift */; };
|
||||||
|
507A67E770DEFAF5BC321FCF /* WindowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9220E02B6D470DD05CA540C /* WindowCoordinator.swift */; };
|
||||||
|
5379DF2FACE924BDDB584377 /* SwiftTerm in Frameworks */ = {isa = PBXBuildFile; productRef = C921E6435A64AA07A0FEA4D5 /* SwiftTerm */; };
|
||||||
|
5A3FDFCF30A1AAFE070290E9 /* SettingsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 728B3125F7F7FDB7313D2DC6 /* SettingsWindowController.swift */; };
|
||||||
|
5F3534F66A5DBE4E081AFFA6 /* SwiftTermView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C55F29B779DA0E5C5FC8627 /* SwiftTermView.swift */; };
|
||||||
|
65C7DB7296C6C6A77598A1F4 /* TerminalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E37A6DCD9C5DE1FE11C4C1CD /* TerminalSettingsView.swift */; };
|
||||||
|
6624A60CB121A22A03365071 /* WorkspaceSwitcherView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF0FFBC96F2446687D6474F4 /* WorkspaceSwitcherView.swift */; };
|
||||||
|
6A18F6635B509FF58669F505 /* HotkeySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03D042117E59DCA9D553844 /* HotkeySettingsView.swift */; };
|
||||||
|
6DD4DAE72E5C3858B230D94C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7A8DCFA77626F9999B432FE /* AppDelegate.swift */; };
|
||||||
|
6F249EDFA2D654457DF385F1 /* TerminalManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567E85A2ED628460CEC760DB /* TerminalManager.swift */; };
|
||||||
|
771088361F981A9AAE976F3C /* SettingsBindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 900F0476BE9E3600FBD371BB /* SettingsBindings.swift */; };
|
||||||
|
787477BB6D2F8AC723EEDA7D /* ScreenContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7181BB1F3926B457445105E5 /* ScreenContext.swift */; };
|
||||||
|
7A69B5AAC686174BCA54D0F0 /* TerminalSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E1791BB45E1505500ACC67 /* TerminalSession.swift */; };
|
||||||
|
7A7D3455D50D041CF698E786 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74463E4EAB78F56345360CD5 /* AppSettings.swift */; };
|
||||||
|
7AB14019B5CC6ED84B96FA47 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6ADD641A088A5005DD0EB4A6 /* Assets.xcassets */; };
|
||||||
|
7D5F3C3B5E173B6F06901812 /* AppSettingsStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CA6F2C2CA32D0AA58F6C43 /* AppSettingsStoreTests.swift */; };
|
||||||
|
86BB4A41E8EC44A5F45CE995 /* WorkspaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CB1DFD6FCDF64B4DF24230A /* WorkspaceController.swift */; };
|
||||||
|
88113BA9B217DA579C36BEBE /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72A1D3D12BAC593838B3125C /* TabBar.swift */; };
|
||||||
|
88D87155D79C493D8956AA3B /* AnimationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE99DBAB656EA80A117D2EE1 /* AnimationSettingsView.swift */; };
|
||||||
|
8A1B2A4CD61B0D9A4BAB075B /* ScreenRegistryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC7F7D8D15A1BC4EE43DDDB /* ScreenRegistryTests.swift */; };
|
||||||
|
9F535B9516A4AE9FB536B1BD /* HotkeyBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BB1C403BC2157756F572ACF /* HotkeyBinding.swift */; };
|
||||||
|
A74E14B8AD19C53820853D8E /* NSScreen+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDC7451F0E7ACFE0BEC5473 /* NSScreen+Extensions.swift */; };
|
||||||
|
AE8215538D4B026A00BAD241 /* WorkspaceStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3B957BD1F6120D2592613ED /* WorkspaceStore.swift */; };
|
||||||
|
B406183CC7B5DCE76989A066 /* AppSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB28950392C0198E69F3564B /* AppSettingsController.swift */; };
|
||||||
|
B605A9311557251A85183383 /* AboutSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48198AFE5473B0F7AECAB3FB /* AboutSettingsView.swift */; };
|
||||||
|
C84FA3100884649FE92BF5DD /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64A11F27E65B342B991629A /* ContentView.swift */; };
|
||||||
|
CFBBE994BA6BAD4658AAB9CB /* TerminalCommandArrowBehaviorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D19729317029008D81F361 /* TerminalCommandArrowBehaviorTests.swift */; };
|
||||||
|
D088BC850F37E717844761C6 /* CommandNotchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5159CB9DBE2BAA0D2E201C39 /* CommandNotchApp.swift */; };
|
||||||
|
D2468B19D6F0A2C1DFDFE2B7 /* ScreenContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A770A63582CF9834F4E7F058 /* ScreenContextTests.swift */; };
|
||||||
|
D4CEB4B895F75D91FA892A06 /* PopoutWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB593A2546BF2C0BE8E40387 /* PopoutWindowController.swift */; };
|
||||||
|
DAD3AB4A0DAADA32C02D959E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3125FD3DC55420122CF85D80 /* SettingsView.swift */; };
|
||||||
|
DCFD5B03E64A46783F46726B /* TerminalCommandArrowBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22AA47452CF798A977A6F47 /* TerminalCommandArrowBehavior.swift */; };
|
||||||
|
E63FD5862C0EC54E284F6A0F /* ScreenRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAF23753B5A0CAF04D7566A3 /* ScreenRegistry.swift */; };
|
||||||
|
E792411FA82E79E810F4B4C3 /* HotkeyRecorderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B81432CECBDB61D21EE4DC3 /* HotkeyRecorderView.swift */; };
|
||||||
|
EE72479BA5A25FF31BACCC50 /* NotchOrchestrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7912AF01E1600B8619AF31 /* NotchOrchestrator.swift */; };
|
||||||
|
F8ED522FDC96B1F9AD8933F7 /* ScreenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB8AF76C0F728897A26D7EF /* ScreenManager.swift */; };
|
||||||
|
F8F8DE2F26608259D72635B7 /* AppSettingsControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F5FF5623898FA150C3B70D4 /* AppSettingsControllerTests.swift */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
13E720E97D079D298D124BBE /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = E99C2EDB39F7B64C1540BCA8 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = D5585E5732CD067DF6EF0C69;
|
||||||
|
remoteInfo = CommandNotch;
|
||||||
|
};
|
||||||
|
9D6387038E18398CA764147F /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = E99C2EDB39F7B64C1540BCA8 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = D5585E5732CD067DF6EF0C69;
|
||||||
|
remoteInfo = CommandNotch;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
0E97758F68FACCFFACA895B7 /* WorkspaceRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceRegistry.swift; sourceTree = "<group>"; };
|
||||||
|
27E7EEEDFBD8D9CEB8FD86A5 /* GeneralSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsView.swift; sourceTree = "<group>"; };
|
||||||
|
297BA3E5B837FBDDEED9DE66 /* NotchSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchSettings.swift; sourceTree = "<group>"; };
|
||||||
|
2B81432CECBDB61D21EE4DC3 /* HotkeyRecorderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeyRecorderView.swift; sourceTree = "<group>"; };
|
||||||
|
3125FD3DC55420122CF85D80 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||||
|
35CBC14E11EBD8486457CE91 /* CommandNotchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CommandNotchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
39CA6F2C2CA32D0AA58F6C43 /* AppSettingsStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsStoreTests.swift; sourceTree = "<group>"; };
|
||||||
|
3CB1DFD6FCDF64B4DF24230A /* WorkspaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceController.swift; sourceTree = "<group>"; };
|
||||||
|
3F57837A7115DEEE11E14B40 /* NotchShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchShape.swift; sourceTree = "<group>"; };
|
||||||
|
3F5FF5623898FA150C3B70D4 /* AppSettingsControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsControllerTests.swift; sourceTree = "<group>"; };
|
||||||
|
48198AFE5473B0F7AECAB3FB /* AboutSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutSettingsView.swift; sourceTree = "<group>"; };
|
||||||
|
496267F03E261FEC9EBD5A9D /* CommandNotchUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CommandNotchUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
49E1791BB45E1505500ACC67 /* TerminalSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSession.swift; sourceTree = "<group>"; };
|
||||||
|
4B352301BC9CAD7C9D8B7AA9 /* NotchState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchState.swift; sourceTree = "<group>"; };
|
||||||
|
4FA62DFE9AD003F4C5B55F14 /* WorkspaceStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceStoreTests.swift; sourceTree = "<group>"; };
|
||||||
|
5159CB9DBE2BAA0D2E201C39 /* CommandNotchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandNotchApp.swift; sourceTree = "<group>"; };
|
||||||
|
567E85A2ED628460CEC760DB /* TerminalManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalManager.swift; sourceTree = "<group>"; };
|
||||||
|
591FCE91AF83A8A8E44E1625 /* WorkspaceRegistryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceRegistryTests.swift; sourceTree = "<group>"; };
|
||||||
|
5DDC7451F0E7ACFE0BEC5473 /* NSScreen+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSScreen+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
6ADD641A088A5005DD0EB4A6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
7181BB1F3926B457445105E5 /* ScreenContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenContext.swift; sourceTree = "<group>"; };
|
||||||
|
726B935606FD961FD7E8C2BE /* CommandNotchUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandNotchUITests.swift; sourceTree = "<group>"; };
|
||||||
|
728B3125F7F7FDB7313D2DC6 /* SettingsWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsWindowController.swift; sourceTree = "<group>"; };
|
||||||
|
72A1D3D12BAC593838B3125C /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = "<group>"; };
|
||||||
|
74463E4EAB78F56345360CD5 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
||||||
|
7B2BCA543CE54DAB1DB80E43 /* WorkspaceSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceSummary.swift; sourceTree = "<group>"; };
|
||||||
|
8210D7783A614FD7190F5DDD /* LaunchAtLoginHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAtLoginHelper.swift; sourceTree = "<group>"; };
|
||||||
|
8796768C84519077354A95C7 /* CommandNotch.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CommandNotch.entitlements; sourceTree = "<group>"; };
|
||||||
|
8B3C8C11834931A9D53BC2B6 /* AppearanceSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceSettingsView.swift; sourceTree = "<group>"; };
|
||||||
|
8BB1C403BC2157756F572ACF /* HotkeyBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeyBinding.swift; sourceTree = "<group>"; };
|
||||||
|
8CB8AF76C0F728897A26D7EF /* ScreenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenManager.swift; sourceTree = "<group>"; };
|
||||||
|
900F0476BE9E3600FBD371BB /* SettingsBindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsBindings.swift; sourceTree = "<group>"; };
|
||||||
|
9C55F29B779DA0E5C5FC8627 /* SwiftTermView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftTermView.swift; sourceTree = "<group>"; };
|
||||||
|
9E6C98C406899F4B242075AF /* AppSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsStore.swift; sourceTree = "<group>"; };
|
||||||
|
A64A11F27E65B342B991629A /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||||
|
A770A63582CF9834F4E7F058 /* ScreenContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenContextTests.swift; sourceTree = "<group>"; };
|
||||||
|
AAF23753B5A0CAF04D7566A3 /* ScreenRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenRegistry.swift; sourceTree = "<group>"; };
|
||||||
|
B5AA68DC0DD3FE07B56EB6EC /* CommandNotch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CommandNotch.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
C0D19729317029008D81F361 /* TerminalCommandArrowBehaviorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalCommandArrowBehaviorTests.swift; sourceTree = "<group>"; };
|
||||||
|
CB593A2546BF2C0BE8E40387 /* PopoutWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoutWindowController.swift; sourceTree = "<group>"; };
|
||||||
|
CFE9E5BADB0C427903A0D874 /* TerminalTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalTheme.swift; sourceTree = "<group>"; };
|
||||||
|
D03D042117E59DCA9D553844 /* HotkeySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeySettingsView.swift; sourceTree = "<group>"; };
|
||||||
|
D288132700770C4A625A15F6 /* WindowFrameCalculatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowFrameCalculatorTests.swift; sourceTree = "<group>"; };
|
||||||
|
DC7912AF01E1600B8619AF31 /* NotchOrchestrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchOrchestrator.swift; sourceTree = "<group>"; };
|
||||||
|
DF0FFBC96F2446687D6474F4 /* WorkspaceSwitcherView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceSwitcherView.swift; sourceTree = "<group>"; };
|
||||||
|
E37A6DCD9C5DE1FE11C4C1CD /* TerminalSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSettingsView.swift; sourceTree = "<group>"; };
|
||||||
|
E7A8DCFA77626F9999B432FE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
EB28950392C0198E69F3564B /* AppSettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsController.swift; sourceTree = "<group>"; };
|
||||||
|
EEB7FE2BDE5C25B7E599F340 /* HotkeyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeyManager.swift; sourceTree = "<group>"; };
|
||||||
|
EEC7F7D8D15A1BC4EE43DDDB /* ScreenRegistryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenRegistryTests.swift; sourceTree = "<group>"; };
|
||||||
|
EFAC70814C72BAF76D90B9DF /* NotchWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchWindow.swift; sourceTree = "<group>"; };
|
||||||
|
F22AA47452CF798A977A6F47 /* TerminalCommandArrowBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalCommandArrowBehavior.swift; sourceTree = "<group>"; };
|
||||||
|
F3B957BD1F6120D2592613ED /* WorkspaceStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceStore.swift; sourceTree = "<group>"; };
|
||||||
|
F70A31EFACF23DD9262A040E /* WorkspacesSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspacesSettingsView.swift; sourceTree = "<group>"; };
|
||||||
|
F9220E02B6D470DD05CA540C /* WindowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowCoordinator.swift; sourceTree = "<group>"; };
|
||||||
|
FE99DBAB656EA80A117D2EE1 /* AnimationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimationSettingsView.swift; sourceTree = "<group>"; };
|
||||||
|
FF6D136A0B3FC79DDE12A826 /* NotchOrchestratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchOrchestratorTests.swift; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
F3D057FF4247F13A1BBAE547 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
5379DF2FACE924BDDB584377 /* SwiftTerm in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
00BC30DD7FA5C3C26404733B /* Models */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
74463E4EAB78F56345360CD5 /* AppSettings.swift */,
|
||||||
|
EB28950392C0198E69F3564B /* AppSettingsController.swift */,
|
||||||
|
9E6C98C406899F4B242075AF /* AppSettingsStore.swift */,
|
||||||
|
8BB1C403BC2157756F572ACF /* HotkeyBinding.swift */,
|
||||||
|
DC7912AF01E1600B8619AF31 /* NotchOrchestrator.swift */,
|
||||||
|
297BA3E5B837FBDDEED9DE66 /* NotchSettings.swift */,
|
||||||
|
4B352301BC9CAD7C9D8B7AA9 /* NotchState.swift */,
|
||||||
|
7181BB1F3926B457445105E5 /* ScreenContext.swift */,
|
||||||
|
AAF23753B5A0CAF04D7566A3 /* ScreenRegistry.swift */,
|
||||||
|
567E85A2ED628460CEC760DB /* TerminalManager.swift */,
|
||||||
|
49E1791BB45E1505500ACC67 /* TerminalSession.swift */,
|
||||||
|
CFE9E5BADB0C427903A0D874 /* TerminalTheme.swift */,
|
||||||
|
3CB1DFD6FCDF64B4DF24230A /* WorkspaceController.swift */,
|
||||||
|
0E97758F68FACCFFACA895B7 /* WorkspaceRegistry.swift */,
|
||||||
|
F3B957BD1F6120D2592613ED /* WorkspaceStore.swift */,
|
||||||
|
7B2BCA543CE54DAB1DB80E43 /* WorkspaceSummary.swift */,
|
||||||
|
);
|
||||||
|
path = Models;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
618799FE544A4373B457DCDA /* Extensions */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5DDC7451F0E7ACFE0BEC5473 /* NSScreen+Extensions.swift */,
|
||||||
|
);
|
||||||
|
path = Extensions;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
7043235A31A4023478DA1302 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
84740FA9CF6A18B35EC82623 /* CommandNotch */,
|
||||||
|
A2F9603AB9C86C4EA62FFA59 /* CommandNotchTests */,
|
||||||
|
E6C841E864A0CC68B9B05BAC /* CommandNotchUITests */,
|
||||||
|
B269158E04E8E603B61448F0 /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
84740FA9CF6A18B35EC82623 /* CommandNotch */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
E7A8DCFA77626F9999B432FE /* AppDelegate.swift */,
|
||||||
|
5159CB9DBE2BAA0D2E201C39 /* CommandNotchApp.swift */,
|
||||||
|
A64A11F27E65B342B991629A /* ContentView.swift */,
|
||||||
|
D3C008AD1EFEF08E3417396F /* Components */,
|
||||||
|
618799FE544A4373B457DCDA /* Extensions */,
|
||||||
|
D87DBBE2E8779343A36F3490 /* Managers */,
|
||||||
|
00BC30DD7FA5C3C26404733B /* Models */,
|
||||||
|
C2F0251EB52960C9F437154D /* Resources */,
|
||||||
|
DEE792769214DF028395EA86 /* Views */,
|
||||||
|
);
|
||||||
|
path = CommandNotch;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
A2F9603AB9C86C4EA62FFA59 /* CommandNotchTests */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3F5FF5623898FA150C3B70D4 /* AppSettingsControllerTests.swift */,
|
||||||
|
39CA6F2C2CA32D0AA58F6C43 /* AppSettingsStoreTests.swift */,
|
||||||
|
FF6D136A0B3FC79DDE12A826 /* NotchOrchestratorTests.swift */,
|
||||||
|
A770A63582CF9834F4E7F058 /* ScreenContextTests.swift */,
|
||||||
|
EEC7F7D8D15A1BC4EE43DDDB /* ScreenRegistryTests.swift */,
|
||||||
|
C0D19729317029008D81F361 /* TerminalCommandArrowBehaviorTests.swift */,
|
||||||
|
D288132700770C4A625A15F6 /* WindowFrameCalculatorTests.swift */,
|
||||||
|
591FCE91AF83A8A8E44E1625 /* WorkspaceRegistryTests.swift */,
|
||||||
|
4FA62DFE9AD003F4C5B55F14 /* WorkspaceStoreTests.swift */,
|
||||||
|
);
|
||||||
|
path = CommandNotchTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
B269158E04E8E603B61448F0 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
B5AA68DC0DD3FE07B56EB6EC /* CommandNotch.app */,
|
||||||
|
35CBC14E11EBD8486457CE91 /* CommandNotchTests.xctest */,
|
||||||
|
496267F03E261FEC9EBD5A9D /* CommandNotchUITests.xctest */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C2F0251EB52960C9F437154D /* Resources */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
6ADD641A088A5005DD0EB4A6 /* Assets.xcassets */,
|
||||||
|
8796768C84519077354A95C7 /* CommandNotch.entitlements */,
|
||||||
|
);
|
||||||
|
path = Resources;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D3C008AD1EFEF08E3417396F /* Components */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2B81432CECBDB61D21EE4DC3 /* HotkeyRecorderView.swift */,
|
||||||
|
3F57837A7115DEEE11E14B40 /* NotchShape.swift */,
|
||||||
|
EFAC70814C72BAF76D90B9DF /* NotchWindow.swift */,
|
||||||
|
9C55F29B779DA0E5C5FC8627 /* SwiftTermView.swift */,
|
||||||
|
72A1D3D12BAC593838B3125C /* TabBar.swift */,
|
||||||
|
F22AA47452CF798A977A6F47 /* TerminalCommandArrowBehavior.swift */,
|
||||||
|
);
|
||||||
|
path = Components;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D87DBBE2E8779343A36F3490 /* Managers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EEB7FE2BDE5C25B7E599F340 /* HotkeyManager.swift */,
|
||||||
|
8210D7783A614FD7190F5DDD /* LaunchAtLoginHelper.swift */,
|
||||||
|
CB593A2546BF2C0BE8E40387 /* PopoutWindowController.swift */,
|
||||||
|
8CB8AF76C0F728897A26D7EF /* ScreenManager.swift */,
|
||||||
|
728B3125F7F7FDB7313D2DC6 /* SettingsWindowController.swift */,
|
||||||
|
F9220E02B6D470DD05CA540C /* WindowCoordinator.swift */,
|
||||||
|
);
|
||||||
|
path = Managers;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
DEE792769214DF028395EA86 /* Views */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
48198AFE5473B0F7AECAB3FB /* AboutSettingsView.swift */,
|
||||||
|
FE99DBAB656EA80A117D2EE1 /* AnimationSettingsView.swift */,
|
||||||
|
8B3C8C11834931A9D53BC2B6 /* AppearanceSettingsView.swift */,
|
||||||
|
27E7EEEDFBD8D9CEB8FD86A5 /* GeneralSettingsView.swift */,
|
||||||
|
D03D042117E59DCA9D553844 /* HotkeySettingsView.swift */,
|
||||||
|
900F0476BE9E3600FBD371BB /* SettingsBindings.swift */,
|
||||||
|
3125FD3DC55420122CF85D80 /* SettingsView.swift */,
|
||||||
|
E37A6DCD9C5DE1FE11C4C1CD /* TerminalSettingsView.swift */,
|
||||||
|
F70A31EFACF23DD9262A040E /* WorkspacesSettingsView.swift */,
|
||||||
|
DF0FFBC96F2446687D6474F4 /* WorkspaceSwitcherView.swift */,
|
||||||
|
);
|
||||||
|
path = Views;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
E6C841E864A0CC68B9B05BAC /* CommandNotchUITests */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
726B935606FD961FD7E8C2BE /* CommandNotchUITests.swift */,
|
||||||
|
);
|
||||||
|
path = CommandNotchUITests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
036FDAECD12C0A679DA1F5D6 /* CommandNotchTests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 983C618ACECB88BD023F005E /* Build configuration list for PBXNativeTarget "CommandNotchTests" */;
|
||||||
|
buildPhases = (
|
||||||
|
4108B7D3B592DEBB95C689C4 /* Sources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
316871D68B87C00F5A8FEECC /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = CommandNotchTests;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = CommandNotchTests;
|
||||||
|
productReference = 35CBC14E11EBD8486457CE91 /* CommandNotchTests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
|
};
|
||||||
|
1C8D00CBB29219BD347E9CC4 /* CommandNotchUITests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = CEAE976FCC3F5BD54E57E585 /* Build configuration list for PBXNativeTarget "CommandNotchUITests" */;
|
||||||
|
buildPhases = (
|
||||||
|
1215938A5211847A086CC444 /* Sources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
F9E4A521E345300B08E257EA /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = CommandNotchUITests;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = CommandNotchUITests;
|
||||||
|
productReference = 496267F03E261FEC9EBD5A9D /* CommandNotchUITests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.ui-testing";
|
||||||
|
};
|
||||||
|
D5585E5732CD067DF6EF0C69 /* CommandNotch */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = E599EC932C075AF0FD763A0E /* Build configuration list for PBXNativeTarget "CommandNotch" */;
|
||||||
|
buildPhases = (
|
||||||
|
D5B7874B63551D908A4B76C8 /* Sources */,
|
||||||
|
3BD8FCDCDA6E37ED22A35CA5 /* Resources */,
|
||||||
|
F3D057FF4247F13A1BBAE547 /* Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = CommandNotch;
|
||||||
|
packageProductDependencies = (
|
||||||
|
C921E6435A64AA07A0FEA4D5 /* SwiftTerm */,
|
||||||
|
);
|
||||||
|
productName = CommandNotch;
|
||||||
|
productReference = B5AA68DC0DD3FE07B56EB6EC /* CommandNotch.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
E99C2EDB39F7B64C1540BCA8 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = YES;
|
||||||
|
LastUpgradeCheck = 1600;
|
||||||
|
TargetAttributes = {
|
||||||
|
1C8D00CBB29219BD347E9CC4 = {
|
||||||
|
DevelopmentTeam = G698BP272N;
|
||||||
|
TestTargetID = D5585E5732CD067DF6EF0C69;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = C47A3896770C98F2A3E62B7A /* Build configuration list for PBXProject "CommandNotch" */;
|
||||||
|
compatibilityVersion = "Xcode 14.0";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
Base,
|
||||||
|
en,
|
||||||
|
);
|
||||||
|
mainGroup = 7043235A31A4023478DA1302;
|
||||||
|
minimizedProjectReferenceProxies = 1;
|
||||||
|
packageReferences = (
|
||||||
|
28377BE3F9997892D4929B6E /* XCRemoteSwiftPackageReference "SwiftTerm" */,
|
||||||
|
);
|
||||||
|
preferredProjectObjectVersion = 77;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
D5585E5732CD067DF6EF0C69 /* CommandNotch */,
|
||||||
|
036FDAECD12C0A679DA1F5D6 /* CommandNotchTests */,
|
||||||
|
1C8D00CBB29219BD347E9CC4 /* CommandNotchUITests */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
3BD8FCDCDA6E37ED22A35CA5 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
7AB14019B5CC6ED84B96FA47 /* Assets.xcassets in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
1215938A5211847A086CC444 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
278010607B0D552DCC8996C5 /* CommandNotchUITests.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
4108B7D3B592DEBB95C689C4 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
F8F8DE2F26608259D72635B7 /* AppSettingsControllerTests.swift in Sources */,
|
||||||
|
7D5F3C3B5E173B6F06901812 /* AppSettingsStoreTests.swift in Sources */,
|
||||||
|
2089566A2BBAA65EA82119B3 /* NotchOrchestratorTests.swift in Sources */,
|
||||||
|
D2468B19D6F0A2C1DFDFE2B7 /* ScreenContextTests.swift in Sources */,
|
||||||
|
8A1B2A4CD61B0D9A4BAB075B /* ScreenRegistryTests.swift in Sources */,
|
||||||
|
CFBBE994BA6BAD4658AAB9CB /* TerminalCommandArrowBehaviorTests.swift in Sources */,
|
||||||
|
0F133E8A88D2E313D90C32AD /* WindowFrameCalculatorTests.swift in Sources */,
|
||||||
|
154F363D434A26105C5999B5 /* WorkspaceRegistryTests.swift in Sources */,
|
||||||
|
2375B9DA559A0777FE558A8B /* WorkspaceStoreTests.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
D5B7874B63551D908A4B76C8 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
B605A9311557251A85183383 /* AboutSettingsView.swift in Sources */,
|
||||||
|
88D87155D79C493D8956AA3B /* AnimationSettingsView.swift in Sources */,
|
||||||
|
6DD4DAE72E5C3858B230D94C /* AppDelegate.swift in Sources */,
|
||||||
|
7A7D3455D50D041CF698E786 /* AppSettings.swift in Sources */,
|
||||||
|
B406183CC7B5DCE76989A066 /* AppSettingsController.swift in Sources */,
|
||||||
|
4D335F67B71F7DD977B6AEF9 /* AppSettingsStore.swift in Sources */,
|
||||||
|
0DE8E7F29FBAA7E2B613E221 /* AppearanceSettingsView.swift in Sources */,
|
||||||
|
D088BC850F37E717844761C6 /* CommandNotchApp.swift in Sources */,
|
||||||
|
C84FA3100884649FE92BF5DD /* ContentView.swift in Sources */,
|
||||||
|
34AF69BF7AF8DC78ADE3774A /* GeneralSettingsView.swift in Sources */,
|
||||||
|
9F535B9516A4AE9FB536B1BD /* HotkeyBinding.swift in Sources */,
|
||||||
|
187F4B521BFC3BD29ADA79E3 /* HotkeyManager.swift in Sources */,
|
||||||
|
E792411FA82E79E810F4B4C3 /* HotkeyRecorderView.swift in Sources */,
|
||||||
|
6A18F6635B509FF58669F505 /* HotkeySettingsView.swift in Sources */,
|
||||||
|
12F68EDA880030DAB644FF5F /* LaunchAtLoginHelper.swift in Sources */,
|
||||||
|
A74E14B8AD19C53820853D8E /* NSScreen+Extensions.swift in Sources */,
|
||||||
|
EE72479BA5A25FF31BACCC50 /* NotchOrchestrator.swift in Sources */,
|
||||||
|
3B69CB3CDEC2E5F2DCE600F9 /* NotchSettings.swift in Sources */,
|
||||||
|
088FE22B3308AAB25027E086 /* NotchShape.swift in Sources */,
|
||||||
|
23E2DDCF36D0DAB2EA72C39C /* NotchState.swift in Sources */,
|
||||||
|
4E0AD14B7427532271E485AA /* NotchWindow.swift in Sources */,
|
||||||
|
D4CEB4B895F75D91FA892A06 /* PopoutWindowController.swift in Sources */,
|
||||||
|
787477BB6D2F8AC723EEDA7D /* ScreenContext.swift in Sources */,
|
||||||
|
F8ED522FDC96B1F9AD8933F7 /* ScreenManager.swift in Sources */,
|
||||||
|
E63FD5862C0EC54E284F6A0F /* ScreenRegistry.swift in Sources */,
|
||||||
|
771088361F981A9AAE976F3C /* SettingsBindings.swift in Sources */,
|
||||||
|
DAD3AB4A0DAADA32C02D959E /* SettingsView.swift in Sources */,
|
||||||
|
5A3FDFCF30A1AAFE070290E9 /* SettingsWindowController.swift in Sources */,
|
||||||
|
5F3534F66A5DBE4E081AFFA6 /* SwiftTermView.swift in Sources */,
|
||||||
|
88113BA9B217DA579C36BEBE /* TabBar.swift in Sources */,
|
||||||
|
DCFD5B03E64A46783F46726B /* TerminalCommandArrowBehavior.swift in Sources */,
|
||||||
|
6F249EDFA2D654457DF385F1 /* TerminalManager.swift in Sources */,
|
||||||
|
7A69B5AAC686174BCA54D0F0 /* TerminalSession.swift in Sources */,
|
||||||
|
65C7DB7296C6C6A77598A1F4 /* TerminalSettingsView.swift in Sources */,
|
||||||
|
1AB4A0F1BE668D3130EFBA93 /* TerminalTheme.swift in Sources */,
|
||||||
|
507A67E770DEFAF5BC321FCF /* WindowCoordinator.swift in Sources */,
|
||||||
|
86BB4A41E8EC44A5F45CE995 /* WorkspaceController.swift in Sources */,
|
||||||
|
4CFA0C79095ACE0A327A2469 /* WorkspaceRegistry.swift in Sources */,
|
||||||
|
AE8215538D4B026A00BAD241 /* WorkspaceStore.swift in Sources */,
|
||||||
|
2DF22798D3A7514E2A9183FC /* WorkspaceSummary.swift in Sources */,
|
||||||
|
6624A60CB121A22A03365071 /* WorkspaceSwitcherView.swift in Sources */,
|
||||||
|
26AE379040149CBE05B314BB /* WorkspacesSettingsView.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
316871D68B87C00F5A8FEECC /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = D5585E5732CD067DF6EF0C69 /* CommandNotch */;
|
||||||
|
targetProxy = 13E720E97D079D298D124BBE /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
F9E4A521E345300B08E257EA /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = D5585E5732CD067DF6EF0C69 /* CommandNotch */;
|
||||||
|
targetProxy = 9D6387038E18398CA764147F /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
15F1D354AC7D5666A8317E25 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = CommandNotch/Resources/CommandNotch.entitlements;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
INFOPLIST_FILE = CommandNotch/Resources/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.app;
|
||||||
|
PRODUCT_NAME = CommandNotch;
|
||||||
|
SDKROOT = macosx;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
4C713B8FE8B6293021AB974F /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
"@loader_path/../Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.CommandNotchTests;
|
||||||
|
PRODUCT_NAME = CommandNotchTests;
|
||||||
|
SDKROOT = macosx;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CommandNotch.app/Contents/MacOS/CommandNotch";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
7D032F3A06E313F1F92D39EC /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"DEBUG=1",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.10;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
860E4EAD454534A04683E7DD /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = CommandNotch/Resources/CommandNotch.entitlements;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
INFOPLIST_FILE = CommandNotch/Resources/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.app;
|
||||||
|
PRODUCT_NAME = CommandNotch;
|
||||||
|
SDKROOT = macosx;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
87EEF9DE40EE78121DC1E68B /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
SWIFT_VERSION = 5.10;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
C9CA7CD89BF1C9A9BC98C4CF /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
DEVELOPMENT_TEAM = G698BP272N;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@loader_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.CommandNotchUITests;
|
||||||
|
PRODUCT_NAME = CommandNotchUITests;
|
||||||
|
SDKROOT = macosx;
|
||||||
|
TEST_TARGET_NAME = CommandNotch;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
E52824EDC7F4752F43B6301A /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
"@loader_path/../Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.CommandNotchTests;
|
||||||
|
PRODUCT_NAME = CommandNotchTests;
|
||||||
|
SDKROOT = macosx;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CommandNotch.app/Contents/MacOS/CommandNotch";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
F697F3FF95C1EB110FC25A5C /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
DEVELOPMENT_TEAM = G698BP272N;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@loader_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.CommandNotchUITests;
|
||||||
|
PRODUCT_NAME = CommandNotchUITests;
|
||||||
|
SDKROOT = macosx;
|
||||||
|
TEST_TARGET_NAME = CommandNotch;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
983C618ACECB88BD023F005E /* Build configuration list for PBXNativeTarget "CommandNotchTests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
E52824EDC7F4752F43B6301A /* Debug */,
|
||||||
|
4C713B8FE8B6293021AB974F /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Debug;
|
||||||
|
};
|
||||||
|
C47A3896770C98F2A3E62B7A /* Build configuration list for PBXProject "CommandNotch" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
7D032F3A06E313F1F92D39EC /* Debug */,
|
||||||
|
87EEF9DE40EE78121DC1E68B /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Debug;
|
||||||
|
};
|
||||||
|
CEAE976FCC3F5BD54E57E585 /* Build configuration list for PBXNativeTarget "CommandNotchUITests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
C9CA7CD89BF1C9A9BC98C4CF /* Debug */,
|
||||||
|
F697F3FF95C1EB110FC25A5C /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Debug;
|
||||||
|
};
|
||||||
|
E599EC932C075AF0FD763A0E /* Build configuration list for PBXNativeTarget "CommandNotch" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
15F1D354AC7D5666A8317E25 /* Debug */,
|
||||||
|
860E4EAD454534A04683E7DD /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Debug;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
28377BE3F9997892D4929B6E /* XCRemoteSwiftPackageReference "SwiftTerm" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/migueldeicaza/SwiftTerm.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 1.2.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
C921E6435A64AA07A0FEA4D5 /* SwiftTerm */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 28377BE3F9997892D4929B6E /* XCRemoteSwiftPackageReference "SwiftTerm" */;
|
||||||
|
productName = SwiftTerm;
|
||||||
|
};
|
||||||
|
/* End XCSwiftPackageProductDependency section */
|
||||||
|
};
|
||||||
|
rootObject = E99C2EDB39F7B64C1540BCA8 /* Project object */;
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "2620"
|
LastUpgradeVersion = "1600"
|
||||||
version = "1.7">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
buildImplicitDependencies = "YES"
|
buildImplicitDependencies = "YES"
|
||||||
buildArchitectures = "Automatic">
|
runPostActionsOnFailure = "NO">
|
||||||
<BuildActionEntries>
|
<BuildActionEntries>
|
||||||
<BuildActionEntry
|
<BuildActionEntry
|
||||||
buildForTesting = "YES"
|
buildForTesting = "YES"
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
buildForAnalyzing = "YES">
|
buildForAnalyzing = "YES">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||||
BuildableName = "CommandNotch.app"
|
BuildableName = "CommandNotch.app"
|
||||||
BlueprintName = "CommandNotch"
|
BlueprintName = "CommandNotch"
|
||||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
@@ -28,7 +28,42 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
shouldAutocreateTestPlan = "YES">
|
onlyGenerateCoverageForSpecifiedTargets = "NO">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||||
|
BuildableName = "CommandNotch.app"
|
||||||
|
BlueprintName = "CommandNotch"
|
||||||
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "036FDAECD12C0A679DA1F5D6"
|
||||||
|
BuildableName = "CommandNotchTests.xctest"
|
||||||
|
BlueprintName = "CommandNotchTests"
|
||||||
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "1C8D00CBB29219BD347E9CC4"
|
||||||
|
BuildableName = "CommandNotchUITests.xctest"
|
||||||
|
BlueprintName = "CommandNotchUITests"
|
||||||
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
<CommandLineArguments>
|
||||||
|
</CommandLineArguments>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@@ -44,12 +79,14 @@
|
|||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||||
BuildableName = "CommandNotch.app"
|
BuildableName = "CommandNotch.app"
|
||||||
BlueprintName = "CommandNotch"
|
BlueprintName = "CommandNotch"
|
||||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
<CommandLineArguments>
|
||||||
|
</CommandLineArguments>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
@@ -61,12 +98,14 @@
|
|||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||||
BuildableName = "CommandNotch.app"
|
BuildableName = "CommandNotch.app"
|
||||||
BlueprintName = "CommandNotch"
|
BlueprintName = "CommandNotch"
|
||||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
<CommandLineArguments>
|
||||||
|
</CommandLineArguments>
|
||||||
</ProfileAction>
|
</ProfileAction>
|
||||||
<AnalyzeAction
|
<AnalyzeAction
|
||||||
buildConfiguration = "Debug">
|
buildConfiguration = "Debug">
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "2620"
|
LastUpgradeVersion = "1600"
|
||||||
version = "1.7">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
buildImplicitDependencies = "YES"
|
buildImplicitDependencies = "YES"
|
||||||
buildArchitectures = "Automatic">
|
runPostActionsOnFailure = "NO">
|
||||||
<BuildActionEntries>
|
<BuildActionEntries>
|
||||||
<BuildActionEntry
|
<BuildActionEntry
|
||||||
buildForTesting = "YES"
|
buildForTesting = "YES"
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
buildForAnalyzing = "YES">
|
buildForAnalyzing = "YES">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||||
BuildableName = "CommandNotch.app"
|
BuildableName = "CommandNotch.app"
|
||||||
BlueprintName = "CommandNotch"
|
BlueprintName = "CommandNotch"
|
||||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
@@ -28,13 +28,48 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
shouldAutocreateTestPlan = "YES">
|
onlyGenerateCoverageForSpecifiedTargets = "NO">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||||
|
BuildableName = "CommandNotch.app"
|
||||||
|
BlueprintName = "CommandNotch"
|
||||||
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "036FDAECD12C0A679DA1F5D6"
|
||||||
|
BuildableName = "CommandNotchTests.xctest"
|
||||||
|
BlueprintName = "CommandNotchTests"
|
||||||
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "1C8D00CBB29219BD347E9CC4"
|
||||||
|
BuildableName = "CommandNotchUITests.xctest"
|
||||||
|
BlueprintName = "CommandNotchUITests"
|
||||||
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
<CommandLineArguments>
|
||||||
|
</CommandLineArguments>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
selectedDebuggerIdentifier = ""
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
launchStyle = "1"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
@@ -44,12 +79,14 @@
|
|||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||||
BuildableName = "CommandNotch.app"
|
BuildableName = "CommandNotch.app"
|
||||||
BlueprintName = "CommandNotch"
|
BlueprintName = "CommandNotch"
|
||||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
<CommandLineArguments>
|
||||||
|
</CommandLineArguments>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
@@ -61,12 +98,14 @@
|
|||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||||
BuildableName = "CommandNotch.app"
|
BuildableName = "CommandNotch.app"
|
||||||
BlueprintName = "CommandNotch"
|
BlueprintName = "CommandNotch"
|
||||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
<CommandLineArguments>
|
||||||
|
</CommandLineArguments>
|
||||||
</ProfileAction>
|
</ProfileAction>
|
||||||
<AnalyzeAction
|
<AnalyzeAction
|
||||||
buildConfiguration = "Debug">
|
buildConfiguration = "Debug">
|
||||||
@@ -76,9 +76,7 @@ struct HotkeyRecorderField: NSViewRepresentable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateNSView(_ nsView: HotkeyNSView, context: Context) {
|
func updateNSView(_ nsView: HotkeyNSView, context: Context) {
|
||||||
nsView.currentLabel = binding.displayString
|
nsView.update(currentLabel: binding.displayString, isRecording: isRecording)
|
||||||
nsView.showRecording = isRecording
|
|
||||||
nsView.needsDisplay = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,9 +97,7 @@ struct OptionalHotkeyRecorderField: NSViewRepresentable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateNSView(_ nsView: HotkeyNSView, context: Context) {
|
func updateNSView(_ nsView: HotkeyNSView, context: Context) {
|
||||||
nsView.currentLabel = binding?.displayString ?? "Not set"
|
nsView.update(currentLabel: binding?.displayString ?? "Not set", isRecording: isRecording)
|
||||||
nsView.showRecording = isRecording
|
|
||||||
nsView.needsDisplay = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,6 +179,12 @@ class HotkeyNSView: NSView {
|
|||||||
updateLabelAppearance()
|
updateLabelAppearance()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func update(currentLabel: String, isRecording: Bool) {
|
||||||
|
self.currentLabel = currentLabel
|
||||||
|
showRecording = isRecording
|
||||||
|
updateLabelAppearance()
|
||||||
|
}
|
||||||
|
|
||||||
private func updateLabelAppearance() {
|
private func updateLabelAppearance() {
|
||||||
label.stringValue = showRecording ? "Press keys..." : currentLabel
|
label.stringValue = showRecording ? "Press keys..." : currentLabel
|
||||||
label.textColor = showRecording ? .controlAccentColor : .labelColor
|
label.textColor = showRecording ? .controlAccentColor : .labelColor
|
||||||
@@ -9,17 +9,6 @@ struct TabBar: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
|
||||||
HStack(spacing: 2) {
|
|
||||||
ForEach(Array(workspace.tabs.enumerated()), id: \.element.id) { index, tab in
|
|
||||||
tabButton(for: tab, at: index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
workspace.newTab()
|
workspace.newTab()
|
||||||
} label: {
|
} label: {
|
||||||
@@ -31,6 +20,15 @@ struct TabBar: View {
|
|||||||
.accessibilityIdentifier("notch.new-tab")
|
.accessibilityIdentifier("notch.new-tab")
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
.padding(.horizontal, 8)
|
.padding(.horizontal, 8)
|
||||||
|
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
HStack(spacing: 2) {
|
||||||
|
ForEach(Array(workspace.tabs.enumerated()), id: \.element.id) { index, tab in
|
||||||
|
tabButton(for: tab, at: index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 4)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.frame(height: 28)
|
.frame(height: 28)
|
||||||
.background(.black)
|
.background(.black)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import AppKit
|
||||||
|
import Carbon.HIToolbox
|
||||||
|
import SwiftTerm
|
||||||
|
|
||||||
|
enum TerminalCommandArrowBehavior {
|
||||||
|
private static let relevantModifiers: NSEvent.ModifierFlags = [.command, .control, .option, .shift]
|
||||||
|
private static let lineKill: [UInt8] = [0x15]
|
||||||
|
private static let clearScreen: [UInt8] = [0x0c]
|
||||||
|
|
||||||
|
static func sequence(
|
||||||
|
for modifierFlags: NSEvent.ModifierFlags,
|
||||||
|
keyCode: UInt16,
|
||||||
|
applicationCursor: Bool
|
||||||
|
) -> [UInt8]? {
|
||||||
|
let flags = modifierFlags.intersection(relevantModifiers)
|
||||||
|
guard flags == [.command] else { return nil }
|
||||||
|
|
||||||
|
switch Int(keyCode) {
|
||||||
|
case kVK_LeftArrow:
|
||||||
|
return applicationCursor ? EscapeSequences.moveHomeApp : EscapeSequences.moveHomeNormal
|
||||||
|
case kVK_RightArrow:
|
||||||
|
return applicationCursor ? EscapeSequences.moveEndApp : EscapeSequences.moveEndNormal
|
||||||
|
case kVK_Delete:
|
||||||
|
return lineKill
|
||||||
|
case kVK_ANSI_L:
|
||||||
|
return clearScreen
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import Combine
|
|||||||
/// Manages global and local hotkeys.
|
/// Manages global and local hotkeys.
|
||||||
///
|
///
|
||||||
/// The toggle hotkey uses Carbon's `RegisterEventHotKey` which works
|
/// The toggle hotkey uses Carbon's `RegisterEventHotKey` which works
|
||||||
/// system-wide without Accessibility permission. Tab-level hotkeys
|
/// system-wide without Accessibility permission. Notch-scoped hotkeys
|
||||||
/// use a local `NSEvent` monitor (only fires when our app is active).
|
/// use a local `NSEvent` monitor (only fires when our app is active).
|
||||||
@MainActor
|
@MainActor
|
||||||
class HotkeyManager {
|
class HotkeyManager {
|
||||||
@@ -19,21 +19,29 @@ class HotkeyManager {
|
|||||||
var onCloseTab: (() -> Void)?
|
var onCloseTab: (() -> Void)?
|
||||||
var onNextTab: (() -> Void)?
|
var onNextTab: (() -> Void)?
|
||||||
var onPreviousTab: (() -> Void)?
|
var onPreviousTab: (() -> Void)?
|
||||||
|
var onNextWorkspace: (() -> Void)?
|
||||||
|
var onPreviousWorkspace: (() -> Void)?
|
||||||
var onDetachTab: (() -> Void)?
|
var onDetachTab: (() -> Void)?
|
||||||
var onApplySizePreset: ((TerminalSizePreset) -> Void)?
|
var onApplySizePreset: ((TerminalSizePreset) -> Void)?
|
||||||
var onSwitchToTab: ((Int) -> Void)?
|
var onSwitchToTab: ((Int) -> Void)?
|
||||||
|
var onSwitchToWorkspace: ((WorkspaceID) -> Void)?
|
||||||
|
|
||||||
/// Tab-level hotkeys only fire when the notch is open.
|
/// Notch-scoped hotkeys only fire when the notch is open.
|
||||||
var isNotchOpen: Bool = false
|
var isNotchOpen: Bool = false
|
||||||
|
|
||||||
private var hotKeyRef: EventHotKeyRef?
|
private var hotKeyRef: EventHotKeyRef?
|
||||||
private var eventHandlerRef: EventHandlerRef?
|
private var eventHandlerRef: EventHandlerRef?
|
||||||
private var localMonitor: Any?
|
private var localMonitor: Any?
|
||||||
private let settingsProvider: TerminalSessionConfigurationProviding
|
private let settingsProvider: TerminalSessionConfigurationProviding
|
||||||
|
private let workspaceRegistry: WorkspaceRegistry
|
||||||
private var settingsCancellable: AnyCancellable?
|
private var settingsCancellable: AnyCancellable?
|
||||||
|
|
||||||
init(settingsProvider: TerminalSessionConfigurationProviding? = nil) {
|
init(
|
||||||
|
settingsProvider: TerminalSessionConfigurationProviding? = nil,
|
||||||
|
workspaceRegistry: WorkspaceRegistry? = nil
|
||||||
|
) {
|
||||||
self.settingsProvider = settingsProvider ?? AppSettingsController.shared
|
self.settingsProvider = settingsProvider ?? AppSettingsController.shared
|
||||||
|
self.workspaceRegistry = workspaceRegistry ?? WorkspaceRegistry.shared
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Resolved bindings from typed runtime settings
|
// MARK: - Resolved bindings from typed runtime settings
|
||||||
@@ -53,6 +61,12 @@ class HotkeyManager {
|
|||||||
private var prevTabBinding: HotkeyBinding {
|
private var prevTabBinding: HotkeyBinding {
|
||||||
settingsProvider.hotkeySettings.previousTab
|
settingsProvider.hotkeySettings.previousTab
|
||||||
}
|
}
|
||||||
|
private var nextWorkspaceBinding: HotkeyBinding {
|
||||||
|
settingsProvider.hotkeySettings.nextWorkspace
|
||||||
|
}
|
||||||
|
private var previousWorkspaceBinding: HotkeyBinding {
|
||||||
|
settingsProvider.hotkeySettings.previousWorkspace
|
||||||
|
}
|
||||||
private var detachBinding: HotkeyBinding {
|
private var detachBinding: HotkeyBinding {
|
||||||
settingsProvider.hotkeySettings.detachTab
|
settingsProvider.hotkeySettings.detachTab
|
||||||
}
|
}
|
||||||
@@ -173,7 +187,7 @@ class HotkeyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Local monitor (tab-level hotkeys, only when our app is active)
|
// MARK: - Local monitor (notch-level hotkeys, only when our app is active)
|
||||||
|
|
||||||
private func installLocalMonitor() {
|
private func installLocalMonitor() {
|
||||||
localMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
|
localMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
|
||||||
@@ -189,9 +203,9 @@ class HotkeyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles tab-level hotkeys. Returns true if the event was consumed.
|
/// Handles notch-scoped hotkeys. Returns true if the event was consumed.
|
||||||
private func handleLocalKeyEvent(_ event: NSEvent) -> Bool {
|
private func handleLocalKeyEvent(_ event: NSEvent) -> Bool {
|
||||||
// Tab hotkeys only when the notch is open and focused
|
// Local shortcuts only fire when the notch is open and focused.
|
||||||
guard isNotchOpen else { return false }
|
guard isNotchOpen else { return false }
|
||||||
|
|
||||||
if newTabBinding.matches(event) {
|
if newTabBinding.matches(event) {
|
||||||
@@ -210,10 +224,25 @@ class HotkeyManager {
|
|||||||
onPreviousTab?()
|
onPreviousTab?()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if nextWorkspaceBinding.matches(event) {
|
||||||
|
onNextWorkspace?()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if previousWorkspaceBinding.matches(event) {
|
||||||
|
onPreviousWorkspace?()
|
||||||
|
return true
|
||||||
|
}
|
||||||
if detachBinding.matches(event) {
|
if detachBinding.matches(event) {
|
||||||
onDetachTab?()
|
onDetachTab?()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
for summary in workspaceRegistry.workspaceSummaries {
|
||||||
|
guard let binding = summary.hotkey else { continue }
|
||||||
|
if binding.matches(event) {
|
||||||
|
onSwitchToWorkspace?(summary.id)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
for preset in sizePresets {
|
for preset in sizePresets {
|
||||||
guard let binding = preset.hotkey else { continue }
|
guard let binding = preset.hotkey else { continue }
|
||||||
if binding.matches(event) {
|
if binding.matches(event) {
|
||||||
@@ -9,6 +9,7 @@ final class ScreenManager: ObservableObject {
|
|||||||
static let shared = ScreenManager()
|
static let shared = ScreenManager()
|
||||||
|
|
||||||
private let screenRegistry = ScreenRegistry.shared
|
private let screenRegistry = ScreenRegistry.shared
|
||||||
|
private let workspaceRegistry = WorkspaceRegistry.shared
|
||||||
private let windowCoordinator = WindowCoordinator()
|
private let windowCoordinator = WindowCoordinator()
|
||||||
private lazy var orchestrator = NotchOrchestrator(screenRegistry: screenRegistry, host: self)
|
private lazy var orchestrator = NotchOrchestrator(screenRegistry: screenRegistry, host: self)
|
||||||
|
|
||||||
@@ -55,6 +56,12 @@ final class ScreenManager: ObservableObject {
|
|||||||
hotkeyManager.onPreviousTab = { [weak self] in
|
hotkeyManager.onPreviousTab = { [weak self] in
|
||||||
MainActor.assumeIsolated { self?.activeWorkspace().previousTab() }
|
MainActor.assumeIsolated { self?.activeWorkspace().previousTab() }
|
||||||
}
|
}
|
||||||
|
hotkeyManager.onNextWorkspace = { [weak self] in
|
||||||
|
MainActor.assumeIsolated { self?.switchWorkspace(offset: 1) }
|
||||||
|
}
|
||||||
|
hotkeyManager.onPreviousWorkspace = { [weak self] in
|
||||||
|
MainActor.assumeIsolated { self?.switchWorkspace(offset: -1) }
|
||||||
|
}
|
||||||
hotkeyManager.onDetachTab = { [weak self] in
|
hotkeyManager.onDetachTab = { [weak self] in
|
||||||
MainActor.assumeIsolated { self?.detachActiveTab() }
|
MainActor.assumeIsolated { self?.detachActiveTab() }
|
||||||
}
|
}
|
||||||
@@ -64,6 +71,9 @@ final class ScreenManager: ObservableObject {
|
|||||||
hotkeyManager.onSwitchToTab = { [weak self] index in
|
hotkeyManager.onSwitchToTab = { [weak self] index in
|
||||||
MainActor.assumeIsolated { self?.activeWorkspace().switchToTab(at: index) }
|
MainActor.assumeIsolated { self?.activeWorkspace().switchToTab(at: index) }
|
||||||
}
|
}
|
||||||
|
hotkeyManager.onSwitchToWorkspace = { [weak self] workspaceID in
|
||||||
|
MainActor.assumeIsolated { self?.switchActiveScreen(to: workspaceID) }
|
||||||
|
}
|
||||||
|
|
||||||
hotkeyManager.start()
|
hotkeyManager.start()
|
||||||
}
|
}
|
||||||
@@ -92,6 +102,33 @@ final class ScreenManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func switchWorkspace(offset: Int) {
|
||||||
|
guard let screenID = screenRegistry.activeScreenID() else { return }
|
||||||
|
let currentWorkspaceID = screenRegistry.screenContext(for: screenID)?.workspaceID ?? workspaceRegistry.defaultWorkspaceID
|
||||||
|
let nextWorkspaceID = offset >= 0
|
||||||
|
? workspaceRegistry.nextWorkspaceID(after: currentWorkspaceID)
|
||||||
|
: workspaceRegistry.previousWorkspaceID(before: currentWorkspaceID)
|
||||||
|
|
||||||
|
guard let nextWorkspaceID else { return }
|
||||||
|
switchScreen(screenID, to: nextWorkspaceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func switchActiveScreen(to workspaceID: WorkspaceID) {
|
||||||
|
guard let screenID = screenRegistry.activeScreenID() else { return }
|
||||||
|
switchScreen(screenID, to: workspaceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func switchScreen(_ screenID: ScreenID, to workspaceID: WorkspaceID) {
|
||||||
|
screenRegistry.assignWorkspace(workspaceID, to: screenID)
|
||||||
|
|
||||||
|
guard let context = screenRegistry.screenContext(for: screenID),
|
||||||
|
context.notchState == .open else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
orchestrator.open(screenID: screenID)
|
||||||
|
}
|
||||||
|
|
||||||
func applySizePreset(_ preset: TerminalSizePreset) {
|
func applySizePreset(_ preset: TerminalSizePreset) {
|
||||||
guard let context = screenRegistry.allScreens().first(where: { $0.notchState == .open }) else {
|
guard let context = screenRegistry.allScreens().first(where: { $0.notchState == .open }) else {
|
||||||
AppSettingsController.shared.update {
|
AppSettingsController.shared.update {
|
||||||
@@ -56,6 +56,8 @@ struct AppSettings: Equatable, Codable {
|
|||||||
closeTab: .cmdW,
|
closeTab: .cmdW,
|
||||||
nextTab: .cmdShiftRB,
|
nextTab: .cmdShiftRB,
|
||||||
previousTab: .cmdShiftLB,
|
previousTab: .cmdShiftLB,
|
||||||
|
nextWorkspace: .cmdShiftDown,
|
||||||
|
previousWorkspace: .cmdShiftUp,
|
||||||
detachTab: .cmdD
|
detachTab: .cmdD
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -121,6 +123,8 @@ extension AppSettings {
|
|||||||
var closeTab: HotkeyBinding
|
var closeTab: HotkeyBinding
|
||||||
var nextTab: HotkeyBinding
|
var nextTab: HotkeyBinding
|
||||||
var previousTab: HotkeyBinding
|
var previousTab: HotkeyBinding
|
||||||
|
var nextWorkspace: HotkeyBinding
|
||||||
|
var previousWorkspace: HotkeyBinding
|
||||||
var detachTab: HotkeyBinding
|
var detachTab: HotkeyBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,6 +60,8 @@ struct UserDefaultsAppSettingsStore: AppSettingsStoreType {
|
|||||||
closeTab: hotkey(NotchSettings.Keys.hotkeyCloseTab, default: .cmdW),
|
closeTab: hotkey(NotchSettings.Keys.hotkeyCloseTab, default: .cmdW),
|
||||||
nextTab: hotkey(NotchSettings.Keys.hotkeyNextTab, default: .cmdShiftRB),
|
nextTab: hotkey(NotchSettings.Keys.hotkeyNextTab, default: .cmdShiftRB),
|
||||||
previousTab: hotkey(NotchSettings.Keys.hotkeyPreviousTab, default: .cmdShiftLB),
|
previousTab: hotkey(NotchSettings.Keys.hotkeyPreviousTab, default: .cmdShiftLB),
|
||||||
|
nextWorkspace: hotkey(NotchSettings.Keys.hotkeyNextWorkspace, default: .cmdShiftDown),
|
||||||
|
previousWorkspace: hotkey(NotchSettings.Keys.hotkeyPreviousWorkspace, default: .cmdShiftUp),
|
||||||
detachTab: hotkey(NotchSettings.Keys.hotkeyDetachTab, default: .cmdD)
|
detachTab: hotkey(NotchSettings.Keys.hotkeyDetachTab, default: .cmdD)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -106,6 +108,8 @@ struct UserDefaultsAppSettingsStore: AppSettingsStoreType {
|
|||||||
defaults.set(settings.hotkeys.closeTab.toJSON(), forKey: NotchSettings.Keys.hotkeyCloseTab)
|
defaults.set(settings.hotkeys.closeTab.toJSON(), forKey: NotchSettings.Keys.hotkeyCloseTab)
|
||||||
defaults.set(settings.hotkeys.nextTab.toJSON(), forKey: NotchSettings.Keys.hotkeyNextTab)
|
defaults.set(settings.hotkeys.nextTab.toJSON(), forKey: NotchSettings.Keys.hotkeyNextTab)
|
||||||
defaults.set(settings.hotkeys.previousTab.toJSON(), forKey: NotchSettings.Keys.hotkeyPreviousTab)
|
defaults.set(settings.hotkeys.previousTab.toJSON(), forKey: NotchSettings.Keys.hotkeyPreviousTab)
|
||||||
|
defaults.set(settings.hotkeys.nextWorkspace.toJSON(), forKey: NotchSettings.Keys.hotkeyNextWorkspace)
|
||||||
|
defaults.set(settings.hotkeys.previousWorkspace.toJSON(), forKey: NotchSettings.Keys.hotkeyPreviousWorkspace)
|
||||||
defaults.set(settings.hotkeys.detachTab.toJSON(), forKey: NotchSettings.Keys.hotkeyDetachTab)
|
defaults.set(settings.hotkeys.detachTab.toJSON(), forKey: NotchSettings.Keys.hotkeyDetachTab)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +88,8 @@ struct HotkeyBinding: Codable, Equatable, Hashable {
|
|||||||
static let cmdW = HotkeyBinding(modifiers: NSEvent.ModifierFlags.command.rawValue, keyCode: 13)
|
static let cmdW = HotkeyBinding(modifiers: NSEvent.ModifierFlags.command.rawValue, keyCode: 13)
|
||||||
static let cmdShiftRB = HotkeyBinding(modifiers: NSEvent.ModifierFlags([.command, .shift]).rawValue, keyCode: 30) // ]
|
static let cmdShiftRB = HotkeyBinding(modifiers: NSEvent.ModifierFlags([.command, .shift]).rawValue, keyCode: 30) // ]
|
||||||
static let cmdShiftLB = HotkeyBinding(modifiers: NSEvent.ModifierFlags([.command, .shift]).rawValue, keyCode: 33) // [
|
static let cmdShiftLB = HotkeyBinding(modifiers: NSEvent.ModifierFlags([.command, .shift]).rawValue, keyCode: 33) // [
|
||||||
|
static let cmdShiftDown = HotkeyBinding(modifiers: NSEvent.ModifierFlags([.command, .shift]).rawValue, keyCode: 125)
|
||||||
|
static let cmdShiftUp = HotkeyBinding(modifiers: NSEvent.ModifierFlags([.command, .shift]).rawValue, keyCode: 126)
|
||||||
static let cmdD = HotkeyBinding(modifiers: NSEvent.ModifierFlags.command.rawValue, keyCode: 2)
|
static let cmdD = HotkeyBinding(modifiers: NSEvent.ModifierFlags.command.rawValue, keyCode: 2)
|
||||||
|
|
||||||
static func cmdShiftDigit(_ digit: Int) -> HotkeyBinding? {
|
static func cmdShiftDigit(_ digit: Int) -> HotkeyBinding? {
|
||||||
@@ -57,6 +57,8 @@ enum NotchSettings {
|
|||||||
static let hotkeyCloseTab = "hotkey_closeTab"
|
static let hotkeyCloseTab = "hotkey_closeTab"
|
||||||
static let hotkeyNextTab = "hotkey_nextTab"
|
static let hotkeyNextTab = "hotkey_nextTab"
|
||||||
static let hotkeyPreviousTab = "hotkey_previousTab"
|
static let hotkeyPreviousTab = "hotkey_previousTab"
|
||||||
|
static let hotkeyNextWorkspace = "hotkey_nextWorkspace"
|
||||||
|
static let hotkeyPreviousWorkspace = "hotkey_previousWorkspace"
|
||||||
static let hotkeyDetachTab = "hotkey_detachTab"
|
static let hotkeyDetachTab = "hotkey_detachTab"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +106,8 @@ enum NotchSettings {
|
|||||||
static let hotkeyCloseTab: String = HotkeyBinding.cmdW.toJSON()
|
static let hotkeyCloseTab: String = HotkeyBinding.cmdW.toJSON()
|
||||||
static let hotkeyNextTab: String = HotkeyBinding.cmdShiftRB.toJSON()
|
static let hotkeyNextTab: String = HotkeyBinding.cmdShiftRB.toJSON()
|
||||||
static let hotkeyPreviousTab: String = HotkeyBinding.cmdShiftLB.toJSON()
|
static let hotkeyPreviousTab: String = HotkeyBinding.cmdShiftLB.toJSON()
|
||||||
|
static let hotkeyNextWorkspace: String = HotkeyBinding.cmdShiftDown.toJSON()
|
||||||
|
static let hotkeyPreviousWorkspace: String = HotkeyBinding.cmdShiftUp.toJSON()
|
||||||
static let hotkeyDetachTab: String = HotkeyBinding.cmdD.toJSON()
|
static let hotkeyDetachTab: String = HotkeyBinding.cmdD.toJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,6 +155,8 @@ enum NotchSettings {
|
|||||||
Keys.hotkeyCloseTab: Defaults.hotkeyCloseTab,
|
Keys.hotkeyCloseTab: Defaults.hotkeyCloseTab,
|
||||||
Keys.hotkeyNextTab: Defaults.hotkeyNextTab,
|
Keys.hotkeyNextTab: Defaults.hotkeyNextTab,
|
||||||
Keys.hotkeyPreviousTab: Defaults.hotkeyPreviousTab,
|
Keys.hotkeyPreviousTab: Defaults.hotkeyPreviousTab,
|
||||||
|
Keys.hotkeyNextWorkspace: Defaults.hotkeyNextWorkspace,
|
||||||
|
Keys.hotkeyPreviousWorkspace: Defaults.hotkeyPreviousWorkspace,
|
||||||
Keys.hotkeyDetachTab: Defaults.hotkeyDetachTab,
|
Keys.hotkeyDetachTab: Defaults.hotkeyDetachTab,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@@ -9,16 +9,26 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
|
|||||||
let id = UUID()
|
let id = UUID()
|
||||||
let terminalView: TerminalView
|
let terminalView: TerminalView
|
||||||
private var process: LocalProcess?
|
private var process: LocalProcess?
|
||||||
|
private var keyEventMonitor: Any?
|
||||||
private let backgroundColor = NSColor.black
|
private let backgroundColor = NSColor.black
|
||||||
private let configuredShellPath: String
|
private let configuredShellPath: String
|
||||||
|
private let launchDirectory: String
|
||||||
|
|
||||||
@Published var title: String = "shell"
|
@Published var title: String = "shell"
|
||||||
@Published var isRunning: Bool = true
|
@Published var isRunning: Bool = true
|
||||||
@Published var currentDirectory: String?
|
@Published var currentDirectory: String?
|
||||||
|
|
||||||
init(fontSize: CGFloat, theme: TerminalTheme, shellPath: String) {
|
init(
|
||||||
|
fontSize: CGFloat,
|
||||||
|
theme: TerminalTheme,
|
||||||
|
shellPath: String,
|
||||||
|
initialDirectory: String? = nil,
|
||||||
|
startImmediately: Bool = true
|
||||||
|
) {
|
||||||
terminalView = TerminalView(frame: NSRect(x: 0, y: 0, width: 600, height: 300))
|
terminalView = TerminalView(frame: NSRect(x: 0, y: 0, width: 600, height: 300))
|
||||||
configuredShellPath = shellPath
|
configuredShellPath = shellPath
|
||||||
|
launchDirectory = Self.resolveInitialDirectory(initialDirectory)
|
||||||
|
currentDirectory = launchDirectory
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
terminalView.terminalDelegate = self
|
terminalView.terminalDelegate = self
|
||||||
@@ -26,8 +36,19 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
|
|||||||
let font = NSFont.monospacedSystemFont(ofSize: fontSize, weight: .regular)
|
let font = NSFont.monospacedSystemFont(ofSize: fontSize, weight: .regular)
|
||||||
terminalView.font = font
|
terminalView.font = font
|
||||||
applyTheme(theme)
|
applyTheme(theme)
|
||||||
|
installCommandArrowMonitor()
|
||||||
|
|
||||||
|
if startImmediately {
|
||||||
startShell()
|
startShell()
|
||||||
|
} else {
|
||||||
|
isRunning = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
if let keyEventMonitor {
|
||||||
|
NSEvent.removeMonitor(keyEventMonitor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Shell management
|
// MARK: - Shell management
|
||||||
@@ -44,12 +65,29 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
|
|||||||
args: ["-l"],
|
args: ["-l"],
|
||||||
environment: nil,
|
environment: nil,
|
||||||
execName: loginExecName,
|
execName: loginExecName,
|
||||||
currentDirectory: NSHomeDirectory()
|
currentDirectory: launchDirectory
|
||||||
)
|
)
|
||||||
process = proc
|
process = proc
|
||||||
title = shellName
|
title = shellName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func resolveInitialDirectory(_ directory: String?) -> String {
|
||||||
|
normalizedDirectory(directory) ?? NSHomeDirectory()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func normalizedDirectory(_ directory: String?) -> String? {
|
||||||
|
let trimmed = directory?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||||
|
guard !trimmed.isEmpty else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let url = URL(string: trimmed), url.isFileURL {
|
||||||
|
return url.path(percentEncoded: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (trimmed as NSString).expandingTildeInPath
|
||||||
|
}
|
||||||
|
|
||||||
private func resolveShell() -> String {
|
private func resolveShell() -> String {
|
||||||
let custom = configuredShellPath.trimmingCharacters(in: .whitespacesAndNewlines)
|
let custom = configuredShellPath.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
if !custom.isEmpty && FileManager.default.isExecutableFile(atPath: custom) {
|
if !custom.isEmpty && FileManager.default.isExecutableFile(atPath: custom) {
|
||||||
@@ -58,6 +96,26 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
|
|||||||
return ProcessInfo.processInfo.environment["SHELL"] ?? "/bin/zsh"
|
return ProcessInfo.processInfo.environment["SHELL"] ?? "/bin/zsh"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func installCommandArrowMonitor() {
|
||||||
|
keyEventMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
|
||||||
|
guard let self else { return event }
|
||||||
|
guard let window = self.terminalView.window else { return event }
|
||||||
|
guard event.window === window else { return event }
|
||||||
|
guard window.firstResponder === self.terminalView else { return event }
|
||||||
|
|
||||||
|
guard let sequence = TerminalCommandArrowBehavior.sequence(
|
||||||
|
for: event.modifierFlags,
|
||||||
|
keyCode: event.keyCode,
|
||||||
|
applicationCursor: self.terminalView.getTerminal().applicationCursor
|
||||||
|
) else {
|
||||||
|
return event
|
||||||
|
}
|
||||||
|
|
||||||
|
self.terminalView.send(data: sequence[...])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateFontSize(_ size: CGFloat) {
|
func updateFontSize(_ size: CGFloat) {
|
||||||
terminalView.font = NSFont.monospacedSystemFont(ofSize: size, weight: .regular)
|
terminalView.font = NSFont.monospacedSystemFont(ofSize: size, weight: .regular)
|
||||||
}
|
}
|
||||||
@@ -117,7 +175,8 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
|
|||||||
}
|
}
|
||||||
|
|
||||||
func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {
|
func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {
|
||||||
currentDirectory = directory
|
guard let normalizedDirectory = Self.normalizedDirectory(directory) else { return }
|
||||||
|
currentDirectory = normalizedDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
func scrolled(source: TerminalView, position: Double) {}
|
func scrolled(source: TerminalView, position: Double) {}
|
||||||
@@ -3,12 +3,27 @@ import Combine
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
protocol TerminalSessionFactoryType {
|
protocol TerminalSessionFactoryType {
|
||||||
func makeSession(fontSize: CGFloat, theme: TerminalTheme, shellPath: String) -> TerminalSession
|
func makeSession(
|
||||||
|
fontSize: CGFloat,
|
||||||
|
theme: TerminalTheme,
|
||||||
|
shellPath: String,
|
||||||
|
initialDirectory: String?
|
||||||
|
) -> TerminalSession
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LiveTerminalSessionFactory: TerminalSessionFactoryType {
|
struct LiveTerminalSessionFactory: TerminalSessionFactoryType {
|
||||||
func makeSession(fontSize: CGFloat, theme: TerminalTheme, shellPath: String) -> TerminalSession {
|
func makeSession(
|
||||||
TerminalSession(fontSize: fontSize, theme: theme, shellPath: shellPath)
|
fontSize: CGFloat,
|
||||||
|
theme: TerminalTheme,
|
||||||
|
shellPath: String,
|
||||||
|
initialDirectory: String?
|
||||||
|
) -> TerminalSession {
|
||||||
|
TerminalSession(
|
||||||
|
fontSize: fontSize,
|
||||||
|
theme: theme,
|
||||||
|
shellPath: shellPath,
|
||||||
|
initialDirectory: initialDirectory
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,6 +33,7 @@ final class WorkspaceController: ObservableObject {
|
|||||||
let createdAt: Date
|
let createdAt: Date
|
||||||
|
|
||||||
@Published private(set) var name: String
|
@Published private(set) var name: String
|
||||||
|
@Published private(set) var hotkey: HotkeyBinding?
|
||||||
@Published private(set) var tabs: [TerminalSession] = []
|
@Published private(set) var tabs: [TerminalSession] = []
|
||||||
@Published private(set) var activeTabIndex: Int = 0
|
@Published private(set) var activeTabIndex: Int = 0
|
||||||
|
|
||||||
@@ -34,6 +50,7 @@ final class WorkspaceController: ObservableObject {
|
|||||||
self.id = summary.id
|
self.id = summary.id
|
||||||
self.name = summary.name
|
self.name = summary.name
|
||||||
self.createdAt = summary.createdAt
|
self.createdAt = summary.createdAt
|
||||||
|
self.hotkey = summary.hotkey
|
||||||
self.sessionFactory = sessionFactory
|
self.sessionFactory = sessionFactory
|
||||||
self.settingsProvider = settingsProvider
|
self.settingsProvider = settingsProvider
|
||||||
|
|
||||||
@@ -51,7 +68,7 @@ final class WorkspaceController: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var summary: WorkspaceSummary {
|
var summary: WorkspaceSummary {
|
||||||
WorkspaceSummary(id: id, name: name, createdAt: createdAt)
|
WorkspaceSummary(id: id, name: name, createdAt: createdAt, hotkey: hotkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
var state: WorkspaceState {
|
var state: WorkspaceState {
|
||||||
@@ -78,12 +95,18 @@ final class WorkspaceController: ObservableObject {
|
|||||||
name = trimmed
|
name = trimmed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateHotkey(_ updatedHotkey: HotkeyBinding?) {
|
||||||
|
guard hotkey != updatedHotkey else { return }
|
||||||
|
hotkey = updatedHotkey
|
||||||
|
}
|
||||||
|
|
||||||
func newTab() {
|
func newTab() {
|
||||||
let config = settingsProvider.terminalSessionConfiguration
|
let config = settingsProvider.terminalSessionConfiguration
|
||||||
let session = sessionFactory.makeSession(
|
let session = sessionFactory.makeSession(
|
||||||
fontSize: config.fontSize,
|
fontSize: config.fontSize,
|
||||||
theme: config.theme,
|
theme: config.theme,
|
||||||
shellPath: config.shellPath
|
shellPath: config.shellPath,
|
||||||
|
initialDirectory: activeTab?.currentDirectory
|
||||||
)
|
)
|
||||||
|
|
||||||
titleObservers[session.id] = session.$title
|
titleObservers[session.id] = session.$title
|
||||||
@@ -104,6 +104,37 @@ final class WorkspaceRegistry: ObservableObject {
|
|||||||
persistWorkspaceSummaries()
|
persistWorkspaceSummaries()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateWorkspaceHotkey(id: WorkspaceID, to hotkey: HotkeyBinding?) {
|
||||||
|
guard let index = workspaceSummaries.firstIndex(where: { $0.id == id }) else { return }
|
||||||
|
guard workspaceSummaries[index].hotkey != hotkey else { return }
|
||||||
|
|
||||||
|
workspaceSummaries[index].hotkey = hotkey
|
||||||
|
controllers[id]?.updateHotkey(hotkey)
|
||||||
|
persistWorkspaceSummaries()
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextWorkspaceID(after id: WorkspaceID) -> WorkspaceID? {
|
||||||
|
guard !workspaceSummaries.isEmpty else { return nil }
|
||||||
|
guard let index = workspaceSummaries.firstIndex(where: { $0.id == id }) else {
|
||||||
|
return workspaceSummaries.first?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextIndex = workspaceSummaries.index(after: index)
|
||||||
|
return workspaceSummaries[nextIndex == workspaceSummaries.endIndex ? workspaceSummaries.startIndex : nextIndex].id
|
||||||
|
}
|
||||||
|
|
||||||
|
func previousWorkspaceID(before id: WorkspaceID) -> WorkspaceID? {
|
||||||
|
guard !workspaceSummaries.isEmpty else { return nil }
|
||||||
|
guard let index = workspaceSummaries.firstIndex(where: { $0.id == id }) else {
|
||||||
|
return workspaceSummaries.last?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
let previousIndex = index == workspaceSummaries.startIndex
|
||||||
|
? workspaceSummaries.index(before: workspaceSummaries.endIndex)
|
||||||
|
: workspaceSummaries.index(before: index)
|
||||||
|
return workspaceSummaries[previousIndex].id
|
||||||
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func deleteWorkspace(id: WorkspaceID) -> Bool {
|
func deleteWorkspace(id: WorkspaceID) -> Bool {
|
||||||
guard canDeleteWorkspace(id: id) else { return false }
|
guard canDeleteWorkspace(id: id) else { return false }
|
||||||
@@ -6,11 +6,13 @@ struct WorkspaceSummary: Identifiable, Equatable, Codable {
|
|||||||
var id: WorkspaceID
|
var id: WorkspaceID
|
||||||
var name: String
|
var name: String
|
||||||
var createdAt: Date
|
var createdAt: Date
|
||||||
|
var hotkey: HotkeyBinding?
|
||||||
|
|
||||||
init(id: WorkspaceID = UUID(), name: String, createdAt: Date = Date()) {
|
init(id: WorkspaceID = UUID(), name: String, createdAt: Date = Date(), hotkey: HotkeyBinding? = nil) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.name = name
|
self.name = name
|
||||||
self.createdAt = createdAt
|
self.createdAt = createdAt
|
||||||
|
self.hotkey = hotkey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 945 B After Width: | Height: | Size: 945 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 735 B After Width: | Height: | Size: 735 B |
|
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 290 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 958 B |
|
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 512 B |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
@@ -17,8 +17,13 @@ struct HotkeySettingsView: View {
|
|||||||
HotkeyRecorderView(label: "Detach tab", binding: settingsController.binding(\.hotkeys.detachTab))
|
HotkeyRecorderView(label: "Detach tab", binding: settingsController.binding(\.hotkeys.detachTab))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section("Workspaces (active when notch is open)") {
|
||||||
|
HotkeyRecorderView(label: "Next workspace", binding: settingsController.binding(\.hotkeys.nextWorkspace))
|
||||||
|
HotkeyRecorderView(label: "Previous workspace", binding: settingsController.binding(\.hotkeys.previousWorkspace))
|
||||||
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Text("⌘1–9 always switch to tab by number. Size preset hotkeys are configured in Terminal > Size Presets.")
|
Text("⌘1–9 always switch to tab by number. Size preset hotkeys are configured in Terminal > Size Presets. Per-workspace jump hotkeys are configured in Workspaces.")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
@@ -2,21 +2,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct TerminalSettingsView: View {
|
struct TerminalSettingsView: View {
|
||||||
@ObservedObject private var settingsController = AppSettingsController.shared
|
@ObservedObject private var settingsController = AppSettingsController.shared
|
||||||
|
@State private var sizePresets: [TerminalSizePreset] = []
|
||||||
private var sizePresetsBinding: Binding<[TerminalSizePreset]> {
|
|
||||||
Binding(
|
|
||||||
get: {
|
|
||||||
TerminalSizePresetStore.decodePresets(
|
|
||||||
from: settingsController.settings.terminal.sizePresetsJSON
|
|
||||||
) ?? TerminalSizePresetStore.loadDefaults()
|
|
||||||
},
|
|
||||||
set: { newValue in
|
|
||||||
settingsController.update {
|
|
||||||
$0.terminal.sizePresetsJSON = TerminalSizePresetStore.encodePresets(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
@@ -55,7 +41,7 @@ struct TerminalSettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Section("Size Presets") {
|
Section("Size Presets") {
|
||||||
ForEach(sizePresetsBinding) { $preset in
|
ForEach($sizePresets) { $preset in
|
||||||
TerminalSizePresetEditor(
|
TerminalSizePresetEditor(
|
||||||
preset: $preset,
|
preset: $preset,
|
||||||
currentOpenWidth: settingsController.settings.display.openWidth,
|
currentOpenWidth: settingsController.settings.display.openWidth,
|
||||||
@@ -67,20 +53,18 @@ struct TerminalSettingsView: View {
|
|||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Button("Add Preset") {
|
Button("Add Preset") {
|
||||||
var presets = sizePresetsBinding.wrappedValue
|
sizePresets.append(
|
||||||
presets.append(
|
|
||||||
TerminalSizePreset(
|
TerminalSizePreset(
|
||||||
name: "Preset \(presets.count + 1)",
|
name: "Preset \(sizePresets.count + 1)",
|
||||||
width: settingsController.settings.display.openWidth,
|
width: settingsController.settings.display.openWidth,
|
||||||
height: settingsController.settings.display.openHeight,
|
height: settingsController.settings.display.openHeight,
|
||||||
hotkey: TerminalSizePresetStore.suggestedHotkey(for: presets)
|
hotkey: TerminalSizePresetStore.suggestedHotkey(for: sizePresets)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
sizePresetsBinding.wrappedValue = presets
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button("Reset Presets") {
|
Button("Reset Presets") {
|
||||||
sizePresetsBinding.wrappedValue = TerminalSizePresetStore.loadDefaults()
|
sizePresets = TerminalSizePresetStore.loadDefaults()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,10 +74,24 @@ struct TerminalSettingsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.formStyle(.grouped)
|
.formStyle(.grouped)
|
||||||
|
.onAppear {
|
||||||
|
synchronizePresetsFromSettings()
|
||||||
|
}
|
||||||
|
.onChange(of: settingsController.settings.terminal.sizePresetsJSON) { _, _ in
|
||||||
|
synchronizePresetsFromSettings()
|
||||||
|
}
|
||||||
|
.onChange(of: sizePresets) { _, newValue in
|
||||||
|
let encoded = TerminalSizePresetStore.encodePresets(newValue)
|
||||||
|
guard encoded != settingsController.settings.terminal.sizePresetsJSON else { return }
|
||||||
|
|
||||||
|
settingsController.update {
|
||||||
|
$0.terminal.sizePresetsJSON = encoded
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func deletePreset(id: UUID) {
|
private func deletePreset(id: UUID) {
|
||||||
sizePresetsBinding.wrappedValue.removeAll { $0.id == id }
|
sizePresets.removeAll { $0.id == id }
|
||||||
}
|
}
|
||||||
|
|
||||||
private func applyPreset(_ preset: TerminalSizePreset) {
|
private func applyPreset(_ preset: TerminalSizePreset) {
|
||||||
@@ -103,6 +101,15 @@ struct TerminalSettingsView: View {
|
|||||||
}
|
}
|
||||||
ScreenManager.shared.applySizePreset(preset)
|
ScreenManager.shared.applySizePreset(preset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func synchronizePresetsFromSettings() {
|
||||||
|
let decoded = TerminalSizePresetStore.decodePresets(
|
||||||
|
from: settingsController.settings.terminal.sizePresetsJSON
|
||||||
|
) ?? TerminalSizePresetStore.loadDefaults()
|
||||||
|
|
||||||
|
guard decoded != sizePresets else { return }
|
||||||
|
sizePresets = decoded
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct TerminalSizePresetEditor: View {
|
private struct TerminalSizePresetEditor: View {
|
||||||
@@ -75,6 +75,11 @@ struct WorkspacesSettingsView: View {
|
|||||||
renameSelectedWorkspace()
|
renameSelectedWorkspace()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OptionalHotkeyRecorderView(
|
||||||
|
label: "Jump Hotkey",
|
||||||
|
binding: workspaceHotkeyBinding(for: summary.id)
|
||||||
|
)
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Button("Save Name") {
|
Button("Save Name") {
|
||||||
renameSelectedWorkspace()
|
renameSelectedWorkspace()
|
||||||
@@ -86,6 +91,10 @@ struct WorkspacesSettingsView: View {
|
|||||||
}
|
}
|
||||||
.accessibilityIdentifier("settings.workspaces.new")
|
.accessibilityIdentifier("settings.workspaces.new")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Text("Workspace jump hotkeys are active when the notch is open and switch the current screen to this workspace.")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
Section("Usage") {
|
Section("Usage") {
|
||||||
@@ -256,6 +265,17 @@ struct WorkspacesSettingsView: View {
|
|||||||
renameDraft = workspaceRegistry.summary(for: workspaceID)?.name ?? ""
|
renameDraft = workspaceRegistry.summary(for: workspaceID)?.name ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func workspaceHotkeyBinding(for workspaceID: WorkspaceID) -> Binding<HotkeyBinding?> {
|
||||||
|
Binding(
|
||||||
|
get: {
|
||||||
|
workspaceRegistry.summary(for: workspaceID)?.hotkey
|
||||||
|
},
|
||||||
|
set: { newValue in
|
||||||
|
workspaceRegistry.updateWorkspaceHotkey(id: workspaceID, to: newValue)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private func deleteSelectedWorkspace() {
|
private func deleteSelectedWorkspace() {
|
||||||
guard let effectiveSelectedWorkspaceID,
|
guard let effectiveSelectedWorkspaceID,
|
||||||
let fallbackWorkspaceID = screenRegistry.deleteWorkspace(
|
let fallbackWorkspaceID = screenRegistry.deleteWorkspace(
|
||||||
@@ -313,7 +313,12 @@ private final class ScreenRegistryTestSettingsProvider: TerminalSessionConfigura
|
|||||||
|
|
||||||
private struct ScreenRegistryUnusedTerminalSessionFactory: TerminalSessionFactoryType {
|
private struct ScreenRegistryUnusedTerminalSessionFactory: TerminalSessionFactoryType {
|
||||||
@MainActor
|
@MainActor
|
||||||
func makeSession(fontSize: CGFloat, theme: TerminalTheme, shellPath: String) -> TerminalSession {
|
func makeSession(
|
||||||
|
fontSize: CGFloat,
|
||||||
|
theme: TerminalTheme,
|
||||||
|
shellPath: String,
|
||||||
|
initialDirectory: String?
|
||||||
|
) -> TerminalSession {
|
||||||
fatalError("ScreenRegistryTests should not create live terminal sessions.")
|
fatalError("ScreenRegistryTests should not create live terminal sessions.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import AppKit
|
||||||
|
import Carbon.HIToolbox
|
||||||
|
import XCTest
|
||||||
|
import SwiftTerm
|
||||||
|
@testable import CommandNotch
|
||||||
|
|
||||||
|
final class TerminalCommandArrowBehaviorTests: XCTestCase {
|
||||||
|
func testCommandLeftUsesHomeSequence() {
|
||||||
|
let sequence = TerminalCommandArrowBehavior.sequence(
|
||||||
|
for: [.command],
|
||||||
|
keyCode: UInt16(kVK_LeftArrow),
|
||||||
|
applicationCursor: false
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertEqual(sequence, EscapeSequences.moveHomeNormal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCommandRightUsesApplicationEndSequence() {
|
||||||
|
let sequence = TerminalCommandArrowBehavior.sequence(
|
||||||
|
for: [.command],
|
||||||
|
keyCode: UInt16(kVK_RightArrow),
|
||||||
|
applicationCursor: true
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertEqual(sequence, EscapeSequences.moveEndApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOptionLeftKeepsSwiftTermWordNavigationPath() {
|
||||||
|
let sequence = TerminalCommandArrowBehavior.sequence(
|
||||||
|
for: [.option],
|
||||||
|
keyCode: UInt16(kVK_LeftArrow),
|
||||||
|
applicationCursor: false
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertNil(sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCommandDeleteUsesLineKillSequence() {
|
||||||
|
let sequence = TerminalCommandArrowBehavior.sequence(
|
||||||
|
for: [.command],
|
||||||
|
keyCode: UInt16(kVK_Delete),
|
||||||
|
applicationCursor: false
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertEqual(sequence, [0x15])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCommandLUsesClearScreenSequence() {
|
||||||
|
let sequence = TerminalCommandArrowBehavior.sequence(
|
||||||
|
for: [.command],
|
||||||
|
keyCode: UInt16(kVK_ANSI_L),
|
||||||
|
applicationCursor: false
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertEqual(sequence, [0x0c])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,6 +74,30 @@ final class WorkspaceRegistryTests: XCTestCase {
|
|||||||
XCTAssertEqual(store.savedSummaries.map(\.name), ["Main"])
|
XCTAssertEqual(store.savedSummaries.map(\.name), ["Main"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testUpdateWorkspaceHotkeyPersistsAndUpdatesSummary() {
|
||||||
|
let store = InMemoryWorkspaceStore()
|
||||||
|
let registry = makeRegistry(store: store)
|
||||||
|
let docsID = registry.createWorkspace(named: "Docs")
|
||||||
|
let hotkey = HotkeyBinding.cmdShiftDigit(4)
|
||||||
|
|
||||||
|
registry.updateWorkspaceHotkey(id: docsID, to: hotkey)
|
||||||
|
|
||||||
|
XCTAssertEqual(registry.summary(for: docsID)?.hotkey, hotkey)
|
||||||
|
XCTAssertEqual(store.savedSummaries.last?.hotkey, hotkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNextAndPreviousWorkspaceWrapAroundRegistryOrder() {
|
||||||
|
let registry = makeRegistry()
|
||||||
|
let mainID = registry.defaultWorkspaceID
|
||||||
|
let docsID = registry.createWorkspace(named: "Docs")
|
||||||
|
let reviewID = registry.createWorkspace(named: "Review")
|
||||||
|
|
||||||
|
XCTAssertEqual(registry.nextWorkspaceID(after: mainID), docsID)
|
||||||
|
XCTAssertEqual(registry.nextWorkspaceID(after: reviewID), mainID)
|
||||||
|
XCTAssertEqual(registry.previousWorkspaceID(before: mainID), reviewID)
|
||||||
|
XCTAssertEqual(registry.previousWorkspaceID(before: docsID), mainID)
|
||||||
|
}
|
||||||
|
|
||||||
private func makeRegistry(
|
private func makeRegistry(
|
||||||
initialWorkspaces: [WorkspaceSummary]? = [],
|
initialWorkspaces: [WorkspaceSummary]? = [],
|
||||||
store: (any WorkspaceStoreType)? = nil
|
store: (any WorkspaceStoreType)? = nil
|
||||||
@@ -96,6 +120,64 @@ final class WorkspaceRegistryTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class WorkspaceControllerTests: XCTestCase {
|
||||||
|
func testNewTabUsesActiveTabCurrentDirectory() {
|
||||||
|
let factory = RecordingTerminalSessionFactory()
|
||||||
|
let controller = WorkspaceController(
|
||||||
|
summary: WorkspaceSummary(name: "Main"),
|
||||||
|
sessionFactory: factory,
|
||||||
|
settingsProvider: TestSettingsProvider(),
|
||||||
|
bootstrapDefaultTab: false
|
||||||
|
)
|
||||||
|
|
||||||
|
controller.newTab()
|
||||||
|
controller.activeTab?.currentDirectory = "/tmp/Raycast"
|
||||||
|
|
||||||
|
controller.newTab()
|
||||||
|
|
||||||
|
XCTAssertEqual(factory.requestedDirectories, [nil, "/tmp/Raycast"])
|
||||||
|
XCTAssertEqual(controller.activeTab?.currentDirectory, "/tmp/Raycast")
|
||||||
|
XCTAssertEqual(controller.tabs.count, 2)
|
||||||
|
XCTAssertEqual(controller.activeTabIndex, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNewTabNormalizesCurrentDirectoryFileURL() {
|
||||||
|
let factory = RecordingTerminalSessionFactory()
|
||||||
|
let controller = WorkspaceController(
|
||||||
|
summary: WorkspaceSummary(name: "Main"),
|
||||||
|
sessionFactory: factory,
|
||||||
|
settingsProvider: TestSettingsProvider(),
|
||||||
|
bootstrapDefaultTab: false
|
||||||
|
)
|
||||||
|
let expectedPath = "/tmp/Raycast Folder"
|
||||||
|
|
||||||
|
controller.newTab()
|
||||||
|
controller.activeTab?.currentDirectory = URL(fileURLWithPath: expectedPath).absoluteString
|
||||||
|
|
||||||
|
controller.newTab()
|
||||||
|
|
||||||
|
XCTAssertEqual(controller.activeTab?.currentDirectory, expectedPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNewTabFallsBackToHomeDirectoryWhenWorkspaceHasNoTabs() {
|
||||||
|
let factory = RecordingTerminalSessionFactory()
|
||||||
|
let controller = WorkspaceController(
|
||||||
|
summary: WorkspaceSummary(name: "Main"),
|
||||||
|
sessionFactory: factory,
|
||||||
|
settingsProvider: TestSettingsProvider(),
|
||||||
|
bootstrapDefaultTab: false
|
||||||
|
)
|
||||||
|
|
||||||
|
controller.newTab()
|
||||||
|
|
||||||
|
XCTAssertEqual(factory.requestedDirectories, [nil])
|
||||||
|
XCTAssertEqual(controller.activeTab?.currentDirectory, NSHomeDirectory())
|
||||||
|
XCTAssertEqual(controller.tabs.count, 1)
|
||||||
|
XCTAssertEqual(controller.activeTabIndex, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class InMemoryWorkspaceStore: WorkspaceStoreType {
|
private final class InMemoryWorkspaceStore: WorkspaceStoreType {
|
||||||
var savedSummaries: [WorkspaceSummary] = []
|
var savedSummaries: [WorkspaceSummary] = []
|
||||||
|
|
||||||
@@ -114,9 +196,35 @@ private final class TestSettingsProvider: TerminalSessionConfigurationProviding
|
|||||||
let terminalSizePresets = TerminalSizePresetStore.loadDefaults()
|
let terminalSizePresets = TerminalSizePresetStore.loadDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class RecordingTerminalSessionFactory: TerminalSessionFactoryType {
|
||||||
|
private(set) var requestedDirectories: [String?] = []
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func makeSession(
|
||||||
|
fontSize: CGFloat,
|
||||||
|
theme: TerminalTheme,
|
||||||
|
shellPath: String,
|
||||||
|
initialDirectory: String?
|
||||||
|
) -> TerminalSession {
|
||||||
|
requestedDirectories.append(initialDirectory)
|
||||||
|
return TerminalSession(
|
||||||
|
fontSize: fontSize,
|
||||||
|
theme: theme,
|
||||||
|
shellPath: shellPath,
|
||||||
|
initialDirectory: initialDirectory,
|
||||||
|
startImmediately: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct UnusedTerminalSessionFactory: TerminalSessionFactoryType {
|
private struct UnusedTerminalSessionFactory: TerminalSessionFactoryType {
|
||||||
@MainActor
|
@MainActor
|
||||||
func makeSession(fontSize: CGFloat, theme: TerminalTheme, shellPath: String) -> TerminalSession {
|
func makeSession(
|
||||||
|
fontSize: CGFloat,
|
||||||
|
theme: TerminalTheme,
|
||||||
|
shellPath: String,
|
||||||
|
initialDirectory: String?
|
||||||
|
) -> TerminalSession {
|
||||||
fatalError("WorkspaceRegistryTests should not create live terminal sessions.")
|
fatalError("WorkspaceRegistryTests should not create live terminal sessions.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,11 @@ final class WorkspaceStoreTests: XCTestCase {
|
|||||||
|
|
||||||
let store = UserDefaultsWorkspaceStore(defaults: defaults)
|
let store = UserDefaultsWorkspaceStore(defaults: defaults)
|
||||||
let summaries = [
|
let summaries = [
|
||||||
WorkspaceSummary(id: UUID(uuidString: "11111111-1111-1111-1111-111111111111")!, name: "Main"),
|
WorkspaceSummary(
|
||||||
|
id: UUID(uuidString: "11111111-1111-1111-1111-111111111111")!,
|
||||||
|
name: "Main",
|
||||||
|
hotkey: HotkeyBinding.cmdShiftDigit(4)
|
||||||
|
),
|
||||||
WorkspaceSummary(id: UUID(uuidString: "22222222-2222-2222-2222-222222222222")!, name: "Docs")
|
WorkspaceSummary(id: UUID(uuidString: "22222222-2222-2222-2222-222222222222")!, name: "Docs")
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -14,6 +14,45 @@ packages:
|
|||||||
SwiftTerm:
|
SwiftTerm:
|
||||||
url: https://github.com/migueldeicaza/SwiftTerm.git
|
url: https://github.com/migueldeicaza/SwiftTerm.git
|
||||||
from: "1.2.0"
|
from: "1.2.0"
|
||||||
|
schemes:
|
||||||
|
CommandNotch:
|
||||||
|
build:
|
||||||
|
targets:
|
||||||
|
CommandNotch: all
|
||||||
|
test:
|
||||||
|
config: Debug
|
||||||
|
targets:
|
||||||
|
- CommandNotchTests
|
||||||
|
- CommandNotchUITests
|
||||||
|
run:
|
||||||
|
config: Debug
|
||||||
|
profile:
|
||||||
|
config: Release
|
||||||
|
analyze:
|
||||||
|
config: Debug
|
||||||
|
archive:
|
||||||
|
config: Release
|
||||||
|
management:
|
||||||
|
shared: true
|
||||||
|
Release-CommandNotch:
|
||||||
|
build:
|
||||||
|
targets:
|
||||||
|
CommandNotch: all
|
||||||
|
test:
|
||||||
|
config: Debug
|
||||||
|
targets:
|
||||||
|
- CommandNotchTests
|
||||||
|
- CommandNotchUITests
|
||||||
|
run:
|
||||||
|
config: Release
|
||||||
|
profile:
|
||||||
|
config: Release
|
||||||
|
analyze:
|
||||||
|
config: Debug
|
||||||
|
archive:
|
||||||
|
config: Release
|
||||||
|
management:
|
||||||
|
shared: true
|
||||||
targets:
|
targets:
|
||||||
CommandNotch:
|
CommandNotch:
|
||||||
type: application
|
type: application
|
||||||
@@ -29,9 +68,9 @@ targets:
|
|||||||
properties:
|
properties:
|
||||||
CFBundleName: CommandNotch
|
CFBundleName: CommandNotch
|
||||||
CFBundleDisplayName: CommandNotch
|
CFBundleDisplayName: CommandNotch
|
||||||
CFBundleIdentifier: com.commandnotch.app
|
CFBundleIdentifier: "$(PRODUCT_BUNDLE_IDENTIFIER)"
|
||||||
CFBundleVersion: "1"
|
CFBundleVersion: "1"
|
||||||
CFBundleShortVersionString: "0.2.0"
|
CFBundleShortVersionString: "0.0.3"
|
||||||
CFBundlePackageType: APPL
|
CFBundlePackageType: APPL
|
||||||
CFBundleExecutable: CommandNotch
|
CFBundleExecutable: CommandNotch
|
||||||
LSMinimumSystemVersion: "14.0"
|
LSMinimumSystemVersion: "14.0"
|
||||||
@@ -1,809 +0,0 @@
|
|||||||
// !$*UTF8*$!
|
|
||||||
{
|
|
||||||
archiveVersion = 1;
|
|
||||||
classes = {
|
|
||||||
};
|
|
||||||
objectVersion = 63;
|
|
||||||
objects = {
|
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
|
||||||
0F4A88A33D93B6E100A1C001 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0F4A88A33D93B6E100A1C002 /* Assets.xcassets */; };
|
|
||||||
A10000000000000000000001 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000001 /* AppSettings.swift */; };
|
|
||||||
A10000000000000000000002 /* AppSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000002 /* AppSettingsController.swift */; };
|
|
||||||
A10000000000000000000003 /* AppSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000003 /* AppSettingsStore.swift */; };
|
|
||||||
A10000000000000000000004 /* WorkspaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000004 /* WorkspaceController.swift */; };
|
|
||||||
A10000000000000000000005 /* WorkspaceRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000005 /* WorkspaceRegistry.swift */; };
|
|
||||||
A10000000000000000000006 /* WorkspaceSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000006 /* WorkspaceSummary.swift */; };
|
|
||||||
A10000000000000000000007 /* AppSettingsStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000008 /* AppSettingsStoreTests.swift */; };
|
|
||||||
A10000000000000000000008 /* WorkspaceRegistryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000009 /* WorkspaceRegistryTests.swift */; };
|
|
||||||
A10000000000000000000009 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2000000000000000000000A /* XCTest.framework */; };
|
|
||||||
A1000000000000000000000B /* ScreenContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000000000000000000000B /* ScreenContext.swift */; };
|
|
||||||
A1000000000000000000000C /* ScreenRegistryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000000000000000000000C /* ScreenRegistryTests.swift */; };
|
|
||||||
A1000000000000000000000D /* ScreenRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000000000000000000000D /* ScreenRegistry.swift */; };
|
|
||||||
A1000000000000000000000E /* WorkspaceStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000000000000000000000E /* WorkspaceStore.swift */; };
|
|
||||||
A1000000000000000000000F /* WorkspaceStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000000000000000000000F /* WorkspaceStoreTests.swift */; };
|
|
||||||
A10000000000000000000010 /* NotchOrchestrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000010 /* NotchOrchestrator.swift */; };
|
|
||||||
A10000000000000000000011 /* NotchOrchestratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000011 /* NotchOrchestratorTests.swift */; };
|
|
||||||
A10000000000000000000012 /* ScreenContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000012 /* ScreenContextTests.swift */; };
|
|
||||||
A10000000000000000000013 /* WindowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000013 /* WindowCoordinator.swift */; };
|
|
||||||
A10000000000000000000014 /* WindowFrameCalculatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000014 /* WindowFrameCalculatorTests.swift */; };
|
|
||||||
A10000000000000000000015 /* SettingsBindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000015 /* SettingsBindings.swift */; };
|
|
||||||
A10000000000000000000016 /* GeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000016 /* GeneralSettingsView.swift */; };
|
|
||||||
A10000000000000000000017 /* AppearanceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000017 /* AppearanceSettingsView.swift */; };
|
|
||||||
A10000000000000000000018 /* AnimationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000018 /* AnimationSettingsView.swift */; };
|
|
||||||
A10000000000000000000019 /* TerminalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20000000000000000000019 /* TerminalSettingsView.swift */; };
|
|
||||||
A1000000000000000000001A /* HotkeySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000000000000000000001A /* HotkeySettingsView.swift */; };
|
|
||||||
A1000000000000000000001B /* AboutSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000000000000000000001B /* AboutSettingsView.swift */; };
|
|
||||||
A1000000000000000000001C /* AppSettingsControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000000000000000000001C /* AppSettingsControllerTests.swift */; };
|
|
||||||
A1000000000000000000001D /* WorkspaceSwitcherView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000000000000000000001D /* WorkspaceSwitcherView.swift */; };
|
|
||||||
A1000000000000000000001E /* WorkspacesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000000000000000000001E /* WorkspacesSettingsView.swift */; };
|
|
||||||
A1000000000000000000001F /* CommandNotchUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2000000000000000000001F /* CommandNotchUITests.swift */; };
|
|
||||||
A10000000000000000000020 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2000000000000000000000A /* XCTest.framework */; };
|
|
||||||
2213F430F3D8A88033607CD2 /* NotchSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6359CF9DDF89413440300D /* NotchSettings.swift */; };
|
|
||||||
247C6F84E7ADE7AED43381E2 /* CommandNotchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B671125208055E5334CB85E /* CommandNotchApp.swift */; };
|
|
||||||
295653929D5B9C0E6C90D6D7 /* SwiftTerm in Frameworks */ = {isa = PBXBuildFile; productRef = 032AECA58EA4C274BE9F3320 /* SwiftTerm */; };
|
|
||||||
37FC0A7CEEA37C9DCC6A8351 /* TerminalSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B598809B19C892470DE7268 /* TerminalSession.swift */; };
|
|
||||||
3A1F0C4BE9D84A5C8E2B7101 /* TerminalTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1F0C4AE9D84A5C8E2B7101 /* TerminalTheme.swift */; };
|
|
||||||
4566E6B87CB62AF5C8D4B9D8 /* NotchShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FC09C538CBE7C2D072008B2 /* NotchShape.swift */; };
|
|
||||||
4D5125E11B4DDBDB3DFACBAF /* HotkeyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B72743F178231E0B06DD3DE /* HotkeyManager.swift */; };
|
|
||||||
5B14FC23928E817FEB8D2A74 /* NotchWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FEFF9074A85F02C43D9408 /* NotchWindow.swift */; };
|
|
||||||
7BD705CA6A34117929B362EC /* HotkeyRecorderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 490C53139360D970099D8F3D /* HotkeyRecorderView.swift */; };
|
|
||||||
7DE94234DC42EAB79896E176 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5CB3313B230019D0E988AFE /* SettingsView.swift */; };
|
|
||||||
7EA51C3720BED7E6189E057D /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F009B75D078A5070B5EA9738 /* TabBar.swift */; };
|
|
||||||
81A912E3E16165D999882078 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0779490DE9020FBBC464BE /* AppDelegate.swift */; };
|
|
||||||
888C45C650327089EBD39B2E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BA7F4716DA3909DA8BC381 /* ContentView.swift */; };
|
|
||||||
88EBFBB2292659EA7C42A8F9 /* HotkeyBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB81B6DA7126E1F5FBCC8B8 /* HotkeyBinding.swift */; };
|
|
||||||
8FC731CF99AB4C1C10C16FAB /* PopoutWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1CD1DE020F0467AFB98DE3 /* PopoutWindowController.swift */; };
|
|
||||||
A70FDB7EEEB895D475ED96E8 /* NotchState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5C99B7CD7F60E55844E40C /* NotchState.swift */; };
|
|
||||||
BE5E64222CF5689AC7088683 /* ScreenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A290D4D21D6C01A583A372 /* ScreenManager.swift */; };
|
|
||||||
C4C93F2911B41BC19A2AE934 /* SettingsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A973877BCE6084D0EBBBDBD /* SettingsWindowController.swift */; };
|
|
||||||
CC26C1677258E44F0D7B106A /* SwiftTermView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E47000112562615C7E59489 /* SwiftTermView.swift */; };
|
|
||||||
E9A064422790735E033E534F /* TerminalManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6843B571B41986DE386F5F /* TerminalManager.swift */; };
|
|
||||||
EA604F3F38D6638C7236CDC2 /* LaunchAtLoginHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B567F3B5D006D2B35630CFF /* LaunchAtLoginHelper.swift */; };
|
|
||||||
F0130A88D1453CD199FA65D7 /* NSScreen+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0CED6A0F25A6E57D8AA308A /* NSScreen+Extensions.swift */; };
|
|
||||||
/* End PBXBuildFile section */
|
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
|
||||||
A60000000000000000000001 /* PBXContainerItemProxy */ = {
|
|
||||||
isa = PBXContainerItemProxy;
|
|
||||||
containerPortal = F72A983360EF3F99042A4895 /* Project object */;
|
|
||||||
proxyType = 1;
|
|
||||||
remoteGlobalIDString = 1485207FA11756EC2DF4F08B;
|
|
||||||
remoteInfo = CommandNotch;
|
|
||||||
};
|
|
||||||
A60000000000000000000002 /* PBXContainerItemProxy */ = {
|
|
||||||
isa = PBXContainerItemProxy;
|
|
||||||
containerPortal = F72A983360EF3F99042A4895 /* Project object */;
|
|
||||||
proxyType = 1;
|
|
||||||
remoteGlobalIDString = 1485207FA11756EC2DF4F08B;
|
|
||||||
remoteInfo = CommandNotch;
|
|
||||||
};
|
|
||||||
/* End PBXContainerItemProxy section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
|
||||||
02FEFF9074A85F02C43D9408 /* NotchWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchWindow.swift; sourceTree = "<group>"; };
|
|
||||||
0A973877BCE6084D0EBBBDBD /* SettingsWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsWindowController.swift; sourceTree = "<group>"; };
|
|
||||||
0B567F3B5D006D2B35630CFF /* LaunchAtLoginHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAtLoginHelper.swift; sourceTree = "<group>"; };
|
|
||||||
0F4A88A33D93B6E100A1C002 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000001 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000002 /* AppSettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsController.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000003 /* AppSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsStore.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000004 /* WorkspaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceController.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000005 /* WorkspaceRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceRegistry.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000006 /* WorkspaceSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceSummary.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000007 /* CommandNotchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CommandNotchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
A20000000000000000000008 /* AppSettingsStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsStoreTests.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000009 /* WorkspaceRegistryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceRegistryTests.swift; sourceTree = "<group>"; };
|
|
||||||
A2000000000000000000000A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = System/Library/Frameworks/XCTest.framework; sourceTree = SDKROOT; };
|
|
||||||
A2000000000000000000000B /* ScreenContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenContext.swift; sourceTree = "<group>"; };
|
|
||||||
A2000000000000000000000C /* ScreenRegistryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenRegistryTests.swift; sourceTree = "<group>"; };
|
|
||||||
A2000000000000000000000D /* ScreenRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenRegistry.swift; sourceTree = "<group>"; };
|
|
||||||
A2000000000000000000000E /* WorkspaceStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceStore.swift; sourceTree = "<group>"; };
|
|
||||||
A2000000000000000000000F /* WorkspaceStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceStoreTests.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000010 /* NotchOrchestrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchOrchestrator.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000011 /* NotchOrchestratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchOrchestratorTests.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000012 /* ScreenContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenContextTests.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000013 /* WindowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000014 /* WindowFrameCalculatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowFrameCalculatorTests.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000015 /* SettingsBindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsBindings.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000016 /* GeneralSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsView.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000017 /* AppearanceSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceSettingsView.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000018 /* AnimationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimationSettingsView.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000019 /* TerminalSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSettingsView.swift; sourceTree = "<group>"; };
|
|
||||||
A2000000000000000000001A /* HotkeySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeySettingsView.swift; sourceTree = "<group>"; };
|
|
||||||
A2000000000000000000001B /* AboutSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutSettingsView.swift; sourceTree = "<group>"; };
|
|
||||||
A2000000000000000000001C /* AppSettingsControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsControllerTests.swift; sourceTree = "<group>"; };
|
|
||||||
A2000000000000000000001D /* WorkspaceSwitcherView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceSwitcherView.swift; sourceTree = "<group>"; };
|
|
||||||
A2000000000000000000001E /* WorkspacesSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspacesSettingsView.swift; sourceTree = "<group>"; };
|
|
||||||
A2000000000000000000001F /* CommandNotchUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandNotchUITests.swift; sourceTree = "<group>"; };
|
|
||||||
A20000000000000000000020 /* CommandNotchUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CommandNotchUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
15A290D4D21D6C01A583A372 /* ScreenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenManager.swift; sourceTree = "<group>"; };
|
|
||||||
1E47000112562615C7E59489 /* SwiftTermView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftTermView.swift; sourceTree = "<group>"; };
|
|
||||||
1FC09C538CBE7C2D072008B2 /* NotchShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchShape.swift; sourceTree = "<group>"; };
|
|
||||||
20BA7F4716DA3909DA8BC381 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
|
||||||
2C5C99B7CD7F60E55844E40C /* NotchState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchState.swift; sourceTree = "<group>"; };
|
|
||||||
3B72743F178231E0B06DD3DE /* HotkeyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeyManager.swift; sourceTree = "<group>"; };
|
|
||||||
490C53139360D970099D8F3D /* HotkeyRecorderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeyRecorderView.swift; sourceTree = "<group>"; };
|
|
||||||
4B671125208055E5334CB85E /* CommandNotchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandNotchApp.swift; sourceTree = "<group>"; };
|
|
||||||
4BB81B6DA7126E1F5FBCC8B8 /* HotkeyBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotkeyBinding.swift; sourceTree = "<group>"; };
|
|
||||||
5C0779490DE9020FBBC464BE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
|
||||||
665CFC051CF185B71199608D /* CommandNotch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CommandNotch.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
7B598809B19C892470DE7268 /* TerminalSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSession.swift; sourceTree = "<group>"; };
|
|
||||||
3A1F0C4AE9D84A5C8E2B7101 /* TerminalTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalTheme.swift; sourceTree = "<group>"; };
|
|
||||||
9547A79F60E46F4521A70674 /* CommandNotch.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CommandNotch.entitlements; sourceTree = "<group>"; };
|
|
||||||
AA6359CF9DDF89413440300D /* NotchSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchSettings.swift; sourceTree = "<group>"; };
|
|
||||||
BA6843B571B41986DE386F5F /* TerminalManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalManager.swift; sourceTree = "<group>"; };
|
|
||||||
C5CB3313B230019D0E988AFE /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
|
||||||
EA1CD1DE020F0467AFB98DE3 /* PopoutWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoutWindowController.swift; sourceTree = "<group>"; };
|
|
||||||
F009B75D078A5070B5EA9738 /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = "<group>"; };
|
|
||||||
F0CED6A0F25A6E57D8AA308A /* NSScreen+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSScreen+Extensions.swift"; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
|
||||||
6085DF2BDFFB2A99C4ABD514 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
295653929D5B9C0E6C90D6D7 /* SwiftTerm in Frameworks */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
A40000000000000000000001 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
A10000000000000000000009 /* XCTest.framework in Frameworks */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
A40000000000000000000003 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
A10000000000000000000020 /* XCTest.framework in Frameworks */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
0EF94ED46B4860C241540F0A /* Resources */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
0F4A88A33D93B6E100A1C002 /* Assets.xcassets */,
|
|
||||||
9547A79F60E46F4521A70674 /* CommandNotch.entitlements */,
|
|
||||||
);
|
|
||||||
path = Resources;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
27C90448ECAC906F0DA429C0 /* Managers */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
3B72743F178231E0B06DD3DE /* HotkeyManager.swift */,
|
|
||||||
0B567F3B5D006D2B35630CFF /* LaunchAtLoginHelper.swift */,
|
|
||||||
EA1CD1DE020F0467AFB98DE3 /* PopoutWindowController.swift */,
|
|
||||||
15A290D4D21D6C01A583A372 /* ScreenManager.swift */,
|
|
||||||
0A973877BCE6084D0EBBBDBD /* SettingsWindowController.swift */,
|
|
||||||
A20000000000000000000013 /* WindowCoordinator.swift */,
|
|
||||||
);
|
|
||||||
path = Managers;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
792DD4F8C079680683D8FF7A /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
665CFC051CF185B71199608D /* CommandNotch.app */,
|
|
||||||
A20000000000000000000007 /* CommandNotchTests.xctest */,
|
|
||||||
A20000000000000000000020 /* CommandNotchUITests.xctest */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
A30000000000000000000001 /* CommandNotchTests */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
A20000000000000000000008 /* AppSettingsStoreTests.swift */,
|
|
||||||
A2000000000000000000001C /* AppSettingsControllerTests.swift */,
|
|
||||||
A20000000000000000000012 /* ScreenContextTests.swift */,
|
|
||||||
A2000000000000000000000C /* ScreenRegistryTests.swift */,
|
|
||||||
A20000000000000000000011 /* NotchOrchestratorTests.swift */,
|
|
||||||
A20000000000000000000014 /* WindowFrameCalculatorTests.swift */,
|
|
||||||
A20000000000000000000009 /* WorkspaceRegistryTests.swift */,
|
|
||||||
A2000000000000000000000F /* WorkspaceStoreTests.swift */,
|
|
||||||
);
|
|
||||||
path = CommandNotchTests;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
A30000000000000000000002 /* CommandNotchUITests */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
A2000000000000000000001F /* CommandNotchUITests.swift */,
|
|
||||||
);
|
|
||||||
path = CommandNotchUITests;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
869AD33E1CDEB9CBAD401BA6 /* Models */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
A20000000000000000000001 /* AppSettings.swift */,
|
|
||||||
A20000000000000000000002 /* AppSettingsController.swift */,
|
|
||||||
A20000000000000000000003 /* AppSettingsStore.swift */,
|
|
||||||
4BB81B6DA7126E1F5FBCC8B8 /* HotkeyBinding.swift */,
|
|
||||||
AA6359CF9DDF89413440300D /* NotchSettings.swift */,
|
|
||||||
2C5C99B7CD7F60E55844E40C /* NotchState.swift */,
|
|
||||||
A20000000000000000000010 /* NotchOrchestrator.swift */,
|
|
||||||
A2000000000000000000000B /* ScreenContext.swift */,
|
|
||||||
A2000000000000000000000D /* ScreenRegistry.swift */,
|
|
||||||
BA6843B571B41986DE386F5F /* TerminalManager.swift */,
|
|
||||||
7B598809B19C892470DE7268 /* TerminalSession.swift */,
|
|
||||||
3A1F0C4AE9D84A5C8E2B7101 /* TerminalTheme.swift */,
|
|
||||||
A20000000000000000000004 /* WorkspaceController.swift */,
|
|
||||||
A20000000000000000000005 /* WorkspaceRegistry.swift */,
|
|
||||||
A20000000000000000000006 /* WorkspaceSummary.swift */,
|
|
||||||
A2000000000000000000000E /* WorkspaceStore.swift */,
|
|
||||||
);
|
|
||||||
path = Models;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
8D95E0324E6AFC9E4DC0C087 /* Extensions */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
F0CED6A0F25A6E57D8AA308A /* NSScreen+Extensions.swift */,
|
|
||||||
);
|
|
||||||
path = Extensions;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
9E1CA4816F67033BBD52D8A3 /* CommandNotch */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
5C0779490DE9020FBBC464BE /* AppDelegate.swift */,
|
|
||||||
20BA7F4716DA3909DA8BC381 /* ContentView.swift */,
|
|
||||||
4B671125208055E5334CB85E /* CommandNotchApp.swift */,
|
|
||||||
F32F526005A2589010E63C76 /* Components */,
|
|
||||||
8D95E0324E6AFC9E4DC0C087 /* Extensions */,
|
|
||||||
27C90448ECAC906F0DA429C0 /* Managers */,
|
|
||||||
869AD33E1CDEB9CBAD401BA6 /* Models */,
|
|
||||||
0EF94ED46B4860C241540F0A /* Resources */,
|
|
||||||
C2B8955F4D0A1DAA7E60326A /* Views */,
|
|
||||||
);
|
|
||||||
path = CommandNotch;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
C2B8955F4D0A1DAA7E60326A /* Views */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
A2000000000000000000001B /* AboutSettingsView.swift */,
|
|
||||||
A20000000000000000000017 /* AppearanceSettingsView.swift */,
|
|
||||||
A20000000000000000000018 /* AnimationSettingsView.swift */,
|
|
||||||
A20000000000000000000016 /* GeneralSettingsView.swift */,
|
|
||||||
A2000000000000000000001A /* HotkeySettingsView.swift */,
|
|
||||||
A20000000000000000000015 /* SettingsBindings.swift */,
|
|
||||||
C5CB3313B230019D0E988AFE /* SettingsView.swift */,
|
|
||||||
A20000000000000000000019 /* TerminalSettingsView.swift */,
|
|
||||||
A2000000000000000000001E /* WorkspacesSettingsView.swift */,
|
|
||||||
A2000000000000000000001D /* WorkspaceSwitcherView.swift */,
|
|
||||||
);
|
|
||||||
path = Views;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
F32F526005A2589010E63C76 /* Components */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
490C53139360D970099D8F3D /* HotkeyRecorderView.swift */,
|
|
||||||
1FC09C538CBE7C2D072008B2 /* NotchShape.swift */,
|
|
||||||
02FEFF9074A85F02C43D9408 /* NotchWindow.swift */,
|
|
||||||
1E47000112562615C7E59489 /* SwiftTermView.swift */,
|
|
||||||
F009B75D078A5070B5EA9738 /* TabBar.swift */,
|
|
||||||
);
|
|
||||||
path = Components;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
FC6F23514BFE2235BD4154E8 = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
9E1CA4816F67033BBD52D8A3 /* CommandNotch */,
|
|
||||||
A30000000000000000000001 /* CommandNotchTests */,
|
|
||||||
A30000000000000000000002 /* CommandNotchUITests */,
|
|
||||||
792DD4F8C079680683D8FF7A /* Products */,
|
|
||||||
);
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
|
||||||
1485207FA11756EC2DF4F08B /* CommandNotch */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = 74CB98309F5464CDCB00C63A /* Build configuration list for PBXNativeTarget "CommandNotch" */;
|
|
||||||
buildPhases = (
|
|
||||||
F3C6D5CD1247D246A3F6F7AB /* Sources */,
|
|
||||||
6085DF2BDFFB2A99C4ABD514 /* Frameworks */,
|
|
||||||
0F4A88A33D93B6E100A1C003 /* Resources */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = CommandNotch;
|
|
||||||
packageProductDependencies = (
|
|
||||||
032AECA58EA4C274BE9F3320 /* SwiftTerm */,
|
|
||||||
);
|
|
||||||
productName = CommandNotch;
|
|
||||||
productReference = 665CFC051CF185B71199608D /* CommandNotch.app */;
|
|
||||||
productType = "com.apple.product-type.application";
|
|
||||||
};
|
|
||||||
A50000000000000000000001 /* CommandNotchTests */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = A90000000000000000000001 /* Build configuration list for PBXNativeTarget "CommandNotchTests" */;
|
|
||||||
buildPhases = (
|
|
||||||
A40000000000000000000002 /* Sources */,
|
|
||||||
A40000000000000000000001 /* Frameworks */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
A70000000000000000000001 /* PBXTargetDependency */,
|
|
||||||
);
|
|
||||||
name = CommandNotchTests;
|
|
||||||
packageProductDependencies = (
|
|
||||||
);
|
|
||||||
productName = CommandNotchTests;
|
|
||||||
productReference = A20000000000000000000007 /* CommandNotchTests.xctest */;
|
|
||||||
productType = "com.apple.product-type.bundle.unit-test";
|
|
||||||
};
|
|
||||||
A50000000000000000000002 /* CommandNotchUITests */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = A90000000000000000000002 /* Build configuration list for PBXNativeTarget "CommandNotchUITests" */;
|
|
||||||
buildPhases = (
|
|
||||||
A40000000000000000000004 /* Sources */,
|
|
||||||
A40000000000000000000003 /* Frameworks */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
A70000000000000000000002 /* PBXTargetDependency */,
|
|
||||||
);
|
|
||||||
name = CommandNotchUITests;
|
|
||||||
packageProductDependencies = (
|
|
||||||
);
|
|
||||||
productName = CommandNotchUITests;
|
|
||||||
productReference = A20000000000000000000020 /* CommandNotchUITests.xctest */;
|
|
||||||
productType = "com.apple.product-type.bundle.ui-testing";
|
|
||||||
};
|
|
||||||
/* End PBXNativeTarget section */
|
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
|
||||||
F72A983360EF3F99042A4895 /* Project object */ = {
|
|
||||||
isa = PBXProject;
|
|
||||||
attributes = {
|
|
||||||
BuildIndependentTargetsInParallel = YES;
|
|
||||||
LastUpgradeCheck = 1600;
|
|
||||||
};
|
|
||||||
buildConfigurationList = D1C4019FEAFC83BB053C9E6E /* Build configuration list for PBXProject "CommandNotch" */;
|
|
||||||
compatibilityVersion = "Xcode 14.0";
|
|
||||||
developmentRegion = en;
|
|
||||||
hasScannedForEncodings = 0;
|
|
||||||
knownRegions = (
|
|
||||||
Base,
|
|
||||||
en,
|
|
||||||
);
|
|
||||||
mainGroup = FC6F23514BFE2235BD4154E8;
|
|
||||||
minimizedProjectReferenceProxies = 1;
|
|
||||||
packageReferences = (
|
|
||||||
80F03B77566BF59C9941EAD4 /* XCRemoteSwiftPackageReference "SwiftTerm" */,
|
|
||||||
);
|
|
||||||
projectDirPath = "";
|
|
||||||
projectRoot = "";
|
|
||||||
targets = (
|
|
||||||
1485207FA11756EC2DF4F08B /* CommandNotch */,
|
|
||||||
A50000000000000000000001 /* CommandNotchTests */,
|
|
||||||
A50000000000000000000002 /* CommandNotchUITests */,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/* End PBXProject section */
|
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
|
||||||
0F4A88A33D93B6E100A1C003 /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
0F4A88A33D93B6E100A1C001 /* Assets.xcassets in Resources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXResourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
|
||||||
A40000000000000000000002 /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
A10000000000000000000007 /* AppSettingsStoreTests.swift in Sources */,
|
|
||||||
A1000000000000000000001C /* AppSettingsControllerTests.swift in Sources */,
|
|
||||||
A10000000000000000000012 /* ScreenContextTests.swift in Sources */,
|
|
||||||
A1000000000000000000000C /* ScreenRegistryTests.swift in Sources */,
|
|
||||||
A10000000000000000000011 /* NotchOrchestratorTests.swift in Sources */,
|
|
||||||
A10000000000000000000014 /* WindowFrameCalculatorTests.swift in Sources */,
|
|
||||||
A10000000000000000000008 /* WorkspaceRegistryTests.swift in Sources */,
|
|
||||||
A1000000000000000000000F /* WorkspaceStoreTests.swift in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
A40000000000000000000004 /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
A1000000000000000000001F /* CommandNotchUITests.swift in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
F3C6D5CD1247D246A3F6F7AB /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
A10000000000000000000001 /* AppSettings.swift in Sources */,
|
|
||||||
A10000000000000000000002 /* AppSettingsController.swift in Sources */,
|
|
||||||
A10000000000000000000003 /* AppSettingsStore.swift in Sources */,
|
|
||||||
81A912E3E16165D999882078 /* AppDelegate.swift in Sources */,
|
|
||||||
888C45C650327089EBD39B2E /* ContentView.swift in Sources */,
|
|
||||||
247C6F84E7ADE7AED43381E2 /* CommandNotchApp.swift in Sources */,
|
|
||||||
88EBFBB2292659EA7C42A8F9 /* HotkeyBinding.swift in Sources */,
|
|
||||||
4D5125E11B4DDBDB3DFACBAF /* HotkeyManager.swift in Sources */,
|
|
||||||
7BD705CA6A34117929B362EC /* HotkeyRecorderView.swift in Sources */,
|
|
||||||
EA604F3F38D6638C7236CDC2 /* LaunchAtLoginHelper.swift in Sources */,
|
|
||||||
F0130A88D1453CD199FA65D7 /* NSScreen+Extensions.swift in Sources */,
|
|
||||||
2213F430F3D8A88033607CD2 /* NotchSettings.swift in Sources */,
|
|
||||||
4566E6B87CB62AF5C8D4B9D8 /* NotchShape.swift in Sources */,
|
|
||||||
A70FDB7EEEB895D475ED96E8 /* NotchState.swift in Sources */,
|
|
||||||
A10000000000000000000010 /* NotchOrchestrator.swift in Sources */,
|
|
||||||
5B14FC23928E817FEB8D2A74 /* NotchWindow.swift in Sources */,
|
|
||||||
8FC731CF99AB4C1C10C16FAB /* PopoutWindowController.swift in Sources */,
|
|
||||||
A1000000000000000000000B /* ScreenContext.swift in Sources */,
|
|
||||||
A1000000000000000000000D /* ScreenRegistry.swift in Sources */,
|
|
||||||
BE5E64222CF5689AC7088683 /* ScreenManager.swift in Sources */,
|
|
||||||
7DE94234DC42EAB79896E176 /* SettingsView.swift in Sources */,
|
|
||||||
C4C93F2911B41BC19A2AE934 /* SettingsWindowController.swift in Sources */,
|
|
||||||
CC26C1677258E44F0D7B106A /* SwiftTermView.swift in Sources */,
|
|
||||||
7EA51C3720BED7E6189E057D /* TabBar.swift in Sources */,
|
|
||||||
E9A064422790735E033E534F /* TerminalManager.swift in Sources */,
|
|
||||||
37FC0A7CEEA37C9DCC6A8351 /* TerminalSession.swift in Sources */,
|
|
||||||
3A1F0C4BE9D84A5C8E2B7101 /* TerminalTheme.swift in Sources */,
|
|
||||||
A10000000000000000000013 /* WindowCoordinator.swift in Sources */,
|
|
||||||
A10000000000000000000004 /* WorkspaceController.swift in Sources */,
|
|
||||||
A10000000000000000000005 /* WorkspaceRegistry.swift in Sources */,
|
|
||||||
A10000000000000000000006 /* WorkspaceSummary.swift in Sources */,
|
|
||||||
A1000000000000000000000E /* WorkspaceStore.swift in Sources */,
|
|
||||||
A1000000000000000000001B /* AboutSettingsView.swift in Sources */,
|
|
||||||
A10000000000000000000017 /* AppearanceSettingsView.swift in Sources */,
|
|
||||||
A10000000000000000000018 /* AnimationSettingsView.swift in Sources */,
|
|
||||||
A10000000000000000000016 /* GeneralSettingsView.swift in Sources */,
|
|
||||||
A1000000000000000000001A /* HotkeySettingsView.swift in Sources */,
|
|
||||||
A10000000000000000000015 /* SettingsBindings.swift in Sources */,
|
|
||||||
A10000000000000000000019 /* TerminalSettingsView.swift in Sources */,
|
|
||||||
A1000000000000000000001E /* WorkspacesSettingsView.swift in Sources */,
|
|
||||||
A1000000000000000000001D /* WorkspaceSwitcherView.swift in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXSourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
|
||||||
A70000000000000000000001 /* PBXTargetDependency */ = {
|
|
||||||
isa = PBXTargetDependency;
|
|
||||||
target = 1485207FA11756EC2DF4F08B /* CommandNotch */;
|
|
||||||
targetProxy = A60000000000000000000001 /* PBXContainerItemProxy */;
|
|
||||||
};
|
|
||||||
A70000000000000000000002 /* PBXTargetDependency */ = {
|
|
||||||
isa = PBXTargetDependency;
|
|
||||||
target = 1485207FA11756EC2DF4F08B /* CommandNotch */;
|
|
||||||
targetProxy = A60000000000000000000002 /* PBXContainerItemProxy */;
|
|
||||||
};
|
|
||||||
/* End PBXTargetDependency section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
|
||||||
0B8C784EF064E46C44076D6B /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
|
||||||
CLANG_USE_OPTIMIZATION_PROFILE = YES;
|
|
||||||
CODE_SIGN_ENTITLEMENTS = CommandNotch/Resources/CommandNotch.entitlements;
|
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
|
||||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = G698BP272N;
|
|
||||||
INFOPLIST_FILE = CommandNotch/Resources/Info.plist;
|
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = CommandNotch;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/../Frameworks",
|
|
||||||
);
|
|
||||||
MARKETING_VERSION = 0.0.3;
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.app;
|
|
||||||
PRODUCT_NAME = CommandNotch;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
3595A9212275B9AEC4448C64 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
ENABLE_TESTABILITY = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"DEBUG=1",
|
|
||||||
);
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
|
||||||
MTL_FAST_MATH = YES;
|
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
|
||||||
SWIFT_VERSION = 5.10;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
7020C02C1BDF63690CC9A3AC /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
|
||||||
CLANG_USE_OPTIMIZATION_PROFILE = YES;
|
|
||||||
CODE_SIGN_ENTITLEMENTS = CommandNotch/Resources/CommandNotch.entitlements;
|
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
|
||||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = G698BP272N;
|
|
||||||
INFOPLIST_FILE = CommandNotch/Resources/Info.plist;
|
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = CommandNotch;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/../Frameworks",
|
|
||||||
);
|
|
||||||
MARKETING_VERSION = 0.0.3;
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.app;
|
|
||||||
PRODUCT_NAME = CommandNotch;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
A80000000000000000000001 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@loader_path/../Frameworks",
|
|
||||||
"@executable_path/../Frameworks",
|
|
||||||
);
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.CommandNotchTests;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
|
||||||
SWIFT_VERSION = 5.10;
|
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CommandNotch.app/Contents/MacOS/CommandNotch";
|
|
||||||
TEST_TARGET_NAME = CommandNotch;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
A80000000000000000000002 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@loader_path/../Frameworks",
|
|
||||||
"@executable_path/../Frameworks",
|
|
||||||
);
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.CommandNotchTests;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SWIFT_VERSION = 5.10;
|
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CommandNotch.app/Contents/MacOS/CommandNotch";
|
|
||||||
TEST_TARGET_NAME = CommandNotch;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
A80000000000000000000003 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
|
||||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = G698BP272N;
|
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.CommandNotchUITests;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
|
||||||
SWIFT_VERSION = 5.10;
|
|
||||||
TEST_TARGET_NAME = CommandNotch;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
A80000000000000000000004 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
|
||||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = G698BP272N;
|
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.CommandNotchUITests;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SWIFT_VERSION = 5.10;
|
|
||||||
TEST_TARGET_NAME = CommandNotch;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
BC741C4C821EA399B645E547 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
|
||||||
MTL_FAST_MATH = YES;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
|
||||||
SWIFT_VERSION = 5.10;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
/* End XCBuildConfiguration section */
|
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
|
||||||
74CB98309F5464CDCB00C63A /* Build configuration list for PBXNativeTarget "CommandNotch" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
7020C02C1BDF63690CC9A3AC /* Debug */,
|
|
||||||
0B8C784EF064E46C44076D6B /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Debug;
|
|
||||||
};
|
|
||||||
A90000000000000000000001 /* Build configuration list for PBXNativeTarget "CommandNotchTests" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
A80000000000000000000001 /* Debug */,
|
|
||||||
A80000000000000000000002 /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Debug;
|
|
||||||
};
|
|
||||||
A90000000000000000000002 /* Build configuration list for PBXNativeTarget "CommandNotchUITests" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
A80000000000000000000003 /* Debug */,
|
|
||||||
A80000000000000000000004 /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Debug;
|
|
||||||
};
|
|
||||||
D1C4019FEAFC83BB053C9E6E /* Build configuration list for PBXProject "CommandNotch" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
3595A9212275B9AEC4448C64 /* Debug */,
|
|
||||||
BC741C4C821EA399B645E547 /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Debug;
|
|
||||||
};
|
|
||||||
/* End XCConfigurationList section */
|
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
|
||||||
80F03B77566BF59C9941EAD4 /* XCRemoteSwiftPackageReference "SwiftTerm" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/migueldeicaza/SwiftTerm.git";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMajorVersion;
|
|
||||||
minimumVersion = 1.2.0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
|
||||||
032AECA58EA4C274BE9F3320 /* SwiftTerm */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = 80F03B77566BF59C9941EAD4 /* XCRemoteSwiftPackageReference "SwiftTerm" */;
|
|
||||||
productName = SwiftTerm;
|
|
||||||
};
|
|
||||||
/* End XCSwiftPackageProductDependency section */
|
|
||||||
};
|
|
||||||
rootObject = F72A983360EF3F99042A4895 /* Project object */;
|
|
||||||
}
|
|
||||||
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Harvey Zuccon
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
111
README.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# CommandNotch
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="icons/Downterm-icon-256.png" width="128" alt="CommandNotch icon">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
A drop-down terminal for macOS that lives in the notch area.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/platform-macOS%2014%2B-black" alt="macOS 14+">
|
||||||
|
<img src="https://img.shields.io/badge/UI-SwiftUI%20%2B%20AppKit-black" alt="SwiftUI and AppKit">
|
||||||
|
<img src="https://img.shields.io/badge/license-MIT-black" alt="MIT License">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
CommandNotch is a notch-native terminal overlay for macOS. It gives you a fast shell without switching spaces, keeping a full Terminal window open, or breaking your flow. Open it with a hotkey, drop into a shell, then get out of the way just as quickly.
|
||||||
|
|
||||||
|
The current Xcode target and bundle name are still `CommandNotch`, but the project is being presented publicly as **CommandNotch**.
|
||||||
|
|
||||||
|
## Why CommandNotch
|
||||||
|
|
||||||
|
- You want a terminal that is always one shortcut away.
|
||||||
|
- You like the idea of a terminal living in the menu bar / notch area instead of a full window.
|
||||||
|
- You work across multiple displays and want each screen to keep its own notch state.
|
||||||
|
- You want lightweight workspaces, detachable tabs, and shell access without giving up native macOS feel.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Native macOS notch overlay with open and closed states.
|
||||||
|
- Fast shell sessions powered by SwiftTerm and a local login shell.
|
||||||
|
- Multiple tabs with hotkeys for new, close, next, previous, and direct tab switching.
|
||||||
|
- Workspace support, including shared workspaces across screens.
|
||||||
|
- Multi-display awareness with per-screen assignment and presentation state.
|
||||||
|
- Detachable tabs that can pop out into standalone terminal windows.
|
||||||
|
- Terminal themes: Classic, Xterm, Solarized Dark, Dracula, and Nord.
|
||||||
|
- Configurable terminal size presets with optional hotkeys.
|
||||||
|
- Hover-to-open behavior and animation tuning.
|
||||||
|
- Launch at login support.
|
||||||
|
- Global toggle hotkey and notch-scoped shortcut handling.
|
||||||
|
- Terminal-friendly macOS key behavior for `Command+Arrow`, `Option+Arrow`, `Command+Backspace`, and `Command+L`.
|
||||||
|
|
||||||
|
## Gallery
|
||||||
|
|
||||||
|
### Demo
|
||||||
|
|
||||||
|
[](.github/assets/CommandNotch-Demo.mp4)
|
||||||
|
|
||||||
|
Click the preview above to watch the demo recording.
|
||||||
|
|
||||||
|
### Open Notch Terminal
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Settings
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- macOS 14 or later
|
||||||
|
- Xcode 16 or later
|
||||||
|
- Homebrew `xcodegen`
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd CommandNotch
|
||||||
|
xcodegen generate --spec project.yml
|
||||||
|
open CommandNotch.xcodeproj
|
||||||
|
```
|
||||||
|
|
||||||
|
Or from the command line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd CommandNotch
|
||||||
|
xcodegen generate --spec project.yml
|
||||||
|
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
|
||||||
|
xcodebuild build -project CommandNotch.xcodeproj -scheme CommandNotch -destination 'platform=macOS'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Layout
|
||||||
|
|
||||||
|
```text
|
||||||
|
CommandNotch/
|
||||||
|
├── CommandNotch/ # XcodeGen spec, app target, tests
|
||||||
|
├── docs/ # architecture and planning notes
|
||||||
|
├── icons/ # app icons and branding assets
|
||||||
|
└── .github/assets/ # README screenshots and support assets
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome. If you want to fix bugs, improve the UX, tighten the architecture, or help polish the public release, start with [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
## Help Fund Development
|
||||||
|
|
||||||
|
If CommandNotch saves you time, support helps justify more polish and faster iteration.
|
||||||
|
|
||||||
|
- Ko-fi: https://ko-fi.com/harvmaster
|
||||||
|
- BCH: `bitcoincash:zq5xlhahsk8svzk562m3kwrzgd9hrm80mcu8slnzv3`
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src=".github/assets/bch-receiving-address.png" width="220" alt="BCH receiving address QR code">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
CommandNotch is released under the [MIT License](LICENSE).
|
||||||
@@ -618,7 +618,7 @@ Exit criteria:
|
|||||||
## Proposed File Layout
|
## Proposed File Layout
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Downterm/CommandNotch/
|
CommandNotch/CommandNotch/
|
||||||
App/
|
App/
|
||||||
CommandNotchApp.swift
|
CommandNotchApp.swift
|
||||||
AppDelegate.swift
|
AppDelegate.swift
|
||||||
|
|||||||
351
docs/workspace-split-pane-plan.md
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
# Workspace Split Pane Plan
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Capture the agreed design for workspace-owned split terminals so the feature can be implemented later without redoing scope and architecture decisions.
|
||||||
|
|
||||||
|
## Agreed Scope
|
||||||
|
|
||||||
|
- Split layouts belong to the workspace, not the screen.
|
||||||
|
- A workspace may only be actively presented on one screen at a time.
|
||||||
|
- Split layouts should be visible the same way when that workspace is opened on another monitor.
|
||||||
|
- Nested splits are supported.
|
||||||
|
- Any two tabs may be joined into a split.
|
||||||
|
- Closing a pane causes its sibling to expand.
|
||||||
|
- Split layout must survive relaunch.
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
- Simultaneous live rendering of the same workspace on multiple screens.
|
||||||
|
- Full multi-user or collaborative state sync.
|
||||||
|
- Arbitrary freeform pane layout. The model is tree-based binary splits.
|
||||||
|
- Solving all interaction polish in the first pass.
|
||||||
|
|
||||||
|
## Current Constraint
|
||||||
|
|
||||||
|
The current workspace model is flat:
|
||||||
|
|
||||||
|
- `WorkspaceController` owns `[TerminalSession]`
|
||||||
|
- one tab is active at a time
|
||||||
|
- `ContentView` renders exactly one active `SwiftTermView`
|
||||||
|
|
||||||
|
This means split panes are not an additive UI feature. They require a workspace model refactor.
|
||||||
|
|
||||||
|
## Recommended Model
|
||||||
|
|
||||||
|
Do not model splits as "adjacent tabs with special behavior" internally.
|
||||||
|
|
||||||
|
Instead:
|
||||||
|
|
||||||
|
1. A workspace owns ordered top-level tabs.
|
||||||
|
2. Each top-level tab owns a pane tree.
|
||||||
|
3. Pane tree leaves own `TerminalSession` instances.
|
||||||
|
4. Pane tree internal nodes represent binary splits.
|
||||||
|
5. The tab bar can visually present grouped/joined tabs based on the active tab's pane composition.
|
||||||
|
|
||||||
|
This keeps the domain model clean while still allowing the desired visual metaphor.
|
||||||
|
|
||||||
|
## Proposed Domain Types
|
||||||
|
|
||||||
|
```swift
|
||||||
|
typealias WorkspaceTabID = UUID
|
||||||
|
typealias PaneID = UUID
|
||||||
|
|
||||||
|
struct WorkspaceTab: Identifiable, Equatable, Codable {
|
||||||
|
var id: WorkspaceTabID
|
||||||
|
var rootPane: PaneNode
|
||||||
|
var selectedPaneID: PaneID?
|
||||||
|
var titleMode: WorkspaceTabTitleMode
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PaneNode: Equatable, Codable, Identifiable {
|
||||||
|
case leaf(PaneLeaf)
|
||||||
|
case split(PaneSplit)
|
||||||
|
|
||||||
|
var id: PaneID { ... }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PaneLeaf: Equatable, Codable, Identifiable {
|
||||||
|
var id: PaneID
|
||||||
|
var sessionID: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PaneSplit: Equatable, Codable, Identifiable {
|
||||||
|
var id: PaneID
|
||||||
|
var axis: SplitAxis
|
||||||
|
var ratio: Double
|
||||||
|
var first: PaneNode
|
||||||
|
var second: PaneNode
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SplitAxis: String, Codable {
|
||||||
|
case horizontal
|
||||||
|
case vertical
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- `ratio` should be persisted so user-adjusted divider positions survive relaunch.
|
||||||
|
- `selectedPaneID` allows focus-based commands such as split active pane, close active pane, and move focus.
|
||||||
|
- The persisted model should store session identity separately from runtime AppKit/SwiftTerm objects.
|
||||||
|
|
||||||
|
## Runtime Ownership
|
||||||
|
|
||||||
|
Global:
|
||||||
|
|
||||||
|
- workspace summaries
|
||||||
|
- workspace ordering
|
||||||
|
- workspace-to-screen assignment
|
||||||
|
- hotkeys
|
||||||
|
|
||||||
|
Workspace:
|
||||||
|
|
||||||
|
- ordered top-level tabs
|
||||||
|
- active top-level tab
|
||||||
|
- per-tab pane tree
|
||||||
|
- active/selected pane
|
||||||
|
- terminal sessions for pane leaves
|
||||||
|
|
||||||
|
Screen:
|
||||||
|
|
||||||
|
- which workspace is assigned
|
||||||
|
- notch open/close state
|
||||||
|
- geometry and transitions
|
||||||
|
|
||||||
|
This remains aligned with the existing workspace architecture.
|
||||||
|
|
||||||
|
## Persistence Strategy
|
||||||
|
|
||||||
|
Split layout must survive relaunch, so workspace persistence needs to grow beyond summary metadata.
|
||||||
|
|
||||||
|
Recommended approach:
|
||||||
|
|
||||||
|
1. Keep `WorkspaceSummary` for list metadata only.
|
||||||
|
2. Introduce a persisted `WorkspaceDocument` or `WorkspaceSnapshot` per workspace.
|
||||||
|
3. Persist:
|
||||||
|
- top-level tabs
|
||||||
|
- pane tree
|
||||||
|
- active tab ID
|
||||||
|
- selected pane ID
|
||||||
|
- divider ratios
|
||||||
|
4. Do not attempt to persist shell process contents.
|
||||||
|
5. On relaunch, recreate sessions for pane leaves and restore layout structure only.
|
||||||
|
|
||||||
|
Important distinction:
|
||||||
|
|
||||||
|
- layout persistence is required
|
||||||
|
- terminal process continuity is not
|
||||||
|
|
||||||
|
## UI Model
|
||||||
|
|
||||||
|
### Main Content
|
||||||
|
|
||||||
|
The active top-level tab renders as a recursive split tree.
|
||||||
|
|
||||||
|
- leaf node -> one `SwiftTermView`
|
||||||
|
- split node -> `HSplitView`/`VSplitView` equivalent SwiftUI container with draggable divider
|
||||||
|
|
||||||
|
### Tab Bar
|
||||||
|
|
||||||
|
Top-level tabs remain the primary navigation unit.
|
||||||
|
|
||||||
|
Visual behavior:
|
||||||
|
|
||||||
|
- a normal unsplit tab looks like today
|
||||||
|
- a joined/split tab should look grouped
|
||||||
|
- grouped tabs should expose child pane titles visually inside the tab item
|
||||||
|
|
||||||
|
Recommended first-pass appearance:
|
||||||
|
|
||||||
|
- one outer tab pill per top-level tab
|
||||||
|
- inside that pill, show compact child title chips for each leaf pane
|
||||||
|
- highlight the selected pane's chip
|
||||||
|
|
||||||
|
This gives the "tabs are joined" feel without making pane leaves first-class top-level tabs in the data model.
|
||||||
|
|
||||||
|
## Command Model
|
||||||
|
|
||||||
|
New actions likely needed:
|
||||||
|
|
||||||
|
- split active pane horizontally
|
||||||
|
- split active pane vertically
|
||||||
|
- focus next pane
|
||||||
|
- focus previous pane
|
||||||
|
- close active pane
|
||||||
|
- resize focused split divider
|
||||||
|
- join tab A into tab B
|
||||||
|
- detach pane into new top-level tab
|
||||||
|
|
||||||
|
Semantics:
|
||||||
|
|
||||||
|
- joining any two tabs merges one tab's root pane into the other's tree
|
||||||
|
- the source top-level tab is removed
|
||||||
|
- the destination top-level tab remains
|
||||||
|
- sibling expansion on close is standard tree collapse
|
||||||
|
|
||||||
|
## Join Semantics
|
||||||
|
|
||||||
|
Joining any two tabs should work as:
|
||||||
|
|
||||||
|
1. Choose destination tab.
|
||||||
|
2. Choose source tab.
|
||||||
|
3. Replace destination root with a new split node:
|
||||||
|
- first child = old destination root
|
||||||
|
- second child = source root
|
||||||
|
4. Remove source tab from workspace order.
|
||||||
|
5. Select a predictable pane, preferably the source pane that was just added.
|
||||||
|
|
||||||
|
An explicit split axis should be required for the join action.
|
||||||
|
|
||||||
|
## Close Semantics
|
||||||
|
|
||||||
|
When closing a pane:
|
||||||
|
|
||||||
|
- if the pane has a sibling, the sibling expands into the parent's position
|
||||||
|
- if the pane was the only leaf in a top-level tab:
|
||||||
|
- close the whole top-level tab if more than one tab exists
|
||||||
|
- otherwise create a replacement shell pane, matching current single-tab safety behavior
|
||||||
|
|
||||||
|
## Focus Semantics
|
||||||
|
|
||||||
|
Pane focus must become explicit.
|
||||||
|
|
||||||
|
Recommended rules:
|
||||||
|
|
||||||
|
- mouse click focuses that pane
|
||||||
|
- splitting focuses the new pane
|
||||||
|
- joining focuses the moved-in pane
|
||||||
|
- closing a pane focuses the surviving sibling
|
||||||
|
- top-level tab switch restores the previously selected pane in that tab
|
||||||
|
|
||||||
|
## Migration Path
|
||||||
|
|
||||||
|
Implement in stages to reduce risk.
|
||||||
|
|
||||||
|
### Stage 1: Domain Refactor
|
||||||
|
|
||||||
|
- Replace flat workspace tabs with top-level tab objects.
|
||||||
|
- Introduce pane tree types.
|
||||||
|
- Keep only one leaf per tab initially so behavior is unchanged.
|
||||||
|
|
||||||
|
### Stage 2: Runtime Layout Rendering
|
||||||
|
|
||||||
|
- Render pane trees recursively in `ContentView`.
|
||||||
|
- Add active pane selection.
|
||||||
|
- Keep persistence off until runtime behavior is stable.
|
||||||
|
|
||||||
|
### Stage 3: Split Actions
|
||||||
|
|
||||||
|
- Split active pane horizontally/vertically.
|
||||||
|
- Close pane with sibling expansion.
|
||||||
|
- Basic focus movement.
|
||||||
|
|
||||||
|
### Stage 4: Joined Tab UI
|
||||||
|
|
||||||
|
- Update tab bar to show grouped child pane chips.
|
||||||
|
- Surface active pane clearly.
|
||||||
|
|
||||||
|
### Stage 5: Join / Unjoin Flows
|
||||||
|
|
||||||
|
- Join any two tabs with explicit axis choice.
|
||||||
|
- Support promoting a pane back to its own top-level tab if needed.
|
||||||
|
|
||||||
|
### Stage 6: Persistence
|
||||||
|
|
||||||
|
- Persist pane trees and top-level tab state.
|
||||||
|
- Recreate sessions on launch.
|
||||||
|
|
||||||
|
### Stage 7: Hotkeys and Polish
|
||||||
|
|
||||||
|
- Add pane-focused shortcuts.
|
||||||
|
- Add divider dragging polish.
|
||||||
|
- Improve visual grouped-tab affordances.
|
||||||
|
|
||||||
|
## Main Risks
|
||||||
|
|
||||||
|
### 1. Model complexity
|
||||||
|
|
||||||
|
The current flat `[TerminalSession]` model is simple. Tree-based layout introduces more state, more edge cases, and more focus semantics.
|
||||||
|
|
||||||
|
Mitigation:
|
||||||
|
|
||||||
|
- refactor the data model before touching complex UI
|
||||||
|
- keep top-level tabs and pane leaves distinct
|
||||||
|
|
||||||
|
### 2. SwiftTerm view ownership
|
||||||
|
|
||||||
|
`TerminalView` cannot be mounted in multiple places safely.
|
||||||
|
|
||||||
|
Mitigation:
|
||||||
|
|
||||||
|
- preserve current rule: only one active presenting screen per workspace
|
||||||
|
- keep one runtime `TerminalSession` per pane leaf
|
||||||
|
|
||||||
|
### 3. Persistence mismatch
|
||||||
|
|
||||||
|
Persisting layout is easy compared with persisting process state.
|
||||||
|
|
||||||
|
Mitigation:
|
||||||
|
|
||||||
|
- persist layout and selection only
|
||||||
|
- recreate shell sessions on launch
|
||||||
|
|
||||||
|
### 4. Joined-tab UX ambiguity
|
||||||
|
|
||||||
|
If grouped tabs are also used as pane chips, the interaction model can get confusing.
|
||||||
|
|
||||||
|
Mitigation:
|
||||||
|
|
||||||
|
- preserve top-level tabs as the real navigation unit
|
||||||
|
- use internal chips only as secondary indicators/actions
|
||||||
|
|
||||||
|
## Recommended First Implementation Boundary
|
||||||
|
|
||||||
|
The first deliverable should include:
|
||||||
|
|
||||||
|
- nested binary split trees
|
||||||
|
- active pane focus
|
||||||
|
- split and close pane actions
|
||||||
|
- sibling expansion
|
||||||
|
- grouped tab appearance
|
||||||
|
- join any two tabs
|
||||||
|
- persisted layout across relaunch
|
||||||
|
- same workspace layout when moved to another screen
|
||||||
|
|
||||||
|
The first deliverable should not include:
|
||||||
|
|
||||||
|
- simultaneous same-workspace rendering on multiple screens
|
||||||
|
- drag-and-drop tree editing
|
||||||
|
- restoring running shell process contents
|
||||||
|
|
||||||
|
## Files Likely Impacted
|
||||||
|
|
||||||
|
- `CommandNotch/CommandNotch/Models/WorkspaceController.swift`
|
||||||
|
- `CommandNotch/CommandNotch/Models/WorkspaceRegistry.swift`
|
||||||
|
- `CommandNotch/CommandNotch/Models/WorkspaceStore.swift`
|
||||||
|
- `CommandNotch/CommandNotch/ContentView.swift`
|
||||||
|
- `CommandNotch/CommandNotch/Components/TabBar.swift`
|
||||||
|
- `CommandNotch/CommandNotch/Models/TerminalSession.swift`
|
||||||
|
- workspace-related tests
|
||||||
|
|
||||||
|
Likely new files:
|
||||||
|
|
||||||
|
- pane tree domain types
|
||||||
|
- persisted workspace document types
|
||||||
|
- split-pane rendering view(s)
|
||||||
|
- pane-focused command helpers
|
||||||
|
|
||||||
|
## Decision Summary
|
||||||
|
|
||||||
|
The feature is feasible.
|
||||||
|
|
||||||
|
The correct architecture is:
|
||||||
|
|
||||||
|
- workspace-owned split tree
|
||||||
|
- top-level tabs remain the primary unit
|
||||||
|
- grouped tab visuals are a UI layer over the pane tree
|
||||||
|
- one presenting screen per workspace
|
||||||
|
- persisted layout, not persisted process contents
|
||||||
|
|
||||||
|
That is the implementation direction to use when this work is resumed.
|
||||||