> ## Documentation Index
> Fetch the complete documentation index at: https://docs.replit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 팀원 초대하기

> 프로젝트에 팀원을 초대하고 함께 빌드하세요 — 각자 Agent와 새 스레드를 시작하면서 같은 프로젝트에서 작업할 수 있습니다.

export const KanbanColumns = ({columns = []}) => {
  if (typeof document !== "undefined" && !document.getElementById("kanban-board-styles")) {
    const style = document.createElement("style");
    style.id = "kanban-board-styles";
    style.textContent = `
      .kb-board {
        --kb-bg: var(--replit-docs-bg, #F6F6F4);
        --kb-col-bg: var(--replit-docs-bg-elevated, #F1F1EE);
        --kb-card-bg: var(--replit-docs-bg-task, #EDECE8);
        --kb-border: var(--replit-docs-border, #DEDAD5);
        --kb-text: var(--replit-docs-text, #1D1D1D);
        --kb-text-dim: var(--replit-docs-text-muted, #5C5C5C);
        --kb-text-dimmer: var(--replit-docs-text-muted, #5C5C5C);
        --kb-text-dimmest: var(--replit-docs-text-subtle, #858585);
        --kb-badge-bg: var(--replit-docs-bg-elevated, #F1F1EE);
      }
      .dark .kb-board,
      html.dark .kb-board,
      [data-theme="dark"] .kb-board {
        --kb-bg: var(--replit-docs-bg, #1E1E1F);
        --kb-col-bg: var(--replit-docs-bg-elevated, #222223);
        --kb-card-bg: var(--replit-docs-bg-task, #252527);
        --kb-border: var(--replit-docs-border, #39393D);
        --kb-text: var(--replit-docs-text, #F5F5F5);
        --kb-text-dim: var(--replit-docs-text-muted, #B8B8BE);
        --kb-text-dimmer: var(--replit-docs-text-muted, #B8B8BE);
        --kb-text-dimmest: var(--replit-docs-text-subtle, #8E8F97);
        --kb-badge-bg: var(--replit-docs-bg-elevated, #222223);
      }
      .kb-board { overflow: visible; }
    `;
    document.head.appendChild(style);
  }
  return <div className="kb-board" style={{
    fontFamily: "'IBM Plex Sans', sans-serif",
    display: "flex",
    gap: "12px",
    padding: "8px",
    background: "var(--kb-bg)",
    borderRadius: "12px"
  }}>
      {columns.map((col, ci) => <div key={ci} data-kb-column={col.title.toLowerCase()} style={{
    flex: "1 1 0",
    minWidth: "200px",
    background: "var(--kb-col-bg)",
    borderRadius: "8px",
    display: "flex",
    flexDirection: "column",
    overflow: "visible"
  }}>
          <div style={{
    display: "flex",
    alignItems: "center",
    gap: "8px",
    padding: "12px 12px 8px 12px"
  }}>
            <span style={{
    fontSize: "14px",
    fontWeight: 500,
    color: "var(--kb-text)"
  }}>
              {col.title}
            </span>
            <span data-kb-count style={{
    fontSize: "12px",
    color: "var(--kb-text-dimmer)",
    background: "var(--kb-badge-bg)",
    padding: "0 6px",
    borderRadius: "4px",
    lineHeight: "20px"
  }}>
              {col.count}
            </span>
          </div>
          <div data-kb-cards style={{
    display: "flex",
    flexDirection: "column",
    gap: "8px",
    padding: "8px",
    flex: "1 1 0"
  }}>
            {col.cards.map((card, i) => <TaskCard key={i} title={card.title} description={card.description} status={card.status} timestamp={card.timestamp} showMenu={card.showMenu} width="100%" />)}
          </div>
        </div>)}
    </div>;
};

export const TaskCard = ({title = "Product Manager next steps discussion", description = "", status = "draft", timestamp = "Created 3 minutes ago", width = "364px", showMenu = false}) => {
  if (typeof document !== "undefined" && !document.getElementById("task-card-styles")) {
    const style = document.createElement("style");
    style.id = "task-card-styles";
    style.textContent = `
      .task-card {
        --tc-bg: var(--replit-docs-bg-task, #EDECE8);
        --tc-border: var(--replit-docs-border, #DEDAD5);
        --tc-text: var(--replit-docs-text, #1D1D1D);
        --tc-text-dim: var(--replit-docs-text-muted, #5C5C5C);
        --tc-text-dimmest: var(--replit-docs-text-subtle, #858585);
        --tc-dot-active: #4A7BF7;
        --tc-dot-draft: #A6A6A6;
        --tc-dot-ready: #F59E0B;
        --tc-dot-done: #22C55E;
      }
      .dark .task-card,
      html.dark .task-card,
      [data-theme="dark"] .task-card {
        --tc-bg: var(--replit-docs-bg-task, #252527);
        --tc-border: var(--replit-docs-border, #39393D);
        --tc-text: var(--replit-docs-text, #F5F5F5);
        --tc-text-dim: var(--replit-docs-text-muted, #B8B8BE);
        --tc-text-dimmest: var(--replit-docs-text-subtle, #8E8F97);
        --tc-dot-active: #6B9EFF;
        --tc-dot-draft: #6B7280;
        --tc-dot-ready: #FBBF24;
        --tc-dot-done: #4ADE80;
      }
      @keyframes task-card-fade-in {
        from { transform: translateY(6px); opacity: 0; }
        to { transform: translateY(0); opacity: 1; }
      }
      @keyframes tc-tl {
        0%{opacity:1;fill:var(--tc-light)} 5%{opacity:1;fill:var(--tc-dark)} 10%{opacity:1;fill:var(--tc-dark)} 15%{opacity:1;fill:var(--tc-light)}
        20%{opacity:0} 25%{opacity:1;fill:var(--tc-dark)} 30%{opacity:1;fill:var(--tc-light)} 35%{opacity:0}
        40%{opacity:1;fill:var(--tc-dark)} 45%{opacity:1;fill:var(--tc-light)} 50%{opacity:0} 55%{opacity:1;fill:var(--tc-dark)}
        60%{opacity:0} 65%{opacity:1;fill:var(--tc-light)} 70%{opacity:1;fill:var(--tc-light)} 75%{opacity:1;fill:var(--tc-light)}
        80%{opacity:1;fill:var(--tc-light)} 85%{opacity:0} 90%{opacity:1;fill:var(--tc-light)} 95%{opacity:1;fill:var(--tc-light)}
      }
      @keyframes tc-tr {
        0%{opacity:1;fill:var(--tc-dark)} 5%{opacity:0} 10%{opacity:0} 15%{opacity:0}
        20%{opacity:1;fill:var(--tc-light)} 25%{opacity:1;fill:var(--tc-light)} 30%{opacity:1;fill:var(--tc-light)} 35%{opacity:1;fill:var(--tc-light)}
        40%{opacity:0} 45%{opacity:1;fill:var(--tc-dark)} 50%{opacity:1;fill:var(--tc-dark)} 55%{opacity:0}
        60%{opacity:1;fill:var(--tc-dark)} 65%{opacity:0} 70%{opacity:0} 75%{opacity:1;fill:var(--tc-light)}
        80%{opacity:0} 85%{opacity:1;fill:var(--tc-dark)} 90%{opacity:0} 95%{opacity:0}
      }
      @keyframes tc-bl {
        0%{opacity:1;fill:var(--tc-light)} 5%{opacity:0} 10%{opacity:0} 15%{opacity:0}
        20%{opacity:1;fill:var(--tc-light)} 25%{opacity:1;fill:var(--tc-light)} 30%{opacity:1;fill:var(--tc-light)} 35%{opacity:1;fill:var(--tc-light)}
        40%{opacity:0} 45%{opacity:1;fill:var(--tc-light)} 50%{opacity:1;fill:var(--tc-light)} 55%{opacity:0}
        60%{opacity:1;fill:var(--tc-light)} 65%{opacity:0} 70%{opacity:0} 75%{opacity:1;fill:var(--tc-light)}
        80%{opacity:0} 85%{opacity:1;fill:var(--tc-light)} 90%{opacity:0} 95%{opacity:0}
      }
      @keyframes tc-br {
        0%{opacity:1;fill:var(--tc-light)} 5%{opacity:1;fill:var(--tc-dark)} 10%{opacity:1;fill:var(--tc-light)} 15%{opacity:1;fill:var(--tc-light)}
        20%{opacity:0} 25%{opacity:1;fill:var(--tc-light)} 30%{opacity:1;fill:var(--tc-dark)} 35%{opacity:0}
        40%{opacity:1;fill:var(--tc-light)} 45%{opacity:1;fill:var(--tc-light)} 50%{opacity:0} 55%{opacity:1;fill:var(--tc-light)}
        60%{opacity:0} 65%{opacity:1;fill:var(--tc-dark)} 70%{opacity:1;fill:var(--tc-light)} 75%{opacity:1;fill:var(--tc-light)}
        80%{opacity:1;fill:var(--tc-light)} 85%{opacity:0} 90%{opacity:1;fill:var(--tc-light)} 95%{opacity:1;fill:var(--tc-dark)}
      }
      @keyframes tc-drop {
        0%{opacity:0} 5%{opacity:1;fill:var(--tc-dark)} 10%{opacity:1;fill:var(--tc-dark)} 15%{opacity:1;fill:var(--tc-dark)}
        20%{opacity:0} 25%{opacity:0} 30%{opacity:0} 35%{opacity:0}
        40%{opacity:1;fill:var(--tc-dark)} 45%{opacity:0} 50%{opacity:0} 55%{opacity:1;fill:var(--tc-dark)}
        60%{opacity:0} 65%{opacity:1;fill:var(--tc-dark)} 70%{opacity:1;fill:var(--tc-dark)} 75%{opacity:0}
        80%{opacity:1;fill:var(--tc-dark)} 85%{opacity:0} 90%{opacity:1;fill:var(--tc-dark)} 95%{opacity:1;fill:var(--tc-dark)}
      }
      @keyframes tc-dropalt {
        0%{opacity:0} 5%{opacity:0} 10%{opacity:0} 15%{opacity:0}
        20%{opacity:1;fill:var(--tc-dark)} 25%{opacity:0} 30%{opacity:0} 35%{opacity:1;fill:var(--tc-dark)}
        40%{opacity:0} 45%{opacity:0} 50%{opacity:1;fill:var(--tc-dark)} 55%{opacity:0}
        60%{opacity:1;fill:var(--tc-dark)} 65%{opacity:0} 70%{opacity:0} 75%{opacity:0}
        80%{opacity:0} 85%{opacity:1;fill:var(--tc-dark)} 90%{opacity:0} 95%{opacity:0}
      }
      .task-card .tc-el-tl { animation: tc-tl 4s step-end infinite; }
      .task-card .tc-el-tr { animation: tc-tr 4s step-end infinite; }
      .task-card .tc-el-bl { animation: tc-bl 4s step-end infinite; }
      .task-card .tc-el-br { animation: tc-br 4s step-end infinite; }
      .task-card .tc-el-drop { animation: tc-drop 4s step-end infinite; }
      .task-card .tc-el-dropalt { animation: tc-dropalt 4s step-end infinite; }
      @media (hover: hover) {
        .task-card:hover {
          border-color: var(--tc-text-dimmest) !important;
        }
        .task-card .tc-menu-btn:hover {
          background: var(--tc-border);
        }
      }
    `;
    document.head.appendChild(style);
  }
  const colorPairs = {
    active: {
      dark: "#57ABFF",
      light: "#A8D4FF"
    },
    "active-queued": {
      dark: "#6BB5FF",
      light: "#6BB5FF"
    },
    draft: {
      dark: "#A6A6A6",
      light: "#D4D4D4"
    },
    "draft-static": {
      dark: "#A6A6A6",
      light: "#D4D4D4"
    },
    "draft-done": {
      dark: "#A6A6A6",
      light: "#D4D4D4"
    },
    ready: {
      dark: "#009118",
      light: "#6CD97E"
    },
    "ready-drop": {
      dark: "#009118",
      light: "#6CD97E"
    },
    applying: {
      dark: "#7C3AED",
      light: "#C4B5FD"
    },
    done: {
      dark: "#22C55E",
      light: "#86EFAC"
    }
  };
  const statusLabels = {
    active: "Active",
    "active-queued": "Queued",
    draft: "Draft",
    "draft-static": "Draft",
    "draft-done": "Done",
    ready: "Ready",
    "ready-drop": "Ready",
    applying: "Applying",
    done: "Done"
  };
  const colors = colorPairs[status] || colorPairs.draft;
  const applyingIconHtml = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" style="flex-shrink:0;--tc-dark:#009118;--tc-light:#6CD97E"><circle class="tc-el-tl" cx="5.332" cy="5.338" r="5.332" fill="#009118"/><circle class="tc-el-tr" cx="18.663" cy="5.338" r="5.332" fill="#009118"/><circle class="tc-el-bl" cx="5.332" cy="18.668" r="5.332" fill="#009118"/><circle class="tc-el-br" cx="18.663" cy="18.668" r="5.332" fill="#009118"/><path class="tc-el-drop" fill="#009118" d="M18.662 0a5.332 5.332 0 0 1 .001 10.664l-.412.01a8 8 0 0 0-7.577 7.59l-.01.398-.008.274A5.331 5.331 0 0 1 0 18.662a5.332 5.332 0 0 1 5.332-5.332l.398-.01a8 8 0 0 0 7.59-7.576l.011-.412A5.331 5.331 0 0 1 18.662 0Z"/><g class="tc-el-dropalt" transform="rotate(90, 12, 12)"><path fill="#009118" d="M18.662 0a5.332 5.332 0 0 1 .001 10.664l-.412.01a8 8 0 0 0-7.577 7.59l-.01.398-.008.274A5.331 5.331 0 0 1 0 18.662a5.332 5.332 0 0 1 5.332-5.332l.398-.01a8 8 0 0 0 7.59-7.576l.011-.412A5.331 5.331 0 0 1 18.662 0Z"/></g></svg>';
  const doneIconHtml = '<svg width="16" height="16" viewBox="0 0 24 24" style="flex-shrink:0;fill:var(--tc-text-dimmest)"><path d="M12 7a5 5 0 1 1 0 10 5 5 0 0 1 0-10Z"/><path fill-rule="evenodd" d="M12 1c6.075 0 11 4.925 11 11s-4.925 11-11 11S1 18.075 1 12 5.925 1 12 1Zm0 2a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z" clip-rule="evenodd"/></svg>';
  const handleApplyChanges = e => {
    const tooltip = e.currentTarget.parentElement;
    tooltip.style.display = "none";
    const menuBtn = tooltip.previousElementSibling;
    if (menuBtn) menuBtn.style.background = "none";
    const card = e.currentTarget.closest(".task-card");
    if (!card) return;
    const headerRow = card.firstElementChild;
    const oldIcon = headerRow.firstElementChild;
    const menuWrapper = headerRow.lastElementChild;
    const meta = card.lastElementChild;
    const temp = document.createElement("div");
    temp.innerHTML = applyingIconHtml;
    headerRow.replaceChild(temp.firstElementChild, oldIcon);
    meta.textContent = "Applying \u2022 just now";
    menuWrapper.style.visibility = "hidden";
    setTimeout(() => {
      const currentIcon = headerRow.firstElementChild;
      const doneTemp = document.createElement("div");
      doneTemp.innerHTML = doneIconHtml;
      headerRow.replaceChild(doneTemp.firstElementChild, currentIcon);
      meta.textContent = "Done \u2022 just now";
      const board = card.closest(".kb-board");
      if (!board) return;
      const doneCol = board.querySelector('[data-kb-column="done"]');
      if (!doneCol) return;
      const cardList = doneCol.querySelector("[data-kb-cards]");
      const readyCol = board.querySelector('[data-kb-column="ready"]');
      card.style.transition = "opacity 300ms, transform 300ms";
      card.style.opacity = "0";
      card.style.transform = "translateY(-10px)";
      setTimeout(() => {
        card.parentElement.removeChild(card);
        if (readyCol) {
          const rc = readyCol.querySelector("[data-kb-count]");
          rc.textContent = parseInt(rc.textContent) - 1;
        }
        const dc = doneCol.querySelector("[data-kb-count]");
        dc.textContent = parseInt(dc.textContent) + 1;
        card.style.opacity = "0";
        card.style.transform = "translateY(6px)";
        cardList.insertBefore(card, cardList.firstChild);
        requestAnimationFrame(() => {
          card.style.transition = "opacity 300ms, transform 300ms";
          card.style.opacity = "1";
          card.style.transform = "translateY(0)";
        });
      }, 300);
    }, 3000);
  };
  return <div className="task-card" style={{
    fontFamily: "'IBM Plex Sans', sans-serif",
    width: width,
    background: "var(--tc-bg)",
    border: "1px solid var(--tc-border)",
    borderRadius: "8px",
    padding: "8px 12px",
    margin: 0,
    display: "flex",
    flexDirection: "column",
    gap: "4px",
    cursor: "pointer",
    opacity: 0,
    animation: "task-card-fade-in 300ms ease-out forwards",
    transition: "border-color 120ms ease-out"
  }}>
      {}
      <div style={{
    display: "flex",
    alignItems: "center",
    gap: "8px"
  }}>
        {status === "done" ? <svg width="16" height="16" viewBox="0 0 24 24" fill="var(--tc-text-dimmest)" style={{
    flexShrink: 0
  }}>
            <path d="M12 7a5 5 0 1 1 0 10 5 5 0 0 1 0-10Z" />
            <path fillRule="evenodd" d="M12 1c6.075 0 11 4.925 11 11s-4.925 11-11 11S1 18.075 1 12 5.925 1 12 1Zm0 2a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z" clipRule="evenodd" />
          </svg> : status === "draft-static" ? <svg width="16" height="16" viewBox="0 0 24 24" fill="none" style={{
    flexShrink: 0
  }}>
            <circle cx="5.332" cy="5.338" r="5.332" fill={colors.dark} />
            <circle cx="18.663" cy="5.338" r="5.332" fill={colors.dark} />
            <circle cx="5.332" cy="18.668" r="5.332" fill={colors.dark} />
            <circle cx="18.663" cy="18.668" r="5.332" fill={colors.dark} />
          </svg> : status === "draft-done" ? <svg width="16" height="16" viewBox="0 0 24 24" fill="none" style={{
    flexShrink: 0
  }}>
            <circle cx="5.332" cy="5.338" r="5.332" fill={colors.light} />
            <circle cx="18.663" cy="5.338" r="5.332" fill={colors.light} />
            <circle cx="5.332" cy="18.668" r="5.332" fill={colors.light} />
            <circle cx="18.663" cy="18.668" r="5.332" fill={colors.light} />
          </svg> : status === "active-queued" ? <svg width="16" height="16" viewBox="0 0 24 24" fill="none" style={{
    flexShrink: 0
  }}>
            <circle cx="5.332" cy="5.338" r="5.332" fill={colors.light} />
            <circle cx="18.663" cy="5.338" r="5.332" fill={colors.light} />
            <circle cx="5.332" cy="18.668" r="5.332" fill={colors.light} />
            <circle cx="18.663" cy="18.668" r="5.332" fill={colors.light} />
          </svg> : status === "ready-drop" ? <svg width="16" height="16" viewBox="0 0 24 24" fill="none" style={{
    flexShrink: 0
  }}>
            <circle cx="5.332" cy="5.338" r="5.332" fill={colors.light} />
            <circle cx="18.663" cy="18.668" r="5.332" fill={colors.light} />
            <path fill={colors.dark} d="M18.662 0a5.332 5.332 0 0 1 .001 10.664l-.412.01a8 8 0 0 0-7.577 7.59l-.01.398-.008.274A5.331 5.331 0 0 1 0 18.662a5.332 5.332 0 0 1 5.332-5.332l.398-.01a8 8 0 0 0 7.59-7.576l.011-.412A5.331 5.331 0 0 1 18.662 0Z" />
          </svg> : <svg width="16" height="16" viewBox="0 0 24 24" fill="none" style={{
    flexShrink: 0,
    "--tc-dark": colors.dark,
    "--tc-light": colors.light
  }}>
            <circle className="tc-el-tl" cx="5.332" cy="5.338" r="5.332" fill={colors.dark} />
            <circle className="tc-el-tr" cx="18.663" cy="5.338" r="5.332" fill={colors.dark} />
            <circle className="tc-el-bl" cx="5.332" cy="18.668" r="5.332" fill={colors.dark} />
            <circle className="tc-el-br" cx="18.663" cy="18.668" r="5.332" fill={colors.dark} />
            <path className="tc-el-drop" fill={colors.dark} d="M18.662 0a5.332 5.332 0 0 1 .001 10.664l-.412.01a8 8 0 0 0-7.577 7.59l-.01.398-.008.274A5.331 5.331 0 0 1 0 18.662a5.332 5.332 0 0 1 5.332-5.332l.398-.01a8 8 0 0 0 7.59-7.576l.011-.412A5.331 5.331 0 0 1 18.662 0Z" />
            <g className="tc-el-dropalt" transform="rotate(90, 12, 12)">
              <path fill={colors.dark} d="M18.662 0a5.332 5.332 0 0 1 .001 10.664l-.412.01a8 8 0 0 0-7.577 7.59l-.01.398-.008.274A5.331 5.331 0 0 1 0 18.662a5.332 5.332 0 0 1 5.332-5.332l.398-.01a8 8 0 0 0 7.59-7.576l.011-.412A5.331 5.331 0 0 1 18.662 0Z" />
            </g>
          </svg>}
        <span style={{
    flex: 1,
    fontSize: "14px",
    lineHeight: "1.4",
    color: "var(--tc-text)",
    minWidth: 0
  }}>
          {title}
        </span>
        <div style={{
    position: "relative",
    flexShrink: 0
  }}>
          <button className="tc-menu-btn" type="button" aria-label="Thread actions" onClick={showMenu ? e => {
    const btn = e.currentTarget;
    const popup = btn.nextElementSibling;
    const card = btn.closest(".task-card");
    const isHidden = popup.style.display === "none";
    popup.style.display = isHidden ? "flex" : "none";
    btn.style.background = isHidden ? "var(--tc-border)" : "none";
    if (card) card.style.zIndex = isHidden ? "9999" : "";
  } : undefined} style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    width: "24px",
    height: "24px",
    border: "none",
    background: "none",
    borderRadius: "4px",
    cursor: "pointer",
    color: "var(--tc-text-dimmest)",
    padding: 0,
    flexShrink: 0,
    transition: "background 120ms ease-out"
  }}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
              <path fillRule="evenodd" d="M10.25 5a1.75 1.75 0 1 1 3.5 0 1.75 1.75 0 0 1-3.5 0Zm0 7a1.75 1.75 0 1 1 3.5 0 1.75 1.75 0 0 1-3.5 0Zm0 7a1.75 1.75 0 1 1 3.5 0 1.75 1.75 0 0 1-3.5 0Z" clipRule="evenodd" />
            </svg>
          </button>
          {showMenu && <div style={{
    display: "none",
    position: "absolute",
    top: 0,
    left: "28px",
    background: "var(--tc-bg)",
    border: "1px solid var(--tc-border)",
    borderRadius: "8px",
    padding: "4px",
    boxShadow: "0 8px 24px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.06)",
    zIndex: 9999,
    minWidth: "160px",
    flexDirection: "column",
    gap: "0"
  }}>
              <div onClick={handleApplyChanges} style={{
    display: "flex",
    alignItems: "center",
    gap: "8px",
    padding: "6px 8px",
    borderRadius: "4px",
    cursor: "pointer",
    color: "var(--tc-text)",
    fontSize: "14px"
  }}>
                <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style={{
    flexShrink: 0
  }}>
                  <path fillRule="evenodd" d="M6 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM2.25 6a3.75 3.75 0 1 1 4.527 3.67 8.25 8.25 0 0 0 7.554 7.553A3.751 3.751 0 0 1 21.75 18a3.75 3.75 0 0 1-7.43.726 9.75 9.75 0 0 1-7.57-4.53V21a.75.75 0 0 1-1.5 0V9.675A3.751 3.751 0 0 1 2.25 6ZM18 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z" clipRule="evenodd" />
                </svg>
                <span>Apply changes</span>
              </div>
              <div style={{
    display: "flex",
    alignItems: "center",
    gap: "8px",
    padding: "6px 8px",
    borderRadius: "4px",
    cursor: "pointer",
    color: "var(--tc-text)",
    fontSize: "14px"
  }}>
                <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style={{
    flexShrink: 0
  }}>
                  <path fillRule="evenodd" d="M5.47 5.47a.75.75 0 0 1 1.06 0L12 10.94l5.47-5.47a.75.75 0 1 1 1.06 1.06L13.06 12l5.47 5.47a.75.75 0 1 1-1.06 1.06L12 13.06l-5.47 5.47a.75.75 0 0 1-1.06-1.06L10.94 12 5.47 6.53a.75.75 0 0 1 0-1.06Z" clipRule="evenodd" />
                </svg>
                <span>Cancel</span>
              </div>
            </div>}
        </div>
      </div>
      {}
      {description && <div style={{
    fontSize: "12px",
    color: "var(--tc-text-dim)",
    lineHeight: "1.5",
    overflow: "hidden",
    display: "-webkit-box",
    WebkitLineClamp: 3,
    WebkitBoxOrient: "vertical",
    paddingLeft: "0"
  }}>
          {description}
        </div>}
      {}
      <div style={{
    fontSize: "12px",
    color: "var(--tc-text-dimmest)",
    paddingLeft: "0"
  }}>
        {statusLabels[status] || status} • {timestamp}
      </div>
    </div>;
};

## 팀원과 함께 빌드하기

Replit에서 여러 사람이 동시에 같은 프로젝트에서 작업할 수 있습니다. 각자 Agent와 새 계획 스레드를 시작할 수 있고, 모든 사람의 작업이 공유 보드에 표시됩니다 — 팀이 번갈아가며 작업하는 대신 함께 빌드할 수 있습니다.

## 팀원과 함께 빌드 시작하기

### 팀원 초대하기

1. 프로젝트를 여세요
2. **Invite**를 클릭하거나 프로젝트 링크를 공유하세요
3. 팀원이 참여하여 Agent와 새 스레드를 시작합니다

<Frame>
  <video autoPlay muted loop playsInline style={{ width: '100%', borderRadius: '8px' }}>
    <source src="https://mintcdn.com/replit/L22mbBMLs80H8_c8/images/replitai/teamwork-invite.mp4?fit=max&auto=format&n=L22mbBMLs80H8_c8&q=85&s=8e86729d83e092ac9cfe88a0a9a48616" type="video/mp4" data-path="images/replitai/teamwork-invite.mp4" />
  </video>
</Frame>

### 병렬로 작업하기

각자 새 스레드를 만들 수 있어 서로를 방해하지 않고 독립적으로 작업할 수 있습니다. 모든 사람의 작업이 공유 Kanban 보드에 표시되어 누가 무엇을 하고 있는지 쉽게 파악하고 작업이 겹치는 것을 방지할 수 있습니다.

<Note>
  Core에서는 프로젝트 전체에서 한 번에 1개의 백그라운드 작업만 실행할 수 있습니다. Pro에서는 최대 10개의 작업을 병렬로 실행할 수 있어 여러 사람이 동시에 Agent가 프로젝트의 다른 부분을 빌드하게 할 수 있습니다.
</Note>

## 공유 작업 보드

팀이 함께 작업하면 모든 사람의 작업이 같은 보드에 표시됩니다. 누가 무엇을 하는지, 무엇이 진행 중인지, 무엇이 적용 준비가 되었는지 한눈에 볼 수 있습니다.

<Frame>
  <img src="https://mintcdn.com/replit/L22mbBMLs80H8_c8/images/replitai/task-board-progress.png?fit=max&auto=format&n=L22mbBMLs80H8_c8&q=85&s=3b4ef2fbede2d057e40c138956ccd74d" alt="Drafts, Active, Ready, Done 컬럼으로 구성된 작업 보드" width="3456" height="1984" data-path="images/replitai/task-board-progress.png" />
</Frame>

각 작업은 네 단계를 거칩니다:

<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: '20px', margin: '20px 0' }}>
  <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
    <TaskCard title="Add gift flow for family and friends" description="Let parents create a story for someone else's child and send it as a gift." status="draft-static" timestamp="2m" width="100%" />

    <span style={{ fontSize: '13px', color: 'var(--kb-text-dimmer, #666)', padding: '0 4px' }}><strong>Draft</strong> — Agent가 이 작업을 제안했습니다. 검토할 준비가 된 계획이 있습니다.</span>
  </div>

  <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
    <TaskCard title="Story continuations — sequels" description="Adding a 'Continue the Adventure' button generates a sequel that picks up where the last one left off." status="active" timestamp="12m" width="100%" />

    <span style={{ fontSize: '13px', color: 'var(--kb-text-dimmer, #666)', padding: '0 4px' }}><strong>Active</strong> — Agent가 지금 프로젝트의 격리된 사본에서 이 작업을 빌드하고 있습니다.</span>
  </div>

  <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
    <TaskCard title="Bedtime mode with dark theme" description="A dedicated bedtime mode with a warm-toned dark theme and ambient sounds." status="ready-drop" timestamp="35m" width="100%" />

    <span style={{ fontSize: '13px', color: 'var(--kb-text-dimmer, #666)', padding: '0 4px' }}><strong>Ready</strong> — 작업이 완료되었습니다. 변경사항을 검토하고 메인 버전에 적용하세요.</span>
  </div>

  <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
    <TaskCard title="Read-aloud with word highlighting" description="Text-to-speech lets kids enjoy stories independently while following along with highlighted words." status="done" timestamp="4h" width="100%" />

    <span style={{ fontSize: '13px', color: 'var(--kb-text-dimmer, #666)', padding: '0 4px' }}><strong>Done</strong> — 이 작업이 완료되었고 변경사항이 메인 버전에 포함되었습니다.</span>
  </div>
</div>

### 팀이 보드를 활용하는 방법

여러 사람이 동시에 빌드하면 각자의 작업이 공유 보드에 표시됩니다. 한 팀원은 속편과 스토리 재생성을 작업하고, 다른 팀원은 선물 흐름을 담당하며, 나머지 팀원의 완료된 작업은 Ready와 Done에 표시됩니다.

<KanbanColumns
  columns={[
{ title: 'Active', count: 3, cards: [
{ title: 'Story continuations — sequels', description: 'Adding a "Continue the Adventure" button generates a sequel that picks up where the last one left off.', status: 'active', timestamp: '12m' },
{ title: 'Story regeneration — redo pages', description: 'Adding the ability to regenerate individual pages gives parents control and reduces frustration.', status: 'active', timestamp: '8m' },
{ title: 'Add gift flow for family and friends', description: 'Let parents create a story for someone else\'s child and send it as a gift.', status: 'active-queued', timestamp: '1m' },
] },
{ title: 'Ready', count: 2, cards: [
{ title: 'Bedtime reading mode with dark theme', description: 'A dedicated bedtime mode with a warm-toned dark theme and ambient sounds.', status: 'ready-drop', timestamp: '35m', showMenu: true },
{ title: 'Shareable story links with OG previews', description: 'Adding public shareable links is the single biggest growth lever.', status: 'ready-drop', timestamp: '31m', showMenu: true },
] },
{ title: 'Done', count: 3, cards: [
{ title: 'Read-aloud mode with word highlighting', description: 'Text-to-speech lets kids enjoy stories independently.', status: 'done', timestamp: '4h' },
{ title: 'Enhanced character customization', description: 'Appearance options for truly personalized illustrations.', status: 'done', timestamp: '4h' },
{ title: 'Mobile responsiveness & production polish', description: 'Better error states, mobile layouts, and loading animations.', status: 'done', timestamp: '4h' },
] }
]}
/>

작업이 Ready로 이동하면 팀 누구든 변경사항을 검토하고 적용할 수 있습니다. 여러 작업이 적용될 때 Agent가 자동으로 충돌을 처리합니다.

## 함께 빌드할 수 있는 것들

* **팀 전체에 기능 나누기**: 한 사람은 Agent와의 채팅을 통해 프론트엔드를 빌드하고 다른 사람은 백엔드를 담당합니다. Kanban 보드가 모든 사람을 정렬된 상태로 유지합니다.
* **새 팀원 온보딩**: 자체 Agent 채팅을 통해 프로젝트를 탐색하고, 작동 방식에 대해 질문하고, 다른 사람을 차단하지 않고 기여를 시작할 수 있습니다.
* **Pro에서 더 빠르게 출시하기**: 병렬 실행으로 두세 사람이 각각 동시에 실행되는 작업을 배정할 수 있습니다. 한 사람이 한 세션 내내 걸릴 프로젝트를 훨씬 짧은 시간에 완료할 수 있습니다.

## 자주 묻는 질문

<Accordion title="Agent를 통해 팀원에게 메시지를 보낼 수 있나요?">
  아니요. Agent는 메시징 도구가 아닙니다 — 항상 듣고 있어 입력하는 모든 것에 반응하려 합니다. 팀 소통에는 Slack, Discord, 또는 다른 도구를 사용하세요.
</Accordion>

<Accordion title="서로의 작업을 덮어쓸 수 있나요?">
  작업 시스템은 각 작업에 격리된 사본을 제공하여 이를 방지하는 데 도움이 되지만, 다른 작업이 무엇이 있는지 인식합니다. 작업이 완료되면 메인 버전에 다시 적용하고 해당 단계에서 충돌을 해결합니다.
</Accordion>

<Accordion title="팀원이 무엇을 하는지 어떻게 알 수 있나요?">
  Kanban 보드에서 활성 작업을 확인하세요. 커서 프레즌스와 팀원이 어떤 파일을 보고 있는지도 볼 수 있습니다.
</Accordion>

<Accordion title="프로젝트에서 누군가를 제거할 수 있나요?">
  예. 프로젝트 소유자는 언제든지 팀원을 관리하고 접근 권한을 철회할 수 있습니다.
</Accordion>

## 이용 가능 여부

| 기능          | Core | Pro |
| ----------- | ---- | --- |
| 협업 시트       | 5    | 15  |
| 동시 채팅       | ✅    | ✅   |
| 공유 Kanban   | ✅    | ✅   |
| 활성 백그라운드 작업 | 1    | 10  |
