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/
|
||||
build/
|
||||
|
||||
**/Release*
|
||||
CommandNotch 20*
|
||||
|
||||
# Mac... files
|
||||
**/.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"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2620"
|
||||
LastUpgradeVersion = "1600"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
runPostActionsOnFailure = "NO">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
@@ -15,7 +15,7 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
||||
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||
BuildableName = "CommandNotch.app"
|
||||
BlueprintName = "CommandNotch"
|
||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||
@@ -28,7 +28,42 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
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>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -44,12 +79,14 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
||||
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||
BuildableName = "CommandNotch.app"
|
||||
BlueprintName = "CommandNotch"
|
||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
</CommandLineArguments>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
@@ -61,12 +98,14 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
||||
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||
BuildableName = "CommandNotch.app"
|
||||
BlueprintName = "CommandNotch"
|
||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
</CommandLineArguments>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2620"
|
||||
LastUpgradeVersion = "1600"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
runPostActionsOnFailure = "NO">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
@@ -15,7 +15,7 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
||||
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||
BuildableName = "CommandNotch.app"
|
||||
BlueprintName = "CommandNotch"
|
||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||
@@ -28,13 +28,48 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
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>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "1"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
@@ -44,12 +79,14 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
||||
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||
BuildableName = "CommandNotch.app"
|
||||
BlueprintName = "CommandNotch"
|
||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
</CommandLineArguments>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
@@ -61,12 +98,14 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "1485207FA11756EC2DF4F08B"
|
||||
BlueprintIdentifier = "D5585E5732CD067DF6EF0C69"
|
||||
BuildableName = "CommandNotch.app"
|
||||
BlueprintName = "CommandNotch"
|
||||
ReferencedContainer = "container:CommandNotch.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
</CommandLineArguments>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
@@ -76,9 +76,7 @@ struct HotkeyRecorderField: NSViewRepresentable {
|
||||
}
|
||||
|
||||
func updateNSView(_ nsView: HotkeyNSView, context: Context) {
|
||||
nsView.currentLabel = binding.displayString
|
||||
nsView.showRecording = isRecording
|
||||
nsView.needsDisplay = true
|
||||
nsView.update(currentLabel: binding.displayString, isRecording: isRecording)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,9 +97,7 @@ struct OptionalHotkeyRecorderField: NSViewRepresentable {
|
||||
}
|
||||
|
||||
func updateNSView(_ nsView: HotkeyNSView, context: Context) {
|
||||
nsView.currentLabel = binding?.displayString ?? "Not set"
|
||||
nsView.showRecording = isRecording
|
||||
nsView.needsDisplay = true
|
||||
nsView.update(currentLabel: binding?.displayString ?? "Not set", isRecording: isRecording)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +179,12 @@ class HotkeyNSView: NSView {
|
||||
updateLabelAppearance()
|
||||
}
|
||||
|
||||
func update(currentLabel: String, isRecording: Bool) {
|
||||
self.currentLabel = currentLabel
|
||||
showRecording = isRecording
|
||||
updateLabelAppearance()
|
||||
}
|
||||
|
||||
private func updateLabelAppearance() {
|
||||
label.stringValue = showRecording ? "Press keys..." : currentLabel
|
||||
label.textColor = showRecording ? .controlAccentColor : .labelColor
|
||||
@@ -9,17 +9,6 @@ struct TabBar: View {
|
||||
|
||||
var body: some View {
|
||||
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 {
|
||||
workspace.newTab()
|
||||
} label: {
|
||||
@@ -31,6 +20,15 @@ struct TabBar: View {
|
||||
.accessibilityIdentifier("notch.new-tab")
|
||||
.buttonStyle(.plain)
|
||||
.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)
|
||||
.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.
|
||||
///
|
||||
/// 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).
|
||||
@MainActor
|
||||
class HotkeyManager {
|
||||
@@ -19,21 +19,29 @@ class HotkeyManager {
|
||||
var onCloseTab: (() -> Void)?
|
||||
var onNextTab: (() -> Void)?
|
||||
var onPreviousTab: (() -> Void)?
|
||||
var onNextWorkspace: (() -> Void)?
|
||||
var onPreviousWorkspace: (() -> Void)?
|
||||
var onDetachTab: (() -> Void)?
|
||||
var onApplySizePreset: ((TerminalSizePreset) -> 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
|
||||
|
||||
private var hotKeyRef: EventHotKeyRef?
|
||||
private var eventHandlerRef: EventHandlerRef?
|
||||
private var localMonitor: Any?
|
||||
private let settingsProvider: TerminalSessionConfigurationProviding
|
||||
private let workspaceRegistry: WorkspaceRegistry
|
||||
private var settingsCancellable: AnyCancellable?
|
||||
|
||||
init(settingsProvider: TerminalSessionConfigurationProviding? = nil) {
|
||||
init(
|
||||
settingsProvider: TerminalSessionConfigurationProviding? = nil,
|
||||
workspaceRegistry: WorkspaceRegistry? = nil
|
||||
) {
|
||||
self.settingsProvider = settingsProvider ?? AppSettingsController.shared
|
||||
self.workspaceRegistry = workspaceRegistry ?? WorkspaceRegistry.shared
|
||||
}
|
||||
|
||||
// MARK: - Resolved bindings from typed runtime settings
|
||||
@@ -53,6 +61,12 @@ class HotkeyManager {
|
||||
private var prevTabBinding: HotkeyBinding {
|
||||
settingsProvider.hotkeySettings.previousTab
|
||||
}
|
||||
private var nextWorkspaceBinding: HotkeyBinding {
|
||||
settingsProvider.hotkeySettings.nextWorkspace
|
||||
}
|
||||
private var previousWorkspaceBinding: HotkeyBinding {
|
||||
settingsProvider.hotkeySettings.previousWorkspace
|
||||
}
|
||||
private var detachBinding: HotkeyBinding {
|
||||
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() {
|
||||
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 {
|
||||
// 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 }
|
||||
|
||||
if newTabBinding.matches(event) {
|
||||
@@ -210,10 +224,25 @@ class HotkeyManager {
|
||||
onPreviousTab?()
|
||||
return true
|
||||
}
|
||||
if nextWorkspaceBinding.matches(event) {
|
||||
onNextWorkspace?()
|
||||
return true
|
||||
}
|
||||
if previousWorkspaceBinding.matches(event) {
|
||||
onPreviousWorkspace?()
|
||||
return true
|
||||
}
|
||||
if detachBinding.matches(event) {
|
||||
onDetachTab?()
|
||||
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 {
|
||||
guard let binding = preset.hotkey else { continue }
|
||||
if binding.matches(event) {
|
||||
@@ -9,6 +9,7 @@ final class ScreenManager: ObservableObject {
|
||||
static let shared = ScreenManager()
|
||||
|
||||
private let screenRegistry = ScreenRegistry.shared
|
||||
private let workspaceRegistry = WorkspaceRegistry.shared
|
||||
private let windowCoordinator = WindowCoordinator()
|
||||
private lazy var orchestrator = NotchOrchestrator(screenRegistry: screenRegistry, host: self)
|
||||
|
||||
@@ -55,6 +56,12 @@ final class ScreenManager: ObservableObject {
|
||||
hotkeyManager.onPreviousTab = { [weak self] in
|
||||
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
|
||||
MainActor.assumeIsolated { self?.detachActiveTab() }
|
||||
}
|
||||
@@ -64,6 +71,9 @@ final class ScreenManager: ObservableObject {
|
||||
hotkeyManager.onSwitchToTab = { [weak self] index in
|
||||
MainActor.assumeIsolated { self?.activeWorkspace().switchToTab(at: index) }
|
||||
}
|
||||
hotkeyManager.onSwitchToWorkspace = { [weak self] workspaceID in
|
||||
MainActor.assumeIsolated { self?.switchActiveScreen(to: workspaceID) }
|
||||
}
|
||||
|
||||
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) {
|
||||
guard let context = screenRegistry.allScreens().first(where: { $0.notchState == .open }) else {
|
||||
AppSettingsController.shared.update {
|
||||
@@ -56,6 +56,8 @@ struct AppSettings: Equatable, Codable {
|
||||
closeTab: .cmdW,
|
||||
nextTab: .cmdShiftRB,
|
||||
previousTab: .cmdShiftLB,
|
||||
nextWorkspace: .cmdShiftDown,
|
||||
previousWorkspace: .cmdShiftUp,
|
||||
detachTab: .cmdD
|
||||
)
|
||||
)
|
||||
@@ -121,6 +123,8 @@ extension AppSettings {
|
||||
var closeTab: HotkeyBinding
|
||||
var nextTab: HotkeyBinding
|
||||
var previousTab: HotkeyBinding
|
||||
var nextWorkspace: HotkeyBinding
|
||||
var previousWorkspace: HotkeyBinding
|
||||
var detachTab: HotkeyBinding
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,8 @@ struct UserDefaultsAppSettingsStore: AppSettingsStoreType {
|
||||
closeTab: hotkey(NotchSettings.Keys.hotkeyCloseTab, default: .cmdW),
|
||||
nextTab: hotkey(NotchSettings.Keys.hotkeyNextTab, default: .cmdShiftRB),
|
||||
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)
|
||||
)
|
||||
)
|
||||
@@ -106,6 +108,8 @@ struct UserDefaultsAppSettingsStore: AppSettingsStoreType {
|
||||
defaults.set(settings.hotkeys.closeTab.toJSON(), forKey: NotchSettings.Keys.hotkeyCloseTab)
|
||||
defaults.set(settings.hotkeys.nextTab.toJSON(), forKey: NotchSettings.Keys.hotkeyNextTab)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -88,6 +88,8 @@ struct HotkeyBinding: Codable, Equatable, Hashable {
|
||||
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 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 func cmdShiftDigit(_ digit: Int) -> HotkeyBinding? {
|
||||
@@ -57,6 +57,8 @@ enum NotchSettings {
|
||||
static let hotkeyCloseTab = "hotkey_closeTab"
|
||||
static let hotkeyNextTab = "hotkey_nextTab"
|
||||
static let hotkeyPreviousTab = "hotkey_previousTab"
|
||||
static let hotkeyNextWorkspace = "hotkey_nextWorkspace"
|
||||
static let hotkeyPreviousWorkspace = "hotkey_previousWorkspace"
|
||||
static let hotkeyDetachTab = "hotkey_detachTab"
|
||||
}
|
||||
|
||||
@@ -104,6 +106,8 @@ enum NotchSettings {
|
||||
static let hotkeyCloseTab: String = HotkeyBinding.cmdW.toJSON()
|
||||
static let hotkeyNextTab: String = HotkeyBinding.cmdShiftRB.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()
|
||||
}
|
||||
|
||||
@@ -151,6 +155,8 @@ enum NotchSettings {
|
||||
Keys.hotkeyCloseTab: Defaults.hotkeyCloseTab,
|
||||
Keys.hotkeyNextTab: Defaults.hotkeyNextTab,
|
||||
Keys.hotkeyPreviousTab: Defaults.hotkeyPreviousTab,
|
||||
Keys.hotkeyNextWorkspace: Defaults.hotkeyNextWorkspace,
|
||||
Keys.hotkeyPreviousWorkspace: Defaults.hotkeyPreviousWorkspace,
|
||||
Keys.hotkeyDetachTab: Defaults.hotkeyDetachTab,
|
||||
])
|
||||
}
|
||||
@@ -9,16 +9,26 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
|
||||
let id = UUID()
|
||||
let terminalView: TerminalView
|
||||
private var process: LocalProcess?
|
||||
private var keyEventMonitor: Any?
|
||||
private let backgroundColor = NSColor.black
|
||||
private let configuredShellPath: String
|
||||
private let launchDirectory: String
|
||||
|
||||
@Published var title: String = "shell"
|
||||
@Published var isRunning: Bool = true
|
||||
@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))
|
||||
configuredShellPath = shellPath
|
||||
launchDirectory = Self.resolveInitialDirectory(initialDirectory)
|
||||
currentDirectory = launchDirectory
|
||||
super.init()
|
||||
|
||||
terminalView.terminalDelegate = self
|
||||
@@ -26,8 +36,19 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
|
||||
let font = NSFont.monospacedSystemFont(ofSize: fontSize, weight: .regular)
|
||||
terminalView.font = font
|
||||
applyTheme(theme)
|
||||
installCommandArrowMonitor()
|
||||
|
||||
startShell()
|
||||
if startImmediately {
|
||||
startShell()
|
||||
} else {
|
||||
isRunning = false
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let keyEventMonitor {
|
||||
NSEvent.removeMonitor(keyEventMonitor)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Shell management
|
||||
@@ -44,12 +65,29 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
|
||||
args: ["-l"],
|
||||
environment: nil,
|
||||
execName: loginExecName,
|
||||
currentDirectory: NSHomeDirectory()
|
||||
currentDirectory: launchDirectory
|
||||
)
|
||||
process = proc
|
||||
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 {
|
||||
let custom = configuredShellPath.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
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"
|
||||
}
|
||||
|
||||
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) {
|
||||
terminalView.font = NSFont.monospacedSystemFont(ofSize: size, weight: .regular)
|
||||
}
|
||||
@@ -117,7 +175,8 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
|
||||
}
|
||||
|
||||
func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {
|
||||
currentDirectory = directory
|
||||
guard let normalizedDirectory = Self.normalizedDirectory(directory) else { return }
|
||||
currentDirectory = normalizedDirectory
|
||||
}
|
||||
|
||||
func scrolled(source: TerminalView, position: Double) {}
|
||||
@@ -3,12 +3,27 @@ import Combine
|
||||
|
||||
@MainActor
|
||||
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 {
|
||||
func makeSession(fontSize: CGFloat, theme: TerminalTheme, shellPath: String) -> TerminalSession {
|
||||
TerminalSession(fontSize: fontSize, theme: theme, shellPath: shellPath)
|
||||
func makeSession(
|
||||
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
|
||||
|
||||
@Published private(set) var name: String
|
||||
@Published private(set) var hotkey: HotkeyBinding?
|
||||
@Published private(set) var tabs: [TerminalSession] = []
|
||||
@Published private(set) var activeTabIndex: Int = 0
|
||||
|
||||
@@ -34,6 +50,7 @@ final class WorkspaceController: ObservableObject {
|
||||
self.id = summary.id
|
||||
self.name = summary.name
|
||||
self.createdAt = summary.createdAt
|
||||
self.hotkey = summary.hotkey
|
||||
self.sessionFactory = sessionFactory
|
||||
self.settingsProvider = settingsProvider
|
||||
|
||||
@@ -51,7 +68,7 @@ final class WorkspaceController: ObservableObject {
|
||||
}
|
||||
|
||||
var summary: WorkspaceSummary {
|
||||
WorkspaceSummary(id: id, name: name, createdAt: createdAt)
|
||||
WorkspaceSummary(id: id, name: name, createdAt: createdAt, hotkey: hotkey)
|
||||
}
|
||||
|
||||
var state: WorkspaceState {
|
||||
@@ -78,12 +95,18 @@ final class WorkspaceController: ObservableObject {
|
||||
name = trimmed
|
||||
}
|
||||
|
||||
func updateHotkey(_ updatedHotkey: HotkeyBinding?) {
|
||||
guard hotkey != updatedHotkey else { return }
|
||||
hotkey = updatedHotkey
|
||||
}
|
||||
|
||||
func newTab() {
|
||||
let config = settingsProvider.terminalSessionConfiguration
|
||||
let session = sessionFactory.makeSession(
|
||||
fontSize: config.fontSize,
|
||||
theme: config.theme,
|
||||
shellPath: config.shellPath
|
||||
shellPath: config.shellPath,
|
||||
initialDirectory: activeTab?.currentDirectory
|
||||
)
|
||||
|
||||
titleObservers[session.id] = session.$title
|
||||
@@ -104,6 +104,37 @@ final class WorkspaceRegistry: ObservableObject {
|
||||
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
|
||||
func deleteWorkspace(id: WorkspaceID) -> Bool {
|
||||
guard canDeleteWorkspace(id: id) else { return false }
|
||||
@@ -6,11 +6,13 @@ struct WorkspaceSummary: Identifiable, Equatable, Codable {
|
||||
var id: WorkspaceID
|
||||
var name: String
|
||||
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.name = name
|
||||
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))
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
@@ -2,21 +2,7 @@ import SwiftUI
|
||||
|
||||
struct TerminalSettingsView: View {
|
||||
@ObservedObject private var settingsController = AppSettingsController.shared
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@State private var sizePresets: [TerminalSizePreset] = []
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
@@ -55,7 +41,7 @@ struct TerminalSettingsView: View {
|
||||
}
|
||||
|
||||
Section("Size Presets") {
|
||||
ForEach(sizePresetsBinding) { $preset in
|
||||
ForEach($sizePresets) { $preset in
|
||||
TerminalSizePresetEditor(
|
||||
preset: $preset,
|
||||
currentOpenWidth: settingsController.settings.display.openWidth,
|
||||
@@ -67,20 +53,18 @@ struct TerminalSettingsView: View {
|
||||
|
||||
HStack {
|
||||
Button("Add Preset") {
|
||||
var presets = sizePresetsBinding.wrappedValue
|
||||
presets.append(
|
||||
sizePresets.append(
|
||||
TerminalSizePreset(
|
||||
name: "Preset \(presets.count + 1)",
|
||||
name: "Preset \(sizePresets.count + 1)",
|
||||
width: settingsController.settings.display.openWidth,
|
||||
height: settingsController.settings.display.openHeight,
|
||||
hotkey: TerminalSizePresetStore.suggestedHotkey(for: presets)
|
||||
hotkey: TerminalSizePresetStore.suggestedHotkey(for: sizePresets)
|
||||
)
|
||||
)
|
||||
sizePresetsBinding.wrappedValue = presets
|
||||
}
|
||||
|
||||
Button("Reset Presets") {
|
||||
sizePresetsBinding.wrappedValue = TerminalSizePresetStore.loadDefaults()
|
||||
sizePresets = TerminalSizePresetStore.loadDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,10 +74,24 @@ struct TerminalSettingsView: View {
|
||||
}
|
||||
}
|
||||
.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) {
|
||||
sizePresetsBinding.wrappedValue.removeAll { $0.id == id }
|
||||
sizePresets.removeAll { $0.id == id }
|
||||
}
|
||||
|
||||
private func applyPreset(_ preset: TerminalSizePreset) {
|
||||
@@ -103,6 +101,15 @@ struct TerminalSettingsView: View {
|
||||
}
|
||||
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 {
|
||||
@@ -75,6 +75,11 @@ struct WorkspacesSettingsView: View {
|
||||
renameSelectedWorkspace()
|
||||
}
|
||||
|
||||
OptionalHotkeyRecorderView(
|
||||
label: "Jump Hotkey",
|
||||
binding: workspaceHotkeyBinding(for: summary.id)
|
||||
)
|
||||
|
||||
HStack {
|
||||
Button("Save Name") {
|
||||
renameSelectedWorkspace()
|
||||
@@ -86,6 +91,10 @@ struct WorkspacesSettingsView: View {
|
||||
}
|
||||
.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") {
|
||||
@@ -256,6 +265,17 @@ struct WorkspacesSettingsView: View {
|
||||
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() {
|
||||
guard let effectiveSelectedWorkspaceID,
|
||||
let fallbackWorkspaceID = screenRegistry.deleteWorkspace(
|
||||
@@ -313,7 +313,12 @@ private final class ScreenRegistryTestSettingsProvider: TerminalSessionConfigura
|
||||
|
||||
private struct ScreenRegistryUnusedTerminalSessionFactory: TerminalSessionFactoryType {
|
||||
@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.")
|
||||
}
|
||||
}
|
||||
@@ -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"])
|
||||
}
|
||||
|
||||
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(
|
||||
initialWorkspaces: [WorkspaceSummary]? = [],
|
||||
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 {
|
||||
var savedSummaries: [WorkspaceSummary] = []
|
||||
|
||||
@@ -114,9 +196,35 @@ private final class TestSettingsProvider: TerminalSessionConfigurationProviding
|
||||
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 {
|
||||
@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.")
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,11 @@ final class WorkspaceStoreTests: XCTestCase {
|
||||
|
||||
let store = UserDefaultsWorkspaceStore(defaults: defaults)
|
||||
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")
|
||||
]
|
||||
|
||||
@@ -14,6 +14,45 @@ packages:
|
||||
SwiftTerm:
|
||||
url: https://github.com/migueldeicaza/SwiftTerm.git
|
||||
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:
|
||||
CommandNotch:
|
||||
type: application
|
||||
@@ -29,9 +68,9 @@ targets:
|
||||
properties:
|
||||
CFBundleName: CommandNotch
|
||||
CFBundleDisplayName: CommandNotch
|
||||
CFBundleIdentifier: com.commandnotch.app
|
||||
CFBundleIdentifier: "$(PRODUCT_BUNDLE_IDENTIFIER)"
|
||||
CFBundleVersion: "1"
|
||||
CFBundleShortVersionString: "0.2.0"
|
||||
CFBundleShortVersionString: "0.0.3"
|
||||
CFBundlePackageType: APPL
|
||||
CFBundleExecutable: CommandNotch
|
||||
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
|
||||
|
||||
```text
|
||||
Downterm/CommandNotch/
|
||||
CommandNotch/CommandNotch/
|
||||
App/
|
||||
CommandNotchApp.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.
|
||||