diff --git a/gc-openai/gcoai-stream-loading-text-animation.php b/gc-openai/gcoai-stream-loading-text-animation.php
new file mode 100644
index 000000000..776194ee8
--- /dev/null
+++ b/gc-openai/gcoai-stream-loading-text-animation.php
@@ -0,0 +1,180 @@
+args = wp_parse_args( $args, array(
+ 'text' => 'Thinking...',
+ 'base_color' => '#000',
+ 'shimmer_color' => '#fff',
+ 'shimmer_duration' => '2.2s',
+ 'show_shimmer' => true,
+ 'show_spinner' => false,
+ 'spinner_size' => '24',
+ 'form_id' => null,
+ ) );
+
+ add_filter( 'gform_gcoai_field_loading_text', array( $this, 'filter_loading_text' ), 10, 3 );
+ add_action( 'gform_register_init_scripts', array( $this, 'register_init_script' ), 10, 3 );
+ add_action( 'wp_head', array( $this, 'output_styles' ) );
+ add_action( 'admin_head', array( $this, 'output_styles' ) );
+ }
+
+ public function register_init_script( $form, $field_values, $is_ajax ) {
+ if ( empty( $form['id'] ) ) {
+ return;
+ }
+
+ // If form_id is specified, only run scripts on those forms
+ if ( $this->args['form_id'] !== null ) {
+ $form_ids = is_array( $this->args['form_id'] ) ? $this->args['form_id'] : array( $this->args['form_id'] );
+ if ( ! in_array( $form['id'], $form_ids ) ) {
+ return;
+ }
+ }
+
+ $markup = $this->get_shimmer_markup();
+ $css = $this->get_styles_css();
+
+ $script = sprintf(
+ "(function($) {
+ var shimmerMarkup = %s;
+ var shimmerStyles = %s;
+
+ function addStylesToPage() {
+ if ( ! $('style.gw-gcoai-shimmer-style').length ) {
+ $('';
+
+ self::$styles_output = true;
+ }
+
+ public function get_shimmer_markup() {
+ $spinner = '';
+
+ if ( $this->args['show_spinner'] ) {
+ $spinner = sprintf(
+ '',
+ esc_attr( $this->args['spinner_size'] ),
+ esc_attr( $this->args['spinner_size'] ),
+ esc_attr( $this->args['base_color'] )
+ );
+ }
+
+ $text_class = $this->args['show_shimmer'] ? 'shimmer' : 'shimmer-text';
+
+ return sprintf(
+ '%s%s',
+ $spinner,
+ $text_class,
+ esc_html( $this->args['text'] )
+ );
+ }
+
+ public function filter_loading_text( $placeholder, $field, $form = null ) {
+ if ( ! class_exists( '\\GC_OpenAI\\Fields\\Stream' ) || ! $field instanceof \GC_OpenAI\Fields\Stream ) {
+ return $placeholder;
+ }
+
+ // If form_id is specified, only apply to those forms
+ if ( $this->args['form_id'] !== null ) {
+ $form_ids = is_array( $this->args['form_id'] ) ? $this->args['form_id'] : array( $this->args['form_id'] );
+ if ( $form && ! in_array( rgar( $form, 'id' ), $form_ids ) ) {
+ return $placeholder;
+ }
+ }
+
+ return $this->get_shimmer_markup();
+ }
+
+ private function get_styles_css() {
+ $base = esc_attr( $this->args['base_color'] );
+ $shimmer = esc_attr( $this->args['shimmer_color'] );
+ $dur = esc_attr( $this->args['shimmer_duration'] );
+
+ return
+ '.shimmer-wrapper { display: inline-flex; align-items: center; gap: 8px; } ' .
+ '.shimmer-spinner { flex-shrink: 0; } ' .
+ '.spinner-rotate { transform-origin: center; animation: spinner-rotation 2s linear infinite; } ' .
+ '.spinner-rotate circle { stroke-linecap: round; animation: spinner-stroke 1.5s ease-in-out infinite; } ' .
+ '@keyframes spinner-rotation { 100% { transform: rotate(360deg); } } ' .
+ '@keyframes spinner-stroke { 0% { stroke-dasharray: 0 150; stroke-dashoffset: 0; } 47.5% { stroke-dasharray: 42 150; stroke-dashoffset: -16; } 95%, 100% { stroke-dasharray: 42 150; stroke-dashoffset: -59; } } ' .
+ ".shimmer-text { display: inline-block; color: {$base}; line-height: 1.2; } " .
+ ".shimmer { display: inline-block; color: {$base}; line-height: 1.2; background: {$base} linear-gradient(to left, {$base}, {$shimmer} 50%, {$base}); background-position: -4rem top; background-repeat: no-repeat; background-size: 4rem 100%; -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; animation: shimmer {$dur} infinite; } " .
+ '@keyframes shimmer { 0% { background-position: -4rem top; } 70%, 100% { background-position: 12.5rem top; } }';
+ }
+}
+
+# Configuration
+
+new GCOAI_Loading_Animation( array(
+ 'text' => 'Thinking...',
+ 'base_color' => '#292929',
+ 'shimmer_color' => '#fff',
+ 'shimmer_duration' => '2.2s',
+ 'show_shimmer' => true,
+ 'show_spinner' => true,
+ 'spinner_size' => '16',
+ // 'form_id' => 123, // Uncomment and set to target specific form(s): 123 or array( 18, 22, 35 )
+) );