text-transformer / docs /quick-start-guide.md
OnyxlMunkey's picture
Text Transformer: Docker app for HF Space
0f5ca68
|
Raw History Blame Contribute Delete
20.9 kB

Quick Start Implementation Guide

Getting Started with Text Transformer Enhancements

This guide provides practical, actionable steps for each role to begin work immediately.


🎨 Frontend UI/UX Designer - Day 1 Checklist

Setup (15 minutes)

# Clone and run the app
git clone <repo-url>
cd converter
npm install
npm run dev
# Open http://localhost:5173

Immediate Observations to Document

  • Current color contrast ratios (use browser DevTools)
  • Mobile breakpoint issues (resize browser to 375px, 768px, 1024px)
  • Tab navigation usability
  • Button states (hover, focus, active)
  • Loading states (currently missing)

Hour 1-2: Mobile Audit

  1. Open Chrome DevTools → Device Toolbar
  2. Test on iPhone SE (375px) and iPad (768px)
  3. Document issues:
    • Touch target sizes
    • Text readability
    • Button spacing
    • Tab overflow behavior

Hour 3-4: Accessibility Quick Wins

// Add to each converter component
// Example for MorseCodeConverter.jsx

<textarea
  id="morse-input"
  aria-label="Input text for Morse code conversion"
  aria-describedby="morse-input-help"
  // ... existing props
/>
<span id="morse-input-help" className="sr-only">
  Enter text to convert to Morse code
</span>

Day 1 Deliverables:

  • Mobile audit report (issues list)
  • Accessibility checklist
  • Quick wins implementation (ARIA labels)
  • Screenshot comparison (before/after)

💅 CSS/Styling Specialist - Day 1 Checklist

Setup (15 minutes)

npm install
npm run dev

Hour 1: Audit Current CSS

# Check current bundle size
npm run build
# Look for dist/assets/*.css file size

# Analyze Tailwind usage
npx tailwindcss -i src/index.css -o dist/output.css --minify

Hour 2-3: Create Custom Utilities

File: src/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

/* Custom Component Classes */
@layer components {
  .converter-card {
    @apply p-4 bg-black rounded-lg border-2 border-white;
  }
  
  .converter-input {
    @apply w-full h-32 p-2 bg-black border border-white rounded-md text-white;
    @apply focus:ring-2 focus:ring-invader-pink focus:border-invader-pink;
    @apply transition-all duration-200;
  }
  
  .converter-output {
    @apply w-full h-32 p-2 bg-black border border-white rounded-md text-white relative;
  }
  
  .btn-primary {
    @apply px-4 py-2 bg-invader-pink text-black rounded-md font-semibold;
    @apply hover:bg-invader-green focus:outline-none focus:ring-2;
    @apply transition-colors duration-200;
  }
  
  .btn-secondary {
    @apply px-3 py-1 bg-invader-green text-black rounded-md;
    @apply hover:bg-invader-pink focus:outline-none focus:ring-2;
    @apply transition-colors duration-200;
  }
}

/* Custom Animations */
@layer utilities {
  .animate-fade-in {
    animation: fadeIn 0.3s ease-in;
  }
  
  .animate-slide-up {
    animation: slideUp 0.3s ease-out;
  }
  
  .animate-pulse-glow {
    animation: pulseGlow 2s ease-in-out infinite;
  }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes slideUp {
  from { 
    opacity: 0;
    transform: translateY(10px);
  }
  to { 
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes pulseGlow {
  0%, 100% { 
    box-shadow: 0 0 5px rgba(255, 0, 255, 0.5);
  }
  50% { 
    box-shadow: 0 0 20px rgba(255, 0, 255, 0.8);
  }
}

Hour 4: Enhance Tailwind Config

File: tailwind.config.js

module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}", "./index.html"],
  theme: {
    extend: {
      colors: {
        'invader-pink': {
          DEFAULT: '#FF00FF',
          '50': '#FFE6FF',
          '100': '#FFCCFF',
          '500': '#FF00FF',
          '600': '#CC00CC',
          '700': '#990099',
        },
        'invader-green': {
          DEFAULT: '#00FF00',
          '50': '#E6FFE6',
          '100': '#CCFFCC',
          '500': '#00FF00',
          '600': '#00CC00',
          '700': '#009900',
        },
      },
      animation: {
        'fade-in': 'fadeIn 0.3s ease-in',
        'slide-up': 'slideUp 0.3s ease-out',
        'pulse-glow': 'pulseGlow 2s ease-in-out infinite',
      },
      keyframes: {
        fadeIn: {
          'from': { opacity: '0' },
          'to': { opacity: '1' },
        },
        slideUp: {
          'from': { opacity: '0', transform: 'translateY(10px)' },
          'to': { opacity: '1', transform: 'translateY(0)' },
        },
        pulseGlow: {
          '0%, 100%': { boxShadow: '0 0 5px rgba(255, 0, 255, 0.5)' },
          '50%': { boxShadow: '0 0 20px rgba(255, 0, 255, 0.8)' },
        },
      },
      spacing: {
        '18': '4.5rem',
        '88': '22rem',
        '112': '28rem',
      },
    },
  },
  plugins: [],
}

Day 1 Deliverables:

  • Custom utility classes
  • Enhanced Tailwind config
  • Animation library
  • Updated converter styling (refactor one component as example)

⚛️ Frontend Developer - Day 1 Checklist

Setup (15 minutes)

npm install
npm run dev
npm run test  # Verify tests work

Hour 1-2: Create Upside Down Text Converter

File: src/components/UpsideDownConverter.jsx

import React, { useState } from 'react';

const upsideDownMap = {
  'a': 'ɐ', 'b': 'q', 'c': 'ɔ', 'd': 'p', 'e': 'ǝ', 'f': 'ɟ', 'g': 'ƃ', 'h': 'ɥ',
  'i': 'ᴉ', 'j': 'ɾ', 'k': 'ʞ', 'l': 'l', 'm': 'ɯ', 'n': 'u', 'o': 'o', 'p': 'd',
  'q': 'b', 'r': 'ɹ', 's': 's', 't': 'ʇ', 'u': 'n', 'v': 'ʌ', 'w': 'ʍ', 'x': 'x',
  'y': 'ʎ', 'z': 'z',
  'A': '∀', 'B': 'B', 'C': 'Ɔ', 'D': 'D', 'E': 'Ǝ', 'F': 'Ⅎ', 'G': 'פ', 'H': 'H',
  'I': 'I', 'J': 'ſ', 'K': 'ʞ', 'L': '˥', 'M': 'W', 'N': 'N', 'O': 'O', 'P': 'Ԁ',
  'Q': 'Ò', 'R': 'ɹ', 'S': 'S', 'T': '┴', 'U': '∩', 'V': 'Λ', 'W': 'M', 'X': 'X',
  'Y': '⅄', 'Z': 'Z',
  '0': '0', '1': 'Ɩ', '2': 'ᄅ', '3': 'Ɛ', '4': 'ㄣ', '5': 'ϛ', '6': '9', '7': 'ㄥ',
  '8': '8', '9': '6',
  '!': '¡', '?': '¿', '.': '˙', ',': '\'', '\'': ',', '"': ',,', '(': ')', ')': '(',
  '[': ']', ']': '[', '{': '}', '}': '{', '<': '>', '>': '<', '&': '⅋',
  '_': '‾', ';': '؛', ' ': ' '
};

// Create reverse map
const reverseUpsideDownMap = Object.entries(upsideDownMap).reduce((acc, [key, value]) => {
  acc[value] = key;
  return acc;
}, {});

const UpsideDownConverter = ({ addToMessage }) => {
  const [input, setInput] = useState('');
  const [output, setOutput] = useState('');
  const [copied, setCopied] = useState(false);
  const [toUpsideDown, setToUpsideDown] = useState(true);

  const convertToUpsideDown = (text) => {
    let result = '';
    for (let char of text) {
      result += upsideDownMap[char] || char;
    }
    // Reverse the string for upside down effect
    setOutput(result.split('').reverse().join(''));
  };

  const convertFromUpsideDown = (upsideDownText) => {
    // Reverse first, then convert
    const reversed = upsideDownText.split('').reverse().join('');
    let result = '';
    for (let char of reversed) {
      result += reverseUpsideDownMap[char] || char;
    }
    setOutput(result);
  };

  const handleInputChange = (e) => {
    const newText = e.target.value;
    setInput(newText);
    if (toUpsideDown) {
      convertToUpsideDown(newText);
    } else {
      convertFromUpsideDown(newText);
    }
  };

  const copyToClipboard = () => {
    navigator.clipboard.writeText(output);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  const swapConversion = () => {
    const oldInput = input;
    setInput(output);
    setOutput(oldInput);
    setToUpsideDown(!toUpsideDown);
  };

  return (
    <div className="p-4 bg-black rounded-lg">
      <h2 className="text-2xl font-bold mb-4 text-white">Upside Down Text Converter</h2>
      <div className="grid grid-cols-1 md:grid-cols-[1fr_auto_1fr] gap-4 items-center">
        <div>
          <label htmlFor="upside-input" className="block text-sm font-medium text-white mb-2">
            {toUpsideDown ? 'Normal Text' : 'Upside Down Text'}
          </label>
          <textarea
            id="upside-input"
            className="w-full h-32 p-2 bg-black border border-white rounded-md text-white focus:ring-invader-pink focus:border-invader-pink"
            placeholder={toUpsideDown ? 'Enter text...' : 'Enter upside down text...'}
            value={input}
            onChange={handleInputChange}
          />
        </div>
        
        <div className="flex justify-center">
          <button
            onClick={swapConversion}
            className="p-2 bg-black border border-white text-white rounded-full hover:bg-invader-green focus:outline-none focus:ring-2"
            title="Swap conversion direction"
          >
            <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7h12m0 0l-4-4m4 4l-4 4m2 5H4m0 0l4 4m-4-4l4-4" />
            </svg>
          </button>
        </div>

        <div>
          <label htmlFor="upside-output" className="block text-sm font-medium text-white mb-2">
            {toUpsideDown ? 'Upside Down Text' : 'Normal Text'}
          </label>
          <div id="upside-output" className="w-full h-32 p-2 bg-black border border-white rounded-md text-white relative">
            {output}
            {output && (
              <div className="absolute top-2 right-2 flex flex-col gap-2">
                <button
                  onClick={copyToClipboard}
                  className="px-3 py-1 bg-invader-pink text-black rounded-md hover:bg-invader-green"
                >
                  {copied ? 'Copied!' : 'Copy'}
                </button>
                <button
                  onClick={() => addToMessage(output)}
                  className="px-3 py-1 bg-invader-green text-black rounded-md hover:bg-invader-pink"
                >
                  Add
                </button>
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
};

export default UpsideDownConverter;

Hour 3: Update App.jsx

File: src/App.jsx

// Add import at top
import UpsideDownConverter from './components/UpsideDownConverter';

// Inside Tabs component, add new tab
<Tab label="Upside Down">
  <UpsideDownConverter addToMessage={addToCompoundedMessage} />
</Tab>

Hour 4: Write Tests

File: src/components/UpsideDownConverter.test.jsx

import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import UpsideDownConverter from './UpsideDownConverter';

describe('UpsideDownConverter', () => {
  it('renders without crashing', () => {
    render(<UpsideDownConverter addToMessage={() => {}} />);
    expect(screen.getByText('Upside Down Text Converter')).toBeInTheDocument();
  });

  it('converts text to upside down', () => {
    render(<UpsideDownConverter addToMessage={() => {}} />);
    const input = screen.getByPlaceholderText('Enter text...');
    
    fireEvent.change(input, { target: { value: 'Hello' } });
    
    const output = screen.getByLabelText('Upside Down Text');
    expect(output.textContent).toBeTruthy();
  });

  it('has copy button when output exists', () => {
    render(<UpsideDownConverter addToMessage={() => {}} />);
    const input = screen.getByPlaceholderText('Enter text...');
    
    fireEvent.change(input, { target: { value: 'test' } });
    
    expect(screen.getByText('Copy')).toBeInTheDocument();
  });
});

Day 1 Deliverables:

  • Upside Down Text Converter component
  • Integration with main app
  • Unit tests
  • Documentation

🔧 Backend/Integration Developer - Day 1 Checklist

Setup (30 minutes)

npm install
# Check Firebase config
cat firebase.js
cat firebase-config.json

Hour 1-2: Set Up Firebase Authentication

File: src/firebase/auth.js

import { getAuth, signInWithPopup, GoogleAuthProvider, signOut } from 'firebase/auth';
import app from '../firebase';

const auth = getAuth(app);
const googleProvider = new GoogleAuthProvider();

export const signInWithGoogle = async () => {
  try {
    const result = await signInWithPopup(auth, googleProvider);
    return result.user;
  } catch (error) {
    console.error('Error signing in:', error);
    throw error;
  }
};

export const logOut = async () => {
  try {
    await signOut(auth);
  } catch (error) {
    console.error('Error signing out:', error);
    throw error;
  }
};

export { auth };

Hour 3: Create Auth Context

File: src/contexts/AuthContext.jsx

import React, { createContext, useContext, useState, useEffect } from 'react';
import { auth } from '../firebase/auth';
import { onAuthStateChanged } from 'firebase/auth';

const AuthContext = createContext();

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider');
  }
  return context;
};

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setUser(user);
      setLoading(false);
    });

    return unsubscribe;
  }, []);

  const value = {
    user,
    loading,
  };

  return (
    <AuthContext.Provider value={value}>
      {!loading && children}
    </AuthContext.Provider>
  );
};

Hour 4: Add Auth UI Component

File: src/components/AuthButton.jsx

import React from 'react';
import { useAuth } from '../contexts/AuthContext';
import { signInWithGoogle, logOut } from '../firebase/auth';

const AuthButton = () => {
  const { user } = useAuth();

  const handleSignIn = async () => {
    try {
      await signInWithGoogle();
    } catch (error) {
      console.error('Sign in failed:', error);
    }
  };

  const handleSignOut = async () => {
    try {
      await logOut();
    } catch (error) {
      console.error('Sign out failed:', error);
    }
  };

  return (
    <div className="flex items-center gap-4">
      {user ? (
        <>
          <span className="text-white">Hello, {user.displayName}</span>
          <button
            onClick={handleSignOut}
            className="px-4 py-2 bg-invader-pink text-black rounded-md hover:bg-invader-green"
          >
            Sign Out
          </button>
        </>
      ) : (
        <button
          onClick={handleSignIn}
          className="px-4 py-2 bg-invader-green text-black rounded-md hover:bg-invader-pink"
        >
          Sign In with Google
        </button>
      )}
    </div>
  );
};

export default AuthButton;

Day 1 Deliverables:

  • Firebase authentication setup
  • Auth context provider
  • Sign in/out UI
  • User state management

🚀 DevOps/Infrastructure - Day 1 Checklist

Setup (15 minutes)

# Verify build works
npm run build
# Check output
ls -lh dist/

Hour 1-2: Create GitHub Actions Workflow

File: .github/workflows/ci.yml

name: CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '20'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run linter
      run: npm run lint || echo "No lint script found"
    
    - name: Run tests
      run: npm run test
    
    - name: Build
      run: npm run build
    
    - name: Check bundle size
      run: |
        SIZE=$(du -sh dist | cut -f1)
        echo "Bundle size: $SIZE"

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '20'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Build
      run: npm run build
    
    - name: Deploy to Firebase
      uses: FirebaseExtended/action-hosting-deploy@v0
      with:
        repoToken: '${{ secrets.GITHUB_TOKEN }}'
        firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'
        channelId: live
        projectId: your-firebase-project-id

Hour 3: Add Performance Budget

File: .github/workflows/performance.yml

name: Performance Budget

on:
  pull_request:
    branches: [ main ]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '20'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Build
      run: npm run build
    
    - name: Run Lighthouse CI
      run: |
        npm install -g @lhci/cli
        lhci autorun

Hour 4: Create Lighthouse Config

File: lighthouserc.json

{
  "ci": {
    "collect": {
      "startServerCommand": "npm run preview",
      "url": ["http://localhost:4173"],
      "numberOfRuns": 3
    },
    "assert": {
      "preset": "lighthouse:recommended",
      "assertions": {
        "categories:performance": ["error", {"minScore": 0.9}],
        "categories:accessibility": ["error", {"minScore": 0.9}],
        "categories:best-practices": ["error", {"minScore": 0.9}],
        "categories:seo": ["error", {"minScore": 0.9}]
      }
    },
    "upload": {
      "target": "temporary-public-storage"
    }
  }
}

Day 1 Deliverables:

  • CI/CD pipeline configured
  • Performance budget enforced
  • Automated testing on PR
  • Build verification

📊 Product/UX Researcher - Day 1 Checklist

Setup (15 minutes)

# Run the app
npm install
npm run dev
# Use the app yourself - note impressions

Hour 1-2: Create User Personas

File: docs/user-personas.md

# User Personas for Text Transformer

## Persona 1: Tech Enthusiast Tom
- **Age**: 22-30
- **Occupation**: Software Developer
- **Goals**: Quick text encoding for development tasks
- **Pain Points**: Needs multiple conversions quickly
- **Usage**: Daily, primarily Binary/Hex/Base64

## Persona 2: Creative Casey
- **Age**: 18-25
- **Occupation**: Social Media Manager
- **Goals**: Create unique, eye-catching text for posts
- **Pain Points**: Limited creative text options
- **Usage**: Weekly, primarily Upside Down/Zalgo/Leet

## Persona 3: Student Sam
- **Age**: 15-20
- **Occupation**: High School/College Student
- **Goals**: Learn about encoding, have fun with friends
- **Pain Points**: Confusing UI, unclear purposes
- **Usage**: Occasional, explores all converters

Hour 3-4: Feature Prioritization Matrix

File: docs/feature-prioritization.md

# Feature Prioritization Matrix

## Priority 0 (Must Have - Week 1-2)
1. **Upside Down Text**: High user value, low effort
2. **Undo/Redo**: High user value, medium effort
3. **Mobile UX Fixes**: High user value, medium effort
4. **Shareable Links**: High user value, medium effort

## Priority 1 (Should Have - Week 3-4)
1. **Markdown to HTML**: High value for developers
2. **Text-to-Speech**: High value for accessibility
3. **Themed Converters**: Medium value, medium effort
4. **User Authentication**: Enables other features

## Priority 2 (Nice to Have - Week 5+)
1. **Gamification**: Medium value, high effort
2. **Custom Mappings**: Low value, high effort
3. **API Access**: Low immediate value

## Metrics to Track
- Feature usage rate
- User engagement time
- Conversion completion rate
- Return user rate

Day 1 Deliverables:

  • 3-5 user personas
  • Feature prioritization matrix
  • Initial usage patterns analysis
  • Research plan for Week 2

🎯 Day 1 Success Criteria

All Roles

  • Development environment set up
  • Current app reviewed and understood
  • Role-specific quick wins identified
  • Day 1 deliverables completed
  • Ready for Day 2 tasks

Team Sync (End of Day 1)

  1. Each role presents their findings
  2. Identify blockers or dependencies
  3. Adjust priorities if needed
  4. Plan Day 2 tasks

📞 Need Help?

Resources

Common Issues

  • Build fails: Check Node version (should be 18+)
  • Firebase errors: Verify firebase-config.json
  • Tests fail: Run npm install again
  • Dev server won't start: Check port 5173 is free

Created: December 25, 2024
By: Winston (Architect Agent)
Status: Ready for Implementation