{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "# Walking Kodee\n",
    "\n",
    "This notebook demonstrates how to create an animated mascot (Kodee) that walks across the IntelliJ IDEA interface using the IntelliJ Platform integration. The notebook shows how to:\n",
    "\n",
    "- Load and display animated GIF images in the IDE\n",
    "- Create custom Swing components for animations\n",
    "- Use the IntelliJ Platform API to interact with the IDE frame\n",
    "- Implement proper resource management with disposables\n",
    "- Handle EDT (Event Dispatch Thread) operations safely\n",
    "\n",
    "The main feature creates an animated Kodee character that continuously walks across the bottom of the IDE window, with the animation cycling through GIF frames and repositioning the character when it reaches the edge of the screen.\n"
   ]
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "%use intellij-platform",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "import com.intellij.openapi.application.runInEdt\n",
    "import com.intellij.openapi.util.Disposer\n",
    "import com.intellij.openapi.wm.WindowManager\n",
    "import java.awt.Dimension\n",
    "import java.awt.Graphics\n",
    "import java.net.URL\n",
    "import javax.imageio.ImageIO\n",
    "import javax.imageio.ImageReader\n",
    "import javax.swing.JLabel\n",
    "import kotlin.concurrent.thread\n",
    "\n",
    "private inline fun <T> ImageReader.use(block: (ImageReader) -> T): T = try {\n",
    "    block(this)\n",
    "} finally {\n",
    "    dispose()\n",
    "}\n",
    "\n",
    "class AnimatedGifLabel(private val gifUrl: URL, private val parentWidth: Int) : JLabel() {\n",
    "    private var currentFrame = 0\n",
    "    private var xPosition = 0\n",
    "    private val frames by lazy {\n",
    "        gifUrl.openStream().use { stream ->\n",
    "            ImageIO.getImageReadersByFormatName(\"gif\").next().use { reader ->\n",
    "                reader.input = ImageIO.createImageInputStream(stream)\n",
    "                (0 until reader.getNumImages(true)).map { reader.read(it) }\n",
    "            }\n",
    "        }\n",
    "    }\n",
    "\n",
    "    init {\n",
    "        frames.firstOrNull()?.apply {\n",
    "            preferredSize = Dimension(width / 2, height / 2)\n",
    "        }\n",
    "    }\n",
    "\n",
    "    override fun paintComponent(g: Graphics) {\n",
    "        g.drawImage(frames[currentFrame], 0, 0, width, height, null)\n",
    "    }\n",
    "\n",
    "    fun startAnimation() = thread {\n",
    "        generateSequence(0) { (it + 1) % frames.size }.forEach { frameIndex ->\n",
    "            currentFrame = frameIndex\n",
    "            repaint()\n",
    "            Thread.sleep(40)\n",
    "            xPosition += 3\n",
    "\n",
    "            if (xPosition > parentWidth) {\n",
    "                xPosition = -width  // Start from left edge again\n",
    "            }\n",
    "\n",
    "            setLocation(xPosition, y)\n",
    "        }\n",
    "    }\n",
    "}\n",
    "\n",
    "runInEdt {\n",
    "    val project = currentProject() ?: error(\"Project not found\")\n",
    "    val frame = WindowManager.getInstance().getFrame(project) ?: error(\"IDE frame not found\")\n",
    "    val kodee = javaClass.getResource(\"Kodee.gif\") ?: error(\"Kodee.gif not found\")\n",
    "\n",
    "    val label = AnimatedGifLabel(kodee, frame.width).apply {\n",
    "        setBounds(\n",
    "            0,\n",
    "            frame.height - preferredSize.height - 32,\n",
    "            preferredSize.width,\n",
    "            preferredSize.height,\n",
    "        )\n",
    "        startAnimation()\n",
    "    }\n",
    "\n",
    "    val layeredPane = frame.layeredPane.apply { add(label) }\n",
    "\n",
    "    Disposer.register(notebookDisposable) {\n",
    "        layeredPane.apply {\n",
    "            remove(label)\n",
    "            revalidate()\n",
    "            repaint()\n",
    "        }\n",
    "    }\n",
    "}"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "Loading `Kodee.gif` from resources is available only in IntelliJ IDEA 2025.2+.\n",
    "\n",
    "For a backward compatibility, you may use:\n",
    "```kotlin\n",
    "val kodee = notebook.workingDir.resolve(\"Kodee.gif\").toUri().toURL()\n",
    "```"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Kotlin",
   "language": "kotlin",
   "name": "kotlin"
  },
  "language_info": {
   "name": "kotlin",
   "version": "2.2.20-dev-7701",
   "mimetype": "text/x-kotlin",
   "file_extension": ".kt",
   "pygments_lexer": "kotlin",
   "codemirror_mode": "text/x-kotlin",
   "nbconvert_exporter": ""
  },
  "ktnbPluginMetadata": {
   "sessionRunMode": "IDE_PROCESS"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
