Skip to content
This repository was archived by the owner on Oct 7, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ lorem-text
platformdirs
pyperclip
simple_parsing
textual[syntax]
textual[syntax] >= 4
tiktoken
websockets
18 changes: 12 additions & 6 deletions src/chap/commands/tui.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ async def get_completion(self, query: str) -> None:
await self.container.mount_all(
[markdown_for_step(User(query)), output], before="#pad"
)
tokens: list[str] = []
update: asyncio.Queue[bool] = asyncio.Queue(1)

for markdown in self.container.children:
Expand All @@ -166,15 +165,22 @@ async def get_completion(self, query: str) -> None:
)

async def render_fun() -> None:
old_len = 0
while await update.get():
if tokens:
output.update("".join(tokens).strip())
self.container.scroll_end()
await asyncio.sleep(0.1)
content = message.content
new_len = len(content)
new_content = content[old_len:new_len]
if new_content:
if old_len:
await output.append(new_content)
else:
output.update(content)
self.container.scroll_end()
old_len = new_len
await asyncio.sleep(0.01)

async def get_token_fun() -> None:
async for token in self.api.aask(session, query):
tokens.append(token)
message.content += token
try:
update.put_nowait(True)
Expand Down
23 changes: 22 additions & 1 deletion src/chap/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
conversations_path = platformdirs.user_state_path("chap") / "conversations"
conversations_path.mkdir(parents=True, exist_ok=True)
configuration_path = platformdirs.user_config_path("chap")
preset_path = configuration_path / "preset"


class ABackend(Protocol):
Expand Down Expand Up @@ -363,7 +364,7 @@ def expand_splats(args: list[str]) -> list[str]:
result.append(a)
continue
if a.startswith("@:"):
fn: pathlib.Path = configuration_path / "preset" / a[2:]
fn: pathlib.Path = preset_path / a[2:]
else:
fn = pathlib.Path(a[1:])
fn = maybe_add_txt_extension(fn)
Expand Down Expand Up @@ -403,6 +404,19 @@ def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command:
except ModuleNotFoundError as exc:
raise click.UsageError(f"Invalid subcommand {cmd_name!r}", ctx) from exc

def gather_preset_info(self) -> list[tuple[str, str]]:
result = []
for p in preset_path.glob("*"):
if p.is_file():
with p.open() as f:
first_line = f.readline()
if first_line.startswith("#"):
help_str = first_line[1:].strip()
else:
help_str = "(A comment on the first line would be shown here)"
result.append((f"@:{p.name}", help_str))
return result

def format_splat_options(
self, ctx: click.Context, formatter: click.HelpFormatter
) -> None:
Expand Down Expand Up @@ -436,6 +450,13 @@ def format_splat_options(
the extension may be omitted."""
)
)
formatter.write_paragraph()
if preset_info := self.gather_preset_info():
formatter.write_text("Presets found:")
formatter.write_paragraph()
formatter.indent()
formatter.write_dl(preset_info)
formatter.dedent()

def format_options(
self, ctx: click.Context, formatter: click.HelpFormatter
Expand Down