ConusAI vendor v0.1.0-dev.20261027+9b1131fb
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-dev.20261027+9b1131fb
2026-10-27 18:05:17 +00:00
commit 3102273ec7
538 changed files with 80203 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
"use client"
// The notes page's way into the feedback survey (feedback.guide.ts): a button that asks the guides
// module to start `notes.feedback` through the shared surface coordinator (`guides.start`, joint
// contract §12), the same command the support chat's help offers send. It shows only while the guides
// module serves that command, so an app without guides renders nothing here; `kit template:prune
// --without guides` removes it with this folder.
import type { SurfaceCoordinator } from "@conusai-kit/core"
import { useSurfaceCoordinator } from "@conusai-kit/ui"
import { type ReactElement, useState, useSyncExternalStore } from "react"
import type { FeedbackButtonLabels } from "./feedback-labels.js"
const never = () => () => undefined
export function FeedbackButton({ labels }: { labels: FeedbackButtonLabels }): ReactElement | null {
const coordinator = useSurfaceCoordinator<SurfaceCoordinator>()
const served = useSyncExternalStore(
coordinator ? (listener) => coordinator.subscribe(listener) : never,
() => coordinator?.serves("guides.start") ?? false,
() => false,
)
const [unavailable, setUnavailable] = useState(false)
if (!coordinator || !served) return null
const start = async () => {
const result = await coordinator.dispatch({
version: 1,
requestId: globalThis.crypto.randomUUID(),
type: "guides.start",
guideKey: "notes.feedback",
})
setUnavailable(result.status !== "ok")
}
return (
<p>
<button type="button" onClick={() => void start()}>
{labels.open}
</button>{" "}
<span role="status">{unavailable ? labels.unavailable : ""}</span>
</p>
)
}
+16
View File
@@ -0,0 +1,16 @@
// The feedback button's labels, resolved on the server: the notes page is a server component and
// cannot call a function of feedback-button.tsx, which is a client module (Next refuses it with
// "Attempted to call feedbackLabels() from the server"). `kit template:prune --without guides`
// removes it with this folder.
export interface FeedbackButtonLabels {
readonly open: string
/** Said when the survey cannot start: already answered, or not served to this person. */
readonly unavailable: string
}
type Translate = (key: string) => string
export function feedbackLabels(t: Translate): FeedbackButtonLabels {
return { open: t("feedback.open"), unavailable: t("feedback.unavailable") }
}
+69
View File
@@ -0,0 +1,69 @@
// The example code survey of the template (ADR-046): a short product-feedback questionnaire about
// notes, published at deploy with the module's other code guides. It starts only when the person asks
// for it -- the "Give feedback" button of the notes page (feedback-button.tsx) or the help offers of
// another module (the support chat lists it by its title) -- and runs once per person.
//
// Answers are stored per guide version on the platform and read in the console (Guides, the survey,
// Results); the free-text question shows a fixed hint not to enter personal data. The optional
// `screenshot` question takes up to three images or PDFs, each up to the project's survey file size
// (`max_file_size_mb: null`: data_policy.survey_file_max_mb, 10 MB unless configured), with the fixed
// hint that files must not contain personal data such as identity documents; the files go through
// this app's own server (guides.runtime.uploadFile) to the project's Blob store, and the console's
// Results list them. The project's guide settings must allow the `survey` kind (console, Guides,
// Settings) before it is served.
import { defineGuide } from "@conusai-kit/guides"
import { guideText } from "./messages.js"
const t = guideText.survey
export const feedbackSurvey = defineGuide({
key: "notes.feedback",
schema_version: 1,
kind: "survey",
default_locale: "en",
audience: { trait: { name: "role", in: ["owner", "admin", "member"] } },
trigger: { manual: true },
frequency: "once",
survey: {
title: t.title,
intro: t.intro,
questions: [
{
type: "rating",
id: "ease",
prompt: t.ease,
required: true,
scale: "1-5",
low_label: t.easeLow,
high_label: t.easeHigh,
},
{
type: "single_choice",
id: "use",
prompt: t.use,
required: false,
options: [
{ id: "meetings", label: t.useMeetings },
{ id: "ideas", label: t.useIdeas },
{ id: "tasks", label: t.useTasks },
],
allow_other: true,
},
{ type: "text", id: "improve", prompt: t.improve, required: false, multiline: true },
{
type: "file",
id: "screenshot",
prompt: t.screenshot,
required: false,
accept: ["image", "pdf"],
max_files: 3,
max_file_size_mb: null,
},
],
submit_label: null,
thank_you: t.thankYou,
allow_dismiss: true,
one_question_per_screen: true,
},
})
+50
View File
@@ -0,0 +1,50 @@
// The example code tour of the template (in-app guides spec §7.3): defined next to the module it
// explains, published to the deployment's environment by the deploy step, and run from memory with
// `pnpm dev` (the fake platform). Its steps point at the `data-guide` anchors of ui/pages.tsx and
// ui/note-form.tsx; `kit check` warns when one of them is removed.
//
// The audience is a trait the app sends (`role`, from the guides module): a viewer cannot add a note,
// so the tour is for the members who can. The platform compares only the traits the project's guide
// settings allow-list, so allow `role` there (console, Guides, Settings) before the tour shows.
import { defineGuide } from "@conusai-kit/guides"
import { guideText } from "./messages.js"
const t = guideText.tour
export const firstNoteTour = defineGuide({
key: "notes.first-note",
schema_version: 1,
kind: "tour",
default_locale: "en",
audience: { trait: { name: "role", in: ["owner", "admin", "member"] } },
trigger: { page: "/:org/:workspace/:project/notes" },
frequency: "once",
steps: [
{
id: "new",
type: "tooltip",
target: { anchor: "notes.new-note" },
title: t.newTitle,
body: t.newBody,
advance: { on: "next_button" },
},
{
id: "title",
type: "tooltip",
target: { anchor: "notes.note-title" },
title: t.titleTitle,
body: t.titleBody,
advance: { on: "next_button" },
},
{
id: "list",
type: "tooltip",
target: { anchor: "notes.note-list" },
title: t.listTitle,
body: t.listBody,
advance: { on: "next_button" },
},
{ id: "done", type: "modal", title: t.doneTitle, body: t.doneBody },
],
})
@@ -0,0 +1,29 @@
// The example checklist of the template: one task completes with the tour, one when the app reports
// `note_created` (the note form emits it, `emitAppEvent` of @conusai-kit/ui, once the note is saved),
// and one the person ticks.
import { defineGuide } from "@conusai-kit/guides"
import { guideText } from "./messages.js"
const t = guideText.checklist
export const gettingStartedChecklist = defineGuide({
key: "notes.getting-started",
schema_version: 1,
kind: "checklist",
default_locale: "en",
audience: { trait: { name: "role", in: ["owner", "admin", "member"] } },
trigger: { page: "/:org/:workspace/:project/notes" },
frequency: "until_completed",
title: t.title,
tasks: [
{
id: "tour",
title: t.tour,
guide: "notes.first-note",
condition: { type: "guide_completed", guide: "notes.first-note" },
},
{ id: "create", title: t.create, condition: { type: "event", name: "note_created" } },
{ id: "share", title: t.share, condition: { type: "manual" } },
],
})
+14
View File
@@ -0,0 +1,14 @@
// What the notes module contributes to the guides module: its code guides, published at deploy. The
// guides module is opt-in: `kit template:prune --without guides` removes this folder and the lines
// marked `kit:guides` that import it.
//
// A module contributes audience traits the same way, to `guidesTraits` ({ key, resolve }); the
// guides module itself contributes `role`, which the guides below use.
import { contribute } from "@conusai-kit/core"
import { guidesCode } from "@conusai-kit/guides"
import { feedbackSurvey } from "./feedback.guide.js"
import { firstNoteTour } from "./first-note.guide.js"
import { gettingStartedChecklist } from "./getting-started.guide.js"
export const NOTES_GUIDES = [contribute(guidesCode, [firstNoteTour, gettingStartedChecklist, feedbackSurvey])]
+55
View File
@@ -0,0 +1,55 @@
// The words of the notes module's guides, in both languages (spec §7.3: a guide's strings come from
// the module's catalogue). A guide's text is per locale, so each message here is one object of both.
const both = (en: string, lt: string) => ({ en, lt })
export const guideText = {
tour: {
newTitle: both("Write a note", "Parašykite pastabą"),
newBody: both(
"Give the note a title and some text, then add it. Notes belong to this project.",
"Suteikite pastabai pavadinimą ir tekstą, tada pridėkite. Pastabos priklauso šiam projektui.",
),
titleTitle: both("A short title", "Trumpas pavadinimas"),
titleBody: both(
"The title is what the list shows. Up to 200 characters.",
"Pavadinimą rodo sąrašas. Iki 200 simbolių.",
),
listTitle: both("Your notes", "Jūsų pastabos"),
listBody: both(
"Every note of the project is here. Open one to edit it or attach files.",
"Čia yra visos projekto pastabos. Atidarykite pastabą, kad ją redaguotumėte ar pridėtumėte failų.",
),
doneTitle: both("That is all", "Tai viskas"),
doneBody: both(
"You can run this tour again from the help menu.",
"Šį turą galite paleisti dar kartą iš pagalbos meniu.",
),
},
checklist: {
title: both("Get started with notes", "Pradėkite naudotis pastabomis"),
tour: both("Take the notes tour", "Peržiūrėkite pastabų turą"),
create: both("Add your first note", "Pridėkite pirmąją pastabą"),
share: both("Tell your team where the notes are", "Praneškite komandai, kur yra pastabos"),
},
survey: {
title: both("How are notes working for you?", "Kaip jums sekasi su pastabomis?"),
intro: both(
"Four short questions help us improve notes. It takes about a minute.",
"Keturi trumpi klausimai padės mums patobulinti pastabas. Tai užtruks apie minutę.",
),
ease: both("How easy are notes to use?", "Kiek lengva naudotis pastabomis?"),
easeLow: both("Very hard", "Labai sunku"),
easeHigh: both("Very easy", "Labai lengva"),
use: both("What do you mostly use notes for?", "Kam dažniausiai naudojate pastabas?"),
useMeetings: both("Meeting notes", "Susitikimų užrašams"),
useIdeas: both("Ideas", "Idėjoms"),
useTasks: both("Tasks and to-dos", "Užduotims"),
improve: both("What would make notes better for you?", "Kas padarytų pastabas jums geresnes?"),
screenshot: both(
"Attach a screenshot or a PDF of what got in your way, if you like.",
"Jei norite, pridėkite ekrano kopiją ar PDF failą su tuo, kas jums trukdė.",
),
thankYou: both("Thank you. We read every answer.", "Ačiū. Perskaitome kiekvieną atsakymą."),
},
}