# Appinapp Widget Schema

This document defines the widget format for Appinapp. Give this to an AI to generate valid widgets.

## Module Exports

A widget file must export one or more of the following:

| Export | Type | Required | Description |
|--------|------|----------|-------------|
| `default` or `render` | React Component | Yes | The component to render |
| `command` | `string` | No | Shell command to execute periodically |
| `refreshFrequency` | `number` (ms) | No | How often to re-run the command |
| `className` | `string` | No | CSS string injected as `<style>` tag |
| `width` | `number` | No | Preferred window width in pixels |
| `height` | `number` | No | Preferred window height in pixels |
| `x` | `number` | No | Preferred window x position |
| `y` | `number` | No | Preferred window y position |

## Component Props

The rendered component receives:

| Prop | Type | Description |
|------|------|-------------|
| `output` | `string` | Result of the `command` execution |
| `error` | `string \| null` | Error message if command failed |
| `run` | `(cmd: string) => Promise<string>` | Execute an arbitrary shell command |

## Available Dependencies

Only these are available in the widget sandbox:

- `react` - React library (already in scope)
- `lucide-react` - Icon library (access via `window.Lucide`)

Import style:
```jsx
// React is globally available, no import needed
const { useState, useEffect } = React;

// Lucide icons
const { Clock, RefreshCw } = window.Lucide;
```

## Custom CSS

Use the `className` export to inject custom styles:

```jsx
const MyWidget = () => {
  return <div className="custom-gradient">Hello</div>;
};

export default MyWidget;
export const className = `
  .custom-gradient {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  }
`;
```

## Minimal Widget

```jsx
const Clock = () => {
  const [time, setTime] = useState(new Date());
  useEffect(() => {
    const id = setInterval(() => setTime(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  return <div>{time.toLocaleTimeString()}</div>;
};

export default Clock;
```

## Widget with Command

```jsx
const Battery = ({ output, error }) => {
  if (error) return <div style={{ color: "red" }}>Error</div>;
  return <div>Battery: {output}</div>;
};

export default Battery;
export const command = "pmset -g batt | grep -Eo '\\d+%' | head -1";
export const refreshFrequency = 60000;
```

## Widget with Styling

```jsx
const Weather = ({ output }) => {
  return <div className="weather">{output}</div>;
};

export default Weather;
export const command = "curl -s wttr.in/?format=%t";
export const refreshFrequency = 300000;
export const className = `
  .weather {
    font-size: 24px;
    color: white;
    background: rgba(0,0,0,0.5);
    padding: 12px;
    border-radius: 8px;
  }
`;
```

## Widget with Window Preferences

```jsx
const Spotify = () => {
  return <div>Now Playing</div>;
};

export default Spotify;
export const width = 300;
export const height = 200;
export const y = 10;
export const x = 10;
```

## Widget with Interactive Commands

```jsx
const SystemMonitor = ({ output, error, run }) => {
  const [data, setData] = useState(output);

  const refresh = async () => {
    const result = await run("top -l 1 | head -5");
    setData(result);
  };

  return (
    <div>
      <pre>{data || "Loading..."}</pre>
      <button onClick={refresh}>Refresh</button>
    </div>
  );
};

export default SystemMonitor;
export const command = "top -l 1 | head -5";
export const refreshFrequency = 5000;
```

## Examples

### Disk Usage Widget

```jsx
const DiskUsage = ({ output }) => {
  const lines = (output || "").split("\n").filter(Boolean);
  return (
    <div style={{ fontFamily: "monospace", color: "white", background: "#171717", padding: "16px", borderRadius: "8px" }}>
      {lines.map((line, i) => <div key={i}>{line}</div>)}
    </div>
  );
};

export default DiskUsage;
export const command = "df -h / | tail -1 | awk '{print $5 \" used of \" $2}'";
export const refreshFrequency = 60000;
```

### CPU Monitor Widget

```jsx
const CpuMonitor = ({ output }) => {
  return (
    <div style={{ color: "#4ade80", fontFamily: "monospace", padding: "8px" }}>
      CPU: {output || "..."}
    </div>
  );
};

export default CpuMonitor;
export const command = "top -l 1 -n 0 | grep CPU | awk '{print $3}'";
export const refreshFrequency = 2000;
```

### Network Status Widget

```jsx
const NetworkStatus = ({ output, error }) => {
  const isOnline = output?.trim() === "Online";
  return (
    <div style={{ padding: "8px", color: isOnline ? "#4ade80" : "#f87171" }}>
      {isOnline ? "● Online" : "● Offline"}
    </div>
  );
};

export default NetworkStatus;
export const command = "ping -c 1 -t 2 8.8.8.8 > /dev/null 2>&1 && echo Online || echo Offline";
export const refreshFrequency = 10000;
```

### Custom Shell Script Widget

```jsx
const MyWidget = ({ output, error, run }) => {
  const [data, setData] = useState(null);

  useEffect(() => {
    if (output) setData(JSON.parse(output));
  }, [output]);

  const handleClick = async () => {
    await run("open https://example.com");
  };

  return (
    <div onClick={handleClick} style={{ cursor: "pointer", padding: "12px", borderRadius: "8px" }}>
      {data ? <pre>{JSON.stringify(data, null, 2)}</pre> : "Loading..."}
    </div>
  );
};

export default MyWidget;
export const command = "some-cli-tool --json";
export const refreshFrequency = 10000;
export const width = 400;
export const height = 300;
```

## Important Notes

1. **No npm packages** - Only `react` and `lucide-react` are available
2. **Classic JSX runtime** - Uses `React.createElement`, not automatic JSX transform
3. **Command output is string** - Parse as needed (JSON, CSV, etc.)
4. **Error handling** - Always check the `error` prop when using `command`
5. **Cleanup** - Return cleanup functions from `useEffect` for intervals/timers
6. **File naming** - Must be `index.jsx` or `index.js` inside `<name>.widget/`
