jupytutor 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jupytutor/__init__.py +38 -0
- jupytutor/_version.py +4 -0
- jupytutor/handlers.py +63 -0
- jupytutor/labextension/package.json +213 -0
- jupytutor/labextension/static/237.fd7acad62cc218608a2d.js +1 -0
- jupytutor/labextension/static/312.773d93d591cc8c4a6cc9.js +1 -0
- jupytutor/labextension/static/392.9bf7a51bbc79ca050285.js +2 -0
- jupytutor/labextension/static/392.9bf7a51bbc79ca050285.js.LICENSE.txt +9 -0
- jupytutor/labextension/static/772.d11ebfddc127515acc55.js +1 -0
- jupytutor/labextension/static/remoteEntry.c49f478ff52de713fe95.js +1 -0
- jupytutor/labextension/static/style.js +4 -0
- jupytutor/labextension/static/third-party-licenses.json +100 -0
- jupytutor-0.1.4.data/data/etc/jupyter/jupyter_server_config.d/jupytutor.json +7 -0
- jupytutor-0.1.4.data/data/share/jupyter/labextensions/jupytutor/install.json +5 -0
- jupytutor-0.1.4.data/data/share/jupyter/labextensions/jupytutor/package.json +213 -0
- jupytutor-0.1.4.data/data/share/jupyter/labextensions/jupytutor/static/237.fd7acad62cc218608a2d.js +1 -0
- jupytutor-0.1.4.data/data/share/jupyter/labextensions/jupytutor/static/312.773d93d591cc8c4a6cc9.js +1 -0
- jupytutor-0.1.4.data/data/share/jupyter/labextensions/jupytutor/static/392.9bf7a51bbc79ca050285.js +2 -0
- jupytutor-0.1.4.data/data/share/jupyter/labextensions/jupytutor/static/392.9bf7a51bbc79ca050285.js.LICENSE.txt +9 -0
- jupytutor-0.1.4.data/data/share/jupyter/labextensions/jupytutor/static/772.d11ebfddc127515acc55.js +1 -0
- jupytutor-0.1.4.data/data/share/jupyter/labextensions/jupytutor/static/remoteEntry.c49f478ff52de713fe95.js +1 -0
- jupytutor-0.1.4.data/data/share/jupyter/labextensions/jupytutor/static/style.js +4 -0
- jupytutor-0.1.4.data/data/share/jupyter/labextensions/jupytutor/static/third-party-licenses.json +100 -0
- jupytutor-0.1.4.dist-info/METADATA +165 -0
- jupytutor-0.1.4.dist-info/RECORD +27 -0
- jupytutor-0.1.4.dist-info/WHEEL +4 -0
- jupytutor-0.1.4.dist-info/licenses/LICENSE +29 -0
jupytutor/__init__.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
try:
|
|
2
|
+
from ._version import __version__
|
|
3
|
+
except ImportError:
|
|
4
|
+
# Fallback when using the package in dev mode without installing
|
|
5
|
+
# in editable mode with pip. It is highly recommended to install
|
|
6
|
+
# the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
|
|
7
|
+
import warnings
|
|
8
|
+
warnings.warn("Importing 'jupytutor' outside a proper installation.")
|
|
9
|
+
__version__ = "dev"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _jupyter_labextension_paths():
|
|
13
|
+
return [{
|
|
14
|
+
"src": "labextension",
|
|
15
|
+
"dest": "jupytutor"
|
|
16
|
+
}]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _jupyter_server_extension_points():
|
|
20
|
+
"""
|
|
21
|
+
Returns a list of dictionaries with metadata describing
|
|
22
|
+
where to find the server extension
|
|
23
|
+
"""
|
|
24
|
+
return [{
|
|
25
|
+
"module": "jupytutor"
|
|
26
|
+
}]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _load_jupyter_server_extension(server_app):
|
|
30
|
+
"""
|
|
31
|
+
Load the jupytutor server extension
|
|
32
|
+
"""
|
|
33
|
+
from .handlers import setup_handlers
|
|
34
|
+
|
|
35
|
+
web_app = server_app.web_app
|
|
36
|
+
setup_handlers(web_app)
|
|
37
|
+
|
|
38
|
+
server_app.log.info("jupytutor server extension loaded")
|
jupytutor/_version.py
ADDED
jupytutor/handlers.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Server extension handlers for jupytutor configuration
|
|
3
|
+
"""
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from jupyter_server.base.handlers import APIHandler
|
|
8
|
+
from jupyter_server.utils import url_path_join
|
|
9
|
+
import tornado
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ConfigHandler(APIHandler):
|
|
13
|
+
"""Handler for loading jupytutor configuration from user's home directory"""
|
|
14
|
+
|
|
15
|
+
@tornado.web.authenticated
|
|
16
|
+
def get(self):
|
|
17
|
+
"""Get the jupytutor configuration from ~/.config/jupytutor/config.json"""
|
|
18
|
+
try:
|
|
19
|
+
# Get the config file path from user's home directory
|
|
20
|
+
config_path = Path.home() / ".config" / "jupytutor" / "config.json"
|
|
21
|
+
|
|
22
|
+
if config_path.exists():
|
|
23
|
+
with open(config_path, 'r') as f:
|
|
24
|
+
config_data = json.load(f)
|
|
25
|
+
self.finish(json.dumps({
|
|
26
|
+
"status": "success",
|
|
27
|
+
"config": config_data
|
|
28
|
+
}))
|
|
29
|
+
else:
|
|
30
|
+
# Config file doesn't exist
|
|
31
|
+
self.set_status(404)
|
|
32
|
+
self.finish(json.dumps({
|
|
33
|
+
"status": "not_found",
|
|
34
|
+
"message": f"Config file not found at {config_path}"
|
|
35
|
+
}))
|
|
36
|
+
except json.JSONDecodeError as e:
|
|
37
|
+
# Invalid JSON
|
|
38
|
+
self.set_status(400)
|
|
39
|
+
self.finish(json.dumps({
|
|
40
|
+
"status": "error",
|
|
41
|
+
"message": f"Invalid JSON in config file: {str(e)}"
|
|
42
|
+
}))
|
|
43
|
+
except Exception as e:
|
|
44
|
+
# Other errors
|
|
45
|
+
self.set_status(500)
|
|
46
|
+
self.finish(json.dumps({
|
|
47
|
+
"status": "error",
|
|
48
|
+
"message": f"Error reading config: {str(e)}"
|
|
49
|
+
}))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def setup_handlers(web_app):
|
|
53
|
+
"""Setup the handlers for the server extension"""
|
|
54
|
+
host_pattern = ".*$"
|
|
55
|
+
base_url = web_app.settings["base_url"]
|
|
56
|
+
|
|
57
|
+
# Register the config handler
|
|
58
|
+
handlers = [
|
|
59
|
+
(url_path_join(base_url, "jupytutor", "config"), ConfigHandler)
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
web_app.add_handlers(host_pattern, handlers)
|
|
63
|
+
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jupytutor",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "A Jupyter extension for providing students LLM feedback based on autograder results and supplied course context.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"jupyter",
|
|
7
|
+
"jupyterlab",
|
|
8
|
+
"jupyterlab-extension"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/kevyg03/jupytutor",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/kevyg03/jupytutor/issues"
|
|
13
|
+
},
|
|
14
|
+
"license": "BSD-3-Clause",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Kevin Gillespie",
|
|
17
|
+
"email": "kevin.gillespie@berkeley.edu"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
|
|
21
|
+
"style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
|
|
22
|
+
"src/**/*.{ts,tsx}"
|
|
23
|
+
],
|
|
24
|
+
"main": "lib/index.js",
|
|
25
|
+
"types": "lib/index.d.ts",
|
|
26
|
+
"style": "style/index.css",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/kevyg03/jupytutor.git"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "jlpm build:lib && jlpm build:labextension:dev",
|
|
33
|
+
"build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
|
|
34
|
+
"build:labextension": "jupyter labextension build .",
|
|
35
|
+
"build:labextension:dev": "jupyter labextension build --development True .",
|
|
36
|
+
"build:lib": "tsc --sourceMap",
|
|
37
|
+
"build:lib:prod": "tsc",
|
|
38
|
+
"clean": "jlpm clean:lib",
|
|
39
|
+
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
|
|
40
|
+
"clean:lintcache": "rimraf .eslintcache .stylelintcache",
|
|
41
|
+
"clean:labextension": "rimraf jupytutor/labextension jupytutor/_version.py",
|
|
42
|
+
"clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
|
|
43
|
+
"eslint": "jlpm eslint:check --fix",
|
|
44
|
+
"eslint:check": "eslint . --cache --ext .ts,.tsx",
|
|
45
|
+
"install:extension": "jlpm build",
|
|
46
|
+
"lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
|
|
47
|
+
"lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
|
|
48
|
+
"prettier": "jlpm prettier:base --write --list-different",
|
|
49
|
+
"prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
|
|
50
|
+
"prettier:check": "jlpm prettier:base --check",
|
|
51
|
+
"stylelint": "jlpm stylelint:check --fix",
|
|
52
|
+
"stylelint:check": "stylelint --cache \"style/**/*.css\"",
|
|
53
|
+
"test": "jest --coverage",
|
|
54
|
+
"watch": "run-p watch:src watch:labextension",
|
|
55
|
+
"watch:src": "tsc -w --sourceMap",
|
|
56
|
+
"watch:labextension": "jupyter labextension watch ."
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@jupyterlab/application": "^4.4.4",
|
|
60
|
+
"@jupyterlab/apputils": "^4.5.4",
|
|
61
|
+
"@jupyterlab/cells": "^4.4.4",
|
|
62
|
+
"@jupyterlab/notebook": "^4.4.4",
|
|
63
|
+
"@lumino/widgets": "^2.7.1",
|
|
64
|
+
"browser": "^0.2.6",
|
|
65
|
+
"html-to-text": "^9.0.5",
|
|
66
|
+
"react": "^18.2.0",
|
|
67
|
+
"react-dom": "^18.2.0"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@jupyterlab/builder": "^4.0.0",
|
|
71
|
+
"@jupyterlab/testutils": "^4.0.0",
|
|
72
|
+
"@types/html-to-text": "^9.0.4",
|
|
73
|
+
"@types/jest": "^29.2.0",
|
|
74
|
+
"@types/json-schema": "^7.0.11",
|
|
75
|
+
"@types/react": "^18.2.21",
|
|
76
|
+
"@types/react-addons-linked-state-mixin": "^0.14.22",
|
|
77
|
+
"@types/react-dom": "^18.2.7",
|
|
78
|
+
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
|
79
|
+
"@typescript-eslint/parser": "^6.1.0",
|
|
80
|
+
"css-loader": "^7.1.2",
|
|
81
|
+
"eslint": "^8.36.0",
|
|
82
|
+
"eslint-config-prettier": "^8.8.0",
|
|
83
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
84
|
+
"html-loader": "^5.1.0",
|
|
85
|
+
"jest": "^29.2.0",
|
|
86
|
+
"npm-run-all2": "^7.0.1",
|
|
87
|
+
"prettier": "^3.0.0",
|
|
88
|
+
"rimraf": "^5.0.1",
|
|
89
|
+
"source-map-loader": "^1.0.2",
|
|
90
|
+
"style-loader": "^4.0.0",
|
|
91
|
+
"stylelint": "^15.10.1",
|
|
92
|
+
"stylelint-config-recommended": "^13.0.0",
|
|
93
|
+
"stylelint-config-standard": "^34.0.0",
|
|
94
|
+
"stylelint-csstree-validator": "^3.0.0",
|
|
95
|
+
"stylelint-prettier": "^4.0.0",
|
|
96
|
+
"ts-loader": "^9.5.2",
|
|
97
|
+
"typescript": "~5.0.2",
|
|
98
|
+
"webpack": "^5.100.2",
|
|
99
|
+
"webpack-cli": "^6.0.1",
|
|
100
|
+
"yjs": "^13.5.0"
|
|
101
|
+
},
|
|
102
|
+
"sideEffects": [
|
|
103
|
+
"style/*.css",
|
|
104
|
+
"style/index.js"
|
|
105
|
+
],
|
|
106
|
+
"styleModule": "style/index.js",
|
|
107
|
+
"publishConfig": {
|
|
108
|
+
"access": "public"
|
|
109
|
+
},
|
|
110
|
+
"jupyterlab": {
|
|
111
|
+
"extension": true,
|
|
112
|
+
"outputDir": "jupytutor/labextension",
|
|
113
|
+
"_build": {
|
|
114
|
+
"load": "static/remoteEntry.c49f478ff52de713fe95.js",
|
|
115
|
+
"extension": "./extension",
|
|
116
|
+
"style": "./style"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"eslintIgnore": [
|
|
120
|
+
"node_modules",
|
|
121
|
+
"dist",
|
|
122
|
+
"coverage",
|
|
123
|
+
"**/*.d.ts",
|
|
124
|
+
"tests",
|
|
125
|
+
"**/__tests__",
|
|
126
|
+
"ui-tests"
|
|
127
|
+
],
|
|
128
|
+
"eslintConfig": {
|
|
129
|
+
"extends": [
|
|
130
|
+
"eslint:recommended",
|
|
131
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
|
132
|
+
"plugin:@typescript-eslint/recommended",
|
|
133
|
+
"plugin:prettier/recommended"
|
|
134
|
+
],
|
|
135
|
+
"parser": "@typescript-eslint/parser",
|
|
136
|
+
"parserOptions": {
|
|
137
|
+
"project": "tsconfig.json",
|
|
138
|
+
"sourceType": "module"
|
|
139
|
+
},
|
|
140
|
+
"plugins": [
|
|
141
|
+
"@typescript-eslint"
|
|
142
|
+
],
|
|
143
|
+
"rules": {
|
|
144
|
+
"@typescript-eslint/naming-convention": [
|
|
145
|
+
"error",
|
|
146
|
+
{
|
|
147
|
+
"selector": "interface",
|
|
148
|
+
"format": [
|
|
149
|
+
"PascalCase"
|
|
150
|
+
],
|
|
151
|
+
"custom": {
|
|
152
|
+
"regex": "^I[A-Z]",
|
|
153
|
+
"match": true
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
"@typescript-eslint/no-unused-vars": [
|
|
158
|
+
"warn",
|
|
159
|
+
{
|
|
160
|
+
"args": "none"
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
164
|
+
"@typescript-eslint/no-namespace": "off",
|
|
165
|
+
"@typescript-eslint/no-use-before-define": "off",
|
|
166
|
+
"@typescript-eslint/quotes": [
|
|
167
|
+
"error",
|
|
168
|
+
"single",
|
|
169
|
+
{
|
|
170
|
+
"avoidEscape": true,
|
|
171
|
+
"allowTemplateLiterals": false
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
"curly": [
|
|
175
|
+
"error",
|
|
176
|
+
"all"
|
|
177
|
+
],
|
|
178
|
+
"eqeqeq": "error",
|
|
179
|
+
"prefer-arrow-callback": "error"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"prettier": {
|
|
183
|
+
"singleQuote": true,
|
|
184
|
+
"trailingComma": "none",
|
|
185
|
+
"arrowParens": "avoid",
|
|
186
|
+
"endOfLine": "auto",
|
|
187
|
+
"overrides": [
|
|
188
|
+
{
|
|
189
|
+
"files": "package.json",
|
|
190
|
+
"options": {
|
|
191
|
+
"tabWidth": 4
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
},
|
|
196
|
+
"stylelint": {
|
|
197
|
+
"extends": [
|
|
198
|
+
"stylelint-config-recommended",
|
|
199
|
+
"stylelint-config-standard",
|
|
200
|
+
"stylelint-prettier/recommended"
|
|
201
|
+
],
|
|
202
|
+
"plugins": [
|
|
203
|
+
"stylelint-csstree-validator"
|
|
204
|
+
],
|
|
205
|
+
"rules": {
|
|
206
|
+
"csstree/validator": true,
|
|
207
|
+
"property-no-vendor-prefix": null,
|
|
208
|
+
"selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$",
|
|
209
|
+
"selector-no-vendor-prefix": null,
|
|
210
|
+
"value-no-vendor-prefix": null
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.webpackChunkjupytutor=self.webpackChunkjupytutor||[]).push([[237],{50:n=>{n.exports=function(n){var t=document.createElement("style");return n.setAttributes(t,n.attributes),n.insert(t,n.options),t}},237:n=>{var t={};n.exports=function(n,o){var r=function(n){if(void 0===t[n]){var o=document.querySelector(n);if(window.HTMLIFrameElement&&o instanceof window.HTMLIFrameElement)try{o=o.contentDocument.head}catch(n){o=null}t[n]=o}return t[n]}(n);if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(o)}},318:n=>{var t=[];function o(n){for(var o=-1,r=0;r<t.length;r++)if(t[r].identifier===n){o=r;break}return o}function r(n,r){for(var e={},i=[],s=0;s<n.length;s++){var l=n[s],p=r.base?l[0]+r.base:l[0],c=e[p]||0,d="".concat(p," ").concat(c);e[p]=c+1;var u=o(d),b={css:l[1],media:l[2],sourceMap:l[3],supports:l[4],layer:l[5]};if(-1!==u)t[u].references++,t[u].updater(b);else{var f=a(b,r);r.byIndex=s,t.splice(s,0,{identifier:d,updater:f,references:1})}i.push(d)}return i}function a(n,t){var o=t.domAPI(t);return o.update(n),function(t){if(t){if(t.css===n.css&&t.media===n.media&&t.sourceMap===n.sourceMap&&t.supports===n.supports&&t.layer===n.layer)return;o.update(n=t)}else o.remove()}}n.exports=function(n,a){var e=r(n=n||[],a=a||{});return function(n){n=n||[];for(var i=0;i<e.length;i++){var s=o(e[i]);t[s].references--}for(var l=r(n,a),p=0;p<e.length;p++){var c=o(e[p]);0===t[c].references&&(t[c].updater(),t.splice(c,1))}e=l}}},495:n=>{n.exports=function(n,t){if(t.styleSheet)t.styleSheet.cssText=n;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(n))}}},551:n=>{n.exports=function(n){return n[1]}},594:(n,t,o)=>{o.d(t,{A:()=>s});var r=o(551),a=o.n(r),e=o(992),i=o.n(e)()(a());i.push([n.id,"/* Use a container class to avoid style collisions with JupyterLab */\n.jupytutor {\n background-color: transparent;\n padding: 12px;\n border: none;\n margin-top: 5px;\n border-radius: 0;\n font-family: var(--jp-ui-font-family); /* Use JupyterLab's theme variables! */\n transition: all 0.3s ease;\n}\n\n/* Loading state for the entire jupytutor container */\n.jupytutor.loading {\n animation: subtleGlow 3s ease-in-out infinite;\n}\n\n/* Subtle glow animation for the container */\n@keyframes subtleGlow {\n 0%,\n 100% {\n filter: brightness(1);\n }\n 50% {\n filter: brightness(1.02);\n }\n}\n\n.jupytutor label {\n font-weight: bold;\n margin-right: 8px;\n}\n\n.jupytutor button {\n /* Use JupyterLab's themed button style for a native look and feel */\n background-color: var(--jp-brand-color1);\n color: white;\n border: none;\n padding: 4px 8px;\n border-radius: 3px;\n margin-left: 5px;\n}\n\n.jupytutor button:hover {\n background-color: var(--jp-brand-color0);\n}\n\n.jupytutor p {\n margin-top: 10px;\n font-style: italic;\n color: var(--jp-content-font-color2);\n}\n\n.tailoredOptionsContainer {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n flex-wrap: wrap;\n align-items: flex-start;\n gap: 4px;\n margin: 2px 0 6px 0;\n padding: 3px 0 3px 6px;\n background: transparent;\n border: none;\n border-left: 2px solid var(--jp-border-color0);\n box-shadow: none;\n animation: fadeUpIn 0.6s ease-out;\n transition:\n all 0.3s ease,\n border-left-color 0.2s ease;\n max-width: 100%;\n}\n\n/* Subtle border color change on hover for better integration */\n.tailoredOptionsContainer:hover {\n border-left-color: var(--jp-border-color1);\n}\n\n/* Loading state for suggestions */\n.tailoredOptionsContainer.loading {\n animation: suggestionsPulse 2s ease-in-out infinite;\n opacity: 0.5;\n}\n\n@keyframes suggestionsPulse {\n 0%,\n 100% {\n opacity: 1;\n transform: translateY(0);\n }\n 50% {\n opacity: 0.8;\n transform: translateY(-2px);\n }\n}\n\n@keyframes fadeUpIn {\n 0% {\n opacity: 0;\n transform: translateY(20px);\n }\n 100% {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.tailoredOption {\n background: transparent;\n width: 120px;\n min-height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 6px 8px;\n margin: 0;\n border: 1px solid var(--jp-border-color0);\n border-radius: 16px;\n font-family: var(--jp-ui-font-family);\n font-size: var(--jp-ui-font-size1);\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: none;\n position: relative;\n overflow: hidden;\n flex-shrink: 0;\n}\n\n.tailoredOption::before {\n content: '';\n position: absolute;\n top: 0;\n left: -100%;\n width: 100%;\n height: 100%;\n background: linear-gradient(\n 90deg,\n transparent,\n rgba(var(--jp-brand-color1-rgb, 0, 123, 255), 0.1),\n transparent\n );\n transition: left 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.tailoredOption:hover {\n background-color: var(--jp-layout-color2);\n border-color: var(--jp-border-color1);\n transform: translateY(-1px);\n box-shadow: 0 2px 8px rgba(var(--jp-brand-color1-rgb, 0, 123, 255), 0.15);\n}\n\n.tailoredOption:hover::before {\n left: 100%;\n}\n\n.tailoredOption:active {\n transform: translateY(0);\n background-color: var(--jp-layout-color3);\n}\n\n.tailoredOption h4 {\n margin: 0;\n padding: 0;\n font-size: var(--jp-ui-font-size1);\n font-weight: 500;\n line-height: 1.2;\n text-align: center;\n word-wrap: break-word;\n hyphens: auto;\n max-width: 100%;\n}\n\n/* Chat Bubble Styles */\n.chat-container {\n margin-top: 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n max-width: 100%;\n max-height: 500px;\n overflow-y: auto;\n padding-right: 8px;\n padding-bottom: 0;\n padding-top: 10px;\n scrollbar-width: thin;\n scrollbar-color: var(--jp-border-color0) transparent;\n /* Smooth scrolling */\n scroll-behavior: smooth;\n /* Add subtle edge indicator */\n border-top: 0.5px solid var(--jp-border-color0);\n}\n\n.chat-container::-webkit-scrollbar {\n width: 6px;\n}\n\n.chat-container::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.chat-container::-webkit-scrollbar-thumb {\n background-color: var(--jp-border-color0);\n border-radius: 3px;\n}\n\n.chat-container::-webkit-scrollbar-thumb:hover {\n background-color: var(--jp-border-color1);\n}\n\n.chat-bubble {\n max-width: 70%;\n padding: 8px 12px;\n border-radius: 18px;\n position: relative;\n word-wrap: break-word;\n font-family: var(--jp-ui-font-family);\n font-size: var(--jp-ui-font-size1);\n line-height: 1.4;\n}\n\n.chat-bubble-left {\n align-self: flex-start;\n background-color: var(--jp-layout-color2);\n color: var(--jp-ui-font-color1);\n border-bottom-left-radius: 6px;\n box-shadow: 0 1px 2px var(--jp-shadow-penumbra-color);\n}\n\n.chat-bubble-right {\n align-self: flex-end;\n background-color: var(--jp-brand-color1);\n color: white;\n border-bottom-right-radius: 6px;\n box-shadow: 0 1px 2px var(--jp-shadow-penumbra-color);\n}\n\n.chat-message-wrapper {\n display: flex;\n flex-direction: column;\n gap: 2px;\n margin-bottom: 0;\n}\n\n.chat-sender-label {\n font-size: var(--jp-ui-font-size0);\n font-weight: 600;\n margin-bottom: 1px;\n padding: 0 2px;\n}\n\n.chat-sender-label.user {\n align-self: flex-end;\n color: var(--jp-brand-color1);\n}\n\n.chat-sender-label.assistant {\n align-self: flex-start;\n color: var(--jp-ui-font-color2);\n}\n\n.chat-sender {\n font-size: var(--jp-ui-font-size0);\n font-weight: 600;\n color: var(--jp-brand-color1);\n margin-bottom: 4px;\n opacity: 0.8;\n}\n\n.chat-message {\n margin-bottom: 4px;\n}\n\n.chat-timestamp {\n font-size: var(--jp-ui-font-size0);\n opacity: 0.6;\n text-align: right;\n margin-top: 4px;\n}\n\n.chat-bubble-left .chat-timestamp {\n text-align: left;\n color: var(--jp-ui-font-color2);\n}\n\n.chat-bubble-right .chat-timestamp {\n color: rgba(255, 255, 255, 0.8);\n}\n\n/* Chat Bubble Animation */\n.chat-bubble {\n opacity: 0;\n transform: translateY(20px);\n transition:\n opacity 0.6s ease-out,\n transform 0.6s ease-out;\n}\n\n.chat-bubble-visible {\n opacity: 1;\n transform: translateY(0);\n}\n\n/* Assistant Message Styles */\n.assistant-message {\n opacity: 0;\n transform: translateY(20px);\n transition:\n opacity 0.6s ease-out,\n transform 0.6s ease-out;\n background: transparent;\n border: none;\n padding: 0;\n margin: 0;\n max-width: 100%;\n font-family: var(--jp-ui-font-family);\n font-size: var(--jp-ui-font-size1);\n line-height: 1.6;\n color: var(--jp-ui-font-color1);\n}\n\n.assistant-visible {\n opacity: 1;\n transform: translateY(0);\n}\n\n.assistant-streaming {\n opacity: 1 !important;\n transform: translateY(0) !important;\n transition: none !important;\n}\n\n.assistant-text-line {\n margin-bottom: 8px;\n word-wrap: break-word;\n}\n\n.assistant-text-line.indent-1 {\n margin-left: 16px;\n}\n\n.assistant-text-line.indent-2 {\n margin-left: 32px;\n}\n\n.assistant-text-line.indent-3 {\n margin-left: 48px;\n}\n\n.assistant-text-line.indent-4 {\n margin-left: 64px;\n}\n\n.assistant-list-item {\n display: flex;\n align-items: flex-start;\n margin-bottom: 6px;\n word-wrap: break-word;\n}\n\n.list-bullet {\n color: var(--jp-brand-color1);\n font-weight: bold;\n margin-right: 8px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.list-number {\n color: var(--jp-brand-color1);\n font-weight: bold;\n margin-right: 8px;\n flex-shrink: 0;\n margin-top: 2px;\n min-width: 20px;\n}\n\n.list-content {\n flex: 1;\n}\n\n.list-content-header {\n flex: 1;\n font-weight: bold;\n}\n\n.assistant-code-line {\n font-family: var(--jp-code-font-family);\n font-size: var(--jp-code-font-size0);\n background-color: var(--jp-layout-color2);\n border: 1px solid var(--jp-border-color1);\n border-radius: 4px;\n padding: 8px 12px;\n margin: 4px 0;\n overflow-x: auto;\n white-space: pre;\n color: var(--jp-content-font-color1);\n}\n\n.assistant-empty-line {\n height: 8px;\n}\n\n.assistant-header-line {\n margin-bottom: 12px;\n word-wrap: break-word;\n font-size: var(--jp-ui-font-size2);\n color: var(--jp-ui-font-color0);\n}\n\n/* Assistant Link Styles */\n.jupytutor .assistant-link {\n color: #0066cc !important;\n text-decoration: underline;\n text-decoration-color: #0066cc;\n transition: all 0.2s ease;\n font-weight: 500;\n cursor: pointer;\n}\n\n.jupytutor .assistant-link:hover {\n color: #004499 !important;\n text-decoration-color: #004499;\n background-color: rgba(0, 102, 204, 0.1);\n border-radius: 2px;\n padding: 1px 2px;\n margin: -1px -2px;\n}\n\n.jupytutor .assistant-link:visited {\n color: #663399 !important;\n text-decoration-color: #663399;\n}\n\n.jupytutor .assistant-link:active {\n color: #004499 !important;\n text-decoration-color: #004499;\n}\n\n/* Stagger animation for multiple chat bubbles */\n.chat-container .chat-bubble:nth-child(1) {\n transition-delay: 0.1s;\n}\n.chat-container .chat-bubble:nth-child(2) {\n transition-delay: 0.2s;\n}\n.chat-container .chat-bubble:nth-child(3) {\n transition-delay: 0.3s;\n}\n.chat-container .chat-bubble:nth-child(4) {\n transition-delay: 0.4s;\n}\n.chat-container .chat-bubble:nth-child(5) {\n transition-delay: 0.5s;\n}\n\n/* Remove bottom margin from last message to eliminate bottom padding */\n.chat-container .chat-message-wrapper:last-child {\n margin-bottom: 0;\n}\n\n/* Chat Input Styles */\n.chat-input-container {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 12px;\n width: 100%;\n max-width: 100%;\n}\n\n.chat-input {\n flex: 1;\n padding: 8px 16px;\n border: 1px solid var(--jp-border-color0);\n border-radius: 18px;\n font-family: var(--jp-ui-font-family);\n font-size: var(--jp-ui-font-size1);\n background-color: var(--jp-layout-color1);\n color: var(--jp-ui-font-color1);\n transition: all 0.2s ease;\n outline: none;\n min-height: 36px;\n box-sizing: border-box;\n}\n\n.chat-input:focus {\n border-color: var(--jp-brand-color1);\n box-shadow: 0 0 0 3px rgba(var(--jp-brand-color1-rgb, 0, 123, 255), 0.1);\n transform: translateY(-1px);\n}\n\n.chat-input:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.chat-input::placeholder {\n color: var(--jp-ui-font-color2);\n opacity: 0.7;\n}\n\n.chat-submit-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: none;\n border-radius: 50%;\n background-color: var(--jp-brand-color1);\n color: white;\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n position: relative;\n overflow: hidden;\n}\n\n.chat-submit-btn:hover:not(:disabled) {\n background-color: var(--jp-brand-color0);\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(var(--jp-brand-color1-rgb, 0, 123, 255), 0.3);\n}\n\n.chat-submit-btn:active:not(:disabled) {\n transform: translateY(0);\n box-shadow: 0 2px 6px rgba(var(--jp-brand-color1-rgb, 0, 123, 255), 0.3);\n}\n\n.chat-submit-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n transform: none;\n box-shadow: none;\n}\n\n.chat-submit-btn .submit-icon {\n width: 20px;\n height: 20px;\n transition: transform 0.2s ease;\n}\n\n.chat-submit-btn:hover:not(:disabled) .submit-icon {\n transform: scale(1.1);\n}\n\n/* Loading State Styles */\n.chat-submit-btn.loading {\n background-color: var(--jp-brand-color1);\n pointer-events: none;\n}\n\n/* Enhanced loading animations for the entire input container */\n.chat-input-container.loading {\n animation: containerPulse 2s ease-in-out infinite;\n position: relative;\n}\n\n.chat-input-container.loading::before {\n content: '';\n position: absolute;\n top: 0;\n left: -100%;\n width: 100%;\n height: 100%;\n background: linear-gradient(\n 90deg,\n transparent,\n rgba(var(--jp-brand-color1-rgb, 0, 123, 255), 0.1),\n transparent\n );\n animation: lightSweep 2s ease-in-out infinite;\n border-radius: 18px;\n}\n\n/* Loading state for the input field */\n.chat-input.loading {\n border-color: var(--jp-brand-color1);\n animation: inputPulse 2s ease-in-out infinite;\n box-shadow: 0 0 0 3px rgba(var(--jp-brand-color1-rgb, 0, 123, 255), 0.1);\n}\n\n/* Loading state for placeholder text */\n.chat-input.loading::placeholder {\n animation: textPulse 1.5s ease-in-out infinite;\n font-style: italic;\n color: var(--jp-brand-color1);\n}\n\n/* Container pulse animation */\n@keyframes containerPulse {\n 0%,\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n 50% {\n transform: scale(1.01);\n opacity: 0.95;\n }\n}\n\n/* Light sweep animation */\n@keyframes lightSweep {\n 0% {\n left: -100%;\n }\n 50% {\n left: 100%;\n }\n 100% {\n left: 100%;\n }\n}\n\n/* Input field pulse animation */\n@keyframes inputPulse {\n 0%,\n 100% {\n border-color: var(--jp-brand-color1);\n box-shadow: 0 0 0 3px rgba(var(--jp-brand-color1-rgb, 0, 123, 255), 0.1);\n }\n 50% {\n border-color: var(--jp-brand-color0);\n box-shadow: 0 0 0 3px rgba(var(--jp-brand-color1-rgb, 0, 123, 255), 0.2);\n }\n}\n\n/* Text pulse animation */\n@keyframes textPulse {\n 0%,\n 100% {\n opacity: 0.7;\n transform: scale(1);\n }\n 50% {\n opacity: 1;\n transform: scale(1.02);\n }\n}\n\n.loading-spinner {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n}\n\n.spinner-ring {\n width: 20px;\n height: 20px;\n border: 2px solid transparent;\n border-top: 2px solid currentColor;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n\n/* Responsive adjustments */\n@media (max-width: 768px) {\n .chat-input-container {\n gap: 6px;\n }\n\n .chat-input {\n padding: 6px 12px;\n min-height: 32px;\n }\n\n .chat-submit-btn {\n width: 32px;\n height: 32px;\n }\n}\n",""]);const s=i},862:(n,t,o)=>{n.exports=function(n){var t=o.nc;t&&n.setAttribute("nonce",t)}},879:n=>{n.exports=function(n){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var t=n.insertStyleElement(n);return{update:function(o){!function(n,t,o){var r="";o.supports&&(r+="@supports (".concat(o.supports,") {")),o.media&&(r+="@media ".concat(o.media," {"));var a=void 0!==o.layer;a&&(r+="@layer".concat(o.layer.length>0?" ".concat(o.layer):""," {")),r+=o.css,a&&(r+="}"),o.media&&(r+="}"),o.supports&&(r+="}");var e=o.sourceMap;e&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(e))))," */")),t.styleTagTransform(r,n,t.options)}(t,n,o)},remove:function(){!function(n){if(null===n.parentNode)return!1;n.parentNode.removeChild(n)}(t)}}}},965:(n,t,o)=>{o.d(t,{A:()=>s});var r=o(551),a=o.n(r),e=o(992),i=o.n(e)()(a());i.push([n.id,"/*\n See the JupyterLab Developer Guide for useful CSS Patterns:\n\n https://jupyterlab.readthedocs.io/en/stable/developer/css.html\n*/\n",""]);const s=i},992:n=>{n.exports=function(n){var t=[];return t.toString=function(){return this.map(function(t){var o="",r=void 0!==t[5];return t[4]&&(o+="@supports (".concat(t[4],") {")),t[2]&&(o+="@media ".concat(t[2]," {")),r&&(o+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),o+=n(t),r&&(o+="}"),t[2]&&(o+="}"),t[4]&&(o+="}"),o}).join("")},t.i=function(n,o,r,a,e){"string"==typeof n&&(n=[[null,n,void 0]]);var i={};if(r)for(var s=0;s<this.length;s++){var l=this[s][0];null!=l&&(i[l]=!0)}for(var p=0;p<n.length;p++){var c=[].concat(n[p]);r&&i[c[0]]||(void 0!==e&&(void 0===c[5]||(c[1]="@layer".concat(c[5].length>0?" ".concat(c[5]):""," {").concat(c[1],"}")),c[5]=e),o&&(c[2]?(c[1]="@media ".concat(c[2]," {").concat(c[1],"}"),c[2]=o):c[2]=o),a&&(c[4]?(c[1]="@supports (".concat(c[4],") {").concat(c[1],"}"),c[4]=a):c[4]="".concat(a)),t.push(c))}},t}}}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.webpackChunkjupytutor=self.webpackChunkjupytutor||[]).push([[312],{312:(t,e,s)=>{var l=s(318),n=s.n(l),r=s(879),a=s.n(r),u=s(237),A=s.n(u),o=s(862),i=s.n(o),c=s(50),b=s.n(c),d=s(495),m=s.n(d),y=s(965),h={};h.styleTagTransform=m(),h.setAttributes=i(),h.insert=A().bind(null,"head"),h.domAPI=a(),h.insertStyleElement=b(),n()(y.A,h),y.A&&y.A.locals&&y.A.locals;var p=s(594),f={};f.styleTagTransform=m(),f.setAttributes=i(),f.insert=A().bind(null,"head"),f.domAPI=a(),f.insertStyleElement=b(),n()(p.A,f),p.A&&p.A.locals&&p.A.locals}}]);
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! For license information please see 392.9bf7a51bbc79ca050285.js.LICENSE.txt */
|
|
2
|
+
"use strict";(self.webpackChunkjupytutor=self.webpackChunkjupytutor||[]).push([[392],{20:(e,t,s)=>{var n=s(345),o=Symbol.for("react.element"),r=(Symbol.for("react.fragment"),Object.prototype.hasOwnProperty),a=n.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,i={key:!0,ref:!0,__self:!0,__source:!0};function l(e,t,s){var n,l={},c=null,u=null;for(n in void 0!==s&&(c=""+s),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(u=t.ref),t)r.call(t,n)&&!i.hasOwnProperty(n)&&(l[n]=t[n]);if(e&&e.defaultProps)for(n in t=e.defaultProps)void 0===l[n]&&(l[n]=t[n]);return{$$typeof:o,type:e,key:c,ref:u,props:l,_owner:a.current}}t.jsx=l,t.jsxs=l},296:(e,t,s)=>{s.d(t,{A:()=>u});var n=s(551),o=s.n(n),r=s(992),a=s.n(r),i=s(965),l=s(594),c=a()(o());c.i(i.A),c.i(l.A),c.push([e.id,"\n",""]);const u=c},392:(e,t,s)=>{s.r(t),s.d(t,{DEMO_PRINTS:()=>G,default:()=>Q});var n=s(980),o=s(345),r=s(848),a=s(436);const i={api:{baseURL:"https://server-jupytutor-cmeghve8dyf3agde.westus-01.azurewebsites.net/"},usage:{show_on_success:!0,show_on_free_response:!0,automatic_first_query_on_error:!1,use_streaming:!0},context_gathering:{enabled:!0,whitelist:["inferentialthinking.com"],blacklist:["data8.org","berkeley.edu","gradescope.com"],jupyterbook:{url:"inferentialthinking.com",link_expansion:!0}},keywords:{free_response_regex:/.*question\s+\d+(?:\.\d+)*\.\s+.*\(?\d+\s+points\)?.*/i,success_regex:/.* passed!.*/i},instructor_note:""},l=i.api.baseURL;var c=s(318),u=s.n(c),d=s(879),h=s.n(d),p=s(237),g=s.n(p),m=s(862),f=s.n(m),x=s(50),y=s.n(x),k=s(495),v=s.n(k),w=s(296),b={};b.styleTagTransform=v(),b.setAttributes=f(),b.insert=g().bind(null,"head"),b.domAPI=h(),b.insertStyleElement=y(),u()(w.A,b),w.A&&w.A.locals&&w.A.locals;var _=s(375);const L="The following input after this is an aggregation of potentially relevant resources to the assignment.\n\n Keep in mind, some are relevant to each particular question, some are not. Each different source is started with its url surrounded by the tokens [LINK] and [/LINK] (e.g. [LINK]https://www.data8.org/[LINK]). You should attempt to cite these source labels when you use the source contents in your response, formatted as link HTML tags. This should function to encourage student agency and help to not reveal answers directly.",T=class{constructor({sourceLinks:e=[],whitelistedURLs:t=[],blacklistedURLs:s=["data8.org","berkeley.edu","gradescope.com"],jupyterbookURL:n="inferentialthinking.com",attemptJupyterbookLinkExpansion:o=!1,debug:r=!1}={}){this._context=null,this._sourceLinks=e,this._blacklistedURLs=s,this._whitelistedURLs=t,this._loaded=!1,r||(o?this._expandJupyterBookLinksAsync(n):this.scrapeSourceLinks())}async scrapeSourceLinks(){if(0===this._sourceLinks.length)return this._context=null,void(this._loaded=!0);const e=e=>this._blacklistedURLs.some(t=>e.includes(t)),t=e=>this._whitelistedURLs.some(t=>e.includes(t));this._whitelistedURLs.length>0&&(this._sourceLinks=this._sourceLinks.filter(e=>t(e))),this._blacklistedURLs.length>0&&(this._sourceLinks=this._sourceLinks.filter(t=>!e(t)));const s=(await Promise.all(this._sourceLinks.map(async e=>await(async(e,t=e=>e)=>{try{const s=await fetch(t(e));if(!s.ok)return 404===s.status||console.log(`HTTP ${s.status}: ${s.statusText}`),null;const n=await s.text(),o=await(async e=>{try{return(0,_.convert)(e,{wordwrap:!1,selectors:[{selector:"script",format:"skip"},{selector:"style",format:"skip"},{selector:"nav",format:"skip"},{selector:"header",format:"skip"},{selector:"footer",format:"skip"},{selector:"aside",format:"skip"},{selector:".navbar",format:"skip"},{selector:".navigation",format:"skip"},{selector:".sidebar",format:"skip"},{selector:".toc",format:"skip"},{selector:".table-of-contents",format:"skip"},{selector:".breadcrumb",format:"skip"},{selector:".pagination",format:"skip"},{selector:".social-share",format:"skip"},{selector:".comments",format:"skip"},{selector:".advertisement",format:"skip"},{selector:".ads",format:"skip"},{selector:".ad",format:"skip"},{selector:".sponsor",format:"skip"},{selector:".menu",format:"skip"},{selector:".navigation-menu",format:"skip"},{selector:".site-header",format:"skip"},{selector:".site-footer",format:"skip"}],baseElements:{selectors:["main",".main-content","#main",".content","body"]}})}catch(t){return console.warn("Error converting HTML to text:",t),e}})(n);return`[LINK] ${e} [/LINK]\n${o}`}catch(e){return e instanceof Error&&(e.message.includes("404")||e.message.includes("Not Found"))||console.error(`Error scraping page text: ${e}`),null}})(e)))).filter(e=>null!==e).join("\n\n");this._context=s||"",""===this._context&&(this._context=null),this._loaded=!0}getContext(){if(this._loaded){if(null===this._context)return console.error("Context does not lead to any detected resource text."),null}else console.error("Context still loading.");return this._context}getSourceLinks(){return this._sourceLinks}async _expandJupyterBookLinksAsync(e){try{this._sourceLinks=await this.expandJupyterBookLinks(this._sourceLinks,e)}catch(e){console.warn("Failed to expand JupyterBook links:",e)}this.scrapeSourceLinks()}async expandJupyterBookLinks(e,t){const s=e.filter(e=>e.includes(t)).map(async e=>{try{return await this.findJupyterBookLinks(e,t)}catch(t){return console.warn(`Failed to expand links for ${e}:`,t),[]}}),n=await Promise.all(s),o=[],r=new Set;let a=0;for(let s=0;s<e.length;s++){const i=e[s];if(i.includes(t)){const e=this.normalizeUrl(i);r.has(e)||(o.push(i),r.add(e));const t=(n[a]||[]).filter(t=>{const s=this.normalizeUrl(t);return s!==e&&!r.has(s)});t.sort((e,t)=>{const s=new URL(e).pathname,n=new URL(t).pathname,o=s.match(/\/chapters\/(\d+)(?:\/(\d+))?/),r=n.match(/\/chapters\/(\d+)(?:\/(\d+))?/);if(o&&r){const e=parseInt(o[1]),t=parseInt(r[1]),s=o[2]?parseInt(o[2]):0,n=r[2]?parseInt(r[2]):0;return e!==t?e-t:s-n}return s.localeCompare(n)});for(const e of t){const t=this.normalizeUrl(e);r.has(t)||(o.push(e),r.add(t))}a++}else{const e=this.normalizeUrl(i);r.has(e)||(o.push(i),r.add(e))}}return o}normalizeUrl(e){try{const t=new URL(e);return t.hash="",t.pathname.length>1&&t.pathname.endsWith("/")&&(t.pathname=t.pathname.slice(0,-1)),t.href}catch(t){return e}}async findJupyterBookLinks(e,t){try{const s=await fetch(e);if(!s.ok)return[];const n=await s.text(),o=[],r=new URL(e).pathname,a=r.match(/\/chapters\/(\d+)/),i=r.match(/\/chapters\/(\d+)\/(\d+)/);let l=null,c=null;i?(l=i[1],c=i[2]):a&&(l=a[1]);const u=/<a[^>]+href\s*=\s*["']([^"']+)["'][^>]*>/gi;let d;for(;null!==(d=u.exec(n));){const s=d[1];if(!s)continue;if(s.includes(".ipynb")||s.includes("mybinder")||s.includes("datahub")||s.includes("_sources")||s.includes("_static")||s.includes("_images"))continue;let n;if(n=s.startsWith("/")?new URL(s,e).href:s.startsWith("http")?s:new URL(s,e).href,n.includes(t)){const e=this.normalizeUrl(n),t=new URL(e).pathname;this.isChapterSectionLink(t,l,c)&&o.push(e)}}const h=[...new Set(o)];return this.sortChapterSectionLinks(h,l,c)}catch(t){return console.warn(`Error finding JupyterBook links for ${e}:`,t),[]}}isChapterSectionLink(e,t,s){if(e.includes("/_sources/")||e.includes("/_static/")||e.includes("/_images/")||e.endsWith(".ipynb")||e.endsWith(".pdf")||e.endsWith(".zip")||e.endsWith(".tar.gz"))return!1;if(!e.includes("/chapters/"))return!1;const n=e.match(/\/chapters\/(\d+)/);if(s){if(n&&n[1]===t)return!0}else if(t&&n&&n[1]===t)return!0;return!1}sortChapterSectionLinks(e,t,s){return e.sort((e,t)=>{const s=new URL(e).pathname,n=new URL(t).pathname,o=s.match(/\/chapters\/(\d+)/),r=s.match(/\/chapters\/(\d+)\/(\d+)/),a=n.match(/\/chapters\/(\d+)/),i=n.match(/\/chapters\/(\d+)\/(\d+)/),l=o?parseInt(o[1]):0,c=r?parseInt(r[2]):0,u=a?parseInt(a[1]):0,d=i?parseInt(i[2]):0;return l!==u?l-u:c!==d?c-d:s.localeCompare(n)})}},j=e=>{const t=/<a\s+href=["']([^"']+)["'][^>]*>(.*?)<\/a>/gi,s=[];let n,o=0;for(;null!==(n=t.exec(e));){n.index>o&&s.push((0,r.jsx)("span",{children:e.slice(o,n.index)},`text-${o}`));const t=n[1],a=n[2];s.push((0,r.jsx)("a",{href:t,target:"_blank",rel:"noopener noreferrer",className:"assistant-link",children:a},`link-${n.index}`)),o=n.index+n[0].length}return o<e.length&&s.push((0,r.jsx)("span",{children:e.slice(o)},`text-${o}`)),s.length>0?s:[(0,r.jsx)("span",{children:e},"text")]},E=e=>{const[t,s]=(0,o.useState)(""),n=(0,o.useRef)(null),a=(0,o.useRef)(!1),i=(0,o.useRef)([]),[c,u]=(0,o.useState)(null),{sendTextbookWithRequest:d,contextRetriever:h,cellType:p,userId:g,config:m}=e,[f,x]=(0,o.useState)([]),[y,k]=(0,o.useState)(!1);(0,o.useEffect)(()=>{n.current&&(n.current.scrollTop=n.current.scrollHeight)},[f]),(0,o.useEffect)(()=>{n.current&&c&&(n.current.scrollTop=n.current.scrollHeight)},[c]),(0,o.useEffect)(()=>{G&&console.log("Chat history changed:",f)},[f]);const v=(e,t="file")=>{try{if(!e.startsWith("data:"))return G&&console.warn('Invalid data URL: must start with "data:"',e),null;const[s,n]=e.split(",");if(!n)return G&&console.warn("Invalid data URL: missing base64 data",e),null;const o=s.match(/data:([^;]+)/),r=o?o[1]:"application/octet-stream";if(!r.startsWith("image/"))return G&&console.warn(`Unexpected MIME type: ${r}, expected image/*`,e),null;const a=atob(n),i=new Array(a.length);for(let e=0;e<a.length;e++)i[e]=a.charCodeAt(e);const l=new Uint8Array(i),c=new File([l],t,{type:r});return G&&console.log(`Created file: ${t}, type: ${r}, size: ${c.size} bytes`),c}catch(t){throw console.error("Error converting data URL to File:",t),console.error("Data URL preview:",e.substring(0,100)+"..."),new Error(`Invalid data URL format: ${t instanceof Error?t.message:String(t)}`)}},w=async(n,o=m.usage.use_streaming)=>{var r;const c=""===t&&!n,y=0===f.length;if(c&&!y)return;let w=n||t,_=[...f];if(c)w="This is my current attempt at the question. Focus on providing concise and accurate feedback that promotes understanding.";else{const e={role:"user",content:w};_=[..._,e],x(_)}k(!0);const T=((t,s,n=5)=>{const o=[];for(let n=e.activeIndex;n>Math.max(0,e.activeIndex-s);n--){const e=t[n];if(e.images.length>0&&"code"===e.type&&o.push(...e.images),e.images.length>0&&"code"!==e.type){o.push(...e.images);break}}return o.slice(0,n)})(e.allCells,10,5);G&&T.length>0&&console.log("First image preview:",T[0].substring(0,100)+"...");try{a.current||(i.current=(()=>{const t=e.allCells.filter(e=>e.images.length>0||""!==e.text||null!=e.text||null!=e.outputText),s=t.findIndex(t=>t.index===e.activeIndex);let n;switch(e.notebookContext){case"whole":n=t;break;case"upToGrader":n=t.slice(0,Math.max(0,s+1));break;case"fiveAround":n=t.slice(Math.max(0,s-5),Math.min(t.length,s+5));break;case"tenAround":n=t.slice(Math.max(0,s-10),Math.min(t.length,s+10));break;case"none":n=[t[s]]}return(e=>{let t=[];if(d&&null!=h){const e=h.getContext();t=[{role:"system",content:[{text:L,type:"input_text"}],noShow:!0},{role:"system",content:[{text:e||"",type:"input_text"}],noShow:!0}],G&&console.log("Sending textbook with request")}else G&&console.log("NOT sending textbook with request");const s=e.map(e=>""!==e.outputText&&null!=e.outputText&&"code"===e.type||"grader"===e.type?{role:"system",content:[{text:e.text+"\nThe above code produced the following output:\n"+e.outputText,type:"input_text"}],noShow:!0}:"free_response"===e.type?(G&&console.log("Sending free response prompt with request!"),{role:"system",content:[{text:"The following is the student's response to the free response question directly above: [response start]"+e.text+"\n[response over]. Provide concise feedback on the response with a focus on accuracy and understanding.",type:"input_text"}],noShow:!0}):{role:"system",content:[{text:e.text,type:"input_text"}],noShow:!0});return[...t,...s]})(n)})());const t=a.current?_.slice(0,-1):[...i.current,..._],s=T.map((e,t)=>{let s="image.png";try{const[n]=e.split(","),o=n.match(/data:([^;]+)/);if(o){const e=o[1];s=`image_${t}.${"image/png"===e?"png":"image/jpeg"===e?"jpg":"image/gif"===e?"gif":"png"}`}}catch(e){console.warn("Could not extract filename from image:",e),s=`image_${t}.png`}return{name:s,file:v(e,s)}});if(o){u("");const e=new FormData;e.append("chatHistory",JSON.stringify(t)),e.append("images",JSON.stringify(T)),e.append("newMessage",w),e.append("currentChatHistory",JSON.stringify(_)),e.append("cellType",p),e.append("userId",g||""),s.filter(e=>e.file instanceof File).forEach(t=>{t.file&&e.append(t.name,t.file)});const n=await fetch(`${m.api.baseURL}interaction/stream`,{method:"POST",body:e,mode:"cors",credentials:"include",cache:"no-cache"});if(!n.ok)throw new Error(`HTTP error! status: ${n.status}`);const o=null===(r=n.body)||void 0===r?void 0:r.getReader();if(!o)throw new Error("No response body reader available");const a=new TextDecoder;let i="",l="";try{for(;;){const{done:e,value:t}=await o.read();if(e)break;i+=a.decode(t,{stream:!0});const s=i.split("\n");i=s.pop()||"";for(const e of s)if(""!==e.trim()&&e.startsWith("data: "))try{const t=JSON.parse(e.slice(6));if("message_delta"===t.type)l+=t.content,u(l);else if("final_response"===t.type){b(t.data,c,w),u(null);break}}catch(t){console.warn("Failed to parse SSE data:",t,"Line:",e)}}}finally{o.releaseLock()}}else{const e=await async function(e,t={}){const{data:s={},files:n=[],headers:o={},authToken:r=null,method:a="POST",baseURL:i=l}=t;try{const t={...o};r&&(t.Authorization=`Bearer ${r}`);let l=null;(n.length>0||Object.keys(s).length>0)&&(l=function(e={},t=[]){const s=new FormData;Object.keys(e).forEach(t=>{null!==e[t]&&void 0!==e[t]&&("object"==typeof e[t]?s.append(t,JSON.stringify(e[t])):s.append(t,e[t]))});for(const e of t)e.file&&e.name&&(e.file instanceof File?s.append(e.name,e.file):console.warn("Invalid file object:",e.file,"Expected File object (browser environment only)"));return s}(s,n));const c={method:a.toUpperCase(),headers:t,body:l,mode:"cors",credentials:"include",cache:"no-cache"};"GET"===a.toUpperCase()&&delete c.body,G&&console.log("API Request:",{url:`${i}${e}`,method:c.method,headers:c.headers,body:l instanceof FormData?"FormData":l});const u=await fetch(`${i}${e}`,c);if(!u.ok)throw new Error(`HTTP error! status: ${u.status}`);let d;const h=u.headers.get("content-type");return d=h&&h.includes("application/json")?await u.json():await u.text(),{success:!0,data:d,status:u.status,headers:u.headers}}catch(e){return{success:!1,error:e.message,status:null}}}("interaction",{method:"POST",data:{chatHistory:t,images:T,newMessage:w,currentChatHistory:_,cellType:p,userId:g||""},files:s.filter(e=>e.file instanceof File)});e.success?b(e.data,c,w):(console.error("API request failed:",e.error),y&&m.usage.automatic_first_query_on_error||x(e=>e.slice(0,-1)))}}catch(e){console.error("API request failed:",e),x(e=>e.slice(0,-1))}k(!1),s("")},b=(e,t,s)=>{if(null==e?void 0:e.newChatHistory){G&&console.log("Server returned newChatHistory:",e.newChatHistory);let n=e.newChatHistory;if(G&&console.log("finalChatHistory",n,"firstQuery",t),t){const e=n.length-3;e>=0&&e<n.length&&n[e]&&(n[e].noShow=!0),n=n.map(e=>{if("user"===(null==e?void 0:e.role)){let t;if("string"==typeof e.content?t=e.content:Array.isArray(e.content)&&e.content.length>0&&e.content[0]&&"string"==typeof e.content[0].text&&(t=e.content[0].text),t===s)return{...e,noShow:!0}}return e})}x(n),a.current||(a.current=!0)}else{G&&console.log("Chat history not send, appending as fallback",e);const t={role:"assistant",content:(null==e?void 0:e.response)||"I apologize, but I encountered an issue processing your request."},n={role:"user",content:s};x(e=>[...e,n,t])}};(0,o.useEffect)(()=>{m.usage.automatic_first_query_on_error&&"grader"===p&&0===f.length&&(w(),s("Generating analysis..."))},[]);const _=(0,o.useMemo)(()=>{let e;return e="grader"===p?[{id:"error",text:"Explain this error."},{id:"source",text:"Provide a concise list of important review materials."},{id:"progress",text:"What progress have I made so far?"}]:"free_response"===p?[{id:"evaluate",text:"Evaluate my answer."}]:"success"===p?[{id:"clarify",text:"I still don't feel confident in my answer."},{id:"source_success",text:"Provide me three important review materials."},{id:"improvements",text:"Can I make further improvements?"}]:[],e},[p]);return(0,r.jsxs)("div",{className:"jupytutor "+(y?"loading":""),children:[(0,r.jsxs)("div",{className:"chat-container",ref:n,children:[f.filter(e=>!e.noShow).map((e,t)=>{const s="string"==typeof e.content?e.content:e.content[0].text,n="user"===e.role;return(0,r.jsxs)("div",{className:"chat-message-wrapper",children:[(0,r.jsx)("div",{className:"chat-sender-label "+(n?"user":"assistant"),children:n?"You":"JupyTutor"}),n?(0,r.jsx)(S,{message:s,position:"right"}):(0,r.jsx)(N,{message:s,streaming:m.usage.use_streaming?"streamed":"none"})]},t)}),c&&(0,r.jsxs)("div",{className:"chat-message-wrapper",children:[(0,r.jsx)("div",{className:"chat-sender-label assistant",children:"JupyTutor"}),(0,r.jsxs)("div",{className:"streaming-message",children:[(0,r.jsx)(N,{message:c,streaming:"streaming"}),(0,r.jsx)("div",{className:"streaming-indicator",children:(0,r.jsxs)("div",{className:"typing-dots",children:[(0,r.jsx)("span",{}),(0,r.jsx)("span",{}),(0,r.jsx)("span",{})]})})]})]})]}),_.length>0&&(0,r.jsx)(R,{options:_,callSuggestion:e=>{y||(s(e),w(e))},isLoading:y}),(0,r.jsx)(I,{value:t,onChange:s,onSubmit:()=>{y||w(t)},isLoading:y,placeholder:"Ask JupyTutor anything..."})]})},R=e=>(0,r.jsx)("div",{className:"tailoredOptionsContainer "+(e.isLoading?"loading":""),children:e.options.map((t,s)=>(0,o.createElement)(C,{...t,key:t.id,callSuggestion:e.callSuggestion}))}),C=e=>(0,r.jsx)("div",{className:"tailoredOption",onClick:()=>e.callSuggestion&&e.callSuggestion(e.text),children:(0,r.jsx)("h4",{children:e.text})}),S=e=>{const{message:t,position:s,timestamp:n}=e,[a,i]=(0,o.useState)(!1);return(0,o.useEffect)(()=>{const e=setTimeout(()=>{i(!0)},100);return()=>clearTimeout(e)},[]),(0,r.jsxs)("div",{className:`chat-bubble chat-bubble-${s} ${a?"chat-bubble-visible":""}`,children:[(0,r.jsx)("div",{className:"chat-message",children:t}),n&&(0,r.jsx)("div",{className:"chat-timestamp",children:n})]})},N=e=>{const{message:t,streaming:s}=e,[n,a]=(0,o.useState)("none"!==s);return(0,o.useEffect)(()=>{if("none"!==s)return;const e=setTimeout(()=>{a(!0)},100);return()=>clearTimeout(e)},[]),(0,r.jsx)("div",{className:`assistant-message ${n?"assistant-visible":""} ${"streaming"===s?"assistant-streaming":""}`,children:(i=t,i.split("\n").map((e,t)=>{const s=e.trim(),n=e.search(/\S/),o=n>0?{marginLeft:.5*n+"em"}:{};if(s.startsWith("- ")){const e=s.substring(2);return e.endsWith(":")&&e.split(" ").length<=15?(0,r.jsxs)("div",{className:"assistant-list-item",style:o,children:[(0,r.jsx)("span",{className:"list-bullet",children:"•"}),(0,r.jsx)("span",{className:"list-content-header",children:j(e)})]},t):(0,r.jsxs)("div",{className:"assistant-list-item",style:o,children:[(0,r.jsx)("span",{className:"list-bullet",children:"•"}),(0,r.jsx)("span",{className:"list-content",children:j(e)})]},t)}if(/^\d+\.\s/.test(s)){const e=s.match(/^(\d+)\.\s(.+)/);if(e)return(0,r.jsxs)("div",{className:"assistant-list-item",style:o,children:[(0,r.jsxs)("span",{className:"list-number",children:[e[1],"."]}),(0,r.jsx)("span",{className:"list-content",children:j(e[2])})]},t)}return/^(\s{4,}|\t)/.test(e)?(0,r.jsx)("div",{className:"assistant-code-line",style:o,children:e},t):""===s?(0,r.jsx)("div",{className:"assistant-empty-line"},t):s.endsWith(":")&&!s.includes("\n")&&s.split(" ").length<=15?(0,r.jsx)("div",{className:"assistant-header-line",style:o,children:(0,r.jsx)("strong",{children:j(s)})},t):(0,r.jsx)("div",{className:"assistant-text-line",style:o,children:j(s)},t)}))});var i},I=e=>{const{value:t,onChange:s,onSubmit:n,isLoading:o,placeholder:a}=e;return(0,r.jsxs)("div",{className:"chat-input-container "+(o?"loading":""),children:[(0,r.jsx)("input",{type:"text",className:"chat-input "+(o?"loading":""),value:t,onChange:e=>s(e.target.value),onKeyPress:e=>{"Enter"!==e.key||e.shiftKey||o||(e.preventDefault(),n())},placeholder:a,disabled:o}),(0,r.jsx)("button",{className:"chat-submit-btn "+(o?"loading":""),onClick:n,disabled:o||!t.trim(),children:o?(0,r.jsx)("div",{className:"loading-spinner",children:(0,r.jsx)("div",{className:"spinner-ring"})}):(0,r.jsx)("svg",{className:"submit-icon",viewBox:"0 0 24 24",fill:"none",children:(0,r.jsx)("path",{d:"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z",fill:"currentColor"})})})]})};class A extends a.ReactWidget{constructor(e={autograderResponse:void 0,allCells:[],activeIndex:-1,notebookContext:"upToGrader",sendTextbookWithRequest:!1,contextRetriever:null,cellType:"code",userId:null,config:{}}){super(),this.props=e,this.addClass("jp-ReactWidget")}render(){return(0,r.jsx)(E,{...this.props})}}const U=A,O=["check"];let $="";const W=i.keywords.free_response_regex,H=i.keywords.success_regex,M=(e,t,s=void 0)=>{var n,o,r,a,i;if("code"===e.model.type){const s=e;if(!t)return"error";const a=null===(n=s.inputArea)||void 0===n?void 0:n.editor.getTokens();if(void 0===a)return console.log("ISSUE RETRIEVING TOKENS FROM CODE CELL"),null;const i=null!==(o=null==a?void 0:a.length)&&void 0!==o?o:0;if(""===$)for(let e=2;e<i;e+=1)"otter"===a[e].value&&"AssignOp"===a[e-1].type&&($=a[e-2].value,G&&console.log("GRADER VARIABLE NAME FOUND",$));if(""===$)return G&&console.log("GRADER NOT INITIALIZED YET"),"grader_not_initialized";let l=!1;for(let e=0;e<i-2;e+=1)"VariableName"===a[e].type&&a[e].value===$&&"."===a[e+1].type&&"PropertyName"===a[e+2].type&&-1!==O.indexOf(a[e+2].value)&&(l=!0);const c=null===(r=s.outputArea)||void 0===r?void 0:r.layout.widgets[0],u=c?c.node.innerText.toLowerCase():"",d=H.test(u);return l?d?(G&&console.log("SUCCESS CELL DETECTED"),"success"):"grader":"code"}{let t=!0;try{const s=e.model.metadata,n=null===(a=null==s?void 0:s.get)||void 0===a?void 0:a.call(s,"locked"),o=null===(i=null==s?void 0:s.get)||void 0===i?void 0:i.call(s,"readonly");t=!n&&!o}catch(e){t=!0}if(!t)return"text";let n=!1;if(s){const e=s.node.innerText.toLowerCase();n=W.test(e)}return t&&n?(G&&console.log("FREE RESPONSE CELL DETECTED"),"free_response"):"text"}};var P=s(256);function D(e,t){const s=e.lastIndexOf(t);return-1===s?e:e.slice(0,s)+e.slice(s+t.length)}function q(e){const t=/<img[^>]+src\s*=\s*["']([^"']+)["']|<source[^>]+srcset\s*=\s*["']([^"']+)["']|url\((['"]?)(?!#)(.*?)\3\)/gi,s=new Set;let n;for(;null!==(n=t.exec(e));){const e=n[1]||n[2]||n[4];e&&s.add(e.trim())}return Array.from(s)}function F(e){const t=/<a[^>]+href\s*=\s*["']([^"']+)["']/gi,s=new Set;let n;for(;null!==(n=t.exec(e));){const e=n[1];if(e){const t=e.trim();if(""===t||t.startsWith("javascript:")||t.startsWith("mailto:")||t.startsWith("#"))continue;try{if(t.startsWith("http://")||t.startsWith("https://"))s.add(t);else{const e=new URL(t,"https://example.com");s.add(e.href)}}catch(e){s.add(t)}}}return Array.from(s)}const J=(e,t=void 0)=>{let s=e.activeCellIndex;const n=e.cellsArray.map((t,s)=>{var n,o,r;const a=M(t,!0,s>0?e.cellsArray[s-1]:void 0);let i={index:s,type:a,html:t.node.innerHTML,text:t.node.innerText,outputText:null,outputHtml:null,images:q(t.node.innerHTML),links:F(t.node.innerHTML)};if("grader"===a||"code"===a||"error"===a||"success"===a){const e=t;(null===(n=e.outputArea.layout.widgets)||void 0===n?void 0:n.length)>0&&(i.outputText=e.outputArea.layout.widgets[0].node.innerText,i.text=D(i.text,null!==(o=i.outputText)&&void 0!==o?o:""),i.outputHtml=e.outputArea.layout.widgets[0].node.innerHTML,i.html=D(i.html,null!==(r=i.outputHtml)&&void 0!==r?r:""),i.images=[...i.images,...q(e.outputArea.layout.widgets[0].node.innerHTML)])}return i});return null!=t&&0!==s&&n[s-1].outputText===t.outputArea.layout.widgets[0].node.innerText&&(s-=1,console.log("ACTIVE INDEX CORRECTION PERFORMED")),[n,s]};var z=s(566);const G=!0,B={id:"jupytutor:plugin",description:"A Jupyter extension for providing students LLM feedback based on autograder results and supplied course context.",autoStart:!0,requires:[n.INotebookTracker],activate:async(e,t)=>{let s=await K();G&&console.log("Loaded configuration:",s);const o=s.context_gathering.enabled,r=(()=>{const e=window.location.pathname.match(/\/user\/([^\/]+)/);return e?e[1]:null})();G&&(console.log("Current URL:",window.location.href),console.log("DataHub User ID:",r));let a=null;const i=async()=>{var e;try{const n=null===(e=t.currentWidget)||void 0===e?void 0:e.content;if(!n)return void console.log("No active notebook found for context gathering");const[o,r]=J(n);G&&console.log("Initial load: Gathered all cells from notebook:",o);const i=new Set;o.forEach(e=>{e.links&&e.links.length>0&&e.links.forEach(e=>i.add(e))});const l=Array.from(i);G&&console.log("Gathered unique links from notebook:",l),a=new T({sourceLinks:l,whitelistedURLs:s.context_gathering.whitelist,blacklistedURLs:s.context_gathering.blacklist,jupyterbookURL:s.context_gathering.jupyterbook.url,attemptJupyterbookLinkExpansion:s.context_gathering.jupyterbook.link_expansion,debug:!1}),setTimeout(()=>{var e,t;G&&(console.log("Textbook Context Gathering Completed\n"),console.log("Starting Textbook Prompt:\n",L),console.log("Textbook Context Snippet:\n",null===(e=null==a?void 0:a.getContext())||void 0===e?void 0:e.substring(528,1028)),console.log("Textbook Context Length:\n",null===(t=null==a?void 0:a.getContext())||void 0===t?void 0:t.length),console.log("Textbook Source Links:\n",null==a?void 0:a.getSourceLinks()))},3e3)}catch(e){console.error("Error gathering context:",e)}},l=e=>new Promise(t=>setTimeout(t,e));t.currentChanged.connect(async()=>{await l(500),i()}),t.currentWidget&&l(500).then(()=>{i()}),n.NotebookActions.executed.connect((e,t)=>{const{cell:n,success:i,notebook:l}=t,c=M(n,i);if("grader_not_initialized"===c){const e=n,t=new P.Widget;t.node.innerHTML="<h4>Did not find autograder. Make sure you have run the cells to initialize it!</h4>",e.outputArea&&e.outputArea.layout&&e.outputArea.layout.addWidget(t)}if("grader"===c||"success"===c&&s.usage.show_on_success){const e=n,[t,i]=J(l,e);if(e.outputArea&&e.outputArea.layout){const n=e.outputArea.layout.widgets[0].node.innerText,l=new U({autograderResponse:n,allCells:t,activeIndex:i,notebookContext:"upToGrader",sendTextbookWithRequest:o,contextRetriever:a,cellType:c,userId:r,config:s});e.outputArea.layout.addWidget(l)}}else if("code"===c);else if(s.usage.show_on_free_response){const[e,t]=J(l,void 0),i=e[t].type;if("free_response"===i){const l=new U({autograderResponse:"",allCells:e,activeIndex:t,notebookContext:"upToGrader",sendTextbookWithRequest:o,contextRetriever:a,cellType:i,userId:r,config:s}),c=n.node.querySelector(".jp-jupytutor-markdown-container");c&&c.remove();const u=document.createElement("div");u.className="jp-jupytutor-markdown-container",u.style.cssText="\n margin-top: 15px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n padding: 0;\n background-color: #ffffff;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n ",u.appendChild(l.node),n.node.appendChild(u),requestAnimationFrame(()=>{l.update()})}}})}},K=async()=>{let e={...i};try{const t=z.ServerConnection.makeSettings(),s=await z.ServerConnection.makeRequest(`${t.baseUrl}jupytutor/config`,{method:"GET"},t);if(s.ok){const t=await s.json();if("success"===t.status&&t.config){if(!Y(e,t.config))return console.error("ERROR: User config does not match the default config. Changes not reflected. Edit ~/.config/jupytutor/config.json to fix this."),e;e=V(e,t.config)}}}catch(e){G&&console.log("No user config found at ~/.config/jupytutor/config.json, using default config")}return e},V=(e,t)=>{const s={...e};return Object.keys(t).reduce((s,n)=>(t[n]&&"object"==typeof t[n]?s[n]=V(s[n],t[n]):n in e&&(s[n]=t[n]),s),s)},Y=(e,t)=>Object.keys(t).every(s=>t[s]&&"object"==typeof t[s]?Y(e[s],t[s]):s in e),Q=B},848:(e,t,s)=>{e.exports=s(20)}}]);
|