{"_id":"@atlarafirm/quillkit","_rev":"13-0a017db276388441cd3b924698c2bd3d","name":"@atlarafirm/quillkit","dist-tags":{"latest":"1.3.8"},"versions":{"1.1.0":{"name":"@atlarafirm/quillkit","version":"1.1.0","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.1.0","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"5820265bef115e6b1731d5d173edc9eba41910a4","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.1.0.tgz","fileCount":68,"integrity":"sha512-71g7nzSgOD44Y7soAZvaJhNwC4jTTIOPzuqlFPiJbQhM8eN8XBzshi/owf9J/Bd8Ls0uL7rSz+SY7uWaPSte6A==","signatures":[{"sig":"MEUCIF4tQoujAzo2sQf1DeZkwABzYjxKoq0WTbYWtV/52oLIAiEA/1Z2Xkdb6794q5GIXu1/kJEYHvGKNC34ehR/jB7qBHA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4146677},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"da8518890df3672b738b38640bfcdf9837cfe4d9","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"11.6.1","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"24.10.0","dependencies":{"katex":"^0.16.27","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.1.0_1768884405786_0.5354509197065267","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@atlarafirm/quillkit","version":"1.1.1","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.1.1","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"9e4e5af6ec769c8eb9e59a45647a65702c4ceea7","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.1.1.tgz","fileCount":68,"integrity":"sha512-6qHCdYrsf8/jBK6N620Ns4n6rhYbo1zVxhWBSY9MWfTfQ6PzxK2Jl2ZWnMo+npnZLZs9w+rg3Nm8Zff7/fYFxw==","signatures":[{"sig":"MEQCIEXAbKkeZ32bfQh0qHBlglifgnASO5Ffuo1+mx6HIExOAiBHOF/pngNKXVUhFNCHoigTnU2PggcVuiOJVcAbYbCzvg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4149149},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"3af683d9aca1b338cda14545b766f36646d5b75a","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"10.9.2","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"22.17.1","dependencies":{"katex":"^0.16.27","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.1.1_1768959658381_0.000584739981961846","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"@atlarafirm/quillkit","version":"1.1.2","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.1.2","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"2f1437c42f60ecd735d6111c0462433a654484d8","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.1.2.tgz","fileCount":68,"integrity":"sha512-YhhBxHuYGrYtQcH/IhNmMj1+XHZIii5/jr5tKBpBhzNvVbtM/xHH02ZYWcgWBucR1jfkyBWGfFB3Oa01lCDN7g==","signatures":[{"sig":"MEUCIQCSeI85XRNbfJqAAo7bWCLzm+WDooxkkNklBA+QuxHCyAIgI1P8Rd+mBsNsPSb/PIAaF9grZKvth4sot0uuJzlC9Ns=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4150054},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"f91ba7052cad2d9e6529238e6b0f88ce7d6a9c97","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"11.6.1","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"24.10.0","dependencies":{"katex":"^0.16.27","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.1.2_1769058918106_0.9854151810623109","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@atlarafirm/quillkit","version":"1.2.0","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.2.0","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"ba138eff6f416418cd6dff3f5d3eccd4208b40d5","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.2.0.tgz","fileCount":68,"integrity":"sha512-9qyeZeKI+KTYSURzHubT5ldqn/YPnk809eIIsGtNXmgn479AQYzCQbBT4GHN124IBHFk5xzsExEw7I+1o597cA==","signatures":[{"sig":"MEUCIFj7hG0I4RpQhGbALjjvGMa2Hn/HN3jNm8hJoQYfIUQDAiEA7DYubLNCAQWfzKZa+gH0nyJndm/uA0/3mOQGZgce+Ak=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4173541},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"b5545cc4905eddb3b17598a7dea090c821740c41","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"10.9.2","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"22.17.1","dependencies":{"tar":"^7.5.6","katex":"^0.16.27","dotenv":"^16.6.1","node-tar":"^1.0.0","supertest":"^7.2.2","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","express":"^5.2.1","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.2.0_1769356465936_0.1637478855569161","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@atlarafirm/quillkit","version":"1.3.0","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.3.0","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"93151f51b3066f48e3a6b0a59d9fa8941db5f714","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.3.0.tgz","fileCount":68,"integrity":"sha512-OMUrNBZqmr7pwlYEtNQvpFzbnrG5tb8fiKLcHkP+a9YBNwZnbrgHrg3ITr0eZs1RJqtpPBvVsSPr9yFqrFCTRw==","signatures":[{"sig":"MEUCIHjvayRxty9zdbuaHHzTlVGma3tHkpPmSX7cSsZvEEM9AiEAmFAhnipr3klqgKMiHECYj4nKaC1AwRBZN4QH3mcWKFQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4184649},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"b3897426222157083cd2aa4ed6363560de6c7598","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"11.6.1","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"24.10.0","dependencies":{"tar":"^7.5.6","katex":"^0.16.27","dotenv":"^17.2.3","node-tar":"^1.0.0","supertest":"^7.2.2","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","express":"^5.2.1","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0","@testing-library/jest-dom":"^6.9.1"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.3.0_1769628472583_0.3441016168273028","host":"s3://npm-registry-packages-npm-production"}},"1.3.1":{"name":"@atlarafirm/quillkit","version":"1.3.1","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.3.1","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"92ba3d65b984cfe95e66fdd917268b7004f5e18e","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.3.1.tgz","fileCount":68,"integrity":"sha512-vJy3if37PZOJS8QpndYKrgmEfV1efyagTnFiGlulLXz1s4WOopZAFwglQIULKf0H6Qaka/ot1p5Sr0lcEgS/BA==","signatures":[{"sig":"MEYCIQDElLAo1T2EbFKYDDoLsHceRihnWXPxE+Dji7w2T2klowIhAKQ8PWjkV2xUKfnVM6RrhIE44bDjBMgRyeP0QmKYGWKj","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4185339},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"cc736d2becde6a3835105e73fecc2623355ae96b","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"10.9.2","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"22.17.1","dependencies":{"tar":"^7.5.7","katex":"^0.16.27","dotenv":"^17.2.3","node-tar":"^1.0.0","supertest":"^7.2.2","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","express":"^5.2.1","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0","@testing-library/jest-dom":"^6.9.1"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.3.1_1769902881052_0.4409931069067914","host":"s3://npm-registry-packages-npm-production"}},"1.3.2":{"name":"@atlarafirm/quillkit","version":"1.3.2","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.3.2","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"6fc4f1ca2f8e132f4aae2cc80ccb5459c6eb6a67","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.3.2.tgz","fileCount":68,"integrity":"sha512-nxIs7VyWi7P/gdAs7f3WzORWmqy1sOtATVl2YvBQpBRAkMBfLuU6h7JKRACeG/yMfcJhayKzQiiar5B2JHrQhA==","signatures":[{"sig":"MEYCIQDDPmnxhPVeEz9/6Xpszj73KGfCaF1P5HKtP+jv/oEThwIhALScHX1sr+vHih5uRcE+OIwNdR4NpWzDI073IxbwCxyL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4185339},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"32ef9e88035f93c6f6a57b53ff6fe2d69c0b769c","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"10.9.2","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"22.17.1","dependencies":{"tar":"^7.5.7","katex":"^0.16.27","dotenv":"^17.2.3","node-tar":"^1.0.0","supertest":"^7.2.2","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","express":"^5.2.1","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0","@testing-library/jest-dom":"^6.9.1"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.3.2_1769904475441_0.23248551768777337","host":"s3://npm-registry-packages-npm-production"}},"1.3.3":{"name":"@atlarafirm/quillkit","version":"1.3.3","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.3.3","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"2b693646b7e4679be9ba47362d3ba4a02ad875d9","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.3.3.tgz","fileCount":68,"integrity":"sha512-N367bPyENmEq63yTHaAJsDQXp/zvhUtYv2zbTVzEzdXQK2GVFmvh3pFMG2vZgw4Fvp65oxluGql4JqaRA7yopw==","signatures":[{"sig":"MEUCIBpvC5wtomLDwubddyFdIO8oP+Zd6RKEyGWiMVSVK6R3AiEA5gROTyMowf2B2U87ALUyNL8QQRSwn2Ai2MOwuM2XLXw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4185339},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"b0ba8ae6b2c4adf446a18ba03bb9bf66000bd527","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"10.9.2","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"22.17.1","dependencies":{"tar":"^7.5.7","katex":"^0.16.27","dotenv":"^17.2.3","node-tar":"^1.0.0","supertest":"^7.2.2","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","express":"^5.2.1","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0","@testing-library/jest-dom":"^6.9.1"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.3.3_1770070638641_0.21015400811949303","host":"s3://npm-registry-packages-npm-production"}},"1.3.4":{"name":"@atlarafirm/quillkit","version":"1.3.4","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.3.4","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"55692ace2d3ddd75a0f5bf7e8cb9409b50cc76e1","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.3.4.tgz","fileCount":68,"integrity":"sha512-I+5nILnUtNel2Du6BHgvx8OezP5SEQ6LhXxueLt9oskkCGms5rfvbNAs6kcUSC+mXQYoX6pPvwBzfYxEW+AmYg==","signatures":[{"sig":"MEUCIQCcPL79c+zkR7nTFuThbIjQ1o9d5kHcBwkyA9P1qOfYwgIgFmzaqjZJNVEYhoKLMTFuHc5pUQtikdQ6WOWH758SsCU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4194572},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"a9d152f1b489044f6097ceea1d46b14a23cba23c","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"11.6.1","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"24.10.0","dependencies":{"tar":"^7.5.7","katex":"^0.16.27","dotenv":"^17.2.3","node-tar":"^1.0.0","supertest":"^7.2.2","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","express":"^5.2.1","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0","@testing-library/jest-dom":"^6.9.1"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.3.4_1770246587908_0.8279321960784429","host":"s3://npm-registry-packages-npm-production"}},"1.3.5":{"name":"@atlarafirm/quillkit","version":"1.3.5","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.3.5","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"f46e3e38ade9bea7b7d5d626d3e90a9ab0d59dca","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.3.5.tgz","fileCount":72,"integrity":"sha512-spUu9wfeO4vjciXYIg8RH2VtNARPMAwg+ZqX8I1yfUx9DfCgUjc+eljgXmkxGtw0By6USCWkY/aOPdCH1mf7uQ==","signatures":[{"sig":"MEUCIFFW3yxlVkSsKP1mqO72aZX1cfmT04iltoNkv656CUmOAiEAmj2XF9YVnxrprE7g5Avhz5OB5suMKWPT98/3MTIvabc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4433565},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"c807e56a3fd69826ac6c6d722db3d54739535b30","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"11.6.1","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"24.10.0","dependencies":{"tar":"^7.5.7","katex":"^0.16.27","dotenv":"^17.2.3","node-tar":"^1.0.0","supertest":"^7.2.2","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","express":"^5.2.1","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0","@testing-library/jest-dom":"^6.9.1"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.3.5_1770342591545_0.4869066767982606","host":"s3://npm-registry-packages-npm-production"}},"1.3.6":{"name":"@atlarafirm/quillkit","version":"1.3.6","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.3.6","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"978858613b6267caf09f90f5b97060abcf9e040b","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.3.6.tgz","fileCount":72,"integrity":"sha512-iqfwq2rvJLUWrz7pTJmYX2reVdQeNgEr1nOLXpqMOR8Vv1HjYUh+Q8ZpY/JOImapl3VRCLZc7mLZLS/hc94xvA==","signatures":[{"sig":"MEYCIQCnnbsPTuDgFrYbnlq9Wn/hF16VkjI74m/X+FR4fCyBzwIhANk00gillL9Yu0fKkkUg9qoU288IRzrXfYWGges6hzw1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4449081},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"584f8e55bd96d3b8c315111fc1058f86d3430671","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"11.6.1","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"24.10.0","dependencies":{"tar":"^7.5.7","katex":"^0.16.27","dotenv":"^17.2.3","node-tar":"^1.0.0","supertest":"^7.2.2","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","express":"^5.2.1","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0","@testing-library/jest-dom":"^6.9.1"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.3.6_1770349065269_0.25942182624591403","host":"s3://npm-registry-packages-npm-production"}},"1.3.7":{"name":"@atlarafirm/quillkit","version":"1.3.7","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","_id":"@atlarafirm/quillkit@1.3.7","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"dist":{"shasum":"98cbc68a91487fec23df383139e13375d6b6e181","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.3.7.tgz","fileCount":72,"integrity":"sha512-+JhCm4iahtmZnsmgqHNK+jW+/NSoJUx5Bb8i2BnYygqn6NM7/irU8auYVmqPz1K9XxVZvU7XSBlJWUt8HqA0ug==","signatures":[{"sig":"MEYCIQCT1ayM/rT0Fva/HtZuCmXCN06TZqrynJB3HBbd7r1S+AIhALx/cQFTvV+j7CqfA0VNBz2onwwI7UVrrlS9dMIKH01E","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4449081},"jest":{"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"collectCoverage":true,"testEnvironment":"jsdom","moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testPathIgnorePatterns":["/node_modules/","/backend/"]},"main":"dist/quillkit.js","gitHead":"648a346cc9bdbd8c19a492918abb4be0e424bbb1","scripts":{"dev":"webpack --mode development --watch","lint":"eslint src/**/*.js","test":"jest","build":"npm run build:editor && npm run build:frontend","start":"cd backend && npm start","format":"eslint --fix src/**/*.js __tests__/**/*.js","test:watch":"jest --watch","build:editor":"webpack --mode production --config webpack.config.js","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js"},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"_npmVersion":"10.9.2","description":"A Javascript library for adding rich-text functionality to HTML textareas","directories":{},"_nodeVersion":"22.17.1","dependencies":{"tar":"^7.5.7","katex":"^0.16.27","dotenv":"^17.2.3","node-tar":"^1.0.0","supertest":"^7.2.2","@fortawesome/fontawesome-free":"^7.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.50.0","express":"^5.2.1","webpack":"^5.104.1","prettier":"^3.7.4","babel-jest":"^30.2.0","css-loader":"^7.1.2","@babel/core":"^7.23.0","webpack-cli":"^6.0.1","babel-loader":"^10.0.0","style-loader":"^3.3.3","@babel/preset-env":"^7.23.0","webpack-obfuscator":"^3.6.0","terser-webpack-plugin":"^5.3.16","jest-environment-jsdom":"^29.7.0","@testing-library/jest-dom":"^6.9.1"},"_npmOperationalInternal":{"tmp":"tmp/quillkit_1.3.7_1770424980578_0.010553994640285058","host":"s3://npm-registry-packages-npm-production"}},"1.3.8":{"name":"@atlarafirm/quillkit","version":"1.3.8","description":"A Javascript library for adding rich-text functionality to HTML textareas","main":"dist/quillkit.js","scripts":{"start":"cd backend && npm start","test":"jest","test:watch":"jest --watch","build":"npm run build:editor && npm run build:frontend","build:editor":"webpack --mode production --config webpack.config.js","build:frontend":"webpack --mode production --config webpack.frontend.config.js","dev":"webpack --mode development --watch","dev:frontend":"webpack --mode development --watch --config webpack.frontend.config.js","lint":"eslint src/**/*.js","format":"eslint --fix src/**/*.js __tests__/**/*.js"},"keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"author":{"name":"AtlaraFirm"},"license":"MIT","publishConfig":{"access":"public"},"devDependencies":{"@babel/core":"^7.23.0","@babel/preset-env":"^7.23.0","@testing-library/jest-dom":"^6.9.1","babel-jest":"^30.2.0","babel-loader":"^10.0.0","css-loader":"^7.1.2","eslint":"^8.50.0","express":"^5.2.1","jest":"^29.7.0","jest-environment-jsdom":"^29.7.0","prettier":"^3.7.4","style-loader":"^3.3.3","terser-webpack-plugin":"^5.3.16","webpack":"^5.104.1","webpack-cli":"^6.0.1","webpack-obfuscator":"^3.6.0"},"dependencies":{"@fortawesome/fontawesome-free":"^7.1.0","dotenv":"^17.2.3","katex":"^0.16.27","node-tar":"^1.0.0","supertest":"^7.2.2","tar":"^7.5.7"},"jest":{"testEnvironment":"jsdom","collectCoverage":true,"coverageDirectory":"coverage","coverageReporters":["text","lcov"],"testMatch":["**/__tests__/**/*.test.js"],"testPathIgnorePatterns":["/node_modules/","/backend/"],"moduleNameMapper":{"\\.(css|less|scss|sass)$":"<rootDir>/__mocks__/styleMock.js"},"transform":{"^.+\\.js$":"babel-jest"}},"_id":"@atlarafirm/quillkit@1.3.8","gitHead":"afc02e7984d318fc6691fff980de9044f6c850b5","_nodeVersion":"22.17.1","_npmVersion":"10.9.2","dist":{"integrity":"sha512-Vzbi18QKcubJuYNBdbFGlncMjz+qdlVlMboohl1WjQCltO1Vruiqd2UgieTyIQWwrYrXioBAAe4qf3X4C7QsHQ==","shasum":"a05ab0aa42b2fc718b4a624915e761d3b330043c","tarball":"https://registry.npmjs.org/@atlarafirm/quillkit/-/quillkit-1.3.8.tgz","fileCount":72,"unpackedSize":4461602,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFebXWW21gM28qtYb/xti+JAEAj72p1DtRog/3zvy3feAiBIOpD6BzRwNiELqIjXjq1BxvtuWPck+uT0jt11XAfFvA=="}]},"_npmUser":{"name":"romulus10","email":"sean@romulusweb.dev"},"directories":{},"maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/quillkit_1.3.8_1770427381740_0.21137362781651037"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-20T04:46:45.629Z","modified":"2026-02-07T01:23:02.095Z","1.1.0":"2026-01-20T04:46:45.996Z","1.1.1":"2026-01-21T01:40:58.618Z","1.1.2":"2026-01-22T05:15:18.330Z","1.2.0":"2026-01-25T15:54:26.164Z","1.3.0":"2026-01-28T19:27:52.794Z","1.3.1":"2026-01-31T23:41:21.302Z","1.3.2":"2026-02-01T00:07:55.687Z","1.3.3":"2026-02-02T22:17:18.912Z","1.3.4":"2026-02-04T23:09:48.199Z","1.3.5":"2026-02-06T01:49:51.822Z","1.3.6":"2026-02-06T03:37:45.532Z","1.3.7":"2026-02-07T00:43:00.835Z","1.3.8":"2026-02-07T01:23:01.937Z"},"author":{"name":"AtlaraFirm"},"license":"MIT","keywords":["rich-text","editor","textarea","wysiwyg","latex","mathml"],"description":"A Javascript library for adding rich-text functionality to HTML textareas","maintainers":[{"name":"romulus10","email":"sean@romulusweb.dev"}],"readme":"# QuillKit\n\nA powerful JavaScript library that transforms basic HTML textareas into full-featured rich-text editors with toolbar support, multiple view modes, and mathematical expression rendering.\n\n## Features\n\n- **Rich Text Editing**: Full WYSIWYG editor with formatting capabilities\n- **Page View**: Render content on a virtual piece of paper (8.5\" x 11\") with realistic margins, similar to Microsoft Word\n- **Comprehensive Toolbar**:\n  - Text formatting (Bold, Italic, Underline, Strikethrough)\n  - Text alignment (Left, Center, Right)\n  - **Lists**:\n    - Bullet lists (Unordered)\n    - Numbered lists (Ordered)\n    - Multiple bullet styles (disc, circle, square)\n    - Multiple numbering styles (1,2,3 | a,b,c | i,ii,iii | etc.)\n    - Edit individual list item numbers with custom values\n  - Headings (H1, H2, H3) and Paragraph styles\n  - Insert links, images, and icons\n  - Undo/Redo functionality\n  - Page view toggle\n- **Advanced Image Support**:\n  - Insert images with URL\n  - Specify pixel dimensions (width/height) when inserting\n  - Drag corner handles to resize images\n  - Proportional resizing maintains aspect ratio\n  - Visual resize handles on all images\n- **Advanced Table Support**:\n  - Create tables with custom rows and columns\n  - Resizable columns (drag to resize)\n  - Resizable rows (drag to resize)\n  - Auto-fit columns to content (double-click column edge)\n  - Custom border colors with color picker\n  - Add/remove rows and columns dynamically\n  - Full manipulation toolbar\n- **Column Layout Support**:\n  - Multi-column layouts (newspaper-style columns)\n  - 1-column, 2-column, and 3-column layouts\n  - Automatic text flow across columns\n  - Visual column dividers\n  - Easy toggle between layouts\n- **Multiple View Modes**:\n  - **Rich Text Mode**: Visual editing with formatting toolbar\n  - **HTML Mode**: Direct HTML code editing\n  - **CSS Mode**: View and edit extracted inline styles\n- **Mathematical Expression Support**:\n  - LaTeX rendering using KaTeX\n  - Native MathML support\n  - Configurable default font size for all math elements\n- **Custom HTML Snippets**:\n  - Define custom snippet groups for reusable HTML content\n  - Browse and insert snippets via toolbar buttons\n  - Perfect for frequently used templates, components, or formulas\n- **Icon Support**:\n  - Bootstrap Icons library with 1,800+ icons\n  - Font Awesome library with 30,000+ icons\n  - Custom icon URLs for your own images\n  - Easy-to-use icon browser modal\n- **Circle Annotation Plugin**:\n  - Select words by double-clicking or dragging to select\n  - Add circular annotations around words with customizable notes\n  - Add parts of speech labels above circled words\n  - Add custom notes below circled words\n  - Perfect for language learning, document review, and educational materials\n  - Integrated deletion and cleanup on click-away\n- **History Management**: Undo/redo with configurable history limit\n- **Keyboard Shortcuts**:\n  - Ctrl/Cmd + B (Bold)\n  - Ctrl/Cmd + I (Italic)\n  - Ctrl/Cmd + U (Underline)\n  - Ctrl/Cmd + Z (Undo)\n  - Ctrl/Cmd + Shift + Z / Ctrl/Cmd + Y (Redo)\n- **Plugin System**:\n  - Modular architecture - load only what you need\n  - Reduce JavaScript bundle size by selecting specific features\n  - Create custom plugins to extend functionality\n  - Dynamic plugin loading/unloading at runtime\n  - All features are now plugins (backward compatible)\n- **Flexible Configuration**: Customizable toolbar, mode switching, and editor height\n- **Easy Integration**: Simple API for getting and setting content\n- **Backend API** (NEW!):\n  - Complete REST API for productization and distribution\n  - API key management with usage tracking\n  - Feature access control based on subscription plans\n  - Stripe payment integration\n  - Docker and Docker Compose support for easy deployment\n  - **Heroku deployment ready** - Deploy backend, frontend, editor library, and examples in one unified container\n  - See [backend/README.md](backend/README.md) for API details\n  - See [DOCKER.md](DOCKER.md) for Docker deployment instructions\n  - See [QUICK_HEROKU_DEPLOY.md](QUICK_HEROKU_DEPLOY.md) for Heroku deployment (or [HEROKU_DEPLOYMENT_REWORK.md](HEROKU_DEPLOYMENT_REWORK.md) for details)\n\n## Installation\n\n> **⚠️ API Key Required**\n>\n> QuillKit requires a valid API key to function. Get your API key from the [QuillKit service](https://quillkit.com).\n>\n> Without an API key, the editor will not be functional.\n\n### Using npm\n\n```bash\nnpm install @atlarafirm/quillkit\n```\n\n### Using CDN\n\nYou can include QuillKit directly in your HTML using a CDN link. This is the easiest way to get started without any build tools.\n\n#### unpkg\n\n```html\n<!-- Latest version -->\n<script src=\"https://unpkg.com/@atlarafirm/quillkit/dist/quillkit.js\"></script>\n\n<!-- Or use the latest version (always up-to-date, but may break with major updates) -->\n<script src=\"https://unpkg.com/@atlarafirm/quillkit/dist/quillkit.js\"></script>\n```\n\n#### jsDelivr\n\n```html\n<!-- Latest version -->\n<script src=\"https://cdn.jsdelivr.net/npm/@atlarafirm/quillkit/dist/quillkit.js\"></script>\n\n<!-- Or use the latest version -->\n<script src=\"https://cdn.jsdelivr.net/npm/@atlarafirm/quillkit/dist/quillkit.js\"></script>\n```\n\n### 📺 Interactive Demo\n\nSee [`demo-api-cdn-integration.html`](demo-api-cdn-integration.html) for a complete, interactive demo that shows:\n- How to use QuillKit with CDN links\n- How to integrate with the backend API using API keys\n- Feature access control based on subscription plans\n- Live editor with all features demonstrated\n- Step-by-step guide with code examples\n\n## Usage\n\n> **⚠️ IMPORTANT: API Key Required**\n>\n> QuillKit requires a valid API key to function. You must provide an API key when initializing the editor. Get your API key from the [QuillKit service](https://quillkit.com).\n>\n> Without a valid API key, the editor will display a blocking modal and will not be functional.\n\nQuillKit supports two initialization patterns: the **Global API** pattern (recommended for new projects) and the traditional constructor pattern (for backward compatibility).\n\n### Global API Pattern (Recommended)\n\nThe new global API provides instance management and a global event system:\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n  <title>QuillKit Example</title>\n</head>\n<body>\n  <textarea id=\"my-editor\">\n    <h1>Hello World!</h1>\n    <p>Start editing here...</p>\n  </textarea>\n\n  <script src=\"dist/quillkit.js\"></script>\n  <script>\n    // Initialize using global API with REQUIRED API key\n    const editor = QuillKit.init('#my-editor', {\n      apiKey: 'ak_your_api_key_here'  // REQUIRED - Get from QuillKit service\n    });\n\n    // Access from anywhere on the page\n    const editor = QuillKit.getInstance('my-editor');\n  </script>\n</body>\n</html>\n```\n\n### Traditional Constructor Pattern\n\n```html\n<script src=\"dist/quillkit.js\"></script>\n<script>\n  // Traditional way (still fully supported) with REQUIRED API key\n  const editor = new QuillKit('#my-editor', {\n    apiKey: 'ak_your_api_key_here'  // REQUIRED - Get from QuillKit service\n  });\n</script>\n```\n\n### With Options\n\nBoth patterns support the same options:\n\n```javascript\n// Global API\nconst editor = QuillKit.init('#my-editor', {\n  apiKey: 'ak_your_api_key_here',  // REQUIRED - Your API key from QuillKit service\n  toolbar: true,          // Show toolbar (default: true)\n  modeSwitching: true,    // Enable mode switching tabs (default: true)\n  mathSupport: true,      // Enable LaTeX and MathML support (default: true)\n  mathFontSize: null,     // Default font size for math (e.g., '20pt', '1.5em') (default: null)\n  height: '500px',        // Custom editor height (default: '400px')\n  pageView: true,         // Enable page view mode (default: true)\n  customSnippets: {}      // Custom HTML snippet groups (default: {})\n});\n\n// Or traditional constructor\nconst editor = new QuillKit('#my-editor', {\n  apiKey: 'ak_your_api_key_here',  // REQUIRED - Your API key from QuillKit service\n  toolbar: true,\n  modeSwitching: true,\n  // ... same options\n});\n```\n\n### Minimal Editor (No Toolbar)\n\n```javascript\nconst editor = QuillKit.init('#my-editor', {\n  apiKey: 'ak_your_api_key_here',  // REQUIRED - Your API key from QuillKit service\n  toolbar: false,\n  modeSwitching: false\n});\n```\n\n### Editor with Custom HTML Snippets\n\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  apiKey: 'ak_your_api_key_here',  // REQUIRED - Your API key from QuillKit service\n  customSnippets: {\n    'math-components': [\n      {\n        name: 'Einstein Equation',\n        html: '<div class=\"equation\">E = mc²</div>'\n      },\n      {\n        name: 'Pythagorean Theorem',\n        html: '<div class=\"equation\">a² + b² = c²</div>'\n      }\n    ],\n    'text-templates': [\n      {\n        name: 'Info Box',\n        html: '<div class=\"info\">Important information here</div>'\n      }\n    ]\n  }\n});\n```\n\n## Plugin System\n\nQuillKit features a modular plugin system that allows you to load only the features you need, significantly reducing bundle size.\n\n> **Note:** All examples require a valid API key. See the [Usage](#usage) section for details.\n\n### Using the Plugin System\n\n**Load all features (default):**\n```javascript\nconst editor = QuillKit.init('#my-editor', {\n  apiKey: 'ak_your_api_key_here'  // REQUIRED\n});\n```\n\n**Load specific plugins only:**\n```javascript\nconst editor = QuillKit.init('#my-editor', {\n  apiKey: 'ak_your_api_key_here',  // REQUIRED\n  plugins: ['textFormatting', 'alignment', 'list', 'history']\n});\n```\n\n**Minimal editor:**\n```javascript\nconst editor = QuillKit.init('#my-editor', {\n  apiKey: 'ak_your_api_key_here',  // REQUIRED\n  plugins: ['textFormatting', 'history']\n});\n```\n\n### Available Built-in Plugins\n\n- `textFormatting` - Bold, italic, underline, strikethrough\n- `fontFamily` - Font family selection\n- `fontSize` - Font size selection\n- `alignment` - Text alignment (left, center, right)\n- `list` - Bullet and numbered lists\n- `blockFormatting` - Headings (H1-H3) and paragraphs\n- `link` - Link insertion\n- `image` - Image insertion and resizing\n- `table` - Table creation and manipulation\n- `column` - Multi-column layouts (1, 2, or 3 columns)\n- `noteBox` - Custom note boxes with collapsible content\n- `symbol` - Symbol picker for special characters\n- `bookmark` - Bookmark management for navigation\n- `pageBreak` - Insert page breaks\n- `circleAnnotation` - Circle words and add annotations for language learning and document review\n- `history` - Undo/redo functionality\n- `pageView` - Page view mode\n- `math` - LaTeX and MathML support\n- `iconLibrary` - Icon insertion from Bootstrap Icons, Font Awesome, and custom sources\n- `customSnippet` - Custom HTML snippet insertion\n- `insert` - General insertion commands\n\n### Creating Custom Plugins\n\n```javascript\nimport { Plugin } from '@atlarafirm/quillkit';\n\nclass MyCustomPlugin extends Plugin {\n  constructor(editor, options = {}) {\n    super(editor, options);\n    this.name = 'MyCustomPlugin';\n  }\n\n  getToolbarButtons() {\n    return [{\n      name: 'my-buttons',\n      buttons: [\n        { command: 'myCommand', label: '⚡', title: 'My Command' }\n      ]\n    }];\n  }\n\n  executeCommand(command, value) {\n    if (command === 'myCommand') {\n      // Your logic here\n    }\n  }\n\n  getMetadata() {\n    return {\n      name: 'My Custom Plugin',\n      version: '1.0.0',\n      description: 'Does something amazing',\n      author: 'Your Name'\n    };\n  }\n}\n\n// Register and use\nconst editor = QuillKit.init('#my-editor', {\n  apiKey: 'ak_your_api_key_here'  // REQUIRED\n});\neditor.pluginManager.register('myCustom', MyCustomPlugin);\neditor.pluginManager.load('myCustom');\n```\n\n### Dynamic Plugin Loading\n\n```javascript\n// Load a plugin at runtime\neditor.pluginManager.load('math');\n\n// Unload a plugin\neditor.pluginManager.unload('alignment');\n\n// Check if loaded\nif (editor.pluginManager.isLoaded('table')) {\n  console.log('Table plugin is available');\n}\n```\n\nFor comprehensive documentation, see [PLUGIN_SYSTEM.md](PLUGIN_SYSTEM.md) and the plugin system demo in `examples/plugin-system-demo.html`.\n\n## API\n\n### Global API Methods\n\nThe Global API provides static methods for managing editor instances across your application.\n\n#### `QuillKit.init(textarea, options)`\n\nInitialize a new editor instance with automatic instance tracking.\n\n```javascript\nconst editor = QuillKit.init('#my-editor', { height: '500px' });\n```\n\n- **textarea**: Either a textarea DOM element or a CSS selector string\n- **options**: Configuration object (optional)\n- **Returns**: Editor instance (returns existing instance if already initialized)\n\n#### `QuillKit.getInstance(id)`\n\nGet an editor instance by its textarea ID.\n\n```javascript\nconst editor = QuillKit.getInstance('my-editor');\nif (editor) {\n  console.log(editor.getValue());\n}\n```\n\n#### `QuillKit.getAllInstances()`\n\nGet an array of all active editor instances.\n\n```javascript\nconst allEditors = QuillKit.getAllInstances();\nconsole.log(`Total editors: ${allEditors.length}`);\n```\n\n#### `QuillKit.destroyInstance(id)`\n\nDestroy a specific editor instance by ID.\n\n```javascript\nconst destroyed = QuillKit.destroyInstance('my-editor');\n// Returns true if destroyed, false if not found\n```\n\n#### `QuillKit.destroyAll()`\n\nDestroy all editor instances.\n\n```javascript\nQuillKit.destroyAll();\n```\n\n#### `QuillKit.on(event, callback)`\n\nRegister a global event listener.\n\n```javascript\nQuillKit.on('init', (data) => {\n  console.log(`Editor ${data.id} initialized`);\n});\n```\n\nBuilt-in events:\n- **init**: Fired when an editor is initialized via `QuillKit.init()`\n- **destroy**: Fired when an editor is destroyed\n\n#### `QuillKit.off(event, callback)`\n\nRemove a global event listener.\n\n```javascript\nconst handler = (data) => console.log(data);\nQuillKit.on('init', handler);\n// Later...\nQuillKit.off('init', handler);\n// Or remove all listeners for an event\nQuillKit.off('init');\n```\n\n#### `QuillKit.emit(event, data)`\n\nEmit a custom event to all listeners.\n\n```javascript\nQuillKit.emit('custom-event', { message: 'Hello!' });\n```\n\n### Constructor (Traditional Pattern)\n\n```javascript\nnew QuillKit(textarea, options)\n```\n\n- **textarea**: Either a textarea DOM element or a CSS selector string\n- **options**: Configuration object (optional)\n\n**Note**: Instances created with `new QuillKit()` are not tracked in the global instance map.\n\n### Instance Methods\n\n#### `getValue()`\nGet the current HTML content as a string.\n\n```javascript\nconst html = editor.getValue();\n```\n\n#### `setValue(value)`\nSet the editor content.\n\n```javascript\neditor.setValue('<h1>New Content</h1><p>Some text...</p>');\n```\n\n#### `getMode()`\nGet the current view mode ('rich', 'html', or 'css').\n\n```javascript\nconst mode = editor.getMode(); // Returns: 'rich', 'html', or 'css'\n```\n\n#### `setMode(mode)`\nSwitch to a different view mode.\n\n```javascript\neditor.setMode('html');  // Switch to HTML mode\neditor.setMode('rich');  // Switch to Rich Text mode\neditor.setMode('css');   // Switch to CSS mode\n```\n\n#### `destroy()`\nRemove the editor and restore the original textarea.\n\n```javascript\neditor.destroy();\n```\n\n## View Modes\n\n### Rich Text Mode\n- Visual WYSIWYG editing\n- Full toolbar functionality\n- Real-time formatting\n\n### HTML Mode\n- Direct HTML code editing\n- Syntax-friendly monospace font\n- Ideal for manual HTML adjustments\n\n### CSS Mode\n- Automatically extracts inline styles from HTML\n- Displays styles in CSS format\n- Useful for viewing and understanding applied styles\n\n## Page View\n\nThe editor includes a **Page View** feature that renders content on a virtual piece of paper, similar to Microsoft Word:\n\n- **Paper Size**: 8.5\" x 11\" (standard US Letter size)\n- **Margins**: 1 inch on all sides\n- **Appearance**: Realistic paper with shadow effect\n- **Toggle**: Click the 📄 button in the toolbar to enable/disable\n\nThis feature provides a better visualization of how documents will look when printed and creates a more professional editing experience.\n\n```javascript\n// Enable page view (default)\nconst editor = new QuillKit('#my-editor', {\n  apiKey: 'ak_your_api_key_here',  // REQUIRED\n  pageView: true\n});\n\n// Disable page view\nconst editor = new QuillKit('#my-editor', {\n  apiKey: 'ak_your_api_key_here',  // REQUIRED\n  pageView: false\n});\n```\n\n## Custom HTML Snippets\n\nThe editor supports **Custom HTML Snippets**, allowing end users to define reusable HTML content that can be easily inserted via the toolbar.\n\n### How It Works\n\n1. **Define snippet groups** when initializing the editor\n2. Each group gets its own **⊕ button** in the toolbar\n3. Clicking the button opens a **modal** showing all snippets in that group\n4. Users can **preview** snippets and **insert** them with one click\n\n### Configuration\n\nAdd custom snippets through the `customSnippets` option:\n\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  apiKey: 'ak_your_api_key_here',  // REQUIRED\n  customSnippets: {\n    'math-components': [\n      {\n        name: 'Einstein Equation',\n        html: '<div style=\"text-align: center;\">E = mc²</div>'\n      },\n      {\n        name: 'Pythagorean Theorem',\n        html: '<div style=\"text-align: center;\">a² + b² = c²</div>'\n      }\n    ],\n    'text-templates': [\n      {\n        name: 'Info Box',\n        html: '<div style=\"background: #e7f3ff; border-left: 4px solid #007bff; padding: 15px;\"><strong>Note:</strong> Important information</div>'\n      },\n      {\n        name: 'Warning Box',\n        html: '<div style=\"background: #fff3cd; border-left: 4px solid #ffc107; padding: 15px;\"><strong>Warning:</strong> Please be careful</div>'\n      }\n    ]\n  }\n});\n```\n\n### Snippet Structure\n\nEach snippet object has two properties:\n- **`name`** (string): The display name shown in the snippet browser\n- **`html`** (string): The HTML content to insert when the snippet is selected\n\n### Use Cases\n\nCustom snippets are perfect for:\n- **Mathematical formulas** and equations\n- **Text templates** (headers, footers, callout boxes)\n- **Reusable components** (contact forms, cards, tables)\n- **Branded content** (company logos, disclaimers, signatures)\n- **Code blocks** or special formatting\n\n### Security Note\n\nCustom snippets are defined by developers during editor initialization and are treated as trusted content. Ensure that snippet HTML comes from trusted sources only, as it will be inserted directly into the editor without sanitization.\n\n## Icon Support\n\nQuillKit includes powerful icon support, allowing users to easily insert icons from popular icon libraries or custom icon URLs into their documents.\n\n### Features\n\n- **Bootstrap Icons**: Access to 1,800+ high-quality icons from the Bootstrap Icons library\n- **Font Awesome**: Access to 30,000+ icons from the world's most popular icon toolkit\n- **Custom Icons**: Use your own icon images via URLs\n- **Interactive Browser**: Beautiful modal interface to browse and select icons\n- **Tab Navigation**: Switch between different icon sources with ease\n- **Automatic CSS Loading**: Automatically injects required icon library CSS when needed\n\n### Configuration\n\nEnable icon support when initializing the editor:\n\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  apiKey: 'ak_your_api_key_here',  // REQUIRED\n  icons: {\n    enabled: true,          // Enable icon support (default: true)\n    bootstrap: true,        // Enable Bootstrap Icons (default: true)\n    fontAwesome: true,      // Enable Font Awesome (default: true)\n    autoLoadCss: true,      // Auto-inject CSS for icon libraries (default: true)\n    bootstrapCdn: 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css',\n    fontAwesomeCdn: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css',\n    customIcons: [          // Custom icon URLs (default: [])\n      {\n        name: 'My Logo',\n        url: 'https://example.com/logo.png'\n      },\n      {\n        name: 'Custom Icon',\n        url: 'https://example.com/icon.svg'\n      }\n    ]\n  }\n});\n```\n\n### Automatic CSS Loading\n\n**New in this version**: QuillKit now automatically injects the required CSS for icon libraries when you open the icon modal. This ensures icons render properly without requiring you to manually include CSS links in your HTML.\n\n**How it works:**\n- When you click the icon button (⭐) to open the icon modal, the editor checks if the required CSS is already loaded\n- If not loaded, it automatically injects the CSS into the document `<head>`\n- Smart duplicate detection prevents loading the same CSS multiple times\n- Works with both default CDN URLs and custom CDN URLs\n\n**Configuration options:**\n- **`autoLoadCss`** (default: `true`): Enable/disable automatic CSS injection\n- **`bootstrapCdn`**: Custom CDN URL for Bootstrap Icons CSS\n- **`fontAwesomeCdn`**: Custom CDN URL for Font Awesome CSS\n\n**To disable auto-loading** (if you prefer to manage CSS yourself):\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  icons: {\n    enabled: true,\n    autoLoadCss: false  // Disable automatic CSS injection\n  }\n});\n```\n\n### Icon Sources\n\n#### Bootstrap Icons\n\nBootstrap Icons are automatically loaded when you open the icon modal (if `autoLoadCss` is enabled).\n\nAlternatively, you can manually include the CDN in your HTML:\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css\">\n```\n\nBootstrap Icons are inserted as icon fonts:\n```html\n<i class=\"bi bi-heart\"></i>\n```\n\n#### Font Awesome\n\nFont Awesome CSS is automatically loaded when you open the icon modal (if `autoLoadCss` is enabled).\n\nAlternatively, you can manually include the CDN in your HTML:\n\n```html\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\">\n```\n\nFont Awesome icons are inserted as icon fonts:\n```html\n<i class=\"fas fa-coffee\"></i>\n```\n\n#### Custom Icons\n\nCustom icons are inserted as `<img>` tags with the URL you provide:\n\n```html\n<img src=\"https://example.com/icon.png\" alt=\"My Icon\" style=\"max-width: 32px; max-height: 32px; vertical-align: middle;\" />\n```\n\n### Usage\n\n1. Click the **⭐ icon button** in the toolbar\n2. Browse icons in the modal that appears\n3. Switch between tabs (Bootstrap Icons, Font Awesome, Custom Icons) if multiple sources are enabled\n4. Click on an icon to insert it at the cursor position\n\n### Examples\n\n**Enable only Bootstrap Icons:**\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  icons: {\n    enabled: true,\n    bootstrap: true,\n    fontAwesome: false,\n    customIcons: []\n  }\n});\n```\n\n**Enable only Font Awesome:**\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  icons: {\n    enabled: true,\n    bootstrap: false,\n    fontAwesome: true,\n    customIcons: []\n  }\n});\n```\n\n**Use only custom icons:**\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  icons: {\n    enabled: true,\n    bootstrap: false,\n    fontAwesome: false,\n    customIcons: [\n      { name: 'Logo', url: 'https://example.com/logo.png' },\n      { name: 'Badge', url: 'https://example.com/badge.svg' }\n    ]\n  }\n});\n```\n\n**Use custom CDN URLs:**\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  icons: {\n    enabled: true,\n    bootstrap: true,\n    fontAwesome: true,\n    autoLoadCss: true,\n    bootstrapCdn: 'https://your-cdn.example.com/bootstrap-icons.css',\n    fontAwesomeCdn: 'https://your-cdn.example.com/font-awesome.css'\n  }\n});\n```\n\n**Disable icon support:**\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  icons: {\n    enabled: false\n  }\n});\n```\n\n### Demo\n\nSee the `examples/icons-demo.html` and `examples/test-icon-css-injection.html` files for complete demos showcasing all icon features, including automatic CSS injection.\n\n## Image Support\n\nQuillKit provides comprehensive image functionality with resizing capabilities and dimension control.\n\n### Inserting Images\n\nClick the **🖼** button in the toolbar to open the image insertion dialog:\n\n1. Enter the image URL (HTTP, HTTPS, or Data URL)\n2. Optionally specify alternative text for accessibility\n3. Optionally specify width in pixels\n4. Optionally specify height in pixels\n5. Click Insert\n\n```javascript\n// You can also insert images programmatically\nimport * as SelectionUtils from './SelectionUtils';\n\n// Insert with URL only\nSelectionUtils.insertImage('https://example.com/image.jpg');\n\n// Insert with alt text\nSelectionUtils.insertImage('https://example.com/image.jpg', 'Description');\n\n// Insert with dimensions\nSelectionUtils.insertImage('https://example.com/image.jpg', 'Description', 400, 300);\n```\n\n### Resizing Images\n\nAfter inserting an image, you can resize it visually:\n\n#### Drag to Resize\n\n- Hover over any corner of an image to see resize handles\n- Click and drag any corner handle to resize the image\n- **Proportional Resizing**: Dragging maintains the original aspect ratio\n- **Minimum Size**: Images cannot be resized smaller than 50px\n\n#### Visual Feedback\n\n- Blue corner handles appear at all four corners: northwest, northeast, southwest, southeast\n- Handles are visible when hovering over or interacting with images\n- Cursor changes to indicate resize direction\n\n### Specify Dimensions\n\nWhen inserting an image, you can specify exact pixel dimensions:\n\n- **Width**: Enter the desired width in pixels\n- **Height**: Enter the desired height in pixels\n- **Leave blank**: Image will use its natural dimensions\n- **Specify one**: Aspect ratio is maintained automatically\n\n### Technical Details\n\n- Images are wrapped in a container div for resize handle positioning\n- Resize handles are automatically added via MutationObserver\n- All images get the `quillkit-image` class\n- Resizing maintains aspect ratio for proportional scaling\n- All image operations support undo/redo\n- Minimum dimensions ensure usability\n\n### Example\n\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  toolbar: true,\n  height: '500px'\n});\n\n// Images are inserted interactively via the UI\n// Resize handles appear automatically on all images\n```\n\n### Demo\n\nSee the `examples/image-resize-demo.html` file for a complete demo showcasing all image features.\n\n## Table Support\n\nQuillKit provides comprehensive table functionality with advanced resizing capabilities and customization options.\n\n### Creating Tables\n\nClick the **⊞** button in the toolbar to open the table creation dialog:\n\n1. Specify the number of rows (1-20)\n2. Specify the number of columns (1-20)\n3. Choose a custom border color using the color picker\n4. Click Insert\n\n### Resizing Tables\n\n#### Column Width Resizing\n\n- Hover over the right edge of any column (except the last one) to see the resize cursor\n- Click and drag to adjust the column width\n- Minimum column width is 50px\n\n#### Row Height Resizing\n\n- Hover over the bottom edge of any row (except the last one) to see the resize cursor\n- Click and drag to adjust the row height\n- Minimum row height is 30px\n\n#### Auto-Fit to Content\n\n- **Double-click** the right edge of any column to automatically fit the column width to its content\n- The editor calculates the maximum content width across all cells in the column\n\n### Custom Border Colors\n\nWhen creating a table, you can specify a custom border color:\n\n- Use the color picker in the table creation dialog\n- Default border color is #dddddd (light gray)\n- Border colors are preserved when adding new rows or columns\n\n### Table Manipulation\n\nUse the table manipulation buttons in the toolbar:\n\n- **↑+** Add Row Above - Insert a row above the current cursor position\n- **↓+** Add Row Below - Insert a row below the current cursor position\n- **←+** Add Column Left - Insert a column to the left of the cursor\n- **→+** Add Column Right - Insert a column to the right of the cursor\n- **↕−** Remove Row - Delete the row containing the cursor\n- **↔−** Remove Column - Delete the column containing the cursor\n- **⊠** Delete Table - Remove the entire table\n\n### Technical Details\n\n- Tables use `table-layout: fixed` for predictable resizing\n- Resize handles are automatically added via MutationObserver\n- Border colors are stored as data attributes on table elements\n- All table operations support undo/redo\n- Minimum dimensions ensure usability\n\n### Example\n\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  toolbar: true,\n  height: '600px'\n});\n\n// Tables are created interactively via the UI\n// Resize handles appear automatically when tables are inserted\n```\n\n### Demo\n\nSee the `examples/table-demo.html` and `examples/table-resize-demo.html` files for complete demos showcasing all table features.\n\n## Column Layout Support\n\nQuillKit supports multi-column layouts, similar to newspaper columns, allowing you to create professional-looking documents with text flowing across multiple columns.\n\n### Features\n\n- **Multiple Column Options**: Choose between 1-column (normal), 2-column, or 3-column layouts\n- **Automatic Text Flow**: Content flows naturally from one column to the next\n- **Visual Column Dividers**: Clear separation between columns with 1px borders\n- **Consistent Spacing**: 20px gap between columns for optimal readability\n- **Element Preservation**: Headings, images, tables, and paragraphs avoid breaking across columns\n\n### Using Column Layouts\n\nClick the column buttons in the toolbar:\n\n- **1 Col** - Single column layout (removes columns, returns to normal)\n- **2 Col** - Two-column layout\n- **3 Col** - Three-column layout\n\n### How It Works\n\nWhen you apply a column layout:\n\n1. Your content is wrapped in a column container\n2. CSS multi-column properties automatically flow the text\n3. Column dividers appear between columns\n4. Elements like headings and images stay intact (no awkward breaks)\n\n### Toggling Between Layouts\n\nYou can easily switch between different column layouts:\n\n- Apply 2 columns to your content\n- Switch to 3 columns for more compact layout\n- Return to 1 column for normal viewing\n\n### Programmatic Usage\n\n```javascript\n// Initialize editor (column support is enabled by default)\nconst editor = QuillKit.init('#my-editor', {\n  toolbar: true,\n  height: '600px'\n});\n\n// Access the column plugin\nconst columnPlugin = editor.pluginManager.get('column');\n\n// Apply 2-column layout programmatically\ncolumnPlugin.setColumnCount(2);\n\n// Apply 3-column layout\ncolumnPlugin.setColumnCount(3);\n\n// Remove columns (back to single column)\ncolumnPlugin.setColumnCount(1);\n```\n\n### Use Cases\n\nColumn layouts are perfect for:\n\n- **Newsletters**: Create professional company newsletters\n- **Magazine Articles**: Design magazine-style content\n- **Brochures**: Marketing materials with attractive layouts\n- **Academic Papers**: Conference proceedings with multi-column format\n- **Reports**: Make long reports more readable\n\n### Technical Details\n\n- Uses CSS `column-count`, `column-gap`, and `column-rule` properties\n- Content is wrapped in containers with class `atlara-column-container`\n- Automatic break-inside prevention for common elements\n- Full undo/redo support\n- Compatible with page view mode\n\n### Example\n\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  toolbar: true,\n  pageView: true,\n  height: '600px'\n});\n\n// Columns are applied interactively via toolbar buttons\n// Or programmatically using the column plugin\n```\n\n### Demo\n\nSee the `examples/column-demo.html` file for a complete interactive demo showcasing all column features.\n\n## Math Support\n\n### LaTeX\n\nClick the \"TeX\" button in the toolbar or use the API:\n\n```javascript\n// Renders using KaTeX\n// Example: E = mc^2\n```\n\n### MathML\n\nClick the \"ML\" button to insert MathML code directly:\n\n```xml\n<math>\n  <mrow>\n    <mi>E</mi>\n    <mo>=</mo>\n    <mi>m</mi>\n    <msup>\n      <mi>c</mi>\n      <mn>2</mn>\n    </msup>\n  </mrow>\n</math>\n```\n\n### Configuring Math Font Size\n\nYou can set a default font size for all mathematical expressions using the `mathFontSize` option:\n\n```javascript\nconst editor = new QuillKit('#my-editor', {\n  mathSupport: true,\n  mathFontSize: '20pt'  // All math will render at 20pt by default\n});\n```\n\nThe `mathFontSize` option accepts any valid CSS font-size value:\n- **Absolute sizes**: `'12pt'`, `'16pt'`, `'20pt'`, `'24pt'`\n- **Relative sizes**: `'1.2em'`, `'1.5em'`, `'120%'`\n- **Keywords**: `'larger'`, `'smaller'` (not recommended)\n\n**Notes:**\n- When `mathFontSize` is not specified (or set to `null`), math elements render at the browser's default size\n- Users can still change individual math element sizes using the Font Size dropdown in the toolbar\n- The default size only applies to newly inserted math and math loaded from content\n- Existing math elements with explicit font sizes are not overridden\n\n**Example:**\n\n```javascript\n// Create an editor with large math by default\nconst editor = new QuillKit('#scientific-doc', {\n  mathSupport: true,\n  mathFontSize: '24pt'  // Great for presentations\n});\n\n// Create an editor with compact math\nconst compactEditor = new QuillKit('#compact-doc', {\n  mathSupport: true,\n  mathFontSize: '12pt'  // Good for dense documentation\n});\n\n// Create an editor with proportional math sizing\nconst flexEditor = new QuillKit('#flex-doc', {\n  mathSupport: true,\n  mathFontSize: '1.5em'  // Scales with surrounding text\n});\n```\n\nSee `demo-math-font-size.html` for a complete working example.\n\n## Development\n\n### Build\n\n```bash\nnpm run build\n```\n\n### Run Tests\n\n```bash\nnpm test\n```\n\n### Lint Code\n\n```bash\nnpm run lint\n```\n\n### Development Mode (with watch)\n\n```bash\nnpm run dev\n```\n\n## Testing\n\nThe library includes a comprehensive test suite covering:\n- Initialization and configuration\n- Content management\n- Mode switching\n- Toolbar commands\n- History management (undo/redo)\n- Math support\n- Events and synchronization\n- CSS extraction\n\nRun tests with:\n\n```bash\nnpm test\n```\n\n## Browser Support\n\nQuillKit works in all modern browsers that support:\n- ES6+ JavaScript\n- ContentEditable API\n- Modern Selection API and Range API\n- DOMParser API\n- CSS Grid/Flexbox\n\nTested in:\n- Chrome/Edge 90+\n- Firefox 88+\n- Safari 14+\n\n## Security Considerations\n\nQuillKit is designed to work with HTML content and allows users to edit and render HTML directly. When using this library:\n\n- **Content Sanitization**: If you're accepting content from untrusted sources, you should sanitize the HTML before passing it to the editor to prevent XSS attacks. Consider using a library like [DOMPurify](https://github.com/cure53/DOMPurify) to sanitize user input.\n- **Trusted Content**: The library is best suited for environments where the content is created by trusted users (e.g., internal content management systems, personal note-taking apps).\n- **Server-Side Validation**: Always validate and sanitize content on the server side before storing or displaying it to other users.\n\nExample of safe usage with DOMPurify:\n\n```javascript\nimport DOMPurify from 'dompurify';\n\nconst editor = new QuillKit('#my-editor');\n\n// When loading untrusted content\nconst untrustedContent = getUserContent();\nconst cleanContent = DOMPurify.sanitize(untrustedContent);\neditor.setValue(cleanContent);\n\n// When saving content\nconst content = editor.getValue();\n// Send to server for validation and sanitization\n```\n\n## Examples\n\nSee the `examples/index.html` file for a complete demo showcasing all features.\n\n## Technical Details\n\n### Modern Selection API and Range API\n\nQuillKit uses the modern [Selection API](https://developer.mozilla.org/en-US/docs/Web/API/Selection) and [Range API](https://developer.mozilla.org/en-US/docs/Web/API/Range) for all text selection and manipulation operations. This ensures compatibility with current web standards and future browser versions.\n\nThe deprecated `document.execCommand()` API has been completely replaced with modern alternatives:\n- Text formatting (bold, italic, underline, strikethrough) uses direct DOM manipulation with Range objects\n- Block formatting uses Range-based node manipulation\n- Link and image insertion uses Range.insertNode()\n- HTML insertion uses document fragments with Range API\n\n## Known Limitations\n\n- **Browser Dialogs**: Link insertion currently uses browser `prompt()` dialogs. A future version may implement custom modal dialogs for better UX.\n\n## Frontend Platform\n\nQuillKit includes a complete web application frontend that provides a professional interface for users to manage their accounts, API keys, and test the editor! The frontend offers:\n\n### Features\n\n- **Landing Page**: Modern marketing site with features, pricing, and call-to-actions\n- **Authentication**: Sign in, sign up, password reset, and session management\n- **User Dashboard**: Overview of API usage, statistics, and quick actions\n- **API Key Management**: Create, view, copy, and delete API keys\n- **Editor Playground**: Live testing environment with plan-based feature access\n- **Settings & Billing**: Profile management, subscription plans, and preferences\n- **Responsive Design**: Mobile, tablet, and desktop optimized\n\n### Quick Start\n\n```bash\n# Navigate to frontend directory\ncd frontend\n\n# Serve the application\npython -m http.server 8080\n# Or using Node.js\nnpx http-server . -p 8080\n```\n\nThen open http://localhost:8080 in your browser.\n\n### Documentation\n\n- **Frontend Guide**: [frontend/README.md](frontend/README.md)\n- **Backend Integration**: Configure API endpoint in `frontend/js/api.js`\n- **Customization**: Modify CSS variables in `frontend/css/main.css` for branding\n\n### Screenshots\n\nSee the [frontend README](frontend/README.md) for screenshots of the landing page, dashboard, API keys management, playground, and settings.\n\n## Backend API\n\nQuillKit also includes a complete backend API system for productization and distribution! The backend provides:\n\n### Features\n\n- **API Key Management**: Create, manage, and track API keys with usage limits\n- **Subscription Plans**: Four-tier system (Free, Basic, Pro, Enterprise) with progressive features\n- **Feature Access Control**: Control which editor features are available based on subscription\n- **Payment Integration**: Stripe-hosted checkout pages for secure payments\n  - **NEW**: Pre-configured Stripe Products/Prices (recommended approach)\n  - See [STRIPE_SETUP.md](STRIPE_SETUP.md) for detailed setup instructions\n  - See [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) if upgrading from older version\n- **Usage Tracking**: Monitor API usage and enforce limits\n- **Webhook Support**: Automatic handling of Stripe payment events\n- **Admin Panel**: Comprehensive admin dashboard for managing users, viewing statistics, and controlling subscriptions\n\n### Quick Start\n\n```bash\n# Navigate to backend directory\ncd backend\n\n# Install dependencies\nnpm install\n\n# Configure environment\ncp .env.example .env\n# Edit .env with your configuration\n\n# Start the server\nnpm start\n```\n\nThe server will start on `http://localhost:3000`\n\n### Demo Pages Configuration\n\nAll demo pages included in the repository can automatically use an API key from an environment variable:\n\n1. Set the `ATLARA_DEMO_API_KEY` environment variable in your `.env` file:\n   ```bash\n   ATLARA_DEMO_API_KEY=ak_your_api_key_here\n   ```\n\n2. The `demo-api-key.js` file automatically loads this key from the environment variable or meta tags\n\n3. All demo HTML files (in root and `examples/` directory) are configured to use this environment variable via `window.QUILLKIT_DEMO_CONFIG?.apiKey` or `window.ATLARA_DEMO_API_KEY`\n\nThis makes it easy to run demo pages without manually entering API keys each time. The API key should be obtained from the QuillKit service.\n\n### HTML Template System\n\nAll demo and example pages use inline templates for consistent headers and footers:\n\n- **Shared Header**: Includes Google Tag Manager (GTM) scripts for analytics (Google Ads and Google Analytics)\n- **Shared Footer**: Consistent copyright and branding\n- **Template Location**: `templates/` directory (header.html and footer.html)\n- **Implementation**: Templates are inlined directly into HTML files to avoid MIME type issues\n\nThe header and footer content from `templates/header.html` and `templates/footer.html` is embedded directly in each page's HTML. This approach:\n- Eliminates MIME type errors from dynamic script loading\n- Ensures templates work in all deployment environments\n- Maintains consistency across all demo and frontend pages\n\nFor more details, see [`templates/README.md`](templates/README.md).\n\n### API Endpoints\n\n| Endpoint | Description |\n|----------|-------------|\n| `POST /api/keys` | Create a new API key |\n| `GET /api/keys` | List all API keys |\n| `GET /api/subscriptions` | Get subscription details |\n| `GET /api/subscriptions/plans` | View available plans |\n| `POST /api/payments/checkout` | Create Stripe checkout session |\n\n### Subscription Plans\n\n- **Free**: Basic editor, HTML mode (1K API calls/month)\n- **Basic**: + Rich text, toolbar (10K API calls/month) - $9/mo\n- **Pro**: + Math, tables, images (100K API calls/month) - $29/mo\n- **Enterprise**: + Custom plugins, unlimited API calls - $99/mo\n\n### Documentation\n\n- **Full API Documentation**: [backend/README.md](backend/README.md)\n- **Quick Start Guide**: [backend/QUICKSTART.md](backend/QUICKSTART.md)\n- **Admin Panel Guide**: [ADMIN_PANEL.md](ADMIN_PANEL.md)\n- **Implementation Summary**: [backend/IMPLEMENTATION_SUMMARY.md](backend/IMPLEMENTATION_SUMMARY.md)\n- **Examples**: See [backend/examples/](backend/examples/)\n\n### Integration Example\n\n```javascript\n// Create an API key\nconst response = await fetch('http://localhost:3000/api/keys', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({\n    name: 'My App',\n    userId: 'user_123',\n    plan: 'pro'\n  })\n});\n\nconst { data } = await response.json();\nconst apiKey = data.key;\n\n// Use the API key\nconst editor = QuillKit.init('#editor', {\n  apiKey: apiKey,\n  // Features will be automatically enabled based on plan\n});\n```\n\n## Future Enhancements\n\nPotential improvements for future versions:\n\n**Editor:**\n- Custom modal dialog for link insertion (images and math already have custom modals)\n- Drag-and-drop image upload with file handling\n- Image cropping and editing tools\n- More comprehensive CSS editing with syntax highlighting\n- Export to PDF or Word formats\n- Collaborative editing support\n- Webpack code splitting for dynamic plugin loading\n\n**Frontend Platform:**\n- Two-factor authentication\n- Email verification\n- Team/organization management\n- Usage analytics dashboard with charts\n- Webhook management UI\n- API documentation viewer\n- Dark mode theme\n- Internationalization (i18n)\n- Mobile app (React Native/Flutter)\n\n## CI/CD\n\nThis project uses GitHub Actions for continuous integration and deployment:\n\n### Continuous Integration (CI)\n\nThe CI workflow runs on every push and pull request to `main` and `develop` branches:\n- **Linting**: Checks code style with ESLint\n- **Testing**: Runs the full test suite with Jest\n- **Building**: Builds the production bundle with Webpack\n- **Multi-version Testing**: Tests against Node.js 16.x, 18.x, and 20.x\n- **Coverage**: Uploads code coverage to Codecov (optional)\n- **Artifacts**: Stores build artifacts for 7 days\n\n### NPM Publishing\n\nThe NPM publish workflow automatically publishes the package to the public NPM registry:\n- **Triggers**: Runs on every push to the `main` branch\n- **Pre-publish Checks**: Runs linting, tests, and build before publishing\n- **Version Check**: Automatically checks if the version already exists on NPM and only publishes new versions\n- **Registry**: Publishes to [npmjs.com](https://www.npmjs.com/package/quillkit)\n- **Provenance**: Uses NPM provenance for supply chain security\n- **Requirements**: Requires `NPM_TOKEN` secret to be configured in repository settings\n\n### Docker Build and Push\n\nThe Docker workflow automatically builds and publishes Docker images to GitHub Container Registry:\n- **Triggers**: Runs on pushes to `main`, version tags (`v*`), releases, and manual dispatch\n- **Multi-platform**: Builds for both `linux/amd64` and `linux/arm64`\n- **Caching**: Uses GitHub Actions cache for faster builds\n- **Tagging**: Automatically tags images with version numbers, branch names, and `latest`\n- **Registry**: Publishes to `ghcr.io/atlarafirm/quillkit`\n\n### Using the Docker Image\n\nPull and run the latest Docker image:\n\n```bash\ndocker pull ghcr.io/atlarafirm/quillkit:latest\ndocker run -p 8080:80 ghcr.io/atlarafirm/quillkit:latest\n```\n\nThen open http://localhost:8080/examples/ in your browser to see the demo.\n\n## License\n\nMIT\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n### Development Setup\n\n1. Clone the repository\n2. Install dependencies: `npm install`\n3. Run tests: `npm test`\n4. Build: `npm run build`\n5. Run development server: `npm run dev`\n\n### CI/CD Requirements\n\n- All pull requests must pass CI checks (linting, tests, build)\n- Code coverage should be maintained or improved\n- Docker builds are automatically triggered on merges to `main`\n- NPM publishing is automatically triggered on merges to `main` (only if version has changed)\n- Update package version in `package.json` before merging to `main` to publish a new version\n","readmeFilename":"README.md"}