Giter Site home page Giter Site logo

joshkarpel / counterweight Goto Github PK

View Code? Open in Web Editor NEW
13.0 13.0 1.0 1.33 MB

An experimental TUI framework for Python, inspired by React and Tailwind

Home Page: https://www.counterweight.dev

License: MIT License

Python 99.87% Just 0.13%
python terminal tui

counterweight's Introduction

Hello Worlds!

I'm Josh Karpel!

Interests:

  • 🐍 Python
  • πŸ¦€ Rust
  • 🦺 Typescript
  • πŸ§ͺ Testing
  • ⌨️ CLI Tools
  • πŸ•ΈοΈ Distributed Systems

I'm currently a Software Engineer in Machine Learning at Workday. I design and develop systems for serving machine learning models.

Before that, I was a postdoc at the Morgridge Institute for Research / Center for High-Throughput Computing. I facilitated high-throughput computing projects that involved machine learning, image processing, and Python. I also helped maintain the HTCondor Python Bindings and worked on projects like HTMap. Before that, I got a PhD in Physics at the University of Wisconsin-Madison in the Yavuz Group, working on simulating interactions between quantum systems and high-power lasers.

counterweight's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

edrogers

counterweight's Issues

Border titles as styles

While playing with absolute positioning to generate titles, I realized that while it works ok for top-left titles, anything else is a pain because you would need to know the border dimensions when you set the offsets. To avoid that, we probably need to let you set border titles through styles.

We'll need a new StyleFragment, probably an attribute of the existing Border, which holds:

  • Alignment: left, center, right
  • Side: top, bottom
  • Text: What the title should read, probably as a List[Chunk] or similar to allow styling (check for newlines?)

I don't think any layout changes should be necessary since it writes over part of the existing border.

Border painting will be handled by

def paint_border(border: Border, rect: Rect) -> Paint:

`Suspend` control to temporarily suspend the application while a callback function runs

This would allow an event handler to run some other program that needs control of the terminal. For example, you could open a text editor on a file.

Probably the control dataclass will have a callback in it, similar to the Screenshot control. Need to think more about what should actually be passed to it.

There's a catch here, which is that if the callback function is async, effects could potentially keep running in the background, but we probably want to suspend them too by not allowing the handler to be async. So at least for the first pass we probably don't want to let the handler be async, similar to Screenshot.

Add functional utilities for styles that need numbers

We recently added utilities for positioning that are functions

def relative(x: int = 0, y: int = 0) -> Style:
    return Style(layout=Flex(position=Relative(x=x, y=y)))


def absolute(x: int = 0, y: int = 0) -> Style:
    return Style(layout=Flex(position=Absolute(x=x, y=y)))


def fixed(x: int = 0, y: int = 0) -> Style:
    return Style(layout=Flex(position=Fixed(x=x, y=y)))

We also have older utilities like

gap_children_0 = Style(layout=Flex(gap_children=0))
gap_children_1 = Style(layout=Flex(gap_children=1))
gap_children_2 = Style(layout=Flex(gap_children=2))
gap_children_3 = Style(layout=Flex(gap_children=3))
gap_children_4 = Style(layout=Flex(gap_children=4))
gap_children_5 = Style(layout=Flex(gap_children=5))
gap_children_6 = Style(layout=Flex(gap_children=6))
gap_children_7 = Style(layout=Flex(gap_children=7))
gap_children_8 = Style(layout=Flex(gap_children=8))

These are convenient for small, common numbers, but we should also have

def gap_children(n: int) -> Style:
    return Style(layout=Flex(gap_children=n))

There are a variety of utilities that end with a trailing number, and each should probably have a functional variant as well.

Also, all functional utilities should be cached, so that we re-use Style objects as much as possible (helps style merge caching).

Implement `z` layers

In layouts that use complex positioning, it might be necessary to decouple the order that elements are painted in (backwards-depth-first in the element tree) from the order they are laid over each other. Right now, they are coupled by

painted = paint_element(layout.element, layout.dims)
for child in layout.children:
painted |= paint_layout(child) # no Z-level support! need something like a chainmap
return painted

To decouple them, we can introduce a z-level style (probably at top level, since its not really part of the layout). When painting, we could collect all the element paints, sort by z (or perhaps use a heap queue as we go?), then merge them in order.

Borders should probably only heal if they are on the same z layer, otherwise you might get unexpected joins between elements that are supposed to be "on top" of other elements.

Cache individual element paints between renders

If a component hasn't changed (see #72, which this probably depends on), then we know that the paints of the elements bounded by that component haven't changed either.

  • There is one other thing (other than state changes) that can break this cache: terminal resize events!
  • It seems like the cache needs to live in the shadow tree - I think it is one node per element now, not per component, so that should be straightforward.
  • Not quite sure how to get this information to the painter. Right now it takes a concrete element tree. Should it be inside that data structure, or somehow adjacent? I guess we could use id() as a cache key, since if we have #72 we'd be caching the actual produced element instances.
  • Now that I'm thinking about it, its a little odd to cache at this layer but not at the previous layer (layout). Caching layout is a little tricky because elements can potentially take imperative actions on their parent's layout... does that break this idea? Need to be careful here.

Consider combining `on_key` and `on_mouse` event handlers

This would make it easier to treat mouse and key events the same way, like in a less-like application you might want to handle a mouse scroll down and an arrow key down using the same code without needing to register two handlers.

Add a small example application to the README as a quick start

  • Show an actual application (including code) with some basic features like styling and handling key events
  • Minimally, screenshots of that application running, but it would be nice to produce an animation (ideally similarly to how docs screenshots are generated; perhaps by stitching together the SVGs into a GIF or something?)

Make sure to synchronize changes between the README and the docs index page.

Granular state changes

Right now, when we detect a state change, we re-run the entire component tree. This is overkill - we can know which component the state changed in, and re-run only that component and its descendants.

  • This should result in some speedup because we'll be running less code overall, especially potentially-expensive things in components that we know didn't change at all.
  • We'll need some way to mark the dirty shadow node, and then to propagate that dirtiness to its children.

Optimize border healing by making it layout-aware

We could potentially optimize border healing by checking fewer cells in the first place, if we knew where all the borders that were drawn ended up. Right now we check every cell, even if (theoretically) we knew we didn't draw a border there.

This would mean that border healing wouldn't work for "hand-drawn" borders, but maybe that's an acceptable tradeoff to increase performance for the average user.

Paint each cell exactly once?

Right now in paint_layout(), we paint every element, then use dictionary merging to layer them on top of each other. This is wasteful, since we can use z-layers and element ordering to determine which element ultimately paints each cell - i.e., a map of cell -> element that will paint that cell. We can then ask that element to paint just that cell.

This might be an optimization for very complex layouts, because we should end up painting fewer cells total (just each cell exactly once instead of some cells many times and then being overridden).

This is pretty bad now that we can paint content too - it means that the amount of painting tends to scale with the area of the divs, not just their surface area.

Options for customizing SVG screenshot generation

  • This would be a nice time to introduce the "pilot" concept from Textual for testing and automation
  • It would be nice to be able to pass a callback through the event handler return to take the raw XML and customize it, and choose how to output it, so that the framework isn't hardcoding what happens to the SVG, it just does the part it knows how to do (convert a Paint to a an SVG element tree). That's tricky because Control is currently an Enum; it could become a Union of dataclasses or Pydantic models instead so that each can have some attributes.

Add a showcase to the README

Goal is to show off some more complex examples of what Counterweight can do. Maybe pull from the baked-in examples like Wordle, and potentially external examples like Kludge

Make sure to synchronize changes between the README and the docs index page.

Implement style overrides on mouse hover

  • Parse mouse events so that we know where the mouse is.
  • Figure out which element the mouse is over based on layout.
  • Pass extra styles through the element constructor that enable on hover.
  • Override the base styles with those extra styles on the hovered element.
  • Are all elements that the mouse is "inside" considered hovered, or only the "top-most" element that actually shows in the resulting render?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.