Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))bug: #378

Open
Davidthecode opened this issue Apr 11, 2024 · 5 comments

Comments

@Davidthecode
Copy link

Provide environment information

System: OS: macOS 12.7.4 CPU: (4) x64 Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz Memory: 18.83 MB / 8.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 20.12.0 - /usr/local/bin/node npm: 10.5.0 - /usr/local/bin/npm pnpm: 8.15.6 - /usr/local/bin/pnpm

Describe the bug

Whenever i highlight a text and click on the ask AI option, i get an error that says - TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator). I can't exactly pinpoint the error because there isn't enough information about it. i did some debugging and i think the error might be coming from the ai-selector-commands component. I noticed that when i comment the code in the return block inside the ai-selector-commands component, i don't get the error but only the input with the ask AI to edit or generate option is displayed in the ui. however when i start typing in the input i get the error again. I have been trying hard to find the issue but i can't seem to figure it out.

here's the "ai-selector" component for reference -

"use client";

import { Command, CommandInput } from "../ui/command";
import { useCompletion } from "ai/react";
import { toast } from "sonner";
import { useEditor } from "novel";
import { useState } from "react";
import Markdown from "react-markdown";
import AISelectorCommands from "./ai-selector-commands";
import AICompletionCommands from "./ai-completion-command";
import { ScrollArea } from "../ui/scroll-area";
import { Button } from "../ui/button";
import { ArrowUp } from "lucide-react";
import Magic from "../ui/Icons/magic";
import CrazySpinner from "../ui/Icons/crazy-spinner";
import { addAIHighlight } from "novel/extensions";

type AISelectorProps = {
  open: boolean;
  onOpenChange: (open: boolean) => void;
}

export function AISelector({ open, onOpenChange }: AISelectorProps) {
  const { editor } = useEditor();
  const [inputValue, setInputValue] = useState("");

  const { completion, complete, isLoading } = useCompletion({
    api: "/api/generate",
    onResponse: (response) => {
      if (response.status === 429) {
        toast.error("You have reached your request limit for the day.");
        return;
      }
    },
    onError: (e) => {
      toast.error(e.message);
      console.log(e)
    },
  });

  const hasCompletion = completion.length > 0;

  return (
    <Command className="w-[350px]">
      {hasCompletion && (
        <div className="flex max-h-[400px]">
          <ScrollArea>
            <div className="prose p-2 px-4 prose-sm">
              <Markdown>{completion}</Markdown>
            </div>
          </ScrollArea>
        </div>
      )}

      {isLoading && (
        <div className="flex h-12 w-full items-center px-4 text-sm font-medium text-muted-foreground text-purple-500">
          <Magic className="mr-2 h-4 w-4 shrink-0  " />
          AI is thinking
          <div className="ml-2 mt-1">
            <CrazySpinner />
          </div>
        </div>
      )}
      {!isLoading && (
        <>
          <div className="relative">
            <CommandInput
              value={inputValue}
              onValueChange={setInputValue}
              autoFocus
              placeholder={
                hasCompletion
                  ? "Tell AI what to do next"
                  : "Ask AI to edit or generate..."
              }
              onFocus={() => editor && addAIHighlight(editor)}
            />
            <Button
              size="icon"
              className="absolute right-2 top-1/2 h-6 w-6 -translate-y-1/2 rounded-full bg-purple-500 hover:bg-purple-900"
              onClick={() => {
                if (completion)
                  return complete(completion, {
                    body: { option: "zap", command: inputValue },
                  }).then(() => setInputValue(""));

                const slice = editor && editor.state.selection.content();
                const text = editor && slice && editor.storage.markdown.serializer.serialize(
                  slice.content,
                );

                complete(text, {
                  body: { option: "zap", command: inputValue },
                }).then(() => setInputValue(""));
              }}
            >
              <ArrowUp className="h-4 w-4" />
            </Button>
          </div>
          {hasCompletion ? (
            <AICompletionCommands
              onDiscard={() => {
                editor && editor.chain().unsetHighlight().focus().run();
                onOpenChange(false);
              }}
              completion={completion}
            />
          ) : (
            <AISelectorCommands
              onSelect={(value, option) =>
                complete(value, { body: { option } })
              }
            />
          )}
        </>
      )}
    </Command>
  );
}

here is the "ai-selector-commands" component for reference -

import React from "react";
import { CommandGroup, CommandItem, CommandSeparator } from "../ui/command";
import {
  ArrowDownWideNarrow,
  CheckCheck,
  RefreshCcwDot,
  StepForward,
  WrapText,
} from "lucide-react";
import { useEditor } from "novel";
import { getPrevText } from "novel/extensions";

const options = [
  {
    value: "improve",
    label: "Improve writing",
    icon: RefreshCcwDot,
  },

  {
    value: "fix",
    label: "Fix grammar",
    icon: CheckCheck,
  },
  {
    value: "shorter",
    label: "Make shorter",
    icon: ArrowDownWideNarrow,
  },
  {
    value: "longer",
    label: "Make longer",
    icon: WrapText,
  },
];

type AISelectorCommandsProps = {
  onSelect: (value: string, option: string) => void;
}

const AISelectorCommands = ({ onSelect }: AISelectorCommandsProps) => {
  const { editor } = useEditor();

  return (
    <>
      <CommandGroup heading="Edit or review selection">
        {options.map((option) => (
          <CommandItem
            onSelect={(value) => {
              const slice = editor && editor.state.selection.content();
              const text = editor && editor.storage.markdown.serializer.serialize(
                slice && slice.content,
              );
              onSelect(text, value);
            }}
            className="flex gap-2 px-4"
            key={option.value}
            value={option.value}
          >
            <option.icon className="h-4 w-4 text-purple-500" />
            {option.label}
          </CommandItem>
        ))}
      </CommandGroup>
      <CommandSeparator />
      <CommandGroup heading="Use AI to do more">
        <CommandItem
          onSelect={() => {
            const text = editor && getPrevText(editor, { chars: 5000 });
            onSelect(text, "continue");
          }}
          value="continue"
          className="gap-2 px-4"
        >
          <StepForward className="h-4 w-4 text-purple-500" />
          Continue writing
        </CommandItem>
      </CommandGroup>
    </>
  );
};

export default AISelectorCommands;

here is the "ai-completion-command" for reference -

import React from "react";
import { CommandGroup, CommandItem, CommandSeparator } from "../ui/command";
import { useEditor } from "novel";
import { Check, TextQuote, TrashIcon } from "lucide-react";

type AICompletionCommandsType = {
  completion: string,
  onDiscard: () => void,
};

const AICompletionCommands = ({ completion, onDiscard }: AICompletionCommandsType) => {
  const { editor } = useEditor();
  return (
    <>
      <CommandGroup>
        <CommandItem
          className="gap-2 px-4"
          value="replace"
          onSelect={() => {
            const selection = editor && editor.view.state.selection;

            editor && selection && editor
              .chain()
              .focus()
              .insertContentAt(
                {
                  from: selection.from,
                  to: selection.to,
                },
                completion,
              )
              .run();
          }}
        >
          <Check className="h-4 w-4 text-muted-foreground" />
          Replace selection
        </CommandItem>
        <CommandItem
          className="gap-2 px-4"
          value="insert"
          onSelect={() => {
            const selection = editor && editor.view.state.selection;
            editor && selection && editor
              .chain()
              .focus()
              .insertContentAt(selection.to + 1, completion)
              .run();
          }}
        >
          <TextQuote className="h-4 w-4 text-muted-foreground" />
          Insert below
        </CommandItem>
      </CommandGroup>
      <CommandSeparator />

      <CommandGroup>
        <CommandItem onSelect={onDiscard} value="thrash" className="gap-2 px-4">
          <TrashIcon className="h-4 w-4 text-muted-foreground" />
          Discard
        </CommandItem>
      </CommandGroup>
    </>
  );
};

export default AICompletionCommands;

Link to reproduction

https://novel.sh

To reproduce

I get the error when i click on the Ask AI option

Additional information

No response

@john-dw
Copy link

john-dw commented Apr 16, 2024

Hey @Davidthecode,
I have the exact same issue. Did you ever find out how to resolve it ?
Thanks

@john-dw
Copy link

john-dw commented Apr 16, 2024

Downgrading cmdk package version to 0.2.1 did the trick. It works now.

@Davidthecode
Copy link
Author

ok @john-dw, i'll try it out

@RexanWONG
Copy link

Downgrading cmdk package version to 0.2.1 did the trick. It works now.

Worked for me, thanks!

@john-dw
Copy link

john-dw commented May 30, 2024

From https://github.com/pacocoursey/cmdk/releases:

Command.List is now required (CommandList in shadcn)

Rendering the Command.List part (CommandList if using shadcn) is now mandatory. Otherwise, you should expect to see an error like this:

TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
The fix:

// Before
<Command label="Command Menu">
	<Command.Input />
	<Command.Item />
	{/* ... */}
</Command>
// After
<Command label="Command Menu">
	<Command.Input />

	<Command.List>
		<Command.Item />
		{/* ... */}
	</Command.List>
</Command>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants