Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea for docs: Add support for playground persistence #832

Open
zaydek opened this issue Jan 4, 2025 · 4 comments
Open

Idea for docs: Add support for playground persistence #832

zaydek opened this issue Jan 4, 2025 · 4 comments
Labels
enhancement New feature or request

Comments

@zaydek
Copy link

zaydek commented Jan 4, 2025

Describe the feature request

Lately, I've been using Claude to prototype a UI, then convert it to StyleX afterward. This works surprisingly well, but I'd love to not rely exclusively on Claude. ChatGPT doesn't support something like artifacts for rendering UI yet.

That said, the new playground opens up the possibility to quickly generate code using any LLM based on the source code provided in the playground and quickly paste the results. This turns out to work!

What I'm hoping for now is simply the possibility for persistence in the playground tool.

Some ideas:

  • SolidJS's playground used to use a string algorithm (I think it was called Lezer string compression?) to inline the code in the URL (ugly but works and no server required)
  • Tailwind Play does the traditional thing which is persist to a server and gives you a simple URL to easily copy, bookmark, etc.

This was basically one shot by Claude once it understood it should use StyleX as opposed to Tailwind. I'd really like to be able to persist playgrounds such as this so I can scaffold dozens of ideas and bookmark them or share them for example on social media.

View Code Example
import { useState } from 'react';
import * as stylex from '@stylexjs/stylex';

export default function TodoApp() {
  const [todos, setTodos] = useState([]);
  const [newTodo, setNewTodo] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!newTodo.trim()) return;
    
    setTodos([...todos, {
      id: Date.now(),
      text: newTodo.trim(),
      completed: false
    }]);
    setNewTodo('');
  };

  const toggleTodo = (id) => {
    setTodos(todos.map(todo => 
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ));
  };

  const deleteTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };

  return (
    <div {...stylex.props(layout.container)}>
      <div {...stylex.props(card.container)}>

        <form {...stylex.props(form.container)} onSubmit={handleSubmit}>
          <div {...stylex.props(form.inputGroup)}>
            <input
              type="text"
              value={newTodo}
              onChange={(e) => setNewTodo(e.target.value)}
              placeholder="Add a new todo..."
              {...stylex.props(form.input)}
            />
            <button type="submit" {...stylex.props(button.primary)}>
              Add Todo
            </button>
          </div>
        </form>

        <div {...stylex.props(todos.container)}>
          {todos.map(todo => (
            <div key={todo.id} {...stylex.props(todos.item)}>
              <label {...stylex.props(todos.label)}>
                <input
                  type="checkbox"
                  checked={todo.completed}
                  onChange={() => toggleTodo(todo.id)}
                  {...stylex.props(todos.checkbox)}
                />
                <span {...stylex.props(todos.text, todo.completed && todos.completed)}>
                  {todo.text}
                </span>
              </label>
              <button
                onClick={() => deleteTodo(todo.id)}
                {...stylex.props(button.delete)}
              >
                Delete
              </button>
            </div>
          ))}
        </div>

        {todos.length === 0 && (
          <p {...stylex.props(text.empty)}>
            No todos yet. Add some tasks above!
          </p>
        )}
      </div>
    </div>
  );
}

/* General color scheme */
const LIGHT_MODE = '@media (prefers-color-scheme: light)';

/* Typography Styles */
const text = stylex.create({
  title: {
    fontSize: '1.8rem',
    marginBottom: '0.5em',
    textAlign: 'center',
  },
  description: {
    fontSize: '0.9rem',
    marginBottom: '1.5em',
    color: '#666',
    textAlign: 'center',
  },
  empty: {
    textAlign: 'center',
    color: '#666',
    marginTop: '2em',
    fontSize: '0.9rem',
  },
});

/* Layout Styles */
const layout = stylex.create({
  container: {
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    padding: '2em',
    minHeight: '100vh',
  },
});

/* Card Styles */
const card = stylex.create({
  container: {
    backgroundColor: {
      default: '#1e1e1e',
      [LIGHT_MODE]: '#fff',
    },
    borderRadius: 8,
    padding: '2em',
    boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
    maxWidth: '800px',
    width: '100%',
  },
});

/* Form Styles */
const form = stylex.create({
  container: {
    marginBottom: '2em',
  },
  inputGroup: {
    display: 'flex',
    gap: '1em',
  },
  input: {
    flex: 1,
    padding: '0.5em',
    borderRadius: 4,
    border: '1px solid #ccc',
    fontSize: '1em',
    ':focus': {
      outline: 'none',
      borderColor: '#646cff',
    },
  },
});

/* Button Styles */
const button = stylex.create({
  primary: {
    padding: '0.6em 1.2em',
    fontSize: '1em',
    fontWeight: 500,
    borderRadius: 4,
    border: 'none',
    backgroundColor: {
      default: '#646cff',
      [LIGHT_MODE]: '#747bff',
    },
    color: '#fff',
    cursor: 'pointer',
    transition: 'background-color 0.2s',
    ':hover': {
      backgroundColor: '#535bf2',
    },
  },
  delete: {
    padding: '0.4em 0.8em',
    fontSize: '0.9em',
    borderRadius: 4,
    border: 'none',
    backgroundColor: '#ff4444',
    color: '#fff',
    cursor: 'pointer',
    transition: 'background-color 0.2s',
    ':hover': {
      backgroundColor: '#cc0000',
    },
  },
});

/* Todos Styles */
const todos = stylex.create({
  container: {
    display: 'flex',
    flexDirection: 'column',
    gap: '0.8em',
  },
  item: {
    display: 'flex',
    justifyContent: 'space-between',
    alignItems: 'center',
    padding: '0.8em',
    backgroundColor: {
      default: '#2a2a2a',
      [LIGHT_MODE]: '#f5f5f5',
    },
    borderRadius: 4,
  },
  label: {
    display: 'flex',
    alignItems: 'center',
    gap: '0.8em',
    flex: 1,
  },
  checkbox: {
    width: '1.2em',
    height: '1.2em',
    cursor: 'pointer',
  },
  text: {
    fontSize: '1em',
    color: {
      default: '#fff',
      [LIGHT_MODE]: '#333',
    },
  },
  completed: {
    textDecoration: 'line-through',
    color: '#666',
  },
});
Screenshot 2025-01-03 at 8 42 45 PM
@zaydek zaydek added the enhancement New feature or request label Jan 4, 2025
@zaydek zaydek changed the title Support for playground persistence Idea: Add support for playground persistence Jan 4, 2025
@zaydek zaydek changed the title Idea: Add support for playground persistence Idea for docs: Add support for playground persistence Jan 4, 2025
@nmn
Copy link
Contributor

nmn commented Jan 4, 2025

This is 100% already planned!

First we want to add support for multiple file tabs. This work is already underway.

Once that is done, I would like to find a way to make the playgrounds shareable. Will have to experiment and decide if the URL state works well for this or not. URL state would be the preferred solution here.

@orta
Copy link

orta commented Jan 9, 2025

In the TypeScript playground we used lzstring with URL state: loading and saving and it worked out pretty well

@nmn
Copy link
Contributor

nmn commented Jan 11, 2025

@orta That's what I want to do too! Btw, any help adding typescript type-checking support to the playground? We're using web containers (the thing that powers StackBlitz) and are willing to change the code editor being used.

@orta
Copy link

orta commented Jan 11, 2025

I've never used CodeMirror, but I'd look at https://github.com/val-town/codemirror-ts and then add the contents of the site's node_modules for react + stylex (and the @types for react) etc and put those inside the fsMap in their docs and you are probably good then

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants