iword-rs 0.1.11

High-speed keyword search — Rust implementation of iWord
Documentation
import { useState, useEffect, useRef } from 'react';
import init, { IwordDict } from 'iword-rs';

const DICT = `shutdown\t0
crash\t0
kernel_panic\t0
disk_full\t1
oom\t1
deprecated_api\t2
slow_query\t3
user_login\t4
health_check\t5
ping\t5
`;

const KEY_META = {
  0: { name: 'BLOCK',    color: '#dc2626' },
  1: { name: 'ALERT',    color: '#ea580c' },
  2: { name: 'FLAG',     color: '#d97706' },
  3: { name: 'THROTTLE', color: '#7c3aed' },
  4: { name: 'LOG',      color: '#2563eb' },
  5: { name: 'PASS',     color: '#16a34a' },
};

export default function App() {
  const [ready, setReady] = useState(false);
  const [text, setText] = useState('');
  const [matches, setMatches] = useState([]);
  const [filtered, setFiltered] = useState('');
  const dictRef = useRef(null);

  useEffect(() => {
    init().then(() => {
      dictRef.current = new IwordDict(DICT);
      setReady(true);
    });
  }, []);

  useEffect(() => {
    if (!ready || !dictRef.current) return;
    const d = dictRef.current;
    const m = d.scan(text, true, false);
    setMatches(m);
    setFiltered(d.filter(text, true));
  }, [text, ready]);

  const blocked = matches.some(m => m.key === 0);

  return (
    <div style={{ fontFamily: 'sans-serif', maxWidth: 640, margin: '40px auto', padding: '0 16px' }}>
      <h1 style={{ fontSize: 20, marginBottom: 4 }}>iword-rs WASM — Real-time Filter</h1>
      <p style={{ color: '#666', fontSize: 13, marginBottom: 16 }}>
        Powered by Rust + WebAssembly. Scanning happens in-browser at near-native speed.
      </p>

      <textarea
        rows={4}
        style={{ width: '100%', fontSize: 15, padding: 8, boxSizing: 'border-box',
          border: blocked ? '2px solid #dc2626' : '1px solid #ccc', borderRadius: 4 }}
        placeholder="Type something..."
        value={text}
        onChange={e => setText(e.target.value)}
        disabled={!ready}
      />

      {matches.length > 0 && (
        <div style={{ marginTop: 12 }}>
          <strong>Matches:</strong>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 6 }}>
            {matches.map((m, i) => {
              const meta = KEY_META[m.key] ?? { name: `USER(${m.key})`, color: '#888' };
              return (
                <span key={i} style={{
                  background: meta.color, color: '#fff', borderRadius: 4,
                  padding: '2px 8px', fontSize: 13,
                }}>
                  {meta.name}: "{m.extract(text)}"
                </span>
              );
            })}
          </div>
        </div>
      )}

      {text && (
        <div style={{ marginTop: 12 }}>
          <strong>Filtered:</strong>
          <div style={{ marginTop: 4, padding: 8, background: '#f5f5f5',
            borderRadius: 4, fontFamily: 'monospace', fontSize: 14 }}>
            {filtered || '(empty)'}
          </div>
        </div>
      )}

      {blocked && (
        <div style={{ marginTop: 12, padding: 10, background: '#fef2f2',
          border: '1px solid #dc2626', borderRadius: 4, color: '#dc2626', fontWeight: 'bold' }}>
          BLOCK word detected — submission disabled
        </div>
      )}

      <button
        disabled={blocked || !text}
        style={{ marginTop: 16, padding: '8px 20px', fontSize: 14,
          background: blocked ? '#ccc' : '#2563eb', color: '#fff',
          border: 'none', borderRadius: 4, cursor: blocked ? 'not-allowed' : 'pointer' }}
      >
        Submit
      </button>

      {!ready && <p style={{ color: '#888', marginTop: 8 }}>Loading WASM...</p>}
    </div>
  );
}