mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-06-27 16:10:30 +08:00
Merge branch 'main' of github.com:LAION-AI/Open-Chat-GPT
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# Train using supervised examples
|
||||
|
||||
Requirements
|
||||
|
||||
```
|
||||
wandb
|
||||
evaluate
|
||||
datasets
|
||||
transformers
|
||||
torch
|
||||
```
|
||||
|
||||
Start training reward model
|
||||
|
||||
```bash
|
||||
python trainer.py --configs defaults galactica-125
|
||||
```
|
||||
|
||||
## Dataset
|
||||
|
||||
For now we only support webgpt and summary dataset from OpenAI. Once
|
||||
open-asisstant dataset are available it will be added here.
|
||||
|
||||
## Model
|
||||
|
||||
TBD
|
||||
|
||||
## Results
|
||||
|
||||
Experimental results in wandb
|
||||
[here](https://wandb.ai/sanagnos/supervised-finetuning?workspace=user-sanagnos).
|
||||
|
||||
## TODOS
|
||||
|
||||
- decide on a model
|
||||
- add special token to declare prompt and reply. Do nto freeze the weights for
|
||||
these
|
||||
- Merge utils etc with reward model
|
||||
@@ -0,0 +1,37 @@
|
||||
defaults:
|
||||
learning_rate: 1e-5
|
||||
gradient_checkpointing: false
|
||||
gradient_accumulation_steps: 32
|
||||
per_device_train_batch_size: 2
|
||||
per_device_eval_batch_size: 2
|
||||
weight_decay: 0.00
|
||||
warmup_steps: 600
|
||||
eval_steps: 200
|
||||
save_steps: 500
|
||||
max_length: 512
|
||||
num_train_epochs: 3
|
||||
logging_steps: 10
|
||||
max_grad_norm: 2.0
|
||||
save_total_limit: 4
|
||||
eval_accumulation_steps:
|
||||
freeze_layer:
|
||||
datasets:
|
||||
- webgpt
|
||||
cache_dir: ~/.cache
|
||||
loss_fn: CrossEntropyLoss
|
||||
eval_size:
|
||||
log_dir: "base"
|
||||
|
||||
galactica-125:
|
||||
learning_rate: 5e-5
|
||||
model_name: facebook/galactica-125m
|
||||
weight_decay: 0.01
|
||||
warmup_steps: 600
|
||||
gradient_checkpointing: false
|
||||
gradient_accumulation_steps: 2
|
||||
per_device_train_batch_size: 4
|
||||
per_device_eval_batch_size: 4
|
||||
|
||||
debug:
|
||||
eval_steps: 20
|
||||
eval_size: 100
|
||||
@@ -0,0 +1,67 @@
|
||||
from datasets import load_dataset
|
||||
from sklearn.model_selection import train_test_split
|
||||
from torch.utils.data import Dataset, Subset
|
||||
|
||||
|
||||
class SquadV2Dataset(Dataset):
|
||||
def __init__(self, cache_dir, split):
|
||||
self.dataset = load_dataset("squad_v2", cache_dir=cache_dir, split=split)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.dataset)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
data = self.dataset[idx]
|
||||
# dummy return first answer
|
||||
return "".join([data["title"], ". ", data["context"], " " + data["question"]]), data["answers"]["text"][0]
|
||||
|
||||
|
||||
class WebGPT(Dataset):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
dataset = load_dataset("openai/webgpt_comparisons")
|
||||
questions = {}
|
||||
# using prompt as our index will allows us
|
||||
# to add additional generated prompt later
|
||||
self.index2question = {}
|
||||
for row in dataset["train"]:
|
||||
question = row["question"]["full_text"]
|
||||
if question not in self.index2question:
|
||||
self.index2question[len(self.index2question)] = question
|
||||
|
||||
# only keep the best answer
|
||||
questions[question] = row["answer_0" if row["score_0"] > row["score_1"] else "answer_1"]
|
||||
|
||||
self.questions = questions
|
||||
|
||||
def __len__(self):
|
||||
return len(self.index2question)
|
||||
|
||||
def __getitem__(self, index):
|
||||
question = self.index2question[index]
|
||||
answer = self.questions[question]
|
||||
return [question, answer]
|
||||
|
||||
|
||||
def train_val_dataset(dataset, val_split=0.2):
|
||||
train_idx, val_idx = train_test_split(
|
||||
list(range(len(dataset))), test_size=val_split, random_state=666, shuffle=True
|
||||
)
|
||||
return Subset(dataset, train_idx), Subset(dataset, val_idx)
|
||||
|
||||
|
||||
def get_one_dataset(conf, dataset_name):
|
||||
dataset_name = dataset_name.lower()
|
||||
|
||||
if dataset_name == "squadv2":
|
||||
raise ValueError("SquadV2 is not diverse enough for generation .. ")
|
||||
train = SquadV2Dataset(conf.cache_dir, "train")
|
||||
eval = SquadV2Dataset(conf.cache_dir, "validation")
|
||||
elif dataset_name == "webgpt":
|
||||
dataset = WebGPT()
|
||||
train, eval = train_val_dataset(dataset, val_split=0.2)
|
||||
else:
|
||||
raise ValueError(f"Unknown dataset {dataset_name}")
|
||||
|
||||
return train, eval
|
||||
@@ -0,0 +1,85 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, Union
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
from torch.nn import functional as F
|
||||
from transformers.tokenization_utils_base import PaddingStrategy, PreTrainedTokenizerBase
|
||||
|
||||
|
||||
@dataclass
|
||||
class DialogueDataCollator:
|
||||
"""
|
||||
Expects a list of texts corresponding to a sequence of [question, answer, question, answer, ...] pairs.
|
||||
"""
|
||||
|
||||
tokenizer: PreTrainedTokenizerBase
|
||||
padding: Union[bool, str, PaddingStrategy] = True
|
||||
max_length: Optional[int] = None
|
||||
pad_to_multiple_of: Optional[int] = None
|
||||
|
||||
def __call__(self, features):
|
||||
# TODO add special tokens for question and answer here
|
||||
# additional_special_tokens = ['<question>', '<answer>']
|
||||
prompt_tokens = ["Question: ", "Answer: "]
|
||||
|
||||
flatten_messages = []
|
||||
label_masks = []
|
||||
|
||||
for messages in features:
|
||||
assert len(messages) % 2 == 0, "Number of messages must be even"
|
||||
messages = [
|
||||
(prompt_tokens[0] if i % 2 == 0 else "") + x + ((" " + prompt_tokens[1]) if i % 2 == 0 else "")
|
||||
for i, x in enumerate(messages)
|
||||
]
|
||||
|
||||
# Add a way for the model to terminate generation, reinitialize prompter
|
||||
messages.append(prompt_tokens[0])
|
||||
|
||||
flatten_messages.append(
|
||||
self.tokenizer(
|
||||
"".join(messages),
|
||||
truncation=True,
|
||||
max_length=self.max_length,
|
||||
return_offsets_mapping=True,
|
||||
)
|
||||
)
|
||||
|
||||
message_change_indices = np.cumsum([len(x) for x in messages[:-1]])
|
||||
# for each token an integer indicating the index of the message it belongs to. Just to create the label mask.
|
||||
# TEXT: Question: Hello, how are you? Answer: I am fine. Question: What is your name? Answer: My name is John.
|
||||
# MESSAGE_INDICES: 0 0 0 0 0 0 1 1 1 2 2 2 2 2 2 3 3 3 3
|
||||
|
||||
# If no result in next, we are predicting the last termination token(s)
|
||||
message_indices = list(
|
||||
map(
|
||||
lambda x: next((i for i, val in enumerate(message_change_indices) if val >= x), -2),
|
||||
list(map(lambda x: x[1], flatten_messages[-1]["offset_mapping"])),
|
||||
)
|
||||
)
|
||||
label_mask = np.roll(list(map(lambda x: x % 2 == 1, message_indices)), -1, -1)
|
||||
try:
|
||||
label_mask[[i for i in range(len(message_indices)) if message_indices[i] == -2][0] - 1] = True
|
||||
except IndexError:
|
||||
# an aftermath of padding
|
||||
pass
|
||||
|
||||
label_masks.append(label_mask)
|
||||
flatten_messages[-1].pop("offset_mapping")
|
||||
|
||||
batch = self.tokenizer.pad(
|
||||
flatten_messages,
|
||||
padding=self.padding,
|
||||
max_length=self.max_length,
|
||||
pad_to_multiple_of=self.pad_to_multiple_of,
|
||||
return_tensors="pt",
|
||||
)
|
||||
dim = batch["input_ids"].shape[-1]
|
||||
|
||||
batch["label_masks"] = torch.stack([F.pad(torch.tensor(x), (0, dim - len(x))) for x in label_masks])
|
||||
|
||||
for k in list(batch.keys()):
|
||||
if k not in ["input_ids", "attention_mask", "label_masks"]:
|
||||
batch.pop(k)
|
||||
|
||||
return batch
|
||||
@@ -0,0 +1,15 @@
|
||||
from torch import nn
|
||||
|
||||
|
||||
class CrossEntropyLoss(nn.CrossEntropyLoss):
|
||||
def __init__(self, weight=None, size_average=None, ignore_index=-100, reduce=None, reduction="mean"):
|
||||
super(CrossEntropyLoss, self).__init__(weight, size_average, ignore_index, reduce, reduction)
|
||||
|
||||
def forward(self, input, target, mask=None):
|
||||
if mask is not None:
|
||||
mask = mask.view(-1)
|
||||
input = input.view(-1, input.size(-1))
|
||||
target = target.view(-1)
|
||||
input = input[mask]
|
||||
target = target[mask]
|
||||
return super(CrossEntropyLoss, self).forward(input, target)
|
||||
@@ -0,0 +1,200 @@
|
||||
import argparse
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from distutils.util import strtobool
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import torch
|
||||
from torch import nn
|
||||
from torch.utils.data import Dataset
|
||||
from transformers import (
|
||||
DataCollator,
|
||||
EvalPrediction,
|
||||
PreTrainedModel,
|
||||
PreTrainedTokenizerBase,
|
||||
Trainer,
|
||||
TrainerCallback,
|
||||
TrainingArguments,
|
||||
get_cosine_schedule_with_warmup,
|
||||
)
|
||||
from utils import get_dataset, get_loss, get_model, get_tokenizer, read_yamls
|
||||
|
||||
os.environ["WANDB_PROJECT"] = "supervised-finetuning"
|
||||
|
||||
|
||||
@dataclass
|
||||
class CustomTrainingArguments(TrainingArguments):
|
||||
loss_function: str = "CrossEntropyLoss"
|
||||
|
||||
|
||||
def compute_metrics(eval_pred):
|
||||
pred_ids = eval_pred.predictions
|
||||
labels = eval_pred.label_ids
|
||||
|
||||
return {"accuracy": (pred_ids[labels > 0] == labels[labels > 0]).mean()}
|
||||
|
||||
|
||||
def preprocess_logits_for_metrics(logits, labels):
|
||||
pred_ids = torch.argmax(logits, dim=-1)
|
||||
return pred_ids
|
||||
|
||||
|
||||
class SFTTrainer(Trainer):
|
||||
def __init__(
|
||||
self,
|
||||
model: Union[PreTrainedModel, nn.Module] = None,
|
||||
args: TrainingArguments = None,
|
||||
data_collator: Optional[DataCollator] = None,
|
||||
train_dataset: Optional[Dataset] = None,
|
||||
eval_dataset: Optional[Dataset] = None,
|
||||
tokenizer: Optional[PreTrainedTokenizerBase] = None,
|
||||
model_init: Callable[[], PreTrainedModel] = None,
|
||||
compute_metrics: Optional[Callable[[EvalPrediction], Dict]] = None,
|
||||
callbacks: Optional[List[TrainerCallback]] = None,
|
||||
optimizers: Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = (None, None),
|
||||
preprocess_logits_for_metrics: Callable[[torch.Tensor, torch.Tensor], torch.Tensor] = None,
|
||||
):
|
||||
super().__init__(
|
||||
model,
|
||||
args,
|
||||
data_collator,
|
||||
train_dataset,
|
||||
eval_dataset,
|
||||
tokenizer,
|
||||
model_init,
|
||||
compute_metrics,
|
||||
callbacks,
|
||||
optimizers,
|
||||
preprocess_logits_for_metrics,
|
||||
)
|
||||
self.loss_fct = get_loss(args.loss_function)
|
||||
|
||||
def fetch_scheduler(self):
|
||||
return get_cosine_schedule_with_warmup(
|
||||
self.optimizer,
|
||||
num_warmup_steps=self.args.warmup_steps,
|
||||
num_training_steps=self.num_train_steps,
|
||||
num_cycles=1,
|
||||
last_epoch=-1,
|
||||
)
|
||||
|
||||
def compute_loss(self, model, inputs, return_outputs=False):
|
||||
labels_mask = inputs.pop("label_masks")
|
||||
|
||||
outputs = model(**inputs)
|
||||
|
||||
loss = self.loss_fct(outputs.get("logits"), torch.roll(inputs["input_ids"], -1, -1), mask=labels_mask)
|
||||
|
||||
return (loss, outputs) if return_outputs else loss
|
||||
|
||||
def _compute_loss(self, model, inputs):
|
||||
|
||||
labels_mask = inputs.pop("label_masks")
|
||||
|
||||
inputs = self._prepare_inputs(inputs)
|
||||
|
||||
outputs = model(**inputs)
|
||||
|
||||
logits = outputs.get("logits")
|
||||
|
||||
targets = torch.roll(inputs["input_ids"], -1, -1)
|
||||
loss = self.loss_fct(outputs.get("logits"), targets, mask=labels_mask)
|
||||
|
||||
return loss, logits, targets, labels_mask
|
||||
|
||||
def prediction_step(
|
||||
self,
|
||||
model: nn.Module,
|
||||
inputs: Dict[str, Union[torch.Tensor, Any]],
|
||||
prediction_loss_only: bool,
|
||||
ignore_keys: Optional[List[str]] = None,
|
||||
) -> Tuple[Optional[torch.Tensor], Optional[torch.Tensor], Optional[torch.Tensor]]:
|
||||
|
||||
with torch.no_grad():
|
||||
loss, logits, labels, labels_mask = self._compute_loss(model, inputs)
|
||||
labels[~labels_mask] = -1
|
||||
|
||||
loss = loss.mean().detach()
|
||||
|
||||
if self.args.prediction_loss_only:
|
||||
return (loss, None, None)
|
||||
|
||||
return (loss, logits, labels)
|
||||
|
||||
|
||||
def _strtobool(x):
|
||||
return bool(strtobool(x))
|
||||
|
||||
|
||||
def argument_parsing(notebook=False, notebook_args=None):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--configs", nargs="+", required=True)
|
||||
|
||||
if notebook:
|
||||
args, remaining = parser.parse_known_args(notebook_args)
|
||||
else:
|
||||
args, remaining = parser.parse_known_args()
|
||||
|
||||
# Config from YAML
|
||||
conf = {}
|
||||
configs = read_yamls("./configs")
|
||||
for name in args.configs:
|
||||
if "," in name:
|
||||
for n in name.split(","):
|
||||
conf.update(configs[n])
|
||||
else:
|
||||
conf.update(configs[name])
|
||||
|
||||
# Override config from command-line
|
||||
parser = argparse.ArgumentParser()
|
||||
for key, value in conf.items():
|
||||
type_ = type(value) if value is not None else str
|
||||
if type_ == bool:
|
||||
type_ = _strtobool
|
||||
parser.add_argument(f"--{key}", type=type_, default=value)
|
||||
|
||||
return parser.parse_args(remaining)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
training_conf = argument_parsing()
|
||||
|
||||
model = get_model(training_conf)
|
||||
tokenizer = get_tokenizer(training_conf)
|
||||
|
||||
train, evals, collate_fn = get_dataset(training_conf, tokenizer)
|
||||
|
||||
args = CustomTrainingArguments(
|
||||
output_dir=f"{training_conf.model_name}-{training_conf.log_dir}-finetuned",
|
||||
num_train_epochs=training_conf.num_train_epochs,
|
||||
warmup_steps=training_conf.warmup_steps,
|
||||
loss_function=training_conf.loss_fn,
|
||||
learning_rate=float(training_conf.learning_rate),
|
||||
fp16=True,
|
||||
gradient_checkpointing=training_conf.gradient_checkpointing,
|
||||
gradient_accumulation_steps=training_conf.gradient_accumulation_steps,
|
||||
per_device_train_batch_size=training_conf.per_device_train_batch_size,
|
||||
per_device_eval_batch_size=training_conf.per_device_eval_batch_size,
|
||||
weight_decay=training_conf.weight_decay,
|
||||
max_grad_norm=training_conf.max_grad_norm,
|
||||
logging_steps=training_conf.logging_steps,
|
||||
save_total_limit=training_conf.save_total_limit,
|
||||
evaluation_strategy="steps",
|
||||
eval_steps=training_conf.eval_steps,
|
||||
save_steps=training_conf.save_steps,
|
||||
eval_accumulation_steps=training_conf.eval_accumulation_steps,
|
||||
report_to="wandb",
|
||||
)
|
||||
|
||||
assert len(evals) > 0
|
||||
trainer = SFTTrainer(
|
||||
model,
|
||||
args,
|
||||
train_dataset=train,
|
||||
eval_dataset=evals,
|
||||
data_collator=collate_fn,
|
||||
tokenizer=tokenizer,
|
||||
compute_metrics=compute_metrics,
|
||||
preprocess_logits_for_metrics=preprocess_logits_for_metrics,
|
||||
)
|
||||
trainer.train()
|
||||
@@ -0,0 +1,111 @@
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
from custom_datasets import get_one_dataset
|
||||
from custom_datasets.dialogue_collator import DialogueDataCollator
|
||||
from losses import CrossEntropyLoss
|
||||
from sklearn.model_selection import train_test_split
|
||||
from torch.utils.data import ConcatDataset, Subset
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
|
||||
SUPPORTED_MODELS = ["galactica"]
|
||||
|
||||
|
||||
def get_tokenizer(conf):
|
||||
tokenizer = AutoTokenizer.from_pretrained(conf.model_name, cache_dir=conf.cache_dir)
|
||||
|
||||
if "galactica" in conf.model_name:
|
||||
tokenizer.add_special_tokens({"pad_token": "<pad>", "eos_token": "</s>"})
|
||||
|
||||
return tokenizer
|
||||
|
||||
|
||||
def get_model(conf):
|
||||
if not any([x in conf.model_name for x in SUPPORTED_MODELS]):
|
||||
raise ValueError(
|
||||
f"Model {conf.model_name} not supported. Supported models: {SUPPORTED_MODELS}. "
|
||||
"To include more make sure the masking is dne correctly... (decoder only supported for now)"
|
||||
)
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained(conf.model_name, cache_dir=conf.cache_dir)
|
||||
|
||||
if conf.freeze_layer:
|
||||
model = freeze_top_n_layers(model, conf.freeze_layer)
|
||||
|
||||
model_parameters = filter(lambda p: p.requires_grad, model.parameters())
|
||||
params = sum([p.numel() for p in model_parameters])
|
||||
print("Number of trainable parameters: {}M".format(int(params / 1e6)))
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def get_dataset(conf, tokenizer):
|
||||
train_datasets, evals = [], {}
|
||||
|
||||
for dataset_name in conf.datasets:
|
||||
train, val = get_one_dataset(conf, dataset_name)
|
||||
train_datasets.append(train)
|
||||
evals[dataset_name] = Subset(val, list(range(min(len(val), conf.eval_size)))) if conf.eval_size else val
|
||||
|
||||
train = ConcatDataset(train_datasets)
|
||||
|
||||
collate_fn = DialogueDataCollator(tokenizer, max_length=conf.max_length)
|
||||
|
||||
return train, evals, collate_fn
|
||||
|
||||
|
||||
def get_loss(loss):
|
||||
if loss == "CrossEntropyLoss":
|
||||
return CrossEntropyLoss()
|
||||
else:
|
||||
raise ValueError(f"Loss {loss} not supported")
|
||||
|
||||
|
||||
def read_yamls(dir):
|
||||
conf = {}
|
||||
no_conf = True
|
||||
|
||||
for config_file in Path(dir).glob("**/*.yaml"):
|
||||
no_conf = False
|
||||
with config_file.open("r") as f:
|
||||
conf.update(yaml.safe_load(f))
|
||||
|
||||
if no_conf:
|
||||
print(f"WARNING: No yaml files found in {dir}")
|
||||
|
||||
return conf
|
||||
|
||||
|
||||
def train_val_dataset(dataset, val_split=0.2):
|
||||
train_idx, val_idx = train_test_split(
|
||||
list(range(len(dataset))), test_size=val_split, random_state=666, shuffle=True
|
||||
)
|
||||
return Subset(dataset, train_idx), Subset(dataset, val_idx)
|
||||
|
||||
|
||||
def freeze_top_n_layers(model, target_layers):
|
||||
# its possible we can simply detect which module is a ModuleList
|
||||
# and simply freeze the module without doing string parsing
|
||||
for name, param in model.named_parameters():
|
||||
if "embed" in name:
|
||||
param.requires_grad = False
|
||||
elif ".layer" in name or ".h." in name:
|
||||
tokens = name.split(".")
|
||||
layer_ = None
|
||||
for token in tokens:
|
||||
if token.isdigit():
|
||||
layer_ = int(token)
|
||||
break
|
||||
|
||||
if layer_ is not None and layer_ < target_layers:
|
||||
# print('freeze ', layer_, name)
|
||||
param.requires_grad = False
|
||||
return model
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from transformers import AutoModelForSequenceClassification
|
||||
|
||||
model = AutoModelForSequenceClassification.from_pretrained("bigscience/bloomz-560m")
|
||||
freeze_top_n_layers(model, 10)
|
||||
print(model.state_dict().keys())
|
||||
@@ -3,7 +3,10 @@ const nextConfig = {
|
||||
output: "standalone",
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
scrollRestoration: true,
|
||||
/* Disabling this for now only because it causes a warning in the console that cannot be silenced for eslint
|
||||
If this can be resolved, we should re-enable this.
|
||||
*/
|
||||
// scrollRestoration: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Generated
+4444
-30
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@
|
||||
"@dnd-kit/core": "^6.0.6",
|
||||
"@dnd-kit/modifiers": "^6.0.1",
|
||||
"@dnd-kit/sortable": "^7.0.1",
|
||||
"@dnd-kit/utilities": "^3.2.1",
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@headlessui/react": "^1.7.7",
|
||||
@@ -39,9 +40,11 @@
|
||||
"eslint-plugin-simple-import-sort": "^8.0.0",
|
||||
"focus-visible": "^5.2.0",
|
||||
"framer-motion": "^6.5.1",
|
||||
"install": "^0.13.0",
|
||||
"next": "13.0.6",
|
||||
"next-auth": "^4.18.6",
|
||||
"nodemailer": "^6.8.0",
|
||||
"npm": "^9.2.0",
|
||||
"postcss-focus-visible": "^7.1.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
export function AuthLayout({ children }) {
|
||||
return (
|
||||
<main className="flex items-center justify-center sm:py-4 subpixel-antialiased">
|
||||
<div className="flex items-center justify-center sm:py-4 subpixel-antialiased">
|
||||
<div className="flex items-center w-full max-w-2xl flex-col px-4 sm:px-6">
|
||||
<div className="flex-auto items-center justify-center w-full py-10 px-4 sm:mx-0 sm:flex-none sm:rounded-2xl sm:p-4">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,48 @@
|
||||
import { CircleBackground } from "./CircleBackground";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import { useId } from "react";
|
||||
|
||||
import { Container } from "./Container";
|
||||
|
||||
export function CallToAction() {
|
||||
function CircleBackground({ width = 558, height = 558, ...props }) {
|
||||
const id = useId();
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const baseRingColor = colorMode === "light" ? "#777" : "#000";
|
||||
const gradStopColor = colorMode === "light" ? "#fff" : "#000";
|
||||
|
||||
return (
|
||||
<section id="join-us" className="relative overflow-hidden bg-gray-900 py-20 sm:py-28">
|
||||
<svg viewBox="0 0 558 558" width={width} height={height} fill="none" aria-hidden="true" {...props}>
|
||||
<defs>
|
||||
<linearGradient id={id} x1="79" y1="16" x2="105" y2="237" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor={gradStopColor} />
|
||||
<stop offset="1" stopColor={baseRingColor} stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
opacity=".2"
|
||||
d="M1 279C1 125.465 125.465 1 279 1s278 124.465 278 278-124.465 278-278 278S1 432.535 1 279Z"
|
||||
stroke={baseRingColor}
|
||||
/>
|
||||
<path d="M1 279C1 125.465 125.465 1 279 1" stroke={`url(#${id})`} strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function CallToAction() {
|
||||
const { colorMode } = useColorMode();
|
||||
const bgColorClass = colorMode === "light" ? "bg-gray-900" : "bg-gray-50";
|
||||
const headingColorClass = colorMode === "light" ? "text-white" : "text-black";
|
||||
const textColorClass = colorMode === "light" ? "text-gray-300" : "text-black";
|
||||
|
||||
return (
|
||||
<section id="join-us" className={`relative overflow-hidden py-20 sm:py-28 ${bgColorClass} ${textColorClass}`}>
|
||||
<div className="absolute top-1/2 left-20 -translate-y-1/2 sm:left-1/2 sm:-translate-x-1/2">
|
||||
<CircleBackground color="#fff" className="animate-spin-slower" />
|
||||
<CircleBackground className="animate-spin-slower" />
|
||||
</div>
|
||||
<Container className="relative">
|
||||
<div className="mx-auto max-w-md sm:text-center">
|
||||
<h2 className="text-3xl font-medium tracking-tight text-white sm:text-4xl">Join Us</h2>
|
||||
<p className="mt-4 text-lg text-gray-300">
|
||||
<h2 className={`text-3xl font-medium tracking-tight sm:text-4xl ${headingColorClass}`}>Join Us</h2>
|
||||
<p className="mt-4 text-lg">
|
||||
All open source projects begin with people like you. Open source is the belief that if we collaborate we can
|
||||
together gift our knowledge and technology to the world for the benefit of humanity. Are you in? Find us
|
||||
here:
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { useId } from "react";
|
||||
|
||||
export function CircleBackground({ color, width = 558, height = 558, ...props }) {
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<svg viewBox="0 0 558 558" width={width} height={height} fill="none" aria-hidden="true" {...props}>
|
||||
<defs>
|
||||
<linearGradient id={id} x1="79" y1="16" x2="105" y2="237" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor={color} />
|
||||
<stop offset="1" stopColor={color} stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
opacity=".2"
|
||||
d="M1 279C1 125.465 125.465 1 279 1s278 124.465 278 278-124.465 278-278 278S1 432.535 1 279Z"
|
||||
stroke={color}
|
||||
/>
|
||||
<path d="M1 279C1 125.465 125.465 1 279 1" stroke={`url(#${id})`} strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
|
||||
import { Container } from "./Container";
|
||||
|
||||
const faqs = [
|
||||
@@ -25,11 +27,16 @@ const faqs = [
|
||||
];
|
||||
|
||||
export function Faq() {
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
const headingColorClass = colorMode === "light" ? "text-gray-900" : "text-white";
|
||||
const textColorClass = colorMode === "light" ? "text-gray-700" : "text-gray-100";
|
||||
|
||||
return (
|
||||
<section id="faq" aria-labelledby="faqs-title" className="border-t border-gray-200 py-20 sm:py-32">
|
||||
<Container className="">
|
||||
<div className="mx-auto max-w-2xl lg:mx-0">
|
||||
<h2 id="faqs-title" className="text-3xl font-medium tracking-tight text-gray-900">
|
||||
<h2 id="faqs-title" className={`text-3xl font-medium tracking-tight ${headingColorClass}`}>
|
||||
Frequently Asked Questions
|
||||
</h2>
|
||||
{/* <p className="mt-2 text-lg text-gray-600">
|
||||
@@ -52,8 +59,8 @@ export function Faq() {
|
||||
<ul role="list" className="space-y-10">
|
||||
{column.map((faq, faqIndex) => (
|
||||
<li key={faqIndex}>
|
||||
<h3 className="text-lg font-semibold leading-6 text-gray-900">{faq.question}</h3>
|
||||
<p className="mt-4 text-sm text-gray-700">{faq.answer}</p>
|
||||
<h3 className={`text-lg font-semibold leading-6 ${headingColorClass}`}>{faq.question}</h3>
|
||||
<p className={`mt-4 text-sm ${textColorClass}`}>{faq.answer}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Flex,
|
||||
Grid,
|
||||
Popover,
|
||||
PopoverAnchor,
|
||||
PopoverArrow,
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
SliderTrack,
|
||||
Spacer,
|
||||
useBoolean,
|
||||
useId,
|
||||
} from "@chakra-ui/react";
|
||||
import { FlagIcon, QuestionMarkCircleIcon } from "@heroicons/react/20/solid";
|
||||
import { useState } from "react";
|
||||
@@ -65,58 +67,47 @@ export const FlaggableElement = (props) => {
|
||||
isLazy
|
||||
lazyBehavior="keepMounted"
|
||||
>
|
||||
<div className="inline-block float-left">
|
||||
<Grid templateColumns="1fr min-content" gap={2}>
|
||||
<PopoverAnchor>{props.children}</PopoverAnchor>
|
||||
<PopoverTrigger>
|
||||
<Button color="transparent">
|
||||
<FlagIcon
|
||||
className="h-5 w-5 ml-3 align-center text-gray-400 group-hover:text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<Button h="full">
|
||||
<FlagIcon className="w-4 text-gray-400 group-hover:text-gray-500" aria-hidden="true" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
</div>
|
||||
</Grid>
|
||||
|
||||
<PopoverContent width="fit-content">
|
||||
<PopoverArrow />
|
||||
<PopoverCloseButton />
|
||||
<div className="flex mt-3 ">
|
||||
<PopoverBody>
|
||||
<ul>
|
||||
{TEXT_LABEL_FLAGS.map((option, i) => {
|
||||
return (
|
||||
<FlagCheckboxLi
|
||||
option={option}
|
||||
key={i}
|
||||
idx={i}
|
||||
checkboxValues={checkboxValues}
|
||||
sliderValues={sliderValues}
|
||||
checkboxHandler={handleCheckboxState}
|
||||
sliderHandler={handleSliderState}
|
||||
></FlagCheckboxLi>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<div className="flex justify-center ml-auto">
|
||||
<Button
|
||||
isDisabled={
|
||||
!checkboxValues.reduce((all, current) => {
|
||||
return all | current;
|
||||
}, false)
|
||||
}
|
||||
onClick={() => submitResponse()}
|
||||
className="bg-indigo-600 text-black hover:bg-indigo-700"
|
||||
>
|
||||
Report
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverBody>
|
||||
<div className="relative h-4">
|
||||
<PopoverCloseButton />
|
||||
</div>
|
||||
<PopoverBody>
|
||||
{TEXT_LABEL_FLAGS.map((option, i) => (
|
||||
<FlagCheckbox
|
||||
option={option}
|
||||
key={i}
|
||||
idx={i}
|
||||
checkboxValues={checkboxValues}
|
||||
sliderValues={sliderValues}
|
||||
checkboxHandler={handleCheckboxState}
|
||||
sliderHandler={handleSliderState}
|
||||
/>
|
||||
))}
|
||||
<Flex justify="center">
|
||||
<Button
|
||||
isDisabled={!checkboxValues.some(Boolean)}
|
||||
onClick={submitResponse}
|
||||
className="bg-indigo-600 text-black hover:bg-indigo-700"
|
||||
>
|
||||
Report
|
||||
</Button>
|
||||
</Flex>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
function FlagCheckboxLi(props: {
|
||||
function FlagCheckbox(props: {
|
||||
option: textFlagLabels;
|
||||
idx: number;
|
||||
checkboxValues: boolean[];
|
||||
@@ -136,37 +127,35 @@ function FlagCheckboxLi(props: {
|
||||
);
|
||||
}
|
||||
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<li>
|
||||
<Flex>
|
||||
<Checkbox
|
||||
onChange={(e) => {
|
||||
props.checkboxHandler(e.target.checked, props.idx);
|
||||
}}
|
||||
/>
|
||||
<label
|
||||
className=" ml-1 mr-1 text-sm form-check-label hover:cursor-pointer"
|
||||
htmlFor={props.option.attributeName}
|
||||
>
|
||||
<span className="text-gray-800 hover:text-blue-700 float-left">{props.option.labelText}</span>
|
||||
{AdditionalExplanation}
|
||||
</label>
|
||||
<Spacer />
|
||||
<Slider
|
||||
width="100px"
|
||||
isDisabled={!props.checkboxValues[props.idx]}
|
||||
defaultValue={100}
|
||||
onChangeEnd={(val) => {
|
||||
props.sliderHandler(val / 100, props.idx);
|
||||
}}
|
||||
>
|
||||
<SliderTrack>
|
||||
<SliderFilledTrack />
|
||||
<SliderThumb />
|
||||
</SliderTrack>
|
||||
</Slider>
|
||||
</Flex>
|
||||
</li>
|
||||
<Flex gap={1}>
|
||||
<Checkbox
|
||||
id={id}
|
||||
onChange={(e) => {
|
||||
props.checkboxHandler(e.target.checked, props.idx);
|
||||
}}
|
||||
/>
|
||||
<label className="text-sm form-check-label" htmlFor={id}>
|
||||
<span className="text-gray-800 hover:text-blue-700 float-left">{props.option.labelText}</span>
|
||||
{AdditionalExplanation}
|
||||
</label>
|
||||
<Spacer />
|
||||
<Slider
|
||||
width="100px"
|
||||
isDisabled={!props.checkboxValues[props.idx]}
|
||||
defaultValue={100}
|
||||
onChangeEnd={(val) => {
|
||||
props.sliderHandler(val / 100, props.idx);
|
||||
}}
|
||||
>
|
||||
<SliderTrack>
|
||||
<SliderFilledTrack />
|
||||
<SliderThumb />
|
||||
</SliderTrack>
|
||||
</Slider>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
interface textFlagLabels {
|
||||
|
||||
@@ -1,71 +1,81 @@
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
import { Container } from "./Container";
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="border-t border-gray-200 bg-white">
|
||||
<main>
|
||||
<Container className="">
|
||||
<div className="flex flex-wrap justify-between gap-y-12 py-10 lg:items-center lg:py-16">
|
||||
<div className="flex items-center text-black pr-8">
|
||||
<Link href="/" aria-label="Home" className="flex items-center">
|
||||
<Image src="/images/logos/logo.svg" className="mx-auto object-fill" width="52" height="52" alt="logo" />
|
||||
</Link>
|
||||
const { colorMode } = useColorMode();
|
||||
const bgColorClass = colorMode === "light" ? "bg-transparent" : "bg-gray-800";
|
||||
const borderClass = colorMode === "light" ? "border-slate-200" : "border-transparent";
|
||||
|
||||
<div className="ml-2">
|
||||
<p className="text-base font-bold">Open Assistant</p>
|
||||
<p className="text-sm">Conversational AI for everyone.</p>
|
||||
return (
|
||||
<footer className={bgColorClass}>
|
||||
<div className={`flex mx-auto max-w-7xl justify-between py-10 px-10 border-t ${borderClass}`}>
|
||||
<div className="flex items-center pr-8">
|
||||
<Link href="/" aria-label="Home" className="flex items-center">
|
||||
<Image src="/images/logos/logo.svg" className="mx-auto object-fill" width="52" height="52" alt="logo" />
|
||||
</Link>
|
||||
|
||||
<div className="ml-2">
|
||||
<p className="text-base font-bold">Open Assistant</p>
|
||||
<p className="text-sm">Conversational AI for everyone.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav className="flex justify-center gap-20">
|
||||
<div className="flex flex-col text-sm leading-7">
|
||||
<b>Information</b>
|
||||
<div className="flex flex-col leading-5">
|
||||
<Link href="#" aria-label="Our Team" className="hover:underline underline-offset-2">
|
||||
Our Team
|
||||
</Link>
|
||||
<Link href="/#join-us" aria-label="Join Us" className="hover:underline underline-offset-2">
|
||||
Join Us
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<nav className="flex justify-center gap-20">
|
||||
<div className="flex flex-col text-sm leading-7">
|
||||
<b>Legal</b>
|
||||
<div className="flex flex-col leading-5">
|
||||
<Link href="/privacy-policy" aria-label="Privacy Policy" className="hover:underline underline-offset-2">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
<Link
|
||||
href="/terms-of-service"
|
||||
aria-label="Terms of Service"
|
||||
className="hover:underline underline-offset-2"
|
||||
>
|
||||
Terms of Service
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<nav className="flex justify-center gap-20">
|
||||
<div className="flex flex-col text-sm leading-7">
|
||||
<b>Legal</b>
|
||||
<div className="flex flex-col leading-5">
|
||||
<Link
|
||||
href="/privacy-policy"
|
||||
aria-label="Privacy Policy"
|
||||
className="hover:underline underline-offset-2"
|
||||
>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
<Link
|
||||
href="/terms-of-service"
|
||||
aria-label="Terms of Service"
|
||||
className="hover:underline underline-offset-2"
|
||||
>
|
||||
Terms of Service
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-col text-sm leading-7">
|
||||
<b>Connect</b>
|
||||
<div className="flex flex-col leading-5">
|
||||
<Link
|
||||
href="https://github.com/LAION-AI/Open-Assistant"
|
||||
rel="noopener noreferrer nofollow"
|
||||
target="_blank"
|
||||
aria-label="Privacy Policy"
|
||||
className="hover:underline underline-offset-2"
|
||||
>
|
||||
Github
|
||||
</Link>
|
||||
<Link
|
||||
href="https://discord.gg/pXtnYk9c"
|
||||
rel="noopener noreferrer nofollow"
|
||||
target="_blank"
|
||||
aria-label="Terms of Service"
|
||||
className="hover:underline underline-offset-2"
|
||||
>
|
||||
Discord
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-col text-sm leading-7">
|
||||
<b>Connect</b>
|
||||
<div className="flex flex-col leading-5">
|
||||
<Link
|
||||
href="https://github.com/LAION-AI/Open-Assistant"
|
||||
rel="noopener noreferrer nofollow"
|
||||
target="_blank"
|
||||
aria-label="Privacy Policy"
|
||||
className="hover:underline underline-offset-2"
|
||||
>
|
||||
Github
|
||||
</Link>
|
||||
<Link
|
||||
href="https://discord.gg/pXtnYk9c"
|
||||
rel="noopener noreferrer nofollow"
|
||||
target="_blank"
|
||||
aria-label="Terms of Service"
|
||||
className="hover:underline underline-offset-2"
|
||||
>
|
||||
Discord
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</Container>
|
||||
</main>
|
||||
</div>
|
||||
</nav>
|
||||
{/* </div> */}
|
||||
</nav>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,4 +22,15 @@ const Template = (args) => {
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = { session: { data: { user: { name: "StoryBook user" } }, status: "authenticated" }, transparent: false };
|
||||
Default.args = {
|
||||
session: {
|
||||
data: {
|
||||
user: {
|
||||
name: "StoryBook user",
|
||||
},
|
||||
},
|
||||
status: "authenticated",
|
||||
},
|
||||
transparent: false,
|
||||
borderClass: undefined,
|
||||
};
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Button } from "@chakra-ui/react";
|
||||
import { Box, Button, useColorMode } from "@chakra-ui/react";
|
||||
import { Popover } from "@headlessui/react";
|
||||
import clsx from "clsx";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { FaUser } from "react-icons/fa";
|
||||
import { Container } from "src/components/Container";
|
||||
|
||||
import { ColorModeIconToggle } from "../UI/ColorModeIconToggle";
|
||||
import { NavLinks } from "./NavLinks";
|
||||
import { UserMenu } from "./UserMenu";
|
||||
|
||||
@@ -55,63 +54,75 @@ function AccountButton() {
|
||||
}
|
||||
|
||||
export function Header(props) {
|
||||
const transparent = props.transparent ?? false;
|
||||
const { colorMode } = useColorMode();
|
||||
const borderClass = props.transparent
|
||||
? ""
|
||||
: colorMode === "light"
|
||||
? "border-b border-gray-400"
|
||||
: "border-b border-zinc-800";
|
||||
|
||||
return (
|
||||
<header className={clsx(!transparent && "bg-white")}>
|
||||
<nav>
|
||||
<Container className="relative z-10 flex justify-between py-8">
|
||||
<div className="relative z-10 flex items-center gap-16">
|
||||
<Link href="/" aria-label="Home" className="flex items-center">
|
||||
<Image src="/images/logos/logo.svg" className="mx-auto object-fill" width="50" height="50" alt="logo" />
|
||||
<span className="text-2xl font-bold ml-3">Open Assistant</span>
|
||||
</Link>
|
||||
<nav className={`oa-basic-theme ${borderClass}`}>
|
||||
<Box className="flex mx-auto max-w-7xl justify-between py-8 px-10">
|
||||
<div className="relative z-10 flex items-center gap-16">
|
||||
<Link href="/" aria-label="Home" className="flex items-center">
|
||||
<Image src="/images/logos/logo.svg" className="mx-auto object-fill" width="50" height="50" alt="logo" />
|
||||
<span className="text-2xl font-bold ml-3">Open Assistant</span>
|
||||
</Link>
|
||||
<div className="hidden lg:flex lg:gap-10">
|
||||
<NavLinks />
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<Popover className="lg:hidden">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button
|
||||
className="relative z-10 inline-flex items-center rounded-lg stroke-gray-900 p-2 hover:bg-gray-200/50 hover:stroke-gray-600 active:stroke-gray-900 [&:not(:focus-visible)]:focus:outline-none"
|
||||
aria-label="Toggle site navigation"
|
||||
>
|
||||
{({ open }) => (open ? <ChevronUpIcon className="h-6 w-6" /> : <MenuIcon className="h-6 w-6" />)}
|
||||
</Popover.Button>
|
||||
<AnimatePresence initial={false}>
|
||||
{open && (
|
||||
<>
|
||||
<Popover.Overlay
|
||||
static
|
||||
as={motion.div}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 z-1 bg-gray-300/60 backdrop-blur"
|
||||
/>
|
||||
<Popover.Panel
|
||||
static
|
||||
as={motion.div}
|
||||
initial={{ opacity: 0, y: -32 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
y: -32,
|
||||
transition: { duration: 0.2 },
|
||||
}}
|
||||
className="absolute inset-x-0 top-0 z-0 origin-top rounded-b-2xl bg-white px-6 pb-6 pt-32 shadow-2xl shadow-gray-900/20"
|
||||
>
|
||||
<div className="mt-8 flex flex-col gap-4"></div>
|
||||
</Popover.Panel>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
<AccountButton />
|
||||
<UserMenu />
|
||||
</div>
|
||||
</Container>
|
||||
</nav>
|
||||
</header>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<Popover className="lg:hidden">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button
|
||||
className="relative z-10 inline-flex items-center rounded-lg stroke-gray-900 p-2 hover:bg-gray-200/50 hover:stroke-gray-600 active:stroke-gray-900 [&:not(:focus-visible)]:focus:outline-none"
|
||||
aria-label="Toggle site navigation"
|
||||
>
|
||||
{({ open }) => (open ? <ChevronUpIcon className="h-6 w-6" /> : <MenuIcon className="h-6 w-6" />)}
|
||||
</Popover.Button>
|
||||
<AnimatePresence initial={false}>
|
||||
{open && (
|
||||
<>
|
||||
<Popover.Overlay
|
||||
static
|
||||
as={motion.div}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 z-1 bg-gray-300/60 backdrop-blur"
|
||||
/>
|
||||
<Popover.Panel
|
||||
static
|
||||
as={motion.div}
|
||||
initial={{ opacity: 0, y: -32 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
y: -32,
|
||||
transition: { duration: 0.2 },
|
||||
}}
|
||||
className="absolute inset-x-0 top-0 z-0 origin-top rounded-b-2xl bg-white px-6 pb-6 pt-32 shadow-2xl shadow-gray-900/20"
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<MobileNavLink href="/#join-us">Join Us</MobileNavLink>
|
||||
<MobileNavLink href="/#faqs">FAQs</MobileNavLink>
|
||||
</div>
|
||||
<div className="mt-8 flex flex-col gap-4"></div>
|
||||
</Popover.Panel>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
<AccountButton />
|
||||
<UserMenu />
|
||||
<ColorModeIconToggle className="ml-5" />
|
||||
</div>
|
||||
</Box>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
|
||||
export function NavLinks(): JSX.Element {
|
||||
const [hoveredIndex, setHoveredIndex] = useState(null);
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
const linkColor = colorMode === "light" ? "text-gray-700 hover:text-gray-900" : "text-gray-50 hover:text-white";
|
||||
|
||||
const hoverBgColor = colorMode === "light" ? "bg-gray-100" : "bg-gray-800";
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -14,14 +20,14 @@ export function NavLinks(): JSX.Element {
|
||||
<Link
|
||||
key={label}
|
||||
href={href}
|
||||
className="relative -my-2 -mx-3 rounded-lg px-3 py-2 text-sm text-gray-700 transition-colors delay-150 hover:text-gray-900 hover:delay-[0ms]"
|
||||
className={`${linkColor} relative -my-2 -mx-3 rounded-lg px-3 py-2 text-sm transition-colors delay-150 hover:delay-[0ms]`}
|
||||
onMouseEnter={() => setHoveredIndex(index)}
|
||||
onMouseLeave={() => setHoveredIndex(null)}
|
||||
>
|
||||
<AnimatePresence>
|
||||
{hoveredIndex === index && (
|
||||
<motion.span
|
||||
className="absolute inset-0 rounded-lg bg-gray-100"
|
||||
className={`${hoverBgColor} absolute inset-0 rounded-lg`}
|
||||
layoutId="hoverBackground"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1, transition: { duration: 0.15 } }}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Box, useColorModeValue } from "@chakra-ui/react";
|
||||
import { Popover } from "@headlessui/react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import Image from "next/image";
|
||||
@@ -7,6 +8,7 @@ import { FaCog, FaSignOutAlt } from "react-icons/fa";
|
||||
|
||||
export function UserMenu() {
|
||||
const { data: session } = useSession();
|
||||
const backgroundColor = useColorModeValue("#FFFFFF", "#000000");
|
||||
|
||||
if (!session) {
|
||||
return <></>;
|
||||
@@ -26,7 +28,7 @@ export function UserMenu() {
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button aria-label="Toggle Account Options" className="flex">
|
||||
<div className="flex items-center gap-4 p-1 lg:pr-6 rounded-full bg-white border border-slate-300/70 hover:bg-gray-200/50 transition-colors duration-300">
|
||||
<div className="flex items-center gap-4 p-1 lg:pr-6 rounded-full border border-slate-300/70 hover:bg-gray-200/50 transition-colors duration-300">
|
||||
<Image
|
||||
src="/images/temp-avatars/av1.jpg"
|
||||
alt="Profile Picture"
|
||||
@@ -41,7 +43,7 @@ export function UserMenu() {
|
||||
</Popover.Button>
|
||||
<AnimatePresence initial={false}>
|
||||
{open && (
|
||||
<>
|
||||
<Box backgroundColor={backgroundColor}>
|
||||
<Popover.Panel
|
||||
static
|
||||
as={motion.div}
|
||||
@@ -52,9 +54,9 @@ export function UserMenu() {
|
||||
y: -10,
|
||||
transition: { duration: 0.2 },
|
||||
}}
|
||||
className="absolute right-0 mt-3 w-screen max-w-xs p-4 rounded-md bg-white border border-slate-300/70"
|
||||
className="absolute right-0 mt-3 w-screen bg-inherit max-w-xs p-4 rounded-md border border-slate-300/70"
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Box className="flex flex-col gap-1">
|
||||
{accountOptions.map((item) => (
|
||||
<a
|
||||
key={item.name}
|
||||
@@ -81,9 +83,9 @@ export function UserMenu() {
|
||||
<p>Sign Out</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</Box>
|
||||
</Popover.Panel>
|
||||
</>
|
||||
</Box>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import Image from "next/image";
|
||||
import { useId } from "react";
|
||||
|
||||
@@ -6,6 +7,10 @@ import { Container } from "./Container";
|
||||
function BackgroundIllustration(props) {
|
||||
const id = useId();
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const baseRingColor = colorMode === "light" ? "#d4d4d4" : "#005a69";
|
||||
const gradStopColor = colorMode === "light" ? "#06b6d4" : "#00f2ff";
|
||||
|
||||
return (
|
||||
<div {...props}>
|
||||
<svg
|
||||
@@ -16,14 +21,14 @@ function BackgroundIllustration(props) {
|
||||
>
|
||||
<path
|
||||
d="M1025 513c0 282.77-229.23 512-512 512S1 795.77 1 513 230.23 1 513 1s512 229.23 512 512Z"
|
||||
stroke="#D4D4D4"
|
||||
stroke={baseRingColor}
|
||||
strokeOpacity="0.7"
|
||||
/>
|
||||
<path d="M513 1025C230.23 1025 1 795.77 1 513" stroke={`url(#${id}-gradient-1)`} strokeLinecap="round" />
|
||||
<defs>
|
||||
<linearGradient id={`${id}-gradient-1`} x1="1" y1="513" x2="1" y2="1025" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#06b6d4" />
|
||||
<stop offset="1" stopColor="#06b6d4" stopOpacity="0" />
|
||||
<stop stopColor={gradStopColor} />
|
||||
<stop offset="1" stopColor={gradStopColor} stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
@@ -35,14 +40,14 @@ function BackgroundIllustration(props) {
|
||||
>
|
||||
<path
|
||||
d="M913 513c0 220.914-179.086 400-400 400S113 733.914 113 513s179.086-400 400-400 400 179.086 400 400Z"
|
||||
stroke="#D4D4D4"
|
||||
stroke={baseRingColor}
|
||||
strokeOpacity="0.7"
|
||||
/>
|
||||
<path d="M913 513c0 220.914-179.086 400-400 400" stroke={`url(#${id}-gradient-2)`} strokeLinecap="round" />
|
||||
<defs>
|
||||
<linearGradient id={`${id}-gradient-2`} x1="913" y1="513" x2="913" y2="913" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#06b6d4" />
|
||||
<stop offset="1" stopColor="#06b6d4" stopOpacity="0" />
|
||||
<stop stopColor={gradStopColor} />
|
||||
<stop offset="1" stopColor={gradStopColor} stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
@@ -51,17 +56,24 @@ function BackgroundIllustration(props) {
|
||||
}
|
||||
|
||||
export function Hero() {
|
||||
const { colorMode } = useColorMode();
|
||||
const pTextColor = colorMode === "light" ? "text-gray-600" : "text-white";
|
||||
const fancyTextGradientClasses =
|
||||
colorMode === "light" ? "from-blue-600 via-sky-400 to-blue-700" : "from-blue-500 via-sky-300 to-blue-400";
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden py-20 sm:py-32 lg:pb-32 xl:pb-36">
|
||||
<Container className="">
|
||||
<div className="lg:grid lg:grid-cols-12 lg:gap-x-8 lg:gap-y-20">
|
||||
<div className="relative z-10 mx-auto max-w-2xl lg:col-span-7 lg:max-w-none lg:pt-6 xl:col-span-6">
|
||||
<h1 className="text-5xl mb-6 font-bold tracking-tight text-gray-900">Open Assistant</h1>
|
||||
<p className="mt-8 text-3xl inline bg-gradient-to-r from-indigo-600 via-sky-400 to-indigo-700 bg-clip-text font-display tracking-tight text-transparent">
|
||||
<h1 className="text-5xl mb-6 font-bold tracking-tight">Open Assistant</h1>
|
||||
<p
|
||||
className={`bg-gradient-to-r ${fancyTextGradientClasses} mt-8 text-3xl inline bg-clip-text font-display tracking-tight text-transparent`}
|
||||
>
|
||||
<b>Conversational AI for everyone.</b>
|
||||
</p>
|
||||
<p className="mt-6 text-lg text-gray-600">We believe we can create a revolution.</p>
|
||||
<p className="mt-6 text-lg text-gray-600">
|
||||
<p className={`mt-6 text-lg ${pTextColor}`}>We believe we can create a revolution.</p>
|
||||
<p className={`mt-6 text-lg ${pTextColor}`}>
|
||||
In the same way that Stable Diffusion helped the world make art and images in new ways, we want to improve
|
||||
the world by providing amazing conversational AI.
|
||||
</p>
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import { Progress } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
|
||||
export const LoadingScreen = ({ text }) => (
|
||||
<div className="bg-slate-100">
|
||||
<Progress size="xs" isIndeterminate />
|
||||
{text && (
|
||||
<div className="flex h-full">
|
||||
<div className="text-xl font-bold text-gray-800 mx-auto my-auto">{text}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
export const LoadingScreen = ({ text }) => {
|
||||
const { colorMode } = useColorMode();
|
||||
const mainClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
|
||||
|
||||
return (
|
||||
<div className={`h-full ${mainClasses}`}>
|
||||
<Progress size="sm" isIndeterminate />
|
||||
{text && (
|
||||
<div className="flex h-full">
|
||||
<div className="text-xl font-bold mx-auto my-auto">{text}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { Grid } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
|
||||
import { FlaggableElement } from "./FlaggableElement";
|
||||
|
||||
export interface Message {
|
||||
@@ -5,23 +8,29 @@ export interface Message {
|
||||
is_assistant: boolean;
|
||||
}
|
||||
|
||||
const getColor = (isAssistant: boolean) => (isAssistant ? "bg-slate-800" : "bg-sky-900");
|
||||
const getBgColor = (isAssistant: boolean, colorMode: "light" | "dark") => {
|
||||
if (colorMode === "light") {
|
||||
return isAssistant ? "bg-slate-800" : "bg-sky-900";
|
||||
} else {
|
||||
return isAssistant ? "bg-black" : "bg-sky-900";
|
||||
}
|
||||
};
|
||||
|
||||
export const Messages = ({ messages, post_id }: { messages: Message[]; post_id: string }) => {
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
const items = messages.map(({ text, is_assistant }: Message, i: number) => {
|
||||
return (
|
||||
<div className="flex" key={i + text}>
|
||||
<FlaggableElement text={text} post_id={post_id}>
|
||||
<div
|
||||
key={i + text}
|
||||
className={`${getColor(is_assistant)} p-4 my-1 rounded-xl text-white whitespace-pre-wrap float-left mr-3`}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
</FlaggableElement>
|
||||
</div>
|
||||
<FlaggableElement text={text} post_id={post_id} key={i + text}>
|
||||
<div
|
||||
key={i + text}
|
||||
className={`${getBgColor(is_assistant, colorMode)} p-4 rounded-md text-white whitespace-pre-wrap`}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
</FlaggableElement>
|
||||
);
|
||||
});
|
||||
// Maybe also show a legend of the colors?
|
||||
return <>{items}</>;
|
||||
return <Grid gap={2}>{items}</Grid>;
|
||||
};
|
||||
|
||||
@@ -2,9 +2,9 @@ import { Flex } from "@chakra-ui/react";
|
||||
import {
|
||||
closestCenter,
|
||||
DndContext,
|
||||
KeyboardSensor,
|
||||
PointerSensor,
|
||||
TouchSensor,
|
||||
KeyboardSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
} from "@dnd-kit/core";
|
||||
@@ -23,6 +23,7 @@ import { SortableItem } from "./SortableItem";
|
||||
export interface SortableProps {
|
||||
items: ReactNode[];
|
||||
onChange: (newSortedIndices: number[]) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface SortableItems {
|
||||
@@ -31,18 +32,18 @@ interface SortableItems {
|
||||
item: ReactNode;
|
||||
}
|
||||
|
||||
export const Sortable = ({ items, onChange }: SortableProps) => {
|
||||
export const Sortable = (props: SortableProps) => {
|
||||
const [itemsWithIds, setItemsWithIds] = useState<SortableItems[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setItemsWithIds(
|
||||
items.map((item, idx) => ({
|
||||
props.items.map((item, idx) => ({
|
||||
item,
|
||||
id: idx + 1, // +1 because dndtoolkit has problem with "falsy" ids
|
||||
originalIndex: idx,
|
||||
}))
|
||||
);
|
||||
}, [items]);
|
||||
}, [props.items]);
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
@@ -50,6 +51,8 @@ export const Sortable = ({ items, onChange }: SortableProps) => {
|
||||
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
||||
);
|
||||
|
||||
const extraClasses = props.className || "";
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
@@ -58,7 +61,7 @@ export const Sortable = ({ items, onChange }: SortableProps) => {
|
||||
modifiers={[restrictToVerticalAxis]}
|
||||
>
|
||||
<SortableContext items={itemsWithIds} strategy={verticalListSortingStrategy}>
|
||||
<Flex direction="column" gap={2}>
|
||||
<Flex direction="column" gap={2} className={extraClasses}>
|
||||
{itemsWithIds.map(({ id, item }) => (
|
||||
<SortableItem key={id} id={id}>
|
||||
{item}
|
||||
@@ -78,7 +81,7 @@ export const Sortable = ({ items, onChange }: SortableProps) => {
|
||||
const oldIndex = items.findIndex((x) => x.id === active.id);
|
||||
const newIndex = items.findIndex((x) => x.id === over.id);
|
||||
const newArray = arrayMove(items, oldIndex, newIndex);
|
||||
onChange(newArray.map((item) => item.originalIndex));
|
||||
props.onChange(newArray.map((item) => item.originalIndex));
|
||||
return newArray;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Button } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { RxDragHandleDots2 } from "react-icons/rx";
|
||||
import { PropsWithChildren } from "react";
|
||||
import { RxDragHandleDots2 } from "react-icons/rx";
|
||||
|
||||
export const SortableItem = ({ children, id }: PropsWithChildren<{ id: number }>) => {
|
||||
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id });
|
||||
@@ -13,9 +14,15 @@ export const SortableItem = ({ children, id }: PropsWithChildren<{ id: number }>
|
||||
touchAction: "none",
|
||||
};
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const themedClasses =
|
||||
colorMode === "light"
|
||||
? "bg-slate-600 hover:bg-slate-500 text-white"
|
||||
: "bg-black hover:bg-slate-900 text-white ring-1 ring-white/30 ring-inset hover:ring-slate-200/50";
|
||||
|
||||
return (
|
||||
<li
|
||||
className="grid grid-cols-[min-content_1fr] items-center rounded-lg shadow-md gap-x-2 p-2 bg-white hover:bg-slate-50"
|
||||
className={`grid grid-cols-[min-content_1fr] items-center rounded-lg shadow-md gap-x-2 p-2 ${themedClasses}`}
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
|
||||
interface SurveyCardProps {
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const SurveyCard = (props: SurveyCardProps) => {
|
||||
const extraClases = props.className || "";
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
const baseCardClasses = "rounded-lg h-full block p-6";
|
||||
const cardClases =
|
||||
colorMode === "light"
|
||||
? `${baseCardClasses} bg-slate-50 text-gray-800 shadow-lg ${extraClases}`
|
||||
: // `${baseCardClasses} bg-slate-800 text-white shadow-xl${extraClases}`;
|
||||
`${baseCardClasses} bg-slate-800 text-slate-400 shadow-xl ring-1 ring-white/10 ring-inset ${extraClases}`;
|
||||
|
||||
return <div className={cardClases}>{props.children}</div>;
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import { SkipButton } from "src/components/Buttons/Skip";
|
||||
import { SubmitButton } from "src/components/Buttons/Submit";
|
||||
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
|
||||
|
||||
interface TaskControlsProps {
|
||||
// we need a task type
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
tasks: any[];
|
||||
className?: string;
|
||||
onSubmitResponse: (task: { id: string }) => void;
|
||||
onSkip: () => void;
|
||||
}
|
||||
|
||||
export const TaskControls = (props: TaskControlsProps) => {
|
||||
const extraClases = props.className || "";
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
const baseClasses = "flex flex-row justify-items-stretch mb-8 p-4 rounded-lg max-w-7xl mx-auto";
|
||||
const taskControlClases =
|
||||
colorMode === "light"
|
||||
? `${baseClasses} bg-white text-gray-800 shadow-lg ${extraClases}`
|
||||
: `${baseClasses} bg-slate-800 text-slate-400 shadow-xl ring-1 ring-white/10 ring-inset ${extraClases}`;
|
||||
|
||||
const endTask = props.tasks[props.tasks.length - 1];
|
||||
return (
|
||||
<section className={taskControlClases}>
|
||||
<TaskInfo id={props.tasks[0].id} output="Submit your answer" />
|
||||
<Flex justify="center" ml="auto" gap={2}>
|
||||
<SkipButton>Skip</SkipButton>
|
||||
{endTask.task.type !== "task_done" ? (
|
||||
<SubmitButton onClick={() => props.onSubmitResponse(props.tasks[0])}>Submit</SubmitButton>
|
||||
) : (
|
||||
<SubmitButton onClick={props.onSkip}>Next Task</SubmitButton>
|
||||
)}
|
||||
</Flex>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
import { SurveyCard } from "src/components/Survey/SurveyCard";
|
||||
|
||||
export const TwoColumnsWithCards = ({ children }: { children: React.ReactNode[] }) => {
|
||||
if (!Array.isArray(children) || children.length !== 2) {
|
||||
throw new Error("TwoColumns expects 2 children");
|
||||
}
|
||||
|
||||
const [first, second] = children;
|
||||
|
||||
return (
|
||||
<div className="mb-8 mx-auto max-w-7xl lt-lg:mb-12 grid lg:gap-x-12 lg:grid-cols-2">
|
||||
<SurveyCard>{first}</SurveyCard>
|
||||
<SurveyCard className="lg:mt-0 lt-lg:mt-6">{second}</SurveyCard>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
export const TaskInfo = ({ id, output }: { id: string; output: string }) => {
|
||||
return (
|
||||
<div className="grid grid-cols-[min-content_auto] gap-x-2 text-gray-700">
|
||||
<div className="grid grid-cols-[min-content_auto] gap-x-2 ">
|
||||
<b>Prompt</b>
|
||||
<span data-cy="task-id">{id}</span>
|
||||
<b>Output</b>
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
|
||||
import { TaskOption } from "./TaskOption";
|
||||
import { TaskOptions } from "./TaskOptions";
|
||||
|
||||
export const TaskSelection = () => {
|
||||
const { colorMode } = useColorMode();
|
||||
const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
|
||||
|
||||
return (
|
||||
<Flex gap={10} wrap="wrap" justifyContent="space-evenly" width="full" height="full" alignItems={"center"}>
|
||||
<Flex
|
||||
gap={10}
|
||||
wrap="wrap"
|
||||
justifyContent="space-evenly"
|
||||
width="full"
|
||||
height="full"
|
||||
alignItems={"center"}
|
||||
className={mainBgClasses}
|
||||
>
|
||||
<TaskOptions key="create" title="Create">
|
||||
{/* <TaskOption
|
||||
alt="Summarize Stories"
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
export const TwoColumns = ({ children }: { children: React.ReactNode[] }) => {
|
||||
if (!Array.isArray(children) || children.length !== 2) {
|
||||
throw new Error("TwoColumns expects 2 children");
|
||||
}
|
||||
|
||||
const [first, second] = children;
|
||||
|
||||
return (
|
||||
<section className="mb-8 lt-lg:mb-12 grid lg:gap-x-12 lg:grid-cols-2">
|
||||
<div className="rounded-lg shadow-lg h-full block bg-white p-6">{first}</div>
|
||||
<div className="rounded-lg shadow-lg h-full block bg-white p-6 mt-6 lg:mt-0">{second}</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import { CiDark } from "react-icons/ci";
|
||||
import { CiLight } from "react-icons/ci";
|
||||
|
||||
export function ColorModeIconToggle(props) {
|
||||
const { colorMode, toggleColorMode } = useColorMode();
|
||||
const propsClassName = props.className ?? "";
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={`flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5 ${propsClassName}`}
|
||||
aria-label="Toggle dark mode"
|
||||
onClick={toggleColorMode}
|
||||
>
|
||||
{colorMode === "light" ? (
|
||||
<CiDark className="h-5 w-5 stroke-zinc-900 dark:hidden" />
|
||||
) : (
|
||||
<CiLight className="h-5 w-5 stroke-white" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Switch, useColorMode } from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
|
||||
const ColorModeSwitch = () => {
|
||||
const { colorMode, toggleColorMode } = useColorMode();
|
||||
return (
|
||||
<Switch
|
||||
onChange={toggleColorMode}
|
||||
defaultChecked={colorMode === "light"}
|
||||
checked={colorMode === "light"}
|
||||
size="lg"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColorModeSwitch;
|
||||
@@ -1,48 +1,25 @@
|
||||
import "../styles/globals.css";
|
||||
import "focus-visible";
|
||||
|
||||
import { ChakraProvider } from "@chakra-ui/react";
|
||||
import { extendTheme } from "@chakra-ui/react";
|
||||
import { Inter } from "@next/font/google";
|
||||
import type { AppProps } from "next/app";
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
import { getDefaultLayout, NextPageWithLayout } from "src/components/Layout";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
variable: "--font-inter",
|
||||
});
|
||||
|
||||
const theme = extendTheme({
|
||||
styles: {
|
||||
global: {
|
||||
body: {
|
||||
bg: "white",
|
||||
},
|
||||
main: {
|
||||
fontFamily: "Inter",
|
||||
},
|
||||
header: {
|
||||
fontFamily: "Inter",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
import { Chakra, getServerSideProps } from "../styles/Chakra";
|
||||
|
||||
type AppPropsWithLayout = AppProps & {
|
||||
Component: NextPageWithLayout;
|
||||
};
|
||||
|
||||
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppPropsWithLayout) {
|
||||
function MyApp({ Component, pageProps: { session, cookies, ...pageProps } }: AppPropsWithLayout) {
|
||||
const getLayout = Component.getLayout ?? getDefaultLayout;
|
||||
const page = getLayout(<Component {...pageProps} />);
|
||||
|
||||
return (
|
||||
<ChakraProvider theme={theme}>
|
||||
<Chakra cookies={cookies}>
|
||||
<SessionProvider session={session}>{page}</SessionProvider>
|
||||
</ChakraProvider>
|
||||
</Chakra>
|
||||
);
|
||||
}
|
||||
|
||||
export { getServerSideProps };
|
||||
export default MyApp;
|
||||
|
||||
@@ -2,24 +2,10 @@ import { Button } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { useSession } from "next-auth/react";
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
|
||||
export default function Account() {
|
||||
const { data: session } = useSession();
|
||||
const [username, setUsername] = useState("null");
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const handleUpdate = async () => {
|
||||
const response = await fetch("../api/update", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ username }),
|
||||
});
|
||||
const { name } = await response.json();
|
||||
setUsername(name);
|
||||
};
|
||||
|
||||
if (!session) {
|
||||
return;
|
||||
@@ -34,7 +20,7 @@ export default function Account() {
|
||||
/>
|
||||
</Head>
|
||||
<main className="h-3/4 z-0 bg-white flex flex-col items-center justify-center">
|
||||
<p>{username}</p>
|
||||
<p>{session.user.name || "No username"}</p>
|
||||
<Button>
|
||||
<Link href="/account/edit">Edit Username</Link>
|
||||
</Button>
|
||||
|
||||
@@ -63,7 +63,8 @@ const handler = async (req, res) => {
|
||||
message_id: registeredTask.id,
|
||||
}),
|
||||
});
|
||||
await ackRes.json();
|
||||
|
||||
ackRes; // calling this only to get rid of the unused variable warning... not sure if anything is intended to be done with ackRes
|
||||
|
||||
// Send the results to the client.
|
||||
res.status(200).json(registeredTask);
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { Button, Input, Stack } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { getCsrfToken, getProviders, signIn } from "next-auth/react";
|
||||
import React, { useRef } from "react";
|
||||
import { FaBug, FaDiscord, FaEnvelope, FaGithub } from "react-icons/fa";
|
||||
import { AuthLayout } from "src/components/AuthLayout";
|
||||
import { Footer } from "src/components/Footer";
|
||||
import { Header } from "src/components/Header";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export default function Signin({ csrfToken, providers }) {
|
||||
function Signin({ csrfToken, providers }) {
|
||||
const { discord, email, github, credentials } = providers;
|
||||
const emailEl = useRef(null);
|
||||
const signinWithEmail = (ev: React.FormEvent) => {
|
||||
@@ -21,8 +24,14 @@ export default function Signin({ csrfToken, providers }) {
|
||||
signIn(credentials.id, { callbackUrl: "/", username: debugUsernameEl.current.value });
|
||||
}
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const bgColorClass = colorMode === "light" ? "bg-gray-50" : "bg-chakra-gray-900";
|
||||
const buttonBgColor = colorMode === "light" ? "#2563eb" : "#2563eb";
|
||||
|
||||
const buttonColorScheme = colorMode === "light" ? "blue" : "dark-blue-btn";
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={bgColorClass}>
|
||||
<Head>
|
||||
<title>Sign Up - Open Assistant</title>
|
||||
<meta name="Sign Up" content="Sign up to access Open Assistant" />
|
||||
@@ -30,11 +39,11 @@ export default function Signin({ csrfToken, providers }) {
|
||||
<AuthLayout>
|
||||
<Stack spacing="2">
|
||||
{credentials && (
|
||||
<form onSubmit={signinWithDebugCredentials} className="border-2 border-orange-200 rounded-md p-4 relative">
|
||||
<span className="text-orange-600 absolute -top-3 left-5 bg-white px-1">For Debugging Only</span>
|
||||
<form onSubmit={signinWithDebugCredentials} className="border-2 border-orange-600 rounded-md p-4 relative">
|
||||
<span className={`text-orange-600 absolute -top-3 left-5 ${bgColorClass} px-1`}>For Debugging Only</span>
|
||||
<Stack>
|
||||
<Input variant="outline" size="lg" placeholder="Username" ref={debugUsernameEl} />
|
||||
<Button size={"lg"} leftIcon={<FaBug />} colorScheme="gray" type="submit">
|
||||
<Button size={"lg"} leftIcon={<FaBug />} colorScheme={buttonColorScheme} color="white" type="submit">
|
||||
Continue with Debug User
|
||||
</Button>
|
||||
</Stack>
|
||||
@@ -43,13 +52,13 @@ export default function Signin({ csrfToken, providers }) {
|
||||
{email && (
|
||||
<form onSubmit={signinWithEmail}>
|
||||
<Stack>
|
||||
<Input data-cy="email-address" variant="outline" size="lg" placeholder="Email Address" ref={emailEl} />
|
||||
<Input variant="outline" size="lg" placeholder="Email Address" ref={emailEl} />
|
||||
<Button
|
||||
data-cy="signin-email-button"
|
||||
size={"lg"}
|
||||
leftIcon={<FaEnvelope />}
|
||||
colorScheme="gray"
|
||||
type="submit"
|
||||
colorScheme={buttonColorScheme}
|
||||
color="white"
|
||||
>
|
||||
Continue with Email
|
||||
</Button>
|
||||
@@ -58,7 +67,7 @@ export default function Signin({ csrfToken, providers }) {
|
||||
)}
|
||||
{discord && (
|
||||
<Button
|
||||
bg="#5865F2"
|
||||
bg={buttonBgColor}
|
||||
_hover={{ bg: "#4A57E3" }}
|
||||
_active={{
|
||||
bg: "#454FBF",
|
||||
@@ -107,12 +116,21 @@ export default function Signin({ csrfToken, providers }) {
|
||||
</Link>
|
||||
</div>
|
||||
</AuthLayout>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export async function getServerSideProps(context) {
|
||||
Signin.getLayout = (page) => (
|
||||
<div className="grid grid-rows-[min-content_1fr_min-content] h-full justify-items-stretch">
|
||||
<Header transparent={true} />
|
||||
{page}
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Signin;
|
||||
|
||||
export async function getServerSideProps() {
|
||||
const csrfToken = await getCsrfToken();
|
||||
const providers = await getProviders();
|
||||
return {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Flex, Textarea } from "@chakra-ui/react";
|
||||
import { Container, Textarea } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import { useRef, useState } from "react";
|
||||
import { SkipButton } from "src/components/Buttons/Skip";
|
||||
import { SubmitButton } from "src/components/Buttons/Submit";
|
||||
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
|
||||
import { Messages } from "src/components/Messages";
|
||||
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
|
||||
import { TwoColumns } from "src/components/TwoColumns";
|
||||
import { TaskControls } from "src/components/Survey/TaskControls";
|
||||
import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards";
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
@@ -45,43 +44,31 @@ const AssistantReply = () => {
|
||||
mutate();
|
||||
};
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingScreen text="Loading..." />;
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
return <div className="p-6 bg-slate-100 text-gray-800">No tasks found...</div>;
|
||||
return <Container className="p-6 text-center text-gray-800">No tasks found...</Container>;
|
||||
}
|
||||
|
||||
const task = tasks[0].task;
|
||||
const endTask = tasks[tasks.length - 1];
|
||||
|
||||
return (
|
||||
<div className="p-6 bg-slate-100 text-gray-800">
|
||||
<TwoColumns>
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<TwoColumnsWithCards>
|
||||
<>
|
||||
<h5 className="text-lg font-semibold">Reply as the assistant</h5>
|
||||
<p className="text-lg py-1">Given the following conversation, provide an adequate reply</p>
|
||||
<Messages messages={task.conversation.messages} post_id={task.id} />
|
||||
</>
|
||||
<Textarea name="reply" data-cy="reply" placeholder="Reply..." ref={inputRef} />
|
||||
</TwoColumns>
|
||||
<Textarea name="reply" placeholder="Reply..." ref={inputRef} />
|
||||
</TwoColumnsWithCards>
|
||||
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
|
||||
<TaskInfo id={tasks[0].id} output="Submit your answer" />
|
||||
|
||||
<Flex justify="center" ml="auto" gap={2}>
|
||||
<SkipButton>Skip</SkipButton>
|
||||
{endTask.task.type !== "task_done" ? (
|
||||
<SubmitButton data-cy="submit" onClick={() => submitResponse(tasks[0])}>
|
||||
Submit
|
||||
</SubmitButton>
|
||||
) : (
|
||||
<SubmitButton data-cy="next-task" onClick={fetchNextTask}>
|
||||
Next Task
|
||||
</SubmitButton>
|
||||
)}
|
||||
</Flex>
|
||||
</section>
|
||||
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { Flex, Textarea } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { Textarea } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import { useRef, useState } from "react";
|
||||
import { SkipButton } from "src/components/Buttons/Skip";
|
||||
import { SubmitButton } from "src/components/Buttons/Submit";
|
||||
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
|
||||
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
|
||||
import { TwoColumns } from "src/components/TwoColumns";
|
||||
import { TaskControls } from "src/components/Survey/TaskControls";
|
||||
import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards";
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
@@ -20,7 +18,7 @@ const SummarizeStory = () => {
|
||||
|
||||
// Fetch the very fist task. We can ignore everything except isLoading
|
||||
// because the onSuccess handler will update `tasks` when ready.
|
||||
const { isLoading } = useSWRImmutable("/api/new_task/summarize_story", fetcher, {
|
||||
const { isLoading, mutate } = useSWRImmutable("/api/new_task/summarize_story", fetcher, {
|
||||
onSuccess: (data) => {
|
||||
setTasks([data]);
|
||||
},
|
||||
@@ -50,6 +48,14 @@ const SummarizeStory = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const fetchNextTask = () => {
|
||||
inputRef.current.value = "";
|
||||
mutate();
|
||||
};
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingScreen text="Loading..." />;
|
||||
}
|
||||
@@ -59,31 +65,20 @@ const SummarizeStory = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Summarize A Story</title>
|
||||
<meta name="description" content="Summarize a story to train our model." />
|
||||
</Head>
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<main className="p-6 h-full mx-auto bg-slate-100 text-gray-800">
|
||||
<TwoColumns>
|
||||
<TwoColumnsWithCards>
|
||||
<>
|
||||
<h5 className="text-lg font-semibold">Instruction</h5>
|
||||
<p className="text-lg py-1">Summarize the following story</p>
|
||||
<div className="bg-slate-800 p-6 rounded-xl text-white whitespace-pre-wrap">{tasks[0].task.story}</div>
|
||||
</>
|
||||
<Textarea name="summary" placeholder="Summary" ref={inputRef} />
|
||||
</TwoColumns>
|
||||
</TwoColumnsWithCards>
|
||||
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
|
||||
<TaskInfo id={tasks[0].id} output="Submit your answer" />
|
||||
|
||||
<Flex justify="center" ml="auto" gap={2}>
|
||||
<SkipButton>Skip</SkipButton>
|
||||
<SubmitButton onClick={() => submitResponse(tasks[0])}>Submit</SubmitButton>
|
||||
</Flex>
|
||||
</section>
|
||||
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
|
||||
</main>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Flex, Textarea } from "@chakra-ui/react";
|
||||
import { Textarea } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import { useRef, useState } from "react";
|
||||
import { SkipButton } from "src/components/Buttons/Skip";
|
||||
import { SubmitButton } from "src/components/Buttons/Submit";
|
||||
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
|
||||
import { Messages } from "src/components/Messages";
|
||||
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
|
||||
import { TwoColumns } from "src/components/TwoColumns";
|
||||
import { TaskControls } from "src/components/Survey/TaskControls";
|
||||
import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards";
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
@@ -45,43 +44,38 @@ const UserReply = () => {
|
||||
mutate();
|
||||
};
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingScreen text="Loading..." />;
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
return <div className="p-6 bg-slate-100 text-gray-800">No tasks found...</div>;
|
||||
return (
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<div className="flex h-full">
|
||||
<div className="text-xl font-bold mx-auto my-auto">No tasks found...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const task = tasks[0].task;
|
||||
const endTask = tasks[tasks.length - 1];
|
||||
|
||||
return (
|
||||
<div className="p-6 bg-slate-100 text-gray-800">
|
||||
<TwoColumns>
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<TwoColumnsWithCards>
|
||||
<>
|
||||
<h5 className="text-lg font-semibold">Reply as a user</h5>
|
||||
<p className="text-lg py-1">Given the following conversation, provide an adequate reply</p>
|
||||
<Messages messages={task.conversation.messages} post_id={task.id} />
|
||||
{task.hint && <p className="text-lg py-1">Hint: {task.hint}</p>}
|
||||
</>
|
||||
<Textarea name="reply" data-cy="reply" placeholder="Reply..." ref={inputRef} />
|
||||
</TwoColumns>
|
||||
<Textarea name="reply" placeholder="Reply..." ref={inputRef} />
|
||||
</TwoColumnsWithCards>
|
||||
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
|
||||
<TaskInfo id={tasks[0].id} output="Submit your answer" />
|
||||
<Flex justify="center" ml="auto" gap={2}>
|
||||
<SkipButton>Skip</SkipButton>
|
||||
{endTask.task.type !== "task_done" ? (
|
||||
<SubmitButton data-cy="submit" onClick={() => submitResponse(tasks[0])}>
|
||||
Submit
|
||||
</SubmitButton>
|
||||
) : (
|
||||
<SubmitButton data-cy="next-task" onClick={fetchNextTask}>
|
||||
Next Task
|
||||
</SubmitButton>
|
||||
)}
|
||||
</Flex>
|
||||
</section>
|
||||
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { useState } from "react";
|
||||
import { SkipButton } from "src/components/Buttons/Skip";
|
||||
import { SubmitButton } from "src/components/Buttons/Submit";
|
||||
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
|
||||
import { Sortable } from "src/components/Sortable/Sortable";
|
||||
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
|
||||
import { SurveyCard } from "src/components/Survey/SurveyCard";
|
||||
import { TaskControls } from "src/components/Survey/TaskControls";
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
@@ -47,48 +46,42 @@ const RankAssistantReplies = () => {
|
||||
mutate();
|
||||
};
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingScreen text="Loading..." />;
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
return <div className="p-6 bg-slate-100 text-gray-800">Loading...</div>;
|
||||
return (
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<div className="flex h-full">
|
||||
<div className="text-xl font-bold mx-auto my-auto">No tasks found...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const replies = tasks[0].task.replies as string[];
|
||||
const endTask = tasks[tasks.length - 1];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Rank Assistant Replies</title>
|
||||
<meta name="description" content="Rank Assistant Replies." />
|
||||
</Head>
|
||||
<main className="p-6 bg-slate-100 text-gray-800">
|
||||
<div className="rounded-lg shadow-lg block bg-white p-6 mb-8">
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<SurveyCard className="max-w-7xl mx-auto h-fit mb-24">
|
||||
<h5 className="text-lg font-semibold mb-4">Instructions</h5>
|
||||
<p className="text-lg py-1">
|
||||
Given the following replies, sort them from best to worst, best being first, worst being last.
|
||||
</p>
|
||||
<Sortable items={replies} onChange={setRanking} />
|
||||
</div>
|
||||
<Sortable items={replies} onChange={setRanking} className="my-8" />
|
||||
</SurveyCard>
|
||||
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch">
|
||||
<TaskInfo id={tasks[0].id} output="Submit your answer" />
|
||||
|
||||
<Flex justify="center" ml="auto" gap={2}>
|
||||
<SkipButton>Skip</SkipButton>
|
||||
{endTask.task.type !== "task_done" ? (
|
||||
<SubmitButton data-cy="submit" onClick={() => submitResponse(tasks[0])} disabled={ranking.length === 0}>
|
||||
Submit
|
||||
</SubmitButton>
|
||||
) : (
|
||||
<SubmitButton data-cy="next-task" onClick={fetchNextTask}>
|
||||
Next Task
|
||||
</SubmitButton>
|
||||
)}
|
||||
</Flex>
|
||||
</section>
|
||||
</main>
|
||||
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { useState } from "react";
|
||||
import { SkipButton } from "src/components/Buttons/Skip";
|
||||
import { SubmitButton } from "src/components/Buttons/Submit";
|
||||
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
|
||||
import { Sortable } from "src/components/Sortable/Sortable";
|
||||
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
|
||||
import { SurveyCard } from "src/components/Survey/SurveyCard";
|
||||
import { TaskControls } from "src/components/Survey/TaskControls";
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
@@ -18,6 +17,7 @@ const RankInitialPrompts = () => {
|
||||
* The best prompt will have index 0, and the worst is the last.
|
||||
*/
|
||||
const [ranking, setRanking] = useState<number[]>([]);
|
||||
// const bg = useColorModeValue("gray.100", "gray.800");
|
||||
|
||||
const { isLoading, mutate } = useSWRImmutable("/api/new_task/rank_initial_prompts", fetcher, {
|
||||
onSuccess: (data) => {
|
||||
@@ -47,47 +47,40 @@ const RankInitialPrompts = () => {
|
||||
mutate();
|
||||
};
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingScreen text="Loading..." />;
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
return <div className="p-6 bg-slate-100 text-gray-800">No tasks found...</div>;
|
||||
return (
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<div className="flex h-full">
|
||||
<div className="text-xl font-bold mx-auto my-auto">No tasks found...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const endTask = tasks[tasks.length - 1];
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Rank Initial Prompts</title>
|
||||
<meta name="description" content="Rank initial prompts." />
|
||||
</Head>
|
||||
<main className="p-6 bg-slate-100 text-gray-800">
|
||||
<div className="rounded-lg shadow-lg block bg-white p-6 mb-8">
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<SurveyCard className="max-w-7xl mx-auto h-fit mb-24">
|
||||
<h5 className="text-lg font-semibold mb-4">Instructions</h5>
|
||||
<p className="text-lg py-1">
|
||||
Given the following prompts, sort them from best to worst, best being first, worst being last.
|
||||
</p>
|
||||
<Sortable items={tasks[0].task.prompts} onChange={setRanking} />
|
||||
</div>
|
||||
<Sortable items={tasks[0].task.prompts} onChange={setRanking} className="my-8" />
|
||||
</SurveyCard>
|
||||
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch">
|
||||
<TaskInfo id={tasks[0].id} output="Submit your answer" />
|
||||
|
||||
<Flex justify="center" ml="auto" gap={2}>
|
||||
<SkipButton>Skip</SkipButton>
|
||||
{endTask.task.type !== "task_done" ? (
|
||||
<SubmitButton data-cy="submit" onClick={() => submitResponse(tasks[0])} disabled={ranking.length === 0}>
|
||||
Submit
|
||||
</SubmitButton>
|
||||
) : (
|
||||
<SubmitButton data-cy="next-task" onClick={fetchNextTask}>
|
||||
Next Task
|
||||
</SubmitButton>
|
||||
)}
|
||||
</Flex>
|
||||
</section>
|
||||
</main>
|
||||
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Flex } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { useState } from "react";
|
||||
import { SkipButton } from "src/components/Buttons/Skip";
|
||||
import { SubmitButton } from "src/components/Buttons/Submit";
|
||||
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
|
||||
import { Sortable } from "src/components/Sortable/Sortable";
|
||||
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
|
||||
import { SurveyCard } from "src/components/Survey/SurveyCard";
|
||||
import { TaskControls } from "src/components/Survey/TaskControls";
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
@@ -47,48 +46,41 @@ const RankUserReplies = () => {
|
||||
mutate();
|
||||
};
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingScreen text="Loading..." />;
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
return <div className="p-6 bg-slate-100 text-gray-800">Loading...</div>;
|
||||
return (
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<div className="flex h-full">
|
||||
<div className="text-xl font-bold mx-auto my-auto">No tasks found...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const replies = tasks[0].task.replies as string[];
|
||||
|
||||
const endTask = tasks[tasks.length - 1];
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Rank User Replies</title>
|
||||
<meta name="description" content="Rank User Replies." />
|
||||
</Head>
|
||||
<main className="p-6 bg-slate-100 text-gray-800">
|
||||
<div className="rounded-lg shadow-lg block bg-white p-6 mb-8">
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<SurveyCard className="max-w-7xl mx-auto h-fit mb-24">
|
||||
<h5 className="text-lg font-semibold mb-4">Instructions</h5>
|
||||
<p className="text-lg py-1">
|
||||
Given the following replies, sort them from best to worst, best being first, worst being last.
|
||||
</p>
|
||||
<Sortable items={replies} onChange={setRanking} />
|
||||
</div>
|
||||
<Sortable items={replies} onChange={setRanking} className="my-8" />
|
||||
</SurveyCard>
|
||||
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
|
||||
<TaskInfo id={tasks[0].id} output="Submit your answer" />
|
||||
|
||||
<Flex justify="center" ml="auto" gap={2}>
|
||||
<SkipButton>Skip</SkipButton>
|
||||
{endTask.task.type !== "task_done" ? (
|
||||
<SubmitButton data-cy="submit" onClick={() => submitResponse(tasks[0])} disabled={ranking.length === 0}>
|
||||
Submit
|
||||
</SubmitButton>
|
||||
) : (
|
||||
<SubmitButton data-cy="next-task" onClick={fetchNextTask}>
|
||||
Next Task
|
||||
</SubmitButton>
|
||||
)}
|
||||
</Flex>
|
||||
</section>
|
||||
</main>
|
||||
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Flex, Textarea } from "@chakra-ui/react";
|
||||
import { Textarea } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import { QuestionMarkCircleIcon } from "@heroicons/react/20/solid";
|
||||
import Head from "next/head";
|
||||
import { useState } from "react";
|
||||
import { SkipButton } from "src/components/Buttons/Skip";
|
||||
import { SubmitButton } from "src/components/Buttons/Submit";
|
||||
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
|
||||
import RatingRadioGroup from "src/components/RatingRadioGroup";
|
||||
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
|
||||
import { TwoColumns } from "src/components/TwoColumns";
|
||||
import { TaskControls } from "src/components/Survey/TaskControls";
|
||||
import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards";
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
@@ -50,15 +49,27 @@ const RateSummary = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const fetchNextTask = () => {
|
||||
mutate();
|
||||
};
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingScreen text="Loading..." />;
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
return <div className="p-6 bg-slate-100 text-gray-800">No tasks found...</div>;
|
||||
return (
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<div className="flex h-full">
|
||||
<div className="text-xl font-bold mx-auto my-auto">No tasks found...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const endTask = tasks[tasks.length - 1];
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
@@ -66,7 +77,7 @@ const RateSummary = () => {
|
||||
<meta name="description" content="Rate a proposed story summary." />
|
||||
</Head>
|
||||
<main className="p-6 bg-slate-100 text-gray-800">
|
||||
<TwoColumns>
|
||||
<TwoColumnsWithCards>
|
||||
<>
|
||||
<h5 className="text-lg font-semibold mb-4">Instruction</h5>
|
||||
<div className="bg-slate-800 p-6 rounded-xl text-white whitespace-pre-wrap">{tasks[0].task.full_text}</div>
|
||||
@@ -89,20 +100,9 @@ const RateSummary = () => {
|
||||
</ul>
|
||||
<Textarea name="notes" placeholder="Optional notes" />
|
||||
</section>
|
||||
</TwoColumns>
|
||||
</TwoColumnsWithCards>
|
||||
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
|
||||
<TaskInfo id={tasks[0].id} output="Submit your answer" />
|
||||
|
||||
<Flex justify="center" ml="auto" gap={2}>
|
||||
<SkipButton>Skip</SkipButton>
|
||||
{endTask.task.type !== "task_done" ? (
|
||||
<SubmitButton onClick={() => submitResponse(tasks[0])}>Submit</SubmitButton>
|
||||
) : (
|
||||
<SubmitButton onClick={mutate}>Next Task</SubmitButton>
|
||||
)}
|
||||
</Flex>
|
||||
</section>
|
||||
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -20,11 +20,9 @@ const Home = () => {
|
||||
/>
|
||||
</Head>
|
||||
{session ? (
|
||||
<main className="my-4">
|
||||
<TaskSelection />
|
||||
</main>
|
||||
<TaskSelection />
|
||||
) : (
|
||||
<main>
|
||||
<main className="oa-basic-theme">
|
||||
<Hero />
|
||||
<CallToAction />
|
||||
<Faq />
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ChakraProvider, cookieStorageManagerSSR, localStorageManager } from "@chakra-ui/react";
|
||||
|
||||
import { theme } from "./Theme";
|
||||
|
||||
export function Chakra({ cookies, children }) {
|
||||
const colorModeManager = typeof cookies === "string" ? cookieStorageManagerSSR(cookies) : localStorageManager;
|
||||
|
||||
return (
|
||||
<ChakraProvider theme={theme} colorModeManager={colorModeManager}>
|
||||
{children}
|
||||
</ChakraProvider>
|
||||
);
|
||||
}
|
||||
|
||||
// also export a reusable function getServerSideProps
|
||||
export function getServerSideProps({ req }) {
|
||||
return {
|
||||
props: {
|
||||
// first time users will not have any cookies and you may not return
|
||||
// undefined here, hence ?? is necessary
|
||||
cookies: req.headers.cookie ?? "",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export const colors = {
|
||||
light: {
|
||||
bg: "rgb(250,250,250)",
|
||||
text: "black",
|
||||
},
|
||||
dark: {
|
||||
bg: "gray.900",
|
||||
text: "white",
|
||||
},
|
||||
"dark-blue-btn": {
|
||||
200: "rgb(29,78,216)",
|
||||
300: "blue",
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { defineStyleConfig } from "@chakra-ui/styled-system";
|
||||
|
||||
const baseStyle = {};
|
||||
|
||||
const variants = {
|
||||
"no-padding": {
|
||||
padding: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export const containerTheme = defineStyleConfig({
|
||||
baseStyle,
|
||||
variants,
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import { type ThemeConfig, extendTheme } from "@chakra-ui/react";
|
||||
import { Styles } from "@chakra-ui/theme-tools";
|
||||
|
||||
import { colors } from "./colors";
|
||||
import { containerTheme } from "./components/Container";
|
||||
|
||||
const config: ThemeConfig = {
|
||||
initialColorMode: "light",
|
||||
useSystemColorMode: true,
|
||||
disableTransitionOnChange: false,
|
||||
};
|
||||
|
||||
const components = {
|
||||
Container: containerTheme,
|
||||
};
|
||||
|
||||
const styles: Styles = {
|
||||
global: (props) => ({
|
||||
"*": {
|
||||
transition: "background-color 200ms cubic-bezier(0.4, 0, 1, 1)",
|
||||
// bg: props.colorMode === "light" ? colors.light.bg : colors.dark.bg,
|
||||
// color: props.colorMode === "light" ? colors.light.text : colors.dark.text,
|
||||
},
|
||||
".oa-basic-theme": {
|
||||
bg: props.colorMode === "light" ? colors.light.bg : colors.dark.bg,
|
||||
color: props.colorMode === "light" ? colors.light.text : colors.dark.text,
|
||||
},
|
||||
main: {
|
||||
fontFamily: "Inter",
|
||||
},
|
||||
header: {
|
||||
fontFamily: "Inter",
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
export const theme = extendTheme({ colors, config, styles, components });
|
||||
@@ -71,6 +71,10 @@ module.exports = {
|
||||
maxWidth: {
|
||||
"2xl": "40rem",
|
||||
},
|
||||
|
||||
colors: {
|
||||
"chakra-gray-900": "#171923",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require("@tailwindcss/forms")],
|
||||
|
||||
Reference in New Issue
Block a user